function never ends - fileReader xlsx.js angularJS - angularjs

I'm using the FileReader Object in combination with xlsx.js to import data from an Excel-Sheet into my AngularJS Web-App.
The function never gets ended, so I need to continue the script manually by invoking an empty dummy function check(){} manually by clicking on a button to continue my script. I don't know, why the script behaves like that.
Code of my controller:
$scope.fileChanged = function(files) {
var i,f;
f = files[0];
reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
var workbook = XLSX.read(data, {type: 'binary'});
formData.data.teams = XLSX.utils.sheet_to_json(workbook.Sheets['latusch']);
var sheet_name_list = workbook.SheetNames;
var worksheet = workbook.Sheets['latusch'];
var result = {};
workbook.SheetNames.forEach(function(sheetName) {
var roa = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
if(roa.length > 0){
result[sheetName] = roa;
}
});
reader.readAsArrayBuffer(f);
}

readAsArrayBuffer is inside the onload callback, so it will not be called.
Remove it from the callback.

Related

Issue with Google Apps Script code copy-paste from Gsheet file

I've created a code to copy-paste the values from the most recent uploaded Gsheet file to another. The code is supposed to copy-paste only values that fulfil a cell condition.
The problem is, this code takes to long to run and doesn't finish to copy all values before having a run-time error.
The file has about 300.000 cells and the final amounts to be pasted on the 2nd file have about 90.000 cells.
Does anyone have a recommendation on how to proceed?
Thank you so much for your kind support.
function Master_Run_DB() {
Copy_EUDB();
}
function Copy_EUDB() {
var myLink = extractLink_EUDB();
sendData_EUDB(myLink);
}
function extractLink_EUDB() {
var newData = new Date().toLocaleString();
var drive = DriveApp.getFolderById("link to file");
var file = drive.getFilesByType(MimeType.GOOGLE_SHEETS);
var link = file.next().getUrl();
var ss = SpreadsheetApp.getActiveSpreadsheet();
return link
}
function sendData_EUDB(link) {
var mainSS = SpreadsheetApp.openById("link to file");
var lastRow = mainSS.getSheetByName("Database").getLastRow();
mainSS.getSheetByName("Database").getRange("A2" + ":R" + lastRow).clearContent();
var baseSS = SpreadsheetApp.openByUrl(link);
Logger.log(SpreadsheetApp.getActiveSpreadsheet().getUrl());
var lRow = baseSS.getSheetByName("Sheet1").getLastRow();
for (var i = 2; i <= lRow; i++) {
var cell = baseSS.getRange("G" + i);
var val = cell.getValue();
if (val == "EU") {
var sourceData = baseSS.getSheetByName("Sheet1").getRange("A" + i + ":R" + i).getValues();
var to = mainSS.getSheetByName("Database").getRange("A" + i + ":R" + i).setValues(sourceData);
}
}
};
Hello again,
Now that the new code is working, I was trying to add an IF AND clause to the copy-paste. The objective would be to copy only values that fulfil the following condition:
Column 7 = "EU"
Column 11 <> "CCCC"
But I'm getting a Syntax Error. Any advice?
Thank you in advance for your kind support
function Master_Run_EU() {
var drive = DriveApp.getFolderById("link to file");
var file = drive.getFilesByType(MimeType.GOOGLE_SHEETS);
var link = file.next().getUrl();
var ss = SpreadsheetApp.openById("link to file");
const sh=ss.getSheetByName('Database');
sh.getRange(2,1,sh.getLastRow(),22).clearContent();
var dbss = SpreadsheetApp.openByUrl(link);
const dbsh=dbss.getSheetByName('Sheet1');
var oA=[];
var vs=dbsh.getRange(2,1,dbsh.getLastRow()-1,22).getValues();
vs.forEach(function(r,i){
if(r[7]=="EU" && r[11]<>"CCCC") {
oA.push(r);
}
});
sh.getRange(2,1,oA.length,oA[0].length).setValues(oA);
}
Try this:
function Master_Run_DB() {
var drive = DriveApp.getFolderById("1ViOyzIkGOI6G6SMrtmPv4vjL-2-2duHC");
var file = drive.getFilesByType(MimeType.GOOGLE_SHEETS);
var link = file.next().getUrl();
var ss = SpreadsheetApp.openById("13CchyoqiWyvGTnYuG5TWA4cggBS-FsoDkW8XGesDkiY");
const sh=ss.getSheetByName('Database');
sh.getRange(2,1,sh.getLastRow(),18).clearContent();
var dbss = SpreadsheetApp.openByUrl(link);
const dbsh=dbss.getSheetByName('Sheet1');
var vs=dbsh.getRange(2,1,dbsh.getLastRow()-1,18).getValues();
vs.forEach(function(r,i){
if(r[6]=="EU") {
sh.getRange(i+2,1,1,18).setValues([r]);
}
});
}
It would be faster to do it this way:
function Master_Run_DB() {
var drive = DriveApp.getFolderById("1ViOyzIkGOI6G6SMrtmPv4vjL-2-2duHC");
var file = drive.getFilesByType(MimeType.GOOGLE_SHEETS);
var link = file.next().getUrl();
var ss = SpreadsheetApp.openById("13CchyoqiWyvGTnYuG5TWA4cggBS-FsoDkW8XGesDkiY");
const sh=ss.getSheetByName('Database');
sh.getRange(2,1,sh.getLastRow(),18).clearContent();
var dbss = SpreadsheetApp.openByUrl(link);
const dbsh=dbss.getSheetByName('Sheet1');
var oA=[];
var vs=dbsh.getRange(2,1,dbsh.getLastRow()-1,18).getValues();
vs.forEach(function(r,i){
if(r[6]=="EU") {
oA.push(r);
}
});
sh.getRange(2,1,oA.length,oA[0].length).setValues(oA);
}

Files upload testing in Enzyme

I have a FileInput in my render function
<FileInput
accept= "image/jpeg,image/png,audio/mp3"
onChange= {this.fileInputOnChange}
children= {<i className="fa fa-paperclip"/>}
className= 'fileInput'
/>
I need to write a test for file upload, when I simulate the change function it call the function fileInputOnChange
fileInputOnChange: function(evt){
var file = evt.target.files[0];
var fileReader = new FileReader();
fileReader.onload = function(readFile){
// Check the file type.
var fileType = file.type.toLowerCase();
if(fileType.lastIndexOf('image') === 0 && (fileType.lastIndexOf('png') >= 0 || fileType.lastIndexOf('jpeg'))){
var image = new Image();
image.onload = function(){
var attachedFile = {
attached: file,
mediaSource: readFile.target.result,
type: 'image'
}
this.props.onChange(attachedFile);
}.bind(this);
image.onerror = function(){
this.props.onError("INVALID_TYPE");
}.bind(this);
image.src = readFile.target.result;
}else if(fileType.lastIndexOf('audio') === 0 && fileType.lastIndexOf('mp3') >= 0){
//#todo: manage audio upload here
var audio = new Audio();
audio.oncanplaythrough = function(){
var attachedFile = {
attached: file,
mediaSource: readFile.target.result,
type: 'audio'
}
this.props.onChange(attachedFile);
}.bind(this);
audio.onerror = function(e){
this.props.onError("INVALID_TYPE");
}.bind(this)
audio.src = readFile.target.result;
}else if (fileType.lastIndexOf('video') === 0 && fileType.lastIndexOf('mp4') >= 0){
var video = document.createElement('video');
video.oncanplaythrough = function(){
var attachedFile = {
attached: file,
mediaSource: readFile.target.result,
type: 'video'
}
this.props.onChange(attachedFile);
}.bind(this);
video.onerror = function(){
this.props.onError("INVALID_TYPE");
}.bind(this)
video.src = readFile.target.result;
}
}.bind(this);
fileReader.onerror = function(){
this.props.onError("READING_ERROR");
}.bind(this)
fileReader.readAsDataURL(file); }
I could not add any files while simulating the upload button, I am confused of how to write the test for this scenario. Anyone ever came across this kinda scenarios? I would be greatful for all sorta helps.
it('testing attachfile change function...',()=>{
const wrapper=shallow(<AttachFile />);
wrapper.find('FileInput').simulate('change');
console.log(wrapper.debug());
});
When I tried the above test I got the following error
TypeError: Cannot read property 'target' of undefined
at [object Object].fileInputOnChange (js/components/home/chats/AttachFile.react.js:11:16)
at node_modules/enzyme/build/ShallowWrapper.js:768:23
at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
at batchedUpdates (node_modules/react/lib/ReactUpdates.js:98:20)
at node_modules/enzyme/build/ShallowWrapper.js:767:45
at withSetStateAllowed (node_modules/enzyme/build/Utils.js:196:3)
at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:764:42)
at Context.<anonymous> (test/sample.js:40:27)
You'll need to provide a mocked event object, something like:
wrapper.find('FileInput').simulate('change', {
target: {
files: [
'dummyValue.something'
]
}
});
Your component its making a lot of work inside, its like a huge side effect (it defines two callbacks with logic nailed). It's going to be difficult, but I suppose you'll need to mock FileReader as well using two spies, one reacting to the readAsDataURL calling the onload and another one calling the onerror.
Then you can check if your callbacks are doing what is supposed to.
Hope it helps!

How to access a method in one object from a second object

Hi I'm new to javascript. I have one object ListModel and I am trying to access the addToModel method from a second object.
function ListModel(){
this.list = {};
}
ListModel.prototype.addToModel = function(s){
var items = s.split("\n");
for( var i =0; i<items.length-1; i++){
console.log(items[i] + "\n");
}
}
After extensive searching I have been unable to find a solution to this problem.
The second object is:
function ListMaker(){
this.model = new ListModel();
}
ListMaker.prototype.read = function(e){
var f = e.target.files[0];
if (f) {
var r = new FileReader();
r.onload = function(e) {
var contents = e.target.result;
this.model.addToModel(contents);
document.getElementById("ta").innerHTML = contents;
}
r.readAsText(f);
} else {
alert("Failed to load file");
}
}
In the read method I call:
this.model.addToModel(contents);
and the console returns
Cannot read property 'addToModel' of undefined
If I change the line to:
this.model = new ListModel().addToModel(contents);
then the method works as excpected.
Maybe I'm going about this the wrong way.
Any help would be appreciated.
The problem is this
http://javascriptplayground.com/blog/2012/04/javascript-variable-scope-this/
Once you call a function, this is no longer pointing to what you think it is.
You need to stash this in a local and then reference the local in your callback.
ListMaker.prototype.read = function(e){
var f = e.target.files[0];
var _this = this;
if (f) {
var r = new FileReader();
r.onload = function(e) {
var contents = e.target.result;
_this.model.addToModel(contents);
document.getElementById("ta").innerHTML = contents;
}
r.readAsText(f);
} else {
alert("Failed to load file");
}
}
See it in action: http://codepen.io/anon/pen/eZrYQj

Titanium Appcelerator HTTPClient return Array

i want to put the httpclient in a separate class and want to return the array of founded data.
My Code
function ServiceRequest(callback){
var data = [];
var xhr = Titanium.Network.createHTTPClient({
onload: function(e){
//Ti.API.info("Received text: " + this.responseText);
var doc = this.responseXML.documentElement;
var elements = doc.getElementsByTagName("record");
for (var r=0;r<elements.length;r++){
var name = elements.item(r).getElementsByTagName("field").item(3).textContent;
var monteur = elements.item(r).getElementsByTagName("field").item(15).textContent;
var adresse =elements.item(r).getElementsByTagName("field").item(10).textContent;
var ort = elements.item(r).getElementsByTagName("field").item(4).textContent +" - "+ elements.item(r).getElementsByTagName("field").item(5).textContent;
var date = elements.item(r).getElementsByTagName("field").item(8).textContent;
var termin
if (date !="") {
var arrayDate = date.split(".");
var newdate = arrayDate[1]+"."+arrayDate[0]+"."+arrayDate[2];
var temptermin = newdate +" - "+ elements.item(r).getElementsByTagName("field").item(9).textContent;
termin = temptermin;
};
data.push({"name":name,"monteur":monteur,"adresse":adresse,"ort":ort,"termin":termin});
callback( data );
};
},
onerror: function(e){
Ti.API.debug(e.error);
alert(e.error);
}
});
xhr.open("GET","http://theurltomyxml.com",false);
xhr.send();
}
module.exports =ServiceRequest;
the code snippet for my initialization
var ServiceRequest = require('ui/common/ServiceRequest');
request = new ServiceRequest(function(data){
});
Ti.API.info(request);
But the request is null, the array in my onLoad function is filled with data.
How can i wait until the httpRequest is ready than return the data array ?
You can use your custom function for callback like this way onload : callBack create your own callback function or you can put your callback( data ); after your forloop.
for (var r=0;r<elements.length;r++){//==your code here for parsing
}
callback( data );

Backbone. Form with file upload, how to handle?

I want to organize the workflow only through the REST API. I have a form that allows to upload image (enctype="multipart/form-data"). How do I handle this form via backbone? Help me please, how I can to serialize it into JSON with a file field.
Thanks.
Vitaliy
If you are using HTML5, you can use the readAsDataURL method from the file api to read and store it on your models.
Here's the code i use to read and store.
var Image = Backbone.Model.extend({
readFile: function(file) {
var reader = new FileReader();
// closure to capture the file information.
reader.onload = (function(theFile,that) {
return function(e) {
//set model
that.set({filename: theFile.name, data: e.target.result});
};
})(file,this);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
});
You could try the jquery.iframe.transport plugin.
IMHO, you cannot serialize a file into JSON.
If you need to send some data along with the file you can send them as query params with POST method.
www.example.com/upload?param1=value1&param2=value2
There's no good way to submit a file via AJAX. So I wrote a function to fake it--it inserts a secret iframe into your DOM that is never visible but still works as a target to submit your form on, and it installs a function for your response to call that cleans house when the file is uploaded.
Have your upload form's submit button fire this function I wrote. It uses jQuery because it's easy and nice, but in principle that shouldn't be strictly necessary:
function backgroundUpload(form, container) {
$(container).append('<iframe name="targetFrame" id="targetFrame" style="display: none; height: 0px; width:0px;" ></iframe>');
$(form).attr('target', 'targetFrame');
window.backgroundUploadComplete = function() {
//clear your form:
$(form).find(':file').val('');
$(form).find(':text').val('');
//do whatever you do to reload your screenful of data (I'm in Backbone.js, so:)
window.Docs.fetch().complete( function() { populateDocs(); });
//get rid of the target iframe
$('#targetFrame').remove();
};
$(form).submit();
}
Then have your form handler that does your file parsing and saving return the string:
<script>window.parent.backgroundUploadComplete();</script>
Your form can look like:
<form id="uploadForm" method="POST" action="/your/form/processor/url" enctype="multipart/form-data">
<input type="file" name="file"/>
<!-- and other fields as needed -->
<input type="button" onClick="backgroundUpload(this.form, $('#documents'));" value="Upload" />
</form>
(#documents is the div that this form lives in. Could probably be any DOM element, it just needs a home.)
events : {
"click #uploadDocument" : "showUploadDocumentDetails",
"change #documents" : "documentsSelected",
"click .cancel-document" : "cancelDocument"
},
showUploadDocumentDetails : function(event) {
$('#id-gen-form').attr("enctype","multipart/form-data");
$('#id-gen-form').attr("action",this.model.url);
var config = {
support : "image/jpg,image/png,image/bmp,image/jpeg,image/gif", // Valid file formats
form: "id-gen-form", // Form ID
dragArea: "dragAndDropFiles", // Upload Area ID
uploadUrl: this.model.url // Server side upload url
};
initMultiUploader(config);
if($('#uploadDocument').attr("checked")){
$('#id-documentCategory-div').show();
$('#id-documentName-div').show();
this.model.set({"uploadDocument": "YES"},{silent: true});
}
else{
$('#id-documentCategory-div').hide();
$('#id-documentName-div').hide();
this.model.set({"uploadDocument": "NO"},{silent: true});
}
},
cancelDocument : function(event) {
var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
$('#' + event.target.id).parent().parent().remove();
var documentDetails = this.model.get("documentDetails");
documentDetails = _.without(documentDetails, _(documentDetails).find(function(x) {return x.seqNum == event.target.id;}));
this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
},
documentsSelected : function(event) {
/*var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
var files = event.target.files; // FileList object
var html = [];
var documentDetails = [];
$(".files").html(html.join(''));
var _this = this;
_this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
var seqNum = 0;
for(var i=0; i< files.length; i++){
(function(file) {
html.push("<tr class='template-upload' style='font-size: 10px;'>");
html.push("<td class='name'><span>"+file.name+"</span></td>");
html.push("<td class='size'><span>"+file.size+" KB <br/>"+file.type+"</span></td>");
//html.push("<td><div class='progress progress-success progress-striped active'style='width: 100px;' role='progressbar' aria-valuemin='0' aria-valuemax='100' aria-valuenow='0'><div class='bar' style='width:0%;'></div></div></td>");
if(LNS.MyesqNG.isMimeTypeSupported(file.type)){
if(!LNS.MyesqNG.isFileSizeExceeded(file.size)){
html.push("<td class='error' colspan='2'></td>");
var reader = new FileReader();
console.log(reader);
reader.onload = function(e) {
var targ;
if (!e) e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
console.log(e.target.result);
var content = e.target.result;
var document = new Object();
document.name = file.name;
document.type = file.type;
document.content = content;
document.seqNum = "document"+seqNum;
seqNum++;
documentDetails.push(document);
// _this.model.set({"documentDetails" : documentDetails},{silent:true});
};
reader.readAsDataURL(file, "UTF-8");
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Too long</td>");
}
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Not suported</td>");
}
html.push("<td><a id='document"+i+"' class='btn btn-warning btn-mini cancel-document'>Cancel</a></td>");
html.push("</tr>");
})(files[i]);
}
$(".files").html(html.join(''));*/
}
LNS.MyesqNG.isMimeTypeSupported = function(mimeType){
var mimeTypes = ['text/plain','application/zip','application/x-rar-compressed','application/pdf'];
if($.inArray(mimeType.toLowerCase(), mimeTypes) == -1) {
return false;
}else{
return true;
}
};
LNS.MyesqNG.isFileSizeExceeded = function(fileSize) {
var size = 2000000000000000000000000000;
if(Number(fileSize) > Number(size)){
return true;
}else{
return false;
}
};
Use this, it can work but not more than 5 MB file
Based on Anthony answer (https://stackoverflow.com/a/10916733/2750451), I've written a solution in coffeescript based on a defer object.
readFile: (file) =>
def = $.Deferred()
reader = new FileReader()
reader.onload = (ev) =>
def.resolve
name: file.name
binary: ev.target.result
reader.onerror = ->
def.reject()
reader.readAsDataURL(file)
def.promise()
Then, you could use it this way
readFile(file)
.done (parsedFile) =>
# do whatever you want with parsedFile
#model.set
image_name: parsedFile.name
image: parsedFile.binary
#model.save
.fail ->
console.log "readFile has failed"
To handle it on the server side (because it's Base64 encoded), here the solution in RoR (based on https://stackoverflow.com/a/16310953/2750451)
my_object.image = decode_image(params[:image])
my_object.image.name = params[:image_name]
def decode_image(encoded_file)
require 'base64'
image_data_string = split_base64(encoded_file)[:data]
Base64.decode64(image_data_string)
end
def split_base64(uri)
if uri.match(%r{^data:(.*?);(.*?),(.*)$})
return {
type: $1, # "image/png"
encoder: $2, # "base64"
data: $3, # data string
extension: $1.split('/')[1] # "png"
}
end
end
To ellaborate on Anthony Chua's answer. You need to add Image handling to Backbone.Form.editors like
Backbone.Form.editors.Image = Backbone.Form.editors.Text.extend({
tagName: 'div',
events: {
'change input[type=file]': 'uploadFile',
'click .remove': 'removeFile'
},
initialize: function(options) {
_.bindAll(this, 'filepickerSuccess', 'filepickerError', 'filepickerProgress');
Backbone.Form.editors.Text.prototype.initialize.call(this, options);
this.$input = $('<input type="hidden" name="'+this.key+'" />');
this.$uploadInput = $('<input type="file" name="'+this.key+'" accept="image/*" />');
this.$loader = $('<p class="upload-status"><span class="loader"></span> please wait..</p>');
this.$error = $('<p class="upload-error error">Error</p>');
this.$list = $('<ul class="file-list">');
},
// return an array of file dicts
getValue: function() {
var val = this.$input.val();
return (val ? JSON.parse(val) : [])[0].value;
},
setValue: function(value) {
var str, files = value;
if (_(value).isObject()) {
str = JSON.stringify(value);
} else {
files = value ? JSON.parse(value) : [];
}
this.$input.val(str);
this.updateList(files);
},
render: function(options) {
Backbone.Form.editors.Text.prototype.render.apply(this, arguments);
this.$el.append(this.$input);
this.$el.append(this.$uploadInput);
this.$el.append(this.$loader.hide());
this.$el.append(this.$error.hide());
this.$el.append(this.$list);
return this;
},
uploadFile: function() {
var fileInput = this.$uploadInput.get(0);
var fileObj = fileInput.files[0]
var reader = new FileReader();
var that = this;
// closure to capture the file information.
reader.onload = function(file){
var dataURL = reader.result;
var fileValue = {
value: dataURL,
name: fileObj.name,
content_type: fileObj.type
}
that.filepickerSuccess(fileValue);
};
// Read in the image file as a data URL.
reader.readAsDataURL(fileObj);
},
filepickerSuccess: function(files) {
console.log('File (raw)', files);
this.$loader.hide();
this.$error.hide();
this.$uploadInput.val('');
// when uploading one file, it returns just an object
if (!_(files).isArray()) { files = [files]; }
// turn response array into a flatter array of objects
var newFiles = _(files).map(function(file, index) {
return {
url: "#",
value: file.value,
filename: file.name,
key: index,
content_type: file.type
};
});
console.log('File (processed)', newFiles);
this.setValue(newFiles);
},
filepickerError: function(msg) {
console.debug('File error', msg);
this.$loader.hide();
this.$error.show();
},
filepickerProgress: function(percent) {
this.$loader.show();
this.$error.hide();
},
updateList: function(files) {
// this code is currently duplicated as a handlebar helper (I wanted to let this
// backbone-forms field stand on its own)
this.$list.empty();
_(files).each(function(file) {
var a = $('<a>', {
target: '_blank',
href: file.url,
text: file.filename + ' (' + file.content_type + ') '
});
var li = $('<li>').append(a);
li.append(a, ' ', $('<i class="icon-remove"></i>').data('key', file.key));
this.$list.append(li);
}, this);
this.$list[files.length ? 'show' : 'hide']();
},
removeFile: function(ev) {
if (ev) ev.preventDefault();
var files = this.getValue();
this.setValue([]);
}
});
You can use above code as follows
var ImgSlot = Backbone.Model.extend({
defaults: {
},
schema: {
imageField: {
type: "Image"
}
}
})
Render form using:
this.form = new Backbone.Form({
model: new ImgSlot(),
submitButton: "Example Image file input handling"
}).render();
var errors = that.form.commit({validate: true})
if(errors != null)
{
return false;
}
var data = that.form.model.attributes;
console.debug(data.imageField); // Will return base64 of image selected.
It is not possible to submit a file over AJAX before HTML5 (including IE9).
You need to sync the model attributes over ajax, and then send another html form post with the file, and then sync them up somehow. Generally, save the model over ajax, get an id back, add the id to the other form, and then post the file.
The jQuery plug in "jquery.form" may help you to construct a form to post the file. It manages the "hidden iframe trick" so that it looks like AJAX to the end user.
You might just need to spend some time googling "hidden iframe trick" ...

Resources