In my project, I'm trying to upload an image, read it as a dataURL and store it into my database.
my HTML ng-click's call these methods with either true or false as a parameter (one does body image and the other a profile image)
Depending on whether true or false is called, it changes the id's for the event.
For some reason, when its "true" everything works perfectly fine. The user can pick the image, it gets sent to the database properly, and the image is loaded properly.
However, when "false" is called, I get the error:
[$rootScope:inprog] $apply already in progress
and then the console.log() test "2222" does not get called.
Html
<img src="{{controller.myCause.causeImage}}" ng-click="controller.uploadImage('true')" alt="Your Image Here" class="cause-img" id="profileImage" style="width: 80%; height: 35%;" height="300" />
<input id="imageUpload" type='file'>
<img src="{{controller.myCause.causeThumbnailImage}}" ng-click="controller.uploadImage('false')" alt="Your Thumbnail Here" id="profileImageThumb" class="cause-img" style="width: 80%; height: 20%;" height="200" />
<input id="profileImageUpload" type='file'>
Client Side
/////////////////////////////////////
//Begin Uploading Cause Profile Image
public uploadImage(bool) {
if (bool === "true") {
$("#imageUpload").click();
this.imgUpload(bool);
} else {
$("#profileImageThumb").click();
this.imgUpload(bool);
}
}
public imgUpload(bool) {
console.log(bool);
var _this = this;
var id = "";
var imgId = "";
if (bool === "true") {
id = "#imageUpload";
imgId = "#profileImage";
} else {
id = "#profileImageUpload";
imgId = "#profileImageThumb";
}
console.log("111");
$(id).change(function () {
console.log("2222");
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.readAsDataURL(this.files[0]);
$(imgId).attr('src', this.files[0]);
_this.convertFileToBase64AndSet(this.files, bool);
}
});
};
convertFileToBase64AndSet(fileList: FileList, bool) {
var self = this;
if (fileList.length > 0) {
var reader = new FileReader();
reader.onloadend = (e: Event) => {
self.postProfileImage(reader.result, bool);
}
reader.readAsDataURL(fileList[0]);
}
}
public postProfileImage(file, bool) {
if (bool === "true") {
this.$http.put(`api/causes/profpic`, JSON.stringify(file)).then((res) => {
this.$state.reload();
});
} else {
this.$http.put(`api/causes/thumbpic`, JSON.stringify(file)).then((res) => {
this.$state.reload();
});
}
}
//End Cause Profile Image Upload
////////////////////////////////
Your click event is running digest cycle which is conflicting with current digest cycle.
you can use setTimeout()
Can you try this
public uploadImage(bool) {
if (bool === "true") {
setTimeout(function({$("#imageUpload").click();})
this.imgUpload(bool);
} else {
setTimeout(function({$("#profileImageThumb").click();})
this.imgUpload(bool);
}
}
Similar to #hasan's answer, I got to this one.
public uploadImage(bool) {
var self = this;
var id = "";
if (bool === "true") {
id = "#imageUpload";
} else {
id = "#profileImageUpload";
}
setTimeout( () => {
$(id).click();
self.imgUpload(bool);
});
}
Thank you Hasan for pointing me in the right direction. setTimeout does indeed work!
Related
I think my javascript in my php file is in conflict with my javascript file.
I have a script that checks of the image is smaller then 2MB and a script that shows the image you selected in a small version. But the second part does not work when the first script is active. how do I fix this?
script in HTML
<script>
window.onload = function() {
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
if(this.files[0].size > 2000000){
alert("File is too big!");
this.value = "";
};
};
var uploadField = document.getElementById("itemImages");
uploadField.onchange = function() {
if(this.files[0].size > 200){
alert("File is too big!");
this.value = "";
};
};
}
</script>
.js file
$("#frontImages").change(function () {
if ($('#frontImages').get(0).files.length > 0) {
$('#frontImages').css('background-color', '#5cb85c');
} else {
$('#frontImages').css('background-color', '#d9534f');
}
});
$("#itemImages").change(function () {
if ($('#itemImages').get(0).files.length > 0) {
$('#itemImages').css('background-color', '#5cb85c');
} else {
$('#itemImages').css('background-color', '#d9534f');
}
});
document.getElementById("frontImages").onchange = function () {
var x = document.getElementById('previewFrontImage');
x.style.display = 'block';
var reader = new FileReader();
reader.onload = function (e) {
document.getElementById("previewFrontImage").src = e.target.result;
};
reader.readAsDataURL(this.files[0]);
};
function previewImages() {
var $preview = $('#previewItemImages').empty();
if (this.files) $.each(this.files, readAndPreview);
function readAndPreview(i, file) {
var reader = new FileReader();
$(reader).on("load", function () {
$preview.append($("<img/>", {src: this.result, height: 100}));
});
reader.readAsDataURL(file);
}
}
$('#itemImages').on("change", previewImages);
I'm guessing that the conflict is between the html script and this
document.getElementById("frontImages").onchange = function ()
I also have a question how I can fix that there will be no small image when the image is too big
Your guess is correct, onchange is simply member variable of various elements, and thus
var uploadField = document.getElementById("frontImages");
uploadField.onchange = function() {
and
document.getElementById("frontImages").onchange = function ()
are setting this single variable (of frontImages), which will store one callback function at a time.
You could use addEventListener() instead, which maintains a list of event listeners, so there can be more than one. Modifying the lines to
var uploadField = document.getElementById("frontImages");
uploadField.addEventListener("change", function() {
and
document.getElementById("frontImages").addEventListener("change", function ()
will register both event listeners on frontImages, regardless of the order they are executed.
Side remark: when you have "nice" ids, document.getElementById() can be omitted, as elements with ids become variables (of window which is the global scope), and thus you could write frontImages.addEventListener(...). You still need the getter in various cases, like when a local variable shadows the id, or when it is not usable as variable identifier (like id="my-favourite-id" or id="Hello World")
I have 2 function that I am trying to run, one after another. For some reason they both run at the same time, but the second one does not load properly. Is there a way to run the first function wait then run the second function?:
//run this first
$('#abc').click(function() {
$('.test1').show();
return false;
});
//run this second
(function ($) {
"use strict";
// A nice closure for our definitions
function getjQueryObject(string) {
// Make string a vaild jQuery thing
var jqObj = $("");
try {
jqObj = $(string)
.clone();
} catch (e) {
jqObj = $("<span />")
.html(string);
}
return jqObj;
}
function printFrame(frameWindow, content, options) {
// Print the selected window/iframe
var def = $.Deferred();
try {
frameWindow = frameWindow.contentWindow || frameWindow.contentDocument || frameWindow;
var wdoc = frameWindow.document || frameWindow.contentDocument || frameWindow;
if(options.doctype) {
wdoc.write(options.doctype);
}
wdoc.write(content);
wdoc.close();
var printed = false;
var callPrint = function () {
if(printed) {
return;
}
// Fix for IE : Allow it to render the iframe
frameWindow.focus();
try {
// Fix for IE11 - printng the whole page instead of the iframe content
if (!frameWindow.document.execCommand('print', false, null)) {
// document.execCommand returns false if it failed -http://stackoverflow.com/a/21336448/937891
frameWindow.print();
}
// focus body as it is losing focus in iPad and content not getting printed
$('body').focus();
} catch (e) {
frameWindow.print();
}
frameWindow.close();
printed = true;
def.resolve();
}
// Print once the frame window loads - seems to work for the new-window option but unreliable for the iframe
$(frameWindow).on("load", callPrint);
// Fallback to printing directly if the frame doesn't fire the load event for whatever reason
setTimeout(callPrint, options.timeout);
} catch (err) {
def.reject(err);
}
return def;
}
function printContentInIFrame(content, options) {
var $iframe = $(options.iframe + "");
var iframeCount = $iframe.length;
if (iframeCount === 0) {
// Create a new iFrame if none is given
$iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>')
.prependTo('body')
.css({
"position": "absolute",
"top": -999,
"left": -999
});
}
var frameWindow = $iframe.get(0);
return printFrame(frameWindow, content, options)
.done(function () {
// Success
setTimeout(function () {
// Wait for IE
if (iframeCount === 0) {
// Destroy the iframe if created here
$iframe.remove();
}
}, 1000);
})
.fail(function (err) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", err);
printContentInNewWindow(content, options);
})
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function printContentInNewWindow(content, options) {
// Open a new window and print selected content
var frameWindow = window.open();
return printFrame(frameWindow, content, options)
.always(function () {
try {
options.deferred.resolve();
} catch (err) {
console.warn('Error notifying deferred', err);
}
});
}
function isNode(o) {
/* http://stackoverflow.com/a/384380/937891 */
return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string");
}
$.print = $.fn.print = function () {
// Print a given set of elements
var options, $this, self = this;
// console.log("Printing", this, arguments);
if (self instanceof $) {
// Get the node if it is a jQuery object
self = self.get(0);
}
if (isNode(self)) {
// If `this` is a HTML element, i.e. for
// $(selector).print()
$this = $(self);
if (arguments.length > 0) {
options = arguments[0];
}
} else {
if (arguments.length > 0) {
// $.print(selector,options)
$this = $(arguments[0]);
if (isNode($this[0])) {
if (arguments.length > 1) {
options = arguments[1];
}
} else {
// $.print(options)
options = arguments[0];
$this = $("html");
}
} else {
// $.print()
$this = $("html");
}
}
// Default options
var defaults = {
globalStyles: true,
mediaPrint: false,
stylesheet: null,
noPrintSelector: ".no-print",
iframe: true,
append: null,
prepend: null,
manuallyCopyFormValues: true,
deferred: $.Deferred(),
timeout: 750,
title: null,
doctype: '<!doctype html>'
};
// Merge with user-options
options = $.extend({}, defaults, (options || {}));
var $styles = $("");
if (options.globalStyles) {
// Apply the stlyes from the current sheet to the printed page
$styles = $("style, link, meta, base, title");
} else if (options.mediaPrint) {
// Apply the media-print stylesheet
$styles = $("link[media=print]");
}
if (options.stylesheet) {
// Add a custom stylesheet if given
$styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">'));
}
// Create a copy of the element to print
var copy = $this.clone();
// Wrap it in a span to get the HTML markup string
copy = $("<span/>")
.append(copy);
// Remove unwanted elements
copy.find(options.noPrintSelector)
.remove();
// Add in the styles
copy.append($styles.clone());
// Update title
if (options.title) {
var title = $("title", copy);
if (title.length === 0) {
title = $("<title />");
copy.append(title);
}
title.text(options.title);
}
// Appedned content
copy.append(getjQueryObject(options.append));
// Prepended content
copy.prepend(getjQueryObject(options.prepend));
if (options.manuallyCopyFormValues) {
// Manually copy form values into the HTML for printing user-modified input fields
// http://stackoverflow.com/a/26707753
copy.find("input")
.each(function () {
var $field = $(this);
if ($field.is("[type='radio']") || $field.is("[type='checkbox']")) {
if ($field.prop("checked")) {
$field.attr("checked", "checked");
}
} else {
$field.attr("value", $field.val());
}
});
copy.find("select").each(function () {
var $field = $(this);
$field.find(":selected").attr("selected", "selected");
});
copy.find("textarea").each(function () {
// Fix for https://github.com/DoersGuild/jQuery.print/issues/18#issuecomment-96451589
var $field = $(this);
$field.text($field.val());
});
}
// Get the HTML markup string
var content = copy.html();
// Notify with generated markup & cloned elements - useful for logging, etc
try {
options.deferred.notify('generated_markup', content, copy);
} catch (err) {
console.warn('Error notifying deferred', err);
}
// Destroy the copy
copy.remove();
if (options.iframe) {
// Use an iframe for printing
try {
printContentInIFrame(content, options);
} catch (e) {
// Use the pop-up method if iframe fails for some reason
console.error("Failed to print from iframe", e.stack, e.message);
printContentInNewWindow(content, options);
}
} else {
// Use a new window for printing
printContentInNewWindow(content, options);
}
return this;
};
})(jQuery);
How would I run the first one wait 5 or so seconds and then run the jquery print? I'm having a hard time with this. So the id would run first and then the print would run adter the id="abc" Here is an example of the code in use:
<div id="test">
<button id="abc" class="btn" onclick="jQuery.print(#test1)"></button>
</div>
If I understand your problem correctly, you want the jQuery click function to be run first, making a div with id="test1" visible and then, once it's visible, you want to run the onclick code which calls jQuery.print.
The very first thing I will suggest is that you don't have two different places where you are handling the click implementation, that can make your code hard to follow.
I would replace your $('#abc').click with the following:
function printDiv(selector) {
$(selector).show();
window.setTimeout(function () {
jQuery.print(selector);
}, 1);
}
This function, when called, will call jQuery.show on the passed selector, wait 1ms and then call jQuery.print. If you need the timeout to be longer, just change the 1 to whatever you need. To use the function, update your example html to the following:
<div id="test">
<button id="abc" class="btn" onclick="printDiv('#test1')"</button>
</div>
When the button is clicked, it will now call the previously mentioned function and pass it the ID of the object that you want to print.
As far as your second function goes, where you have the comment **//run this second**, you should leave that alone. All it does is extend you jQuery object with the print functionality. You need it to run straight away and it currently does.
I am cofused who I should use callbacks in this situation:
I and fire displayHighlights() function after highlightImages() is finished, so I don't need to use setTimeout()
First function is validation if images are not throwing errors, second is displaying only three of them.
<ul class="clear">
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
<li style="display : none" class="highlight-photos"><a class="highlight_photo"></a></li>
</ul>
function highlightImages() {
$(".highlight_photo").each(function() {
var fileName = $(this).data('url')
var image = new Image();
var that = $(this);
image.onerror = badImage;
image.src = fileName;
function badImage( event) {
var el = $(".highlight_photo[data-url='" + fileName+ "']");
el.parent().remove();
}
});
setTimeout(function(){displayHighlights();},500);
};
function displayHighlights() {
if ($(".highlight_photo").parent().length <= 3 ) {
$(".highlight-photos").show();
} else {
$("ul.clear li:lt(3)").addClass("visible");
$(".highlight-photos").not(".visible").remove();
$("ul.clear li:lt(3)").show();
}
}
One solution would be to add an onlaod event to all of the images.
So when adding the image increase a counter like : images loading.
Before!!! adding src attribute to the image attach an onload handler, that will decrease the same counter.
var images_loading = 0; //setting the counter
function highlightImages() {
$(".highlight_photo").each(function() {
var fileName = $(this).data('url')
var image = new Image();
var that = $(this);
image.onerror = badImage;
images_loading++; //yuou are loading one more image
image.onload = function(){images_loading--; //when it is loaded, it is ok}
image.src = fileName;
function badImage( event) {
images_loading--; //if it fails, remove from loading
var el = $(".highlight_photo[data-url='" + fileName+ "']");
el.parent().remove();
}
});
var can_highlight = false;
while(!can_highlight){
if(images_loading == 0){
//all the images are loaded
can_highlight = true;
displayHighlights();
} else {
setTimeout(1) //sparing cpu
}
}
};
I would try using a promise for your each statement.
For example:
JavaScript:
$.when(highlightImages()).done(function () {
console.log('highlightImages complete!');
displayHighlights();
});
function highlightImages() {
$(".highlight_photo").each(function (key, val) {
console.log(val);
});
};
function displayHighlights() {
console.log('displayHighlights called');
};
JSFiddle
You can also use jquery deferred function as described here
best
M
I'm maintaining an existing app (cannot change server-side code, only client-side js) and was asked to add the capability to upload files from clipboard.
At present moment there is a standard file-selection form
<form id="file_form" name="file_form" enctype="multipart/form-data" method="post" action="/upload">
<label for="selector">
File Location
</label>
<input type="file" id="selector" name="selector" title="Choose file">
<input type="submit" id="submit" value="ОК">
<input type="button" id="cancel" onclick="cancel()" value="Cancel">
</form>
So what I need is a way to fill input#selector with the file from clipboard. I don't need progress bars, preview, image crop, etc... as simple as possible, but remember that I cannot change anything on the server.
Is there solution that would work in Chrome, FF and IE?
Most things I googled were either full of excessive functionality and required a lot of external js or server-side code changes or didn't work in anything other than Chrome...
Your can refere to this site: https://www.pastefile.com
the core code paste.js such as
(function ($) {
'use strict';
var readImagesFromEditable = function (element, callback) {
setTimeout(function () {
$(element).find('img').each(function (i, img) {
getImageData(img.src, callback);
});
}, 1);
};
var getImageData = function (src, callback) {
var loader = new Image();
loader.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = loader.width;
canvas.height = loader.height;
var context = canvas.getContext('2d');
context.drawImage(loader, 0, 0, canvas.width, canvas.height);
try {
var dataURL = canvas.toDataURL('image/png');
if (dataURL) {
callback({
dataURL: dataURL
});
}
} catch (err) {}
};
loader.src = src;
};
$.paste = function () {
var handler = function (e) {
var pasteBoard = $(this);
var trigger = function (event, data) {
if (arguments.length > 1)
return pasteBoard.trigger(event, data);
return function (data) {
return pasteBoard.trigger(event, data);
};
};
var clipboardData, text;
if (e.originalEvent) {
clipboardData = e.originalEvent.clipboardData;
if (clipboardData.items) {
var items = clipboardData.items;
// Copy-paste on OSX
if (items.length === 2) {
// If a user pastes image data, there are 2 items: the file name (at index 0) and the file (at index 1)
// but if the user pastes plain text or HTML, /index 0/ is the data with markup and /index 1/ is the plain, unadorned text.
if (items[0].kind === 'string' && items[1].kind === 'file' && items[1].type.match(/^image/)) {
// If the user copied a file from Finder (OS X) and pasted it in the window, this is the result. This is also the result if a user takes a screenshot and pastes it.
// Unfortunately, you can't copy & paste a file from the desktop. It just returns the file's icon image data & filename (on OS X).
} else if (items[0].kind === 'string' && items[1].kind === 'string') {
// Get the plain text
items[0].getAsString(trigger('pasteText'));
}
} else {
var item = items[0];
if (!item) return;
if (item.type.match(/^image\//)) {
trigger('pasteImage', item.getAsFile());
} else if (item.type === 'text/plain') {
item.getAsString(trigger('pasteText'));
}
}
} else {
if (clipboardData.types.length) {
text = clipboardData.getData('Text');
trigger('pasteText', text);
} else {
readImagesFromEditable(pasteBoard, trigger('pasteImage'));
}
}
} else if ((clipboardData = window.clipboardData)) {
text = clipboardData.getData('Text');
if (text) {
trigger('pasteText', text);
} else {
readImagesFromEditable(pasteBoard, trigger('pasteImage'));
}
}
setTimeout(function() {
pasteBoard.empty();
}, 1);
};
return $('<div/>')
.prop('contentEditable', true)
.css({
width: 1,
height: 1,
position: 'fixed',
left: -10000,
overflow: 'hidden'
})
.on('paste', handler);
};
})(jQuery);
it is not so hard to understand.
After a user uploads a file we have to do some additional processing with the images such as resizing and upload to S3. This can take up to 10 extra seconds. Obviously we do this in a background. However, we want to show the user the result page immediately and simply show spinners in place until the images arrive in their permanent home on s3.
I'm looking for a way to detect that a certain image failed to load correctly (404) in a cross browser way. If that happens, we want to use JS to show a spinner in it's place and reload the image every few seconds until it can be successfully loaded from s3.
Handle the <img> element's onerror event.
First option:
<img src="picture1.gif" onerror="this.onerror=null;this.src='missing.gif';"/>
Second option:
<html>
<head>
<script type="text/javascript">
function ImgError(source){
source.src = "/noimage.gif";
source.onerror = "";
return true;
}
</script>
</head>
<body>
<img src="image_example1.jpg" onerror="ImgError(this)" />
</body>
</html>
PS: it's pure javascript! you don't need any libraries. (Vanilla JS)
Example in Fidler
https://jsfiddle.net/dorathoto/8z4Ltzp8/71/
From: http://lucassmith.name/2008/11/is-my-image-loaded.html
// First a couple helper functions
function $(id) {
return !id || id.nodeType === 1 ? id : document.getElementById(id);
}
function isType(o,t) { return (typeof o).indexOf(t.charAt(0).toLowerCase()) === 0;}
// Here's the meat and potatoes
function image(src,cfg) { var img, prop, target;
cfg = cfg || (isType(src,'o') ? src : {});
img = $(src);
if (img) {
src = cfg.src || img.src;
} else {
img = document.createElement('img');
src = src || cfg.src;
}
if (!src) {
return null;
}
prop = isType(img.naturalWidth,'u') ? 'width' : 'naturalWidth';
img.alt = cfg.alt || img.alt;
// Add the image and insert if requested (must be on DOM to load or
// pull from cache)
img.src = src;
target = $(cfg.target);
if (target) {
target.insertBefore(img, $(cfg.insertBefore) || null);
}
// Loaded?
if (img.complete) {
if (img[prop]) {
if (isType(cfg.success,'f')) {
cfg.success.call(img);
}
} else {
if (isType(cfg.failure,'f')) {
cfg.failure.call(img);
}
}
} else {
if (isType(cfg.success,'f')) {
img.onload = cfg.success;
}
if (isType(cfg.failure,'f')) {
img.onerror = cfg.failure;
}
}
return img;
}
And here how to use it:
image('imgId',{
success : function () { alert(this.width); },
failure : function () { alert('Damn your eyes!'); },
});
image('http://somedomain.com/image/typooed_url.jpg', {
success : function () {...},
failure : function () {...},
target : 'myContainerId',
insertBefore : 'someChildOfmyContainerId'
});
just bind the attr trigger on the error event.
$(myimgvar).bind('error',function(ev){
//error has been thrown
$(this).attr('src','/path/to/no-artwork-available.jpg');
}).attr('src',urlvar);
This worked for me (mine is in coffeescript). You'll need to replace with a spinner instead, of course.
checkImages = ->
$("img").each ->
$(this).error ->
$(this).attr("src", "../default/image.jpg")
$(document).on('page:load', checkImages)
I'm guessing the javascript equivalent is something like
function checkImages() {
$("img").each(function() {
$(this).error(function() {
$(this).attr("src", "../default/image.jpg");
});
});
};
$(document).on("page:load", checkImages);
I just did
if ($('#img')[0].naturalWidth > 0) {
as i noticed there was no naturalWidth if the image 404'd.
However, i can understand wanting to use a method above.