I have a collection of beacons that I want to show on a map. I want their positions to be synchronized between all instances of the application.
That is, when I move a beacons to another position I want this change to be reflected in the other instances of the application.
This using the Parse javascript SDK in its version 1.11.0.
I have defined the following Parse model, which represents each object in the collection saved on the server:
var Baliza = Parse.Object.extend("Balizas");
Baliza.prototype.show = function(){
var self = this;
var start= '<div class="row" style="width: 350px;">\n' +
' <div class="control-group" id="fields">\n' +
' <label class="control-label" style="text-align: center;" for="field1">Introducir Mac Asociadas a Ese Punto</label>\n' +
' <div class="controls">\n' +
' <form id="form" role="form" autocomplete="off">';
var tmp = '';
tmp = tmp + '<div class="entry input-group col-xs-12">\n' +
' <input class="form-control" name="fields[]" type="text" value="'+self.get("mac")[0]+'">\n' +
' <span class="input-group-btn">\n' +
' <button baliza-id='+self.id+' class="btn btn-success btn-add" type="button">\n' +
' <span class="glyphicon glyphicon-plus"></span>\n' +
' </button>\n' +
' </span>\n' +
' </div>';
if (self.get("mac").length>1){
for (var i=1; i<self.get("mac").length; i++) {
tmp = tmp + '<div class="entry input-group col-xs-12">\n' +
' <input class="form-control" name="fields[]" type="text" value="'+self.get("mac")[i]+'">\n' +
' <span class="input-group-btn">\n' +
' <button baliza-id='+self.id+' class="btn btn-remove col-xs-12" type="button">\n' +
' <span class="glyphicon glyphicon-minus"></span>\n' +
' </button>\n' +
' </span>\n' +
' </div>';
}
}
var end = '</form>\n' +
' <br>\n' +
' <button type="button" baliza-id='+self.id+' class="btn btn-block btn-info btn-sm ">Salvar</button>\n' +
' <button type="button" baliza-id='+self.id+' class="btn btn-block btn-danger btn-sm ">Eliminar</button>\n' +
' </div>\n' +
' </div>\n' +
'</div>';
//console.log('impirmiendo marker');
//console.log(this.marker);
console.log("Localización -> ", self.get("localizacion"));
if(self.marker != null) {
self.marker.setLatLng(new L.LatLng(self.get("localizacion").latitude, self.get("localizacion").longitude),{draggable:'true'});
} else {
self.marker = new L.marker([ self.get("localizacion").latitude, self.get("localizacion").longitude], {
icon: L.AwesomeMarkers.icon({icon: 'certificate', prefix: 'glyphicon', markerColor: 'blue'}),
draggable: 'true'
}).bindPopup("", {maxWidth: 560});
self.marker.on('dragend', function(event){
var position = event.target.getLatLng();
console.log("Notify new Location -> ", position.lat, position.lng);
//Enviamos El Dato a Parse
this.set("localizacion", new Parse.GeoPoint(position.lat, position.lng));
this.save(null, {
success: function (balizaSaved) {
console.log("Baliza Guardad con éxito: ", balizaSaved);
},
error: function (baliza, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
}.bind(this));
map.addLayer(self.marker);
}
self.marker.getPopup().setContent(start+tmp+end);
};
The code responsible for notifying the change of position in the map is the following:
self.marker.on('dragend', function(event){
var position = event.target.getLatLng();
console.log("Notify new Location -> ", position.lat, position.lng);
//Enviamos El Dato a Parse
this.set("localizacion", new Parse.GeoPoint(position.lat, position.lng));
this.save(null, {
success: function (balizaSaved) {
console.log("Baliza Guardad con éxito: ", balizaSaved);
},
error: function (baliza, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
}.bind(this));
The subscription was created as follows,
var query = new Parse.Query(Baliza);
var subscription = query.subscribe();
var mapaBalizasParse = new Map();
// After specifying the Monster subclass...
//Parse.Object.registerSubclass('Balizas',Baliza);
subscription.on('create', function (balizaCreated) {
console.log("New baliza created -> ", balizaCreated.toJSON());
var baliza = new Baliza(balizaCreated.toJSON());
baliza.show();
mapaBalizasParse.set(baliza.id, baliza);
});
subscription.on('update', function (balizaSaved) {
console.log('BALIZA ACTUALIZADA -> ', balizaSaved.toJSON());
var baliza = mapaBalizasParse.get(balizaSaved.id);
baliza.set("mac", balizaSaved.get("mac"));
baliza.set("localizacion", balizaSaved.get("localizacion"));
baliza.show();
});
subscription.on('delete', function (baliza) {
//console.log(mapaBalizasParse.get(baliza.id));
map.removeLayer(mapaBalizasParse.get(baliza.id).marker);
mapaBalizasParse.delete(baliza.id);
});
subscription.on('leave', function (balizaLeave) {
console.log('Leave called -> ', balizaLeave.id, balizaLeave.get("localizacion"));
});
subscription.on('enter', function (balizaCalled) {
console.log('Enter called -> ', balizaCalled.id, balizaCalled.get("localizacion"));
});
Each time I click on a position on the map I create a Beacons as follows:
function onMapClick(e) {
var baliza = new Baliza();
baliza.set("localizacion",new Parse.GeoPoint(e.latlng.lat, e.latlng.lng));
baliza.set("mac",["11:22:33:44:55:10"]);
baliza.set("editando",true);
baliza.save(null, {
success: function(baliza) {
//Añadimos La Baliza Al Mapa Interno
//mapaBalizasParse.set(baliza.id,baliza);
//baliza.show();
},
error: function(baliza, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
}
The list of currently registered "beacons" is shown as follows:
query.find({
success: function(results) {
console.log("Total Balizas -> " + results.length);
for (var i = 0; i < results.length; i++) {
var currentBaliza = results[i];
console.log("Baliza " + i + " -> ", currentBaliza);
currentBaliza.show();
mapaBalizasParse.set(currentBaliza.id, currentBaliza);
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
The problem is if for example I move the beacon from position ( latitude: 40.961229844235234, longitude: -5.669621229171753 ) to another point in the map (latitude: 40.9604196476232, longitude: -5.6707102060318)
The other instances of the application receive in the update event the old version of the object, its location field has not changed ( latitude: 40.961229844235234, longitude: -5.669621229171753 )
Someone can tell me what I'm doing wrong in the code.
Thanks in advance.
I have been doing some test with minimal configuration and this is that I saw:
In the callback for loading all beacons in the map set you use this code:
var beacon = new Baliza({id: baliza.id, localizacion:
baliza.get("localizacion") , mac: baliza.get("mac"), editando:
baliza.get("editando")});
but when you add a new beacon created in the callback of 'create' event, you use this one:
var beacon = new Baliza();
beacon.id = baliza.id;
beacon.localizacion = baliza.get("localizacion");
beacon.mac = baliza.get("mac");
beacon.editando = baliza.get("editando");
In my tests, if you use the first code the beacons updates normally, but if you use the second one, a old version of the beacon is logged in javascript console.
It seems that something special happens in the empty constructor that you are not including in the overloaded constructor.
I hope that it helps you. :)
Here my complete code:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Simple test parse JS</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<input type="text" id="txtIndex" class="textbox" value="1">
<button type="button" class="btn-add">Add beacon</button>
<button type="button" class="btn-modify">Modify beacon</button>
<button type="button" class="btn-remove">Remove beacon</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="http://chancejs.com/chance.min.js"></script>
<script src="https://unpkg.com/parse#1.11.0/dist/parse.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- Own JS-->
<script src="./js/demo_parse.js"></script>
</body>
</html>
JS
var mapBeacons = new Map();
var query;
var subscription;
var Baliza;
function loadAllBeacons(){
query.find({
success: function(results) {
for (var i = 0; i < results.length; i++) {
//Vamos Creando Los Objetos Que Recibimos
var beacon = new Baliza();
beacon.id = results[i].id;
beacon.localizacion = results[i].get("localizacion");
beacon.mac = results[i].get("mac");
mapBeacons.set(beacon.id,beacon);
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
}
function modifyBeacon(){
var key = $("#txtIndex").val();
var baliza = mapBeacons.get(key);
var my_chance = new Chance();
var randomLocation = new Parse.GeoPoint(my_chance.latitude(), my_chance.longitude())
baliza.set("localizacion",randomLocation);
baliza.save(null, {
success: function(baliza) {
// Execute any logic that should take place after the object is saved.
mapBeacons.set(baliza.id,baliza);
},
error: function(baliza, error) {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and message.
alert('Failed to remove new object, with error code: ' + error.message);
}
});
}
function removeBeacon(){
var key = $("#txtIndex").val();
var baliza = mapBeacons.get(key);
baliza.destroy({
success: function(myObject) {
// The object was deleted from the Parse Cloud.
//map.removeLayer(baliza.marker);
//mapaBalizasParse.delete($(this).attr('id'));
console.log("Beacon removed sucessfully");
console.log(myObject);
},
error: function(myObject, error) {
// The delete failed.
// error is a Parse.Error with an error code and message.
}
});
}
function addNewBeacon(){
var baliza = new Baliza();
var my_chance = new Chance();
var randomLocation = new Parse.GeoPoint(my_chance.latitude(), my_chance.longitude())
baliza.set("localizacion",randomLocation);
baliza.set("mac",["11:22:33:44:55:10"]);
baliza.set("editando",true);
baliza.save(null, {
success: function(baliza) {
console.log("OnSave Beacon saved sucessfully");
console.log(baliza);
},
error: function(baliza, error) {
alert('Failed to create new object, with error code: ' + error.message);
}
});
}
$(function() {
console.log( "ready!" );
//Init parse
Parse.initialize("6xuPkPgqEfCdzXwaxAfUuKbTAglJL5ALa1mmY8de");
Parse.serverURL = 'http://encelocalizacion.com:1337/parse';
//Objeto Ppara interacturar con el Objeto Baliza de Parser
Baliza = Parse.Object.extend("Balizas", {
/**
* Instance properties go in an initialize method
*/
id: '',
localizacion: '',
mac:'',
marker:'',
popup:'',
initialize: function (attr, options) {
}
});
query = new Parse.Query(Baliza);
subscription = query.subscribe();
// Subscription for create
subscription.on('create', function (baliza) {
// TODO CHANGE THIS
var beacon = new Baliza({id: baliza.id, localizacion: baliza.get("localizacion") , mac: baliza.get("mac"), editando: baliza.get("editando")});
/*
var beacon = new Baliza();
beacon.id = baliza.id;
beacon.localizacion = baliza.get("localizacion");
beacon.mac = baliza.get("mac");
beacon.editando = baliza.get("editando");
*/
mapBeacons.set(beacon.id,beacon);
console.log("New beacon added to BBDD")
console.log(mapBeacons)
});
// Subscription for update
subscription.on('update', function (kk) {
console.log('Updated beacon:' + kk.get("localizacion").latitude + " " + kk.get("localizacion").longitude + " " + kk.get("mac")+ " " + kk.get("id"));
});
// Subscription for delete
subscription.on('delete', function (kk) {
mapBeacons.delete(kk.id);
console.log('Deleted beacon:' + kk.get("localizacion").latitude + " " + kk.get("localizacion").longitude + " " + kk.get("mac"));
console.log(mapBeacons)
});
loadAllBeacons();
$(document).on('click', '.btn-add', function(e) {
addNewBeacon();
}).on('click', '.btn-remove', function(e) {
removeBeacon();
return false;
}).on('click', '.btn-modify', function(e) {
modifyBeacon();
});
});
Related
I have a problem when trying to edit a post in a rest api called Spark, the project aims to simulate a social network, here is the code for the editing part of the post:
var abreEdit = function() {
$(document).on('click','.abre-editar' ,function(){
var id = $(this).data('id'),
title = $(this).data('title'),
categories = $(this).data('categories'),
content = $(this).data('content');
console.log(id);
$.confirm({
title: 'Editar Postagem',
columnClass: 'col-md-12',
content: '' +
'<form action="javascript:void(0);" class="form-novo-cliente">' +
'<div class="form-group">' +
'<label>Título*</label>' +
'<input type="text" placeholder="Informe o título" maxlength="100" name="titulo" class="titulo form-control" required value="'+title+'"/>' +
'</div>' +
'<div class="form-group">' +
'<label>Conteúdo*</label>' +
'<textarea name="conteudo" id="conteudo" class="form-control" rows="3">'+content+'</textarea>'+
'</div>' +
'</div>' +
'<div class="form-group">' +
'<label>Categorias</label>' +
'<input type="text" placeholder="Informe as Categorias" maxlength="80" name="categorias" class="categorias form-control" value="'+categories+'" />' +
'</div>' +
'</form>',
buttons: {
formSubmit: {
text: 'Salvar',
btnClass: 'btn-green',
action: function () {
var titulo = this.$content.find('.titulo').val(),
conteudo = this.$content.find('#conteudo').val(),
categorias = this.$content.find('.categorias').val(),
form = this.$content.find('form');
if(!titulo){
$.alert('Informe o Título!');
return false;
}
if(!categorias){
$.alert('Informe ao menos uma categoria!');
return false;
}else{
var v_categorias = [];
v_categorias = categorias.split(' ');
}
if(!conteudo){
$.alert('Informe o conteúdo!');
return false;
}
var array = {'id': id,
'title': titulo,
'content':conteudo,
'categories': v_categorias};
var json = JSON.stringify(array);
console.log(json);
var ajax = new XMLHttpRequest();
// Arrow request type: Post and the API URL
ajax.open("PUT", "https://localhost:4567/postagem", true);
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Set request parameters and send the request
ajax.send(json);
// Cria um evento para receber o retorno.
ajax.onreadystatechange = function() {
// If the state is 4 and the http.status is 200, it is because the request was successful.
if (ajax.readyState == 4 && ajax.status == 200) {
var data = ajax.responseText;
$.confirm({
title: 'Sucesso',
columnClass: 'small',
content: 'Postagem alterada no ORKUT!',
buttons: {
formSubmit: {
text: 'OK',
btnClass: 'btn-green',
action: function () {
window.location.reload();
}
}
}
});
}
}
}
},
enter image description here
I try to edit, save, but don't edit via the "put" method in Spark, there is something I can do to make this part functional.
enter image description here
I've created a nice little KendoUI grid with drag and drop. It works great... as long as I have the developer tools open. Any idea why this would work with the dev tools open, but doesn't work at all with just using the browser?
Edit to add the code:
my cshtml page:
<div id="DisplayOrderGridContainer">
<div class="validation-error-box" id="errorMessages" style="display:none">
<span>Error!</span>
<ul id="message">
<li>The Record you attempted to edit was modified by another user after you got the original value. The edit operation was canceled.</li>
</ul>
</div>
<div style="padding-bottom: 15px;">
#Html.ActionLink("Back", Model.BackActionName, Model.ControllerName, Model.BackRouteValues, new { id = Model.ControllerName + "_Back", #class = "k-button" })
#if (Model.AllowClearingDisplayOrder)
{
#Html.ActionLink("Clear Order", Model.ClearActionName, Model.ControllerName, Model.BackRouteValues, new { #id = "clear-button", #class = "k-button float-right" })
}
</div>
<div id="KendoGridContainer">
<div id="ChangeDisplayOrderGrid"
class="grid"
data-role="grid"
data-bind="source:data, events:{dataBound:onDataBound,columnHide:OnColumnHide,columnShow:OnColumnShow}"
data-filterable="false"
data-sortable="false"
data-column-menu="true"
data-row-template="rowTemplate"
#*data-toolbar='[{ "template": kendo.template($("#toolbarTemplate").html()) }]'*#
data-columns='[{title:"Short Name", field:"NameField", width: 80, headerAttributes:{id: "#Model.ControllerName" + "_ShortName"}},
{title:"Description", field:"DescriptionField", width:300, headerAttributes:{id: "#Model.ControllerName" + "_Description"}},
{title:"Display Order", field:"Display", width:140, headerAttributes:{id: "#Model.ControllerName" + "_Display"}},
{title:"Value", field:"Value", hidden: true, headerAttributes:{id: "#Model.ControllerName" + "_Value"}}]'>
</div>
<script id="rowTemplate" type="text/x-kendo-tmpl">
<tr class="k-master-row" data-uid="#:uid#">
<td class="text-right">#=NameField#</td>
<td class="text-right">#=DescriptionField#</td>
<td class="text-right">#=Display#</td>
<td class="text-right" style="display:none;">#=Value#</td>
</tr>
</script>
<div id="grid" data-grid-url="#(Url.Action(Model.ActionName, Model.ControllerName))" data-grid-viewdataid="#ViewData.ID()"></div>
</div>
<input type="hidden" id="displayOrderId" />
<input type="hidden" id="Id" />
my js page:
$(document).ready(function () {
UnsavedWarningsModule.ClearUnsavedChanges();
var newData = new kendo.data.DataSource({
transport: {
read: {
url: $('#grid').attr('data-grid-url'),
dataType: "json"
}
},
schema: {
model: {
id: "Id"
}
}
})
var viewModel = new kendo.observable({
data: newData,
onDataBound: function (e) {
pfsKendoGridEvents.SetSelectedRow_MVVMGrid("KendoGridContainer", e.sender, $('#grid').attr('data-grid-viewdataId'))
},
OnColumnHide: function (e) {
pfsKendoGridEvents.OnHideShowColumns(e);
},
OnColumnShow: function (e) {
pfsKendoGridEvents.OnHideShowColumns(e);
}
});
kendo.bind($("#DisplayOrderGridContainer"), viewModel);
kendo.addDragAndDropToGrid = function (gridId, rowClass, viewModel) {
if (!gridId) { throw "Parameter [gridId] is not set."; }
if (!rowClass) { throw "Parameter [rowClass] is not set."; }
$(rowClass).kendoDraggable({
hint: function (element) {
var shortName = element[0].cells[0].firstChild.data;
var desc = element[0].cells[1].firstChild.data;
var dispOrder = element[0].cells[2].firstChild.data;
element[0].innerHTML = "<td class=\"text-right dragOver\" style=\"width:95px\">" + shortName + "</td><td class=\"text-right dragOver\" style=\"width:382px\">" + desc + "</td><td class=\"text-right dragOver\" style=\"width:173px\">" + dispOrder + "</td>";
return element;
},
axis: "y",
container: $(gridId)
});
$(gridId).kendoDropTargetArea({
filter: rowClass,
drop: function (e) {
var srcUid = e.draggable.element.data("uid");
var tgtUid = e.dropTarget.data("uid");
var ds = $(gridId).data("kendoGrid").dataSource;
var srcItem = ds.getByUid(srcUid);
var tgtItem = ds.getByUid(tgtUid);
var dstIdx = ds.indexOf(tgtItem);
ds.remove(srcItem);
ds.insert(dstIdx, srcItem);
e.draggable.destroy();
UnsavedWarningsModule.SetUnsavedChanges();
kendo.addDragAndDropToGrid(gridId, rowClass, viewModel);
},
dragenter: function (e) {
e.draggable.hint.css("opacity", 0.3);
},
dragleave: function (e) {
e.draggable.hint.css("opacity", 1);
var srcUid = e.draggable.element.data("uid");
var tgtUid = e.dropTarget.data("uid");
var ds = $(gridId).data("kendoGrid").dataSource;
var srcItem = ds.getByUid(srcUid);
var srcDispOrd = srcItem.Display;
var tgtItem = ds.getByUid(tgtUid);
var tgtDispOrd = tgtItem.Display;
var dstIdx = ds.indexOf(tgtItem);
//--update display orders after dropping
ds._data.forEach(function (data) {
//if dragging it to a spot with higher dispOrder
if (tgtDispOrd > srcDispOrd) {
if (data.Display <= tgtDispOrd && data.Display > srcDispOrd) {
data.Display -= 1;
}
}
//if dragging it to a spot with lower dispOrder
if (srcDispOrd > tgtDispOrd) {
if (data.Display >= tgtDispOrd && data.Display < srcDispOrd) {
data.Display += 1;
}
}
});
srcItem.Display = tgtDispOrd;
//--end
ds.remove(srcItem);
ds.insert(dstIdx, srcItem);
}
});
};
var dataService = (function () {
"use strict";
var self = {};
self.getAddresses = function () {
var data = new kendo.data.ObservableArray([newData]);
// Manual create a promise, so this function mimicks an Ajax call.
var dfd = new $.Deferred();
dfd.resolve(data);
return dfd.promise();
};
return self;
})(kendo);
var controller = (function (dataService, viewModel) {
"use strict";
var _dataService = dataService;
var _vm = viewModel;
var self = {};
self.handleAddressesRefresh = function (data) {
_vm.set("addresses", new kendo.data.DataSource({ data: data }));
kendo.bind($("#KendoGridContainer"), _vm);
kendo.addDragAndDropToGrid("#ChangeDisplayOrderGrid", ".k-master-row", _vm);
};
self.show = function () {
$.when(_dataService.getAddresses())
.then(self.handleAddressesRefresh);
};
return self;
})(dataService, viewModel);
controller.show();});
I think it's something to do with the timing of the loading of the page, possibly with the promise I'm using?
Thanks!
I am facing problem here as in phonegap image is uploaded to the server once u select a picture.I don't want to upload image before submitting form. Image is uploaded automatically to server which is something i don't want.I want to upload image with the form, where form contains many more fields which is required to send along with image. What are the possible ways to submit with form?
<!DOCTYPE HTML >
<html>
<head>
<title>Registration Form</title>
<script type="text/javascript" charset="utf-8" src="phonegap-1.2.0.js"></script>
<script type="text/javascript" charset="utf-8">
// Wait for PhoneGap to load
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is ready
function onDeviceReady() {
// Do cool things here...
}
function getImage() {
// Retrieve image file location from specified source
navigator.camera.getPicture(uploadPhoto, function(message) {
alert('get picture failed');
},{
quality: 50,
destinationType: navigator.camera.DestinationType.FILE_URI,
sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY
});}
function uploadPhoto(imageURI) {
var options = new FileUploadOptions();
options.fileKey="file";
options.fileName=imageURI.substr(imageURI.lastIndexOf('/')+1);
options.mimeType="image/jpeg";
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload(imageURI, "http://yourdomain.com/upload.php", win, fail, options);
}
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
console.log("Sent = " + r.bytesSent);
alert(r.response);
}
function fail(error) {
alert("An error has occurred: Code = " = error.code);
}
</script>
</head>
<body>
<form id="regform">
<button onclick="getImage();">select Avatar<button>
<input type="text" id="firstname" name="firstname" />
<input type="text" id="lastname" name="lastname" />
<input type="text" id="workPlace" name="workPlace" class="" />
<input type="submit" id="btnSubmit" value="Submit" />
</form>
</body>
</html>
Create two functions you can call separately. One function for just getting the image, and another function to upload the image.
You can do something like below.
<!DOCTYPE html>
<html>
<head>
<title>Submit form</title>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
var pictureSource; // picture source
var destinationType; // sets the format of returned value
// Wait for device API libraries to load
//
document.addEventListener("deviceready",onDeviceReady,false);
// device APIs are available
//
function onDeviceReady() {
pictureSource = navigator.camera.PictureSourceType;
destinationType = navigator.camera.DestinationType;
}
// Called when a photo is successfully retrieved
//
function onPhotoURISuccess(imageURI) {
// Show the selected image
var smallImage = document.getElementById('smallImage');
smallImage.style.display = 'block';
smallImage.src = imageURI;
}
// A button will call this function
//
function getPhoto(source) {
// Retrieve image file location from specified source
navigator.camera.getPicture(onPhotoURISuccess, onFail, { quality: 50,
destinationType: destinationType.FILE_URI,
sourceType: source });
}
function uploadPhoto() {
//selected photo URI is in the src attribute (we set this on getPhoto)
var imageURI = document.getElementById('smallImage').getAttribute("src");
if (!imageURI) {
alert('Please select an image first.');
return;
}
//set upload options
var options = new FileUploadOptions();
options.fileKey = "file";
options.fileName = imageURI.substr(imageURI.lastIndexOf('/')+1);
options.mimeType = "image/jpeg";
options.params = {
firstname: document.getElementById("firstname").value,
lastname: document.getElementById("lastname").value,
workplace: document.getElementById("workplace").value
}
var ft = new FileTransfer();
ft.upload(imageURI, encodeURI("http://some.server.com/upload.php"), win, fail, options);
}
// Called if something bad happens.
//
function onFail(message) {
console.log('Failed because: ' + message);
}
function win(r) {
console.log("Code = " + r.responseCode);
console.log("Response = " + r.response);
//alert("Response =" + r.response);
console.log("Sent = " + r.bytesSent);
}
function fail(error) {
alert("An error has occurred: Code = " + error.code);
console.log("upload error source " + error.source);
console.log("upload error target " + error.target);
}
</script>
</head>
<body>
<form id="regform">
<button onclick="getPhoto(pictureSource.PHOTOLIBRARY);">Select Photo:</button><br>
<img style="display:none;width:60px;height:60px;" id="smallImage" src="" />
First Name: <input type="text" id="firstname" name="firstname"><br>
Last Name: <input type="text" id="lastname" name="lastname"><br>
Work Place: <input type="text" id="workplace" name="workPlace"><br>
<input type="button" id="btnSubmit" value="Submit" onclick="uploadPhoto();">
</form>
</body>
</html>
You're already sending custom fields in your example.
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
Just populate params with your form fields.
I also faced same problem, but I have done using two server side calls on one click. In this, in first call submit data and get its id in callback using JSON then upload image using this id. On server side updated data and image using this id.
$('#btn_Submit').on('click',function(event) {
event.preventDefault();
if(event.handled !== true)
{
var ajax_call = serviceURL;
var str = $('#frm_id').serialize();
$.ajax({
type: "POST",
url: ajax_call,
data: str,
dataType: "json",
success: function(response){
//console.log(JSON.stringify(response))
$.each(response, function(key, value) {
if(value.Id){
if($('#vImage').attr('src')){
var imagefile = imageURI;
$('#vImage').attr('src', imagefile);
/* Image Upload Start */
var ft = new FileTransfer();
var options = new FileUploadOptions();
options.fileKey="vImage";
options.fileName=imagefile.substr(imagefile.lastIndexOf('/')+1);
options.mimeType="image/jpeg";
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
options.chunkedMode = false;
ft.upload(imagefile, your_service_url+'&Id='+Id+'&mode=upload', win, fail, options);
/* Image Upload End */
}
}
});
}
}).done(function() {
$.mobile.hidePageLoadingMsg();
})
event.handled = true;
}
return false;
});
On server side using PHP
if($_GET['type'] != "upload"){
// Add insert logic code
}else if($_GET['type'] == "upload"){
// Add logic for image
if(!empty($_FILES['vImage']) ){
// Copy image code and update data
}
}
I could not get these plugins to upload a file with the other answers.
The problem seemed to stem from the FileTransfer plugin, which states:
fileURL: Filesystem URL representing the file on the device or a data URI.
But that did not appear to work properly for me. Instead I needed to use the File plugin to create a temporary file using the data uri to get me a blob object: in their example, writeFile is a function which takes a fileEntry (returned by createFile) and dataObj (blob). Once the file is written, its path can be retrieved and passed to the FileTransfer instance. Seems like an awful lot of work, but at least it's now uploading.
I need to test various web services which are posts that take an uploaded file as the content of the body. To do this I'd like to do quick tests using ajax call. I found the following page which describes how to do this:
http://www.captain.at/ajax-file-upload.php
However, it requires that the page have "UniversalXPConnect" privileges in firefox.
How do I enable that privilege? I tried editing prefs.js and adding:
user_pref("capability.principal.foo.id", "http://localhost:8080/access/index.html");
user_pref("capability.principal.foo.granted", "UniversalXPConnect");
which should give access to the page http://localhost:8080/access/index.html. But, it doesn't seem to work.
Improving on panzi's answer, you can use the FormData object to send files with Ajax in a very simple manner:
<html>
<head>
<title>HTML5 File API</title>
</head>
<script type="text/javascript">
// <!--
// See: https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Using_FormData_objects
function upload() {
var uploadRequest = new XMLHttpRequest,
uploadForm = document.getElementById('file_upload');
function transferProgress(progressEvent) {
/*Executes For each update of the progress of the Ajax transfer.*/
// show progress bar or something....
}
function transferComplete() {
/*Executes when the transfer is complete.*/
//Do something like show a nice message...
}
function transferFailed() {
/*Executes when the transfer fails.*/
alert('Upload failed!');
}
function transferCanceled() {
/*Executes when the transfer is canceled.*/
alert('Upload canceled!');
}
uploadRequest.upload.addEventListener('progress', transferProgress, false);
//uploadRequest.upload.addEventListener('load', transferComplete, false); // transfer complete, but this doesn't mean a response from the server has been received.
uploadRequest.addEventListener('load', transferComplete, false); // ajax request complete, response from the server has been received and all has been processed.
uploadRequest.upload.addEventListener('error', transferFailed, false);
uploadRequest.upload.addEventListener('abort', transferCanceled, false);
uploadRequest.open('POST', action, true);
uploadRequest.send(new FormData(uploadForm));
}
// -->
</script>
<body>
<form id="file_upload" enctype="multipart/form-data">
<input type="text" id="text" value="blah blah blah"/>
<input type="file" onchange="upload();"/>
</form>
</body>
</html>
If the user specifies the file you don't need UniversalXPConnect. The HTML5 File API is enough:
<html>
<head>
<title>HTML5 File API</title>
</head>
<script type="text/javascript">
// <!--
// See: http://dev.w3.org/2006/webapi/FileAPI/
function upload (input) {
for (var i = 0; i < input.files.length; ++ i) {
// makes multiple uploads
uploadFile(input.files[i]);
}
}
function uploadFile (file) {
var reader = new FileReader();
reader.onprogress = function (event) {
var percent = 100 * event.loaded / event.total;
// TODO: display progress
};
reader.onerror = function (event) {
// display error
alert(errorMessage(reader.error)+': '+file.name);
};
reader.onload = function (event) {
if (reader.error) {
// display error
alert(errorMessage(reader.error)+': '+file.name);
}
else {
// You could also use reader.readAsBinaryString(file)
// and the mozilla specific function call btoa(reader.result).
// For more mozilla specific stuff (e.g. sending data as binary)
// see: https://developer.mozilla.org/en/using_xmlhttprequest
var data = reader.result.substring(reader.result.search(',')+1);
var text = document.getElementById('text').value;
var request = new XMLHttpRequest();
var boundaryString = guid();
var boundary = '--' + boundaryString;
while (text.search(boundary) != -1) {
boundaryString = guid();
boundary = '--' + boundaryString;
}
var requestbody = boundary + '\n' +
'Content-Disposition: form-data; name="mytext"\n' +
'\n' +
text +
'\n' +
boundary + '\n' +
'Content-Disposition: form-data; name="myfile"; filename="' +
file.name.replace(/"/g, '') + '"\n' +
'Content-Type: application/octet-stream\n' +
'Content-Transfer-Encoding: base64\n' +
'\n' +
data + '\n' +
boundary;
request.onreadystatechange = function () {
if (request.readyState == 4) {
if (request.status == 200) {
alert('Result: ' + request.responseText);
}
else {
alert(
'Error "' + request.statusText + '" occured while uploading: ' +
file.name);
}
}
};
/* a non-standard variant (still supported by many browsers) would be:
request.onuploadprogress = function () {
// possibly only mozilla, but awesome! upload progress!
var percent = 100 * event.loaded / event.total;
// TODO: display progress
};
request.onload = function () {
if (request.status == 200) {
alert('Result: ' + request.responseText);
}
else {
alert(
'Error "' + request.statusText + '" occured while uploading: ' +
file.name);
}
};
request.onerror = function () {
alert(
'There was a problem with the request when uploading file: ' +
file.name);
};
*/
request.open('POST', 'post.php', true);
request.setRequestHeader('Content-type', 'multipart/form-data; boundary="' +
boundaryString + '"');
request.setRequestHeader('Connection', 'close');
request.setRequestHeader('Content-Length', requestbody.length);
request.send(requestbody);
}
};
reader.readAsDataURL(file);
// there would also be:
// reader.readAsBinaryString(file);
// reader.readAsText(file, 'UTF-8');
// reader.readAsArrayBuffer(file);
}
function errorMessage (error) {
switch (error.code) {
case FileError.ABORT_ERR:
return 'Aborted';
case FileError.ENCODING_ERR:
return 'Encoding Error';
case FileError.NOT_FOUND_ERR:
return 'File not found';
case FileError.NOT_READABLE_ERR:
return 'File is not readable';
case FileError.NO_MODIFICATION_ALLOWED_ERR:
return 'File is not writeable';
case FileError.SECURITY_ERR:
return 'Security Error';
default:
return 'Unknown error code: ' + error.code;
}
}
// from: https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function guid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}
// -->
</script>
<body>
<input type="text" id="text" value="My text.."/>
<input type="file" onchange="upload(this);"/>
</body>
</html>
Still, I would recommend to use an iframe, because it is best supported by all browsers:
Is it possible to use Ajax to do file upload?
I found a tutorial on how to create editable regions on a page using AJAX.
This is great, except it was written for a single element with a unique ID. I'd like to be able to click on multiple elements on the same page and have them also be editable (e.g., I'd like to alter the script below so it works not with a single element, but with multiple elements of a particular class).
Here is my HTML:
<h2>Edit This</h2>
<p class="edit">This is some editable content</p>
<p class="edit">This is some more editable content</p>
<p class="edit">I could do this all day</p>
Here is the JS file I'm working with (I updated the script per Rex's answer below): This script is, unfortunately, not working - can anyone point me in the right direction?
Event.observe(window, 'load', init, false);
function init() {
makeEditable('edit');
}
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function(){edit($(className))}, false);
Event.observe(editElements[i], 'mouseover', function(){showAsEditable($(className))}, false);
Event.observe(editElements[i], 'mouseout', function(){showAsEditable($(className), true)}, false);
}
}
function showAsEditable(obj, clear) {
if (!clear) {
Element.addClassName(obj, 'editable');
} else {
Element.removeClassName(obj, 'editable');
}
}
function edit(obj) {
Element.hide(obj);
var textarea ='<div id="' + obj.id + '_editor"><textarea cols="60" rows="4" name="' + obj.id + '" id="' + obj.id + '_edit">' + obj.innerHTML + '</textarea>';
var button = '<input type="button" value="SAVE" id="' + obj.id + '_save"/> OR <input type="button" value="CANCEL" id="' + obj.id + '_cancel"/></div>';
new Insertion.After(obj, textarea+button);
Event.observe(obj.id+'_save', 'click', function(){saveChanges(obj)}, false);
Event.observe(obj.id+'_cancel', 'click', function(){cleanUp(obj)}, false);
}
function cleanUp(obj, keepEditable) {
Element.remove(obj.id+'_editor');
Element.show(obj);
if (!keepEditable) showAsEditable(obj, true);
}
function saveChanges(obj) {
var new_content = escape($F(obj.id+'_edit'));
obj.preUpdate = obj.innerHTML // stow contents prior to saving in case of an error
obj.innerHTML = "Saving…";
cleanUp(obj, true);
var success = function(t){editComplete(t, obj);}
var failure = function(t){editFailed(t, obj);}
var url = 'http://portal.3roadsmedia.com/scripts/edit.php';
var pars = 'id=' + obj.id + '&content=' + new_content + '&pre=' + obj.preUpdate;
var myAjax = new Ajax.Request(url, {method:'post',
postBody:pars, onSuccess:success, onFailure:failure});
}
function editComplete(t, obj) {
obj.innerHTML = t.responseText;
showAsEditable(obj, true);
}
function editFailed(t, obj) {
obj.innerHTML = 'Sorry, the update failed.';
cleanUp(obj);
}
The Event.observe method currently attaches to a single element with the ID specified. You should change this to iterate over a collection of elements located by classname and attach to each of them. According to the Prototype documentation, you can provide an element object as the first parameter, instead of an ID.
Currently, id is a string:
function makeEditable(id) {
Event.observe(id, 'click', function(){edit($(id))}, false);
//...
Which means Event.observe is attaching to the click event of the element with the ID provided. You want to attach to all elements with a class. Try:
function makeEditable(className) {
var editElements = document.getElementsByClassName(className);
for(var i=0;i<editElements.length;i++) {
Event.observe(editElements[i], 'click', function()
//...
}
//...