I'm currently doing a plugin on firefox, which should be very simple. But since I am new on that, some problems have occured:
The purpose is to bind an item on the context menu when user clicking on an image(). I want to manipulate this image a lot(some work like visual encryption) using a canvas, and display the result in a new panel or dialog.
To bind the new item onto the menu, following code is used.
cm.Item({
label: _("menu-label-encrypt"),
context: cm.SelectorContext("img"),
contentScriptFile: [
data.url('jquery.js'),
data.url('encrypt.menu.js'),
],
onMessage: function(cmd){
var cryptWorker = imgcrypt();
cryptWorker.key(cmd.password);
var ret = cryptWorker.encrypt( cmd.width, cmd.height, cmd.data);
console.log(ret.length);
},
});
My idea is to use the contextScriptFile encrypt.menu.js to inject a code into the page, and fetch the canvas data as an array, which will then be posted using self.postMessage to the addon and get processed:
self.on('click', function(node){
var canvasID = 'cache';
var img = $(node)[0];
$('<canvas>', {id: canvasID}).appendTo('body').hide();
var canvas = $('#' + canvasID)[0];
var ctx = canvas.getContext('2d');
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
var data = ctx.getImageData(0, 0, img.width, img.height).data,
dataAry = new Array(data.length);
for(var i=0; i<data.length; i++)
dataAry[i] = data[i];
var command = {
'password': 'test',
'width': img.width,
'height': img.height,
'data': dataAry,
};
self.postMessage(command);
});
and now the problem came to my surprise: when I tried this addon on some page hosted at localhost:4000, it works. On some real web page, it shows:
menu.js:14 - SecurityError: The operation is insecure.
I know that this may be caused by a violation of some same-origin policy, but this is a content-script injected by an addon. Is it also impossible to read the image data without some help of a external server, or am I doing something totally wrong?
Thank you.
I've not found any solution to this approach(that being said, to inject a content to get the data from an remotely retrived image). But the net/xhr in mozilla's sdk works at the side of addon well. Alternatively I have wrote a XHR working in main.js and used it as a proxy.
At the side of addon, this XMLHttpRequest have no same-domain restrictions and can be used to retrive the binary data of the image given by a URL. The retrived data can be returned in a form of ArrayBuffer by setting xhr.responseType='arraybuffer';
The URL given to the addon's XHR is simply the src of <IMG>, which is being clicked. The browser seems to be caching the image and using XHR to retrive this image is very fast and doesn't need another request to the server.
Related
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.
I have a canvas on one page that is built using fabricjs
can i send that canvas to another page so that it retains all its objects as it is with all their properties and attributes to be same aswell.
Sure you can, just export your canvas to JSON
var canvas = new fabric.Canvas('c');
data = JSON.stringify(canvas)
Now you can send that data using a post request or store it in a database or whatever you want.
canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));
Example: http://jsfiddle.net/P9cEf/3/
The example uses two canvases one page, but the concept is the same.
Edit:
To download the image client side
var canvas1 = document.getElementById("c");
var image = canvas1.toDataURL("image/png").replace("image/png", "image/octet-stream");
window.location.href = image;
I am trying to allow users upload their own images to the kineticJS stage through an input in the html. I prefer to keep all my code in a separate js file, here is what i have so far:
$(document).ready(function() {
var stage = new Kinetic.Stage({
container: 'container',
width: 900,
height: 500
});
var layer = new Kinetic.Layer();
});
function addImage(){
var imageObj = new Image();
imageObj.onload = function() {
var myImage = new Kinetic.Image({
x: 140,
y: stage.getHeight() / 2 - 59,
image: imageObj,
width: 106,
height: 118
});
layer.add(myImage);
stage.add(layer);
}
var f = document.getElementById('uploadimage').files[0];
var name = f.name;
var url = window.URL;
var src = url.createObjectURL(f);
imageObj.src = src;
}
How do I expose the stage to the addImage() method? It is out of its scope at the moment and I havent been able to figure out how to solve the problem as the canvas doesn't show in the html until something is added to it. I need these images to be added as layers for future manipulation so want to use kineticJS. Any suggestions would be much appreciated!
http://jsfiddle.net/8XKBM/12/
I managed to get your addImage function working by attaching an event to it. If you use the Firebug console in Firefox or just press Ctrl+Shift+J you can get javascript errors. It turns out your function was being read as undefined, so now the alert is working, but your image isn't added because they aren't stored anywhere yet, like on a server (must be uploaded somewhere first)
I used jQuery to attach the event as you should use that instead of onclick='function()'
$('#addImg').on('click', function() {
addImage();
});
and changed
<div>
<input type="file" name="img" size="5" id="uploadimage" />
<button id='addImg' value="Upload" >Upload</button>
</div>
What you would really want to do is have the user upload the photos (to the server) on the fly using AJAX, (available with jQuery, doesn't interfere with KineticJS). Then, on success, you can draw the photo onto the canvas using your function. Make sure to use:
layer.draw()
or
stage.draw()
at the end of the addImage() function so that the photo is drawn on your canvas, as the browser does not draw the image until after the page loads and img.src is defined at the end. So, this will basically just require things to be in correct order rather than being difficult.
So, step 1: upload using AJAX (to server), step 2: add to stage, step 3: redraw stage
I got this web service that gives me a (jpeg) image. What I want is take this image, convert it into a Data URI and display it on an HTML5 canvas, like that:
obj = {};
obj.xmlDoc = new window.XMLHttpRequest();
obj.xmlDoc.open("GET", "/cgi-bin/mjpegcgi.cgi?x=1",false, "admin", "admin");
obj.xmlDoc.send("");
obj.oCanvas = document.getElementById("canvas-processor");
obj.canvasProcessorContext = obj.oCanvas.getContext("2d");
obj.base64Img = window.btoa(unescape(encodeURIComponent( obj.xmlDoc.responseText )));
obj.img = new Image();
obj.src = 'data:image/jpeg;base64,' + obj.base64Img;
obj.img.src = obj.src
obj.canvasProcessorContext.drawImage(obj.img,0,0);
Unfortunately, this piece of code doesn't work; the image is not painted on the canvas at all (plus it seems to have width and height = 0, could it be not decoded correctly? I get no exceptions). img.src looks like data:image/jpeg;base64,77+977+977+977+9ABBKRklG....
Resolved: turns out I should have overridden the mime type with:
req.overrideMimeType('text/plain; charset=x-user-defined');
and set the response type with:
req.responseType = 'arraybuffer';
(see this. You should make an asynchronous request if you change the response type, too).
First you need to create an img element (which is hidden)
Then you do exactly what you have done except that you listen to your onload event on your img element.
When this event is launched you are able to get the width and height of your pictures so you can set your canvas to the same size.
The you can draw your image as you did in last line.
With firebugs net tab open i stated zooming in and out of google map and i found its making requests for the png images.
Ok with this i understood that we can request the images using the Ajax. but wait ? i always used to request html, jsox, txt and xml. Image ........ its strange for me ? Can some one please elaborate on this ?
Also i would like to know if text is downloaded we add it to some DOM element and it show up. How images retried from the ajax can be accessed ?
Any help or pointer will be great help :)
GMaps doesn't make XMLHttpRequests to get the images. They instead use a normal <img /> element that is injected via javascript.
They then use the "onload" and "onerror" events to detect if the image was loaded correctly.
They retry a download of the image if it times out. This is achieved by setting a timer and once it expires re-setting the src property of the image.
var tile = document.createElement('img');
var url = 'http://sometilehost/X.Y.Z.png';
var downloadTimeout = 10000; // Retry in 10 seconds
// Tile downloaded successfully
tile.onload = function (e) {
window.clearTimeout(tile.downloadTimer);
alert('tile downloaded');
};
// The tile couldn't be downloaded. 404, 503 ...
tile.onerror = function (e) {
window.clearTimeout(tile.downloadTimer);
alert('tile not downloaded');
};
// This will fire if the tile doesn't download in time
tile.downloadTimer = window.setTimeout(function () {
tile.src = url; // Start the download again
}, downloadTimeout);
tile.src = url;