This title may not be 100% accurate with regards to my question, and I apologize beforehand if that's the case. Here's my problem, I found this cool applet that allows you to paste an image from your clipboard without the need for an upload. However, my site does not use explicitly written HTML, so I don't know how to insert this applet. For example, here is my code for an Upload button, the kind you would use normally to input an image:
getUploadControl: function(data) {
var uploadData = data.uploadData || {},
resultHandler = generateCallbackHandler({
success: data.onSuccess,
error: data.onError,
busyMethod: noop
}),
eventHandler = function(e) {
var jsonResponse = parseJSON(e.XMLHttpRequest.responseText);
resultHandler(jsonResponse);
};
return extend(true, {
type: "upload",
options: {
async: {
saveUrl: window.CaledonianAPIWebServiceRoot + "WriteFile.aspx"
},
multiple: false,
upload: function(e) {
var ud = copyNestedProperties({}, uploadData);
api.addAuthToData(ud);
e.data = ud;
},
success: eventHandler,
error: eventHandler
}
}, data);
},
I would like to make a similar button like this using the SUPA Applet. Here is a demo of the applet.
I have downloaded the source code for Supa.js, here it is, not very long.
function Supa() {
this.ping = function (supaApplet) {
try {
// IE will throw an exception if you try to access the method in a
// scalar context, i.e. if( supaApplet.pasteFromClipboard ) ...
return supaApplet.ping();
} catch (e) {
return false;
}
};
this.ajax_post = function (actionUrl, bytes, fieldname_filename, filename, params) {
// some constants for the request body
//FIXME: make sure boundaryString is not part of bytes or the form values
var boundaryString = 'AaB03x' + parseInt(Math.random() * 9999999, 10),
boundary = '--' + boundaryString,
cr = '\r\n',
body,
i,
isAsync,
xrequest;
// sanity checks
if (!fieldname_filename || fieldname_filename === "") {
throw "Developer Error: fieldname_filename not set or empty";
}
if (!filename || filename === "") {
throw "Filename required";
}
// build request body
body = '';
body += boundary + cr;
if (isArray(params)) {
for (i = 0; i < params.length; i += 1) {
body += "Content-disposition: form-data; name=\"" + escape(params[i].name) + "\";" + cr;
body += cr;
body += encodeURI(params[i].value) + cr;
body += boundary + cr;
}
}
// add the screenshot as a file
body += "Content-Disposition: form-data; name=\"" + escape(fieldname_filename) + "\"; filename=\"" + encodeURI(filename) + "\"" + cr;
body += "Content-Type: application/octet-stream" + cr;
body += "Content-Transfer-Encoding: base64" + cr;
body += cr;
body += bytes + cr;
// last boundary, no extra cr here!
body += boundary + "--" + cr;
// finally, the Ajax request
isAsync = false;
xrequest = new XMLHttpRequest();
xrequest.open("POST", actionUrl, isAsync);
// set request headers
xrequest.setRequestHeader("Content-Type", "multipart/form-data; charset=UTF-8; boundary=" + boundaryString);
xrequest.send(body);
return xrequest.responseText;
};
}
function supa() {
return new Supa();
}
My question is how do I make a similar widget to use this library for the pasting process? The paste() method can be found in the source HTML for the demo I referenced earlier, it's not in Supa.js, the main file. Plus, I'm not using HTML, so I might have to combine all that code into one? Anyway, any help is appreciated, to get me going on the right track.
Related
I am building a chrome-extension and would like to enable users to create a new spreadsheet. Per the documentation. Here is how I am attempting to do it:
Sweeper.GSheets.prototype.createSpreadsheet = function(title) {
var data, headers, callback, delimiter, boundary;
callback = function(status, responseText){
console.log(responseText);
};
title = title + ' --CD.xls';
delimiter = '\r\n';
boundary = '--Sweeper';
data = boundary + delimiter + 'Content-Type: application/json; charset=UTF-8' + delimiter + delimiter + '{' + delimiter + '"title"' + ': ' + '"' + title + '"' + delimiter + '}' + delimiter + delimiter + boundary + delimiter + 'Content-Type: application/vnd.google-apps.spreadsheet' + delimiter + ' ' + delimiter + boundary + '--';
headers = { 'Content-Type': 'multipart/related; boundary="Sweeper"', "Convert": "true" };
this.makeRequest('post','https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart', callback, data, headers );
};
Sweeper.GSheets.prototype.makeRequest = function(method, url, callback, opt_data, opt_headers) {
setTimeout(function(){
var data = opt_data || null;
var headers = opt_headers || {};
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
// Include common headers (auth and version) and add rest.
xhr.setRequestHeader('Authorization', 'Bearer ' + State.authToken());
for (var key in headers) {
xhr.setRequestHeader(key, headers[key]);
}
xhr.onload = function(e) {
this.lastResponse = e.srcElement;
callback(e.srcElement.status, e.srcElement.responseText);
}.bind(this);
xhr.onerror = function(e) {
Sweeper.log(this, this.status, this.response,
this.getAllResponseHeaders());
};
xhr.send(data);
}.bind(this), 500);
};
This actually creates a document that Google Drive recognizes as a spreadsheet, however, I cannot view the sheet. When I click on it, I receive a generic error.
Does anyone know how to make this request so that it creates an empty spreadsheet?
I am writing a small Cordova (PhoneGap) app. that is sending an image from a file input - using a post method. It works fine in my Android device, but fails in both broswer and Ripple emulator. Here is the code:
function queryImageByData(dataURL) {
var imgType = dataURL.substring(5, dataURL.indexOf(";"));
var imgExt = imgType.split("/")[1];
var imgData = atob(dataURL.substring(dataURL.indexOf(",") + 1));
var filenameTimestamp = (new Date().getTime());
var separator = "----------12345-multipart-boundary-" + filenameTimestamp;
var formData = "--" + separator + "\r\n" +
"Content-Disposition: file; name=\"file\"; filename=\"snapshot_" + filenameTimestamp + "." + imgExt + "\"\r\n" +
"Content-Type: " + imgType + "\r\nContent-Transfer-Encoding: base64" + "\r\n\r\n" + imgData + "\r\n--" + separator + "\r\n";
var xhr = new XMLHttpRequest();
xhr.sendAsBinary = function (data) {
var arrb = new ArrayBuffer(data.length);
var ui8a = new Uint8Array(arrb, 0);
for (var i = 0; i < data.length; i++) {
ui8a[i] = (data.charCodeAt(i) & 0xff);
}
var blob = new Blob([arrb]);
this.send(blob);
};
xhr.open("POST", "https:/my_endpoint_here", true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
parseResult(xhr.responseText);
}
else {
onFailedResponse(xhr.responseText);
}
}
};
xhr.setRequestHeader("Content-type", "multipart/form-data; boundary=" + separator);
xhr.sendAsBinary(formData);
}
The error I get is:
Error: MultipartParser.end(): stream ended unexpectedly: state = HEADER_FIELD_START
at MultipartParser.end
EDIT:
I have a problem also with a get method. It fails on Ripple/Browser but runs OK on the device. here is some sample code:
var url = document.getElementById("urlInput").value;
var query = "my_url_here";
var jqxhr = $.ajax(query)
.done(function (data) {
alert("success" + data);
})
.fail(function (data) {
alert("error" + data);
})
Well I found the core issue, which cross domain calls.
The browsers do not allow it, and apperently so does Ripple emulator,
but mobile devices do allow it.
Now I just need to figure out how to make it work using CORS.
I am using a javascript library that generates annotations within an ePub. I am hoping to use HTML5 localStorage for storing the annotation data.
Is there a way to have an endpoint url associated with localStorage?
In the hook that invokes the library there is a variable for 'server', which is currently set to a url for the local server, port 5000.
I am hoping to replace that with some pointer to localStorage, but I'm not sure what endpoint would be involved. Seems like the only method for adding in information would be the method "localStorage.setItem", which in turn needs a variable name as an argument, so it wouldn't be appropriate as an endpoint.
The unadulterated hook, before I customized it is in this github repo,
My current version after editing, (which isn't functioning) is as follows:
EPUBJS.Hooks.register("beforeChapterDisplay").annotate = function(callback, chapter){
var chap = chapter.bodyEl,
server = localStorage;
files = [
EPUBJS.filePath + "annotator-full.js"
];
console.log(files);//show the pathways defined above
EPUBJS.core.load(files, chapter.doc.head);
$(chapter.doc.body).annotator().annotator('setupPlugins', {}, {
Filter:false,
Store: {
annotationData: {
'uri': chapter.path
},
loadFromSearch: {
'limit': 100,
'uri': chapter.path
}
}
});
Util.mousePosition = function(e, offsetEl) {
var offset;
offset = $(offsetEl).position();
return {
top: e.pageY,
left: e.pageX
};
};
devAnnotator = new Annotator(chapter.doc.body)
// .addPlugin('Auth', {
// tokenUrl: 'http://annotateit.org/api/token',//'http://localhost:5001/api/token'
// })
// .addPlugin('Unsupported')
// .addPlugin('AnnotateItPermissions')
// .addPlugin('Store', {
// prefix: 'http://annotateit.org/api',//'http://localhost:5000',
// loadFromSearch: {
// uri: chapter.currentChapter.href
// },
// annotationData: {
// uri: chapter.currentChapter.href
// }
// });
// devAnnotator.plugins.Auth.withToken(function (tok) {
// console.log(tok);
// })
// EPUBJS.core.addCss("../libs/annotator/css/annotator.css", false, chapter.doc.head);
if(callback) callback();
function annotate(){
// EPUBJS.core.addCss("css/annotator.css", false, chapter.doc.head);
var s = document.createElement("script");
s.type = 'text/javascript';
console.log(jQuery);
var a = "jQuery.migrateTrace = false;";
a += "console.log(document.getElementById('c001p0002').getBoundingClientRect());";
a += "var content = $('body').annotator().annotator('setupPlugins', {}, {Filter:false});";
//-- Use Local Server:
a += "var content = $('body').annotator(),";
a += " server = '" + server + "';";
a += " path = '" + chapter.path + "';";
a += " content.annotator('addPlugin', 'Store', {";
// The endpoint of the store on your server.
a += " prefix: server,";
// Attach the uri of the current page to all annotations to allow search.
a += " annotationData: {";
a += " 'uri': path";
a += " }";
// This will perform a search action rather than read when the plugin
// loads. Will request the last 20 annotations for the current url.
// eg. /store/endpoint/search?limit=20&uri=http://this/document/only
a += ","
a += " loadFromSearch: {";
a += " 'limit': 20,";
a += " 'uri': path";
a += " }";
a += "});";
s.innerHTML = a;
chapter.doc.body.appendChild(s);
if(callback) callback();
}
}
Just to close the loop on this, solution:
The file isn't an actual ePub, it is a "shell" for ingesting an ePub with ePub.js and then displaying it in a custom way...
If I dynamically insert a form object into a page, submit and remove the form and it works fine.
Here is an example of the form code:
<form target="_blank" enctype="multipart/form-data"
action="https://www.example.com/" method="POST">
<input value="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" name="image_content" type="hidden">
<input value="" name="filename" type="hidden">
<input value="" name="image_url" type="hidden">
</form>
When I try to do the same process with loadOneTab(), result POST is not exactly the same and therefore the result is not the same as above.
On checking the headers, "some value" is not sent fully (gets cropped) and it sets Content-Length: 0.
I must be missing something.
let postStream = Components.classes['#mozilla.org/network/mime-input-stream;1']
.createInstance(Components.interfaces.nsIMIMEInputStream);
postStream.addHeader('Content-Type', 'multipart/form-data');
postStream.addHeader('filename', '');
postStream.addHeader('image_url', '');
postStream.addHeader('image_content', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==');
postStream.addContentLength = true;
window.gBrowser.loadOneTab('https://www.example.com/',
{inBackground: false, postData: postStream});
Note: image_content value is 'data:image/png;base64' Data URI
NoScript causes issues with sending form and XSS and I prefer to use loadOneTab for the inBackground
Normally, one would use FormData to compose the postData of a request, but unfortunately we cannot do so here, as there is currently no way to get the stream (and other information) from a FormData instance (nsIXHRSendable is not scriptable, unfortunately), so we'll have to create a multipart/form-data stream ourselves.
Since it is likely you'll want to post some file data as well, I added file uploads as well. ;)
function encodeFormData(data, charset) {
let encoder = Cc["#mozilla.org/intl/saveascharset;1"].
createInstance(Ci.nsISaveAsCharset);
encoder.Init(charset || "utf-8",
Ci.nsISaveAsCharset.attr_EntityAfterCharsetConv +
Ci.nsISaveAsCharset.attr_FallbackDecimalNCR,
0);
let encode = function(val, header) {
val = encoder.Convert(val);
if (header) {
val = val.replace(/\r\n/g, " ").replace(/"/g, "\\\"");
}
return val;
}
let boundary = "----boundary--" + Date.now();
let mpis = Cc['#mozilla.org/io/multiplex-input-stream;1'].
createInstance(Ci.nsIMultiplexInputStream);
let item = "";
for (let k of Object.keys(data)) {
item += "--" + boundary + "\r\n";
let v = data[k];
if (v instanceof Ci.nsIFile) {
let fstream = Cc["#mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fstream.init(v, -1, -1, Ci.nsIFileInputStream.DEFER_OPEN);
item += "Content-Disposition: form-data; name=\"" + encode(k, true) + "\";" +
" filename=\"" + encode(v.leafName, true) + "\"\r\n";
let ctype = "application/octet-stream";
try {
let mime = Cc["#mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
ctype = mime.getTypeFromFile(v) || ctype;
}
catch (ex) {
console.warn("failed to get type", ex);
}
item += "Content-Type: " + ctype + "\r\n\r\n";
let ss = Cc["#mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
ss.data = item;
mpis.appendStream(ss);
mpis.appendStream(fstream);
item = "";
}
else {
item += "Content-Disposition: form-data; name=\"" + encode(k, true) + "\"\r\n\r\n";
item += encode(v);
}
item += "\r\n";
}
item += "--" + boundary + "--\r\n";
let ss = Cc["#mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
ss.data = item;
mpis.appendStream(ss);
let postStream = Cc["#mozilla.org/network/mime-input-stream;1"].
createInstance(Ci.nsIMIMEInputStream);
postStream.addHeader("Content-Type",
"multipart/form-data; boundary=" + boundary);
postStream.setData(mpis);
postStream.addContentLength = true;
return postStream;
}
(You could use additional nsIMIMEInputStream instead of string-concatenating stuff together, but this would perform worse and has no real merit).
Which can then be used like e.g.:
let file = Services.dirsvc.get("Desk", Ci.nsIFile);
file.append("australis-xp hällow, wörld.png");
let postData = encodeFormData({
"filename": "",
"image_url": "",
"image_content": "--somne value ---",
"contents": file
}, "iso8859-1");
gBrowser.loadOneTab("http://www.example.org/", {
inBackground: false,
postData: postData
});
While invoking a http adapter procedure, it popsup a dialog with ProcedureName, Signature and Paramaters and when I hit Run button after entering two string type parameters, I am getting "Class Cast: java.lang.String cannot be cast to org.mozilla.javascript.Scriptable" error.
FYI, I created a worklight adapter using worklight application framework data object editor(automatically generates .xml and impl.js files)
impl.js file
function CurrencyConvertor_ConversionRate(params, headers){
var soapEnvNS;
soapEnvNS = 'http://schemas.xmlsoap.org/soap/envelope/';
var request = buildBody(params, 'xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.webserviceX.NET/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" ', soapEnvNS);
return invokeWebService(request, headers);
}
function buildBody(params, namespaces, soapEnvNS){
var body =
'<soap:Envelope xmlns:soap="' + soapEnvNS + '">\n' +
'<soap:Body>\n';
body = jsonToXml(params, body, namespaces);
body +=
'</soap:Body>\n' +
'</soap:Envelope>\n';
return body;
}
function getAttributes(jsonObj) {
var attrStr = '';
for(var attr in jsonObj) {
var val = jsonObj[attr];
if (attr.charAt(0) == '#') {
attrStr += ' ' + attr.substring(1);
attrStr += '="' + val + '"';
}
}
return attrStr;
}
function jsonToXml(jsonObj, xmlStr, namespaces) {
var toAppend = '';
for(var attr in jsonObj) {
var val = jsonObj[attr];
if (attr.charAt(0) != '#') {
toAppend += "<" + attr;
if (typeof val === 'object') {
toAppend += getAttributes(val);
if (namespaces != null)
toAppend += ' ' + namespaces;
toAppend += ">\n";
toAppend = jsonToXml(val, toAppend);
}
else {
toAppend += ">" + val;
}
toAppend += "</" + attr + ">\n";
}
}
return xmlStr += toAppend;
}
function invokeWebService(body, headers){
var input = {
method : 'post',
returnedContentType : 'xml',
path : '/CurrencyConvertor.asmx',
body: {
content : body.toString(),
contentType : 'text/xml; charset=utf-8'
}
};
//Adding custom HTTP headers if they were provided as parameter to the procedure call
headers && (input['headers'] = headers);
return WL.Server.invokeHttp(input);
}
The error indicates that there is an invalid JSON object somewhere in your code.
Most probably this error raised while converting the body to String using body.toString()
as toString will return [object Object] which is invalid JSON object value (neither valid String nor valid Array)
use json.stringify(body) instead, it should make what you intended to do.
besides, try to add some log lines to ease tracing the error