I'm post a question here before( How to load an image in Dart ) and get answer, but same time get more questions.
Why I'm can draw image only this way:
image.on.load.add((e) {
context.drawImage(image, 0, 0);
});
but if I'm write something like:
ImageElement image = new ImageElement();
image.src = 'myImage.png';
context.drawImage(image, 0, 0);
^^^ this don't work and don't draw image. Why?
bool loaded = false;
ImageElement image = new ImageElement();
image.src = 'myImage.png';
image.on.load.add((e) {
loaded = true;
});
print(loaded); // on console get - false
^^^ why I'm get false? I'm created if image loaded variable loaded become true, but loaded don't become true.
Sorry for my bad English language. Thanks!
As for your first question, if you leave out
image.onLoad.listen((e) {
context.drawImage(image, 0, 0);
});
then no code will be executed when the image is loaded.
The on.load.add(...) construction basically assigns a function (often referred to as a handler) that will be executed when the browser loads the image. If no such function is assigned then nothing happens when the image is loaded and consequently context.drawImage(image, 0, 0) will not work.
Your second questions is related to this function. Loading an image is an asynchronous process, meaning your on load handler (on.load.add(...)) is first fired when the client has successfully loaded the image. This means that after assininging your on load handler execution continues to your next call: print(loaded) which will be false as the image is not loaded yet.
Newer image onload syntax:
readFile() {
ImageElement image = new ImageElement(src: "plant.png");
document.body.nodes.add(image);
image.onLoad.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: true
);
}
onData(Event e) {
print("success");
}
onError(Event e) {
print("error: $e");
}
onDone() {
print("done");
}
Related
The webapp I am developing is opened inside a webview via post. The post body parameters(contextual user inputs) are inserted in to the index.html.
So the repeat loads are failing because the contextual input is absent.
The official docs say that nothing can be done about it. It says all you can do now is go network first and enable navigation preload.
(https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload ---------
"This feature is intended to reduce navigation latency for developers who can't precache their HTML......")
Hence I am looking for a way to edit my cached index.html before is gets used. I want to insert the post body parameters in to the index.html. I am not able to find any documentation on editing the cache. Hence any help/inputs from community would be appreciated.
Workbox !== service worker. Workbox is built on top of service worker, but raw service workers give you full control over the request and response, so you can do pretty much whatever you want.
Editing a response
Here's how you might change the text of a response:
addEventListener('fetch', event => {
event.respondWith(async function() {
// Get a cached response:
const cachedResponse = await caches.match('/');
// Get the text of the response:
const responseText = await cachedResponse.text();
// Change it:
const newText = responseText.replace(/Hello/g, 'Goodbye');
// Serve it:
return new Response(newText, cachedResponse);
}());
});
There's a potential performance issue here, that you end up loading the full response into memory, and doing the replacement work, before you serve the first byte. With a little more effort, you can do the replacement in a streaming manner:
function streamingReplace(find, replace) {
let buffer = '';
return new TransformStream({
transform(chunk, controller) {
buffer += chunk;
let outChunk = '';
while (true) {
const index = buffer.indexOf(find);
if (index === -1) break;
outChunk += buffer.slice(0, index) + replace;
buffer = buffer.slice(index + find.length);
}
outChunk += buffer.slice(0, -(find.length - 1));
buffer = buffer.slice(-(find.length - 1));
controller.enqueue(outChunk);
},
flush(controller) {
if (buffer) controller.enqueue(buffer);
}
})
}
addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (!(url.origin === location.origin && url.pathname === '/sw-content-change/')) return;
event.respondWith((async function() {
const response = await fetch(event.request);
const bodyStream = response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(streamingReplace('Hello', 'Goodbye'))
.pipeThrough(new TextEncoderStream());
return new Response(bodyStream, response);
})());
});
Here's a live demo of the above.
Getting the POST params of a response
The other part you need, is getting the POST body of the response:
addEventListener('fetch', event => {
event.respondWith(async function() {
if (event.request.method !== 'POST') return;
const formData = await event.request.formData();
// Do whatever you want with the form data…
console.log(formData.get('foo'));
}());
});
See the MDN page for FormData for the API.
I am trying to improve a gnome-shell-extension by allowing retrieving of remote image (jpg) and set as icon for a certain widget.
Here is what I got so far, but it does not work, due to mismatch of data type:
// allow remote album art url
const GdkPixbuf = imports.gi.GdkPixbuf;
const Soup = imports.gi.Soup;
const _httpSession = new Soup.SessionAsync();
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
function getAlbumArt(url, callback) {
var request = Soup.Message.new('GET', url);
_httpSession.queue_message(request, function(_httpSession, message) {
if (message.status_code !== 200) {
callback(message.status_code, null);
return;
} else {
var albumart = request.response_body_data;
// this line gives error message:
// JS ERROR: Error: Expected type guint8 for Argument 'data'
// but got type 'object'
// getAlbumArt/<#~/.local/share/gnome-shell/extensions
// /laine#knasher.gmail.com/streamMenu.js:42
var icon = GdkPixbuf.Pixbuf.new_from_inline(albumart, true);
callback(null, icon);
};
});
Here is the callback:
....
log('try retrieve albumart: ' + filePath);
if(GLib.file_test(iconPath, GLib.FileTest.EXISTS)){
let file = Gio.File.new_for_path(iconPath)
let icon = new Gio.FileIcon({file:file});
this._albumArt.gicon = icon;
} else if (filePath.indexOf('http') == 0) {
log('try retrieve from url: ' + filePath);
getAlbumArt(filePath, function(code, icon){
if (code) {
this._albumArt.gicon = icon;
} else {
this._albumArt.hide();
}
});
}
....
My question is, how to parse the response, which is a jpg image, so that I can set the widget icon with it?
Thank you very much!
I was able achieve this by simply doing:
const St = imports.gi.St;
const Gio = imports.gi.Gio;
// ...
this.icon = new St.Icon()
// ...
let url = 'https://some.url'
let icon = Gio.icon_new_for_string(url);
this.icon.set_gicon(icon);
And it will automatically download it.
I had been struggling for hours with this issue until I finally figured out a way to do it with a local image cache (downloading the image and storing it in an icons/ folder). Then I tried this approach for fun (just to see what would happen, expecting it to fail miserably), and guess what? It just worked. This is not mentioned anywhere in the very scarce documentation I was able to find.
For anyone still having the same problem here is my solution:
_httpSession.queue_message(request, function(_httpSession, message) {
let buffer = message.response_body.flatten();
let bytes = buffer.get_data();
let gicon = Gio.BytesIcon.new(bytes);
// your code here
});
I'm building a hybrid app in ionic, which runs over cordova. I use the $cordovaCamera plugin to capture images from the phone, either by selecting from the phone's gallery or by using the camera to take a picture.
I then send that image using Restangular to my server, and when that action finishes, I want to display a status message on the screen.
My problem: All of the above works perfectly on Android. On iOS, it is working only when the image is selected from the gallery, but not when the image is directly captured from the phone. In that case, the image is correctly transferred to the server, the request returns a 201 Created just as it should - but the then() callback function is never entered.
If anyone can explain this behavior, that would be awesome...my second best would be to capture the image on the iPhone, save to gallery, and then attempt to retrieve the last saved image, but I haven't been able to figure out how to yet and I'd rather just get this working.
Update: I've narrowed it down to the Restangular part - if instead of calling the Restangular upload function, I use $http, the callback is triggered as expected and all is good...so that's what I'm going to do, but if anyone can tell me what the problem was I'd be grateful.
Relevant code:
/** cameraService functions **/
//when the user chooses to snap a picture from the camera
takePicture: function(){
var options = {
quality: 50,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions
};
return $cordovaCamera.getPicture(options).then(
function(imageData) {
return imageData;
},
function(err) {
console.log("error", err);
});
},
//when the user chooses to select image from the gallery
choosePicture: function(){
var options = {
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY
};
return $cordovaCamera.getPicture(options).then(
function(imageData) {
return imageData;
},
function(err) {
console.log("error", err);
});
},
uploadPicture: function(imageSource, caption, is_logo){
if (typeof is_logo == 'undefined') is_logo = false;
var upload_object = {
caption: caption,
source: imageSource,
is_logo: is_logo
};
//apiService is my wrapper for Restangular
return apiService.uploadFile(loadingService.getClientUrl('images'), upload_object);
},
/**apiService uploadFile - apparently the problem is here ***/
uploadFile: function(baseElem, object, route, path){
var headers = apiFunctions.setHeaders({'Content-Type': undefined});
//this DOES NOT WORK (on iPhone with image from camera) - request completes but callback not triggered
return Restangular.all(baseElem).customPOST(object, path, route, headers);
//this works fine:
return $http.post('https://api.mysite.dev/v1/clients/'+localStorageService.getValue('client_id')+'/images', JSON.stringify(object), {headers:headers}
);
},
/** controller functions **/
$scope.takePicture = function () {
cameraService.takePicture().then(function (imageData) {
$scope.data.imageSource = imageData;
});
};
$scope.choosePicture = function () {
cameraService.choosePicture().then(function (imageData) {
$scope.data.imageSource = imageData;
});
};
$scope.uploadPicture = function () {
cameraService.uploadPicture($scope.data.imageSource, $scope.data.caption)
.then(function (response) { //this is never entered if image is captured from camera on iPhone
$ionicScrollDelegate.scrollTop();
$scope.data.caption = '';
$scope.data.imageSource = '';
if (response.data.response.is_success.data_value == true) {
$scope.messages.success.push("Photo uploaded successfully");
} else {
$scope.messages.failure.push("Error uploading photo.");
}
});
}
I'm trying out Dart for the first time and I can't get the error handling to work for me. Here's some information about it.
Resources:
Gist with HTML, CSS and Dart: gist.github.com/enjikaka/8164610
ZIP with the project: ge.tt/6StW4cB1/v/0?c
JavaScript version on CodePen: codepen.io/enjikaka/pen/giurk
How I want it:
Making an instance of MinecraftSkin should throw an StateError if the image source returns a 403 error code. The exception should be handled in the generateHead() function where the instance of MineCraft skin is attempted to be made.
How it is:
If an image representing the skin of a MineCraft player does not exist (when the image source does not exist and returns 403) the code stops on line 22 (onError; where I throw the StateError) and prints to console "Breaking on exception: Bad state: User has no skin".
However, in the catch on generateHead, nothing gets executed. It doesn't print the StateError message when I prompt it to, neither does it insert the StateError message to the selected element in the DOM.
Code
import 'dart:html';
class MinecraftSkin {
String user;
CanvasElement ce = new CanvasElement();
void _generateCanvas(Event e) {
CanvasRenderingContext2D ctx = ce.getContext('2d');
ctx.imageSmoothingEnabled = false;
ctx.drawImageScaledFromSource((e.target as ImageElement),8,8,8,8,0,0,ce.width,ce.height);
}
CanvasImageSource getHead() => ce;
String name() => user;
MinecraftSkin(String minecraftUser, num size) {
user = (minecraftUser == null) ? 'Notch' : minecraftUser;
ce.width = size;
ce.height = size;
ImageElement img = new ImageElement()
..onLoad.listen(_generateCanvas)
..onError.listen((_) => throw new StateError('User has no skin'));
img.src = "http://s3.amazonaws.com/MinecraftSkins/"+user+".png";
}
}
void generateHead(Event e) {
MinecraftSkin ms;
try {
ms = new MinecraftSkin((querySelector('#userName') as InputElement).value, 128);
} on StateError catch(se) {
print(se.message);
querySelector('#status').text = se.message;
}
CanvasElement cems = ms.getHead();
cems.id = "mc" + ms.name();
cems.title = "mc" + ms.name();
document.body.append(cems);
querySelector('#status').text = "Got head!";
}
void main() {
querySelector('#generateHead').onClick.listen(generateHead);
}
Thanks in advance!
Sincerely, Jeremy
The image listeners (onLoad, onError) are asynchronous. The MincraftSkin instantiation is completed without any errors, and only after the image resource is loaded or an error is received, is the StateError thrown, probably several hundred milliseconds later. The constructor does not wait around to see if the image will properly load or not.
So, I've implemented plupload using flash runtime in MVC3.
It works perfectly, in the sense that it uploads using the correction Action and runs it all. However, I'd really like to be able to control the response, and handle it in plupload, but I can't seem to get any response through.
I've tried overriding fileUploaded, but I can't seem to get anything out of the arguments. I've tried return simple strings, json and what have you. I can't seem to get anything out on the client side. And of course being sent through flash, I can't even debug the requests with firebug :/
The same with the Error event, and throwing exceptions. It correctly interprets the exception as an error, but it's always that #IO ERROR with some code like 2038 or something coming out the other end. I can't show my exception string or anything at all. Can anyone help?
Bonus question: How would I send session/cookie data along with the plupload, so I can access the session in my action?
The following has worked for me:
[HttpPost]
public ActionResult Upload(int? chunk, string name)
{
var fileUpload = Request.Files[0];
var uploadPath = Server.MapPath("~/App_Data");
chunk = chunk ?? 0;
using (var fs = new FileStream(Path.Combine(uploadPath, name), chunk == 0 ? FileMode.Create : FileMode.Append))
{
var buffer = new byte[fileUpload.InputStream.Length];
fileUpload.InputStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, buffer.Length);
}
return Json(new { message = "chunk uploaded", name = name });
}
and on the client:
$('#uploader').pluploadQueue({
runtimes: 'html5,flash',
url: '#Url.Action("Upload")',
max_file_size: '5mb',
chunk_size: '1mb',
unique_names: true,
multiple_queues: false,
preinit: function (uploader) {
uploader.bind('FileUploaded', function (up, file, data) {
// here file will contain interesting properties like
// id, loaded, name, percent, size, status, target_name, ...
// data.response will contain the server response
});
}
});
As far as the bonus question is concerned I am willing to answer it by don't use sessions, as they don't scale well, but because I know that you probably won't like this answer you have the possibility to pass a session id in the request using the multipart_params:
multipart_params: {
ASPSESSID: '#Session.SessionID'
},
and then on the server perform some hacks to create the proper session.
Look here:
$("#uploader").pluploadQueue({
// General settings
runtimes: 'silverlight',
url: '/Home/Upload',
max_file_size: '10mb',
chunk_size: '1mb',
unique_names: true,
multiple_queues: false,
// Resize images on clientside if we can
resize: { width: 320, height: 240, quality: 90 },
// Specify what files to browse for
filters: [
{ title: "Image files", extensions: "jpg,gif,png" },
{ title: "Zip files", extensions: "zip" }
],
// Silverlight settings
silverlight_xap_url: '../../../Scripts/upload/plupload.silverlight.xap'
});
// Client side form validation
$('form').submit(function (e) {
var uploader = $('#uploader').pluploadQueue();
// Files in queue upload them first
if (uploader.files.length > 0) {
// When all files are uploaded submit form
uploader.bind('StateChanged', function () {
if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) {
$('form')[0].submit();
}
});
uploader.start();
} else {
alert('You must queue at least one file.');
}
return false;
});
And in Controller:
[HttpPost]
public string Upload( ) {
HttpPostedFileBase FileData = Request.Files[0];
if ( FileData.ContentLength > 0 ) {
var fileName = Path.GetFileName( FileData.FileName );
var path = Path.Combine( Server.MapPath( "~/Content" ), fileName );
FileData.SaveAs( path );
}
return "Files was uploaded successfully!";
}
That's all...No chunk is needed in Controller...