Javascript Objects in Arrays - javascript

I'm trying to use the Ajax File Upload as featured here: http://valums.com/ajax-upload/
As you can see, I need to create a qq.FileUploader object to initialize the script. However, I need to be able to dynamically create this objects without knowing the IDs of the elements. I've tried creating something like this:
var uploader, i = 0;
$(".file-upload").each(function() {
$e = $(this);
i++;
uploader[i] = new qq.FileUploader({
element: $(this)[0],
action: 'uploadfile.php',
allowedExtensions: ['doc', 'docx', 'pdf'],
multiple: false,
onComplete: function(id, fileName, responseJSON) {
$($e).siblings('input').val(responseJSON.newfilename);
}
});
});
I've learned that the [i] part I have added breaks the script, because I cannot have objects inside of an array.
Is there another way I can create this objects dynamically? They need to all have a unique name, otherwise the onComplete function gets overwritten for all of them. I experimented with using eval(), but I can't seem to make it work correctly.

You have to declare uploader as an array first :
var uploader = [];
Because you declared the variable without defining it, it has the default value of undefined , and your code was translated into something like undefined[i] which triggers an error.

Has to be something like
var uploader = {};
or else uploader is null and you cannot assign anything to it.
EDIT:
So there're two opitions, in my opinion, if one wants to have an array than it makes sense to declare one, var uploader = []; and then use the uploader.push() method or define it as an object var uploader = {}; and just do uploader[i] = ....
It is also possible to do the latter with an a array, but in the latter case I see no point in maintaining the counter (i).

Related

Get reference to existing OpenSeadragon Viewer

I need to add an overlay to an existing OpenSeadragon viewer object which isn't created by my code, but elsewhere in the application.
I have got to a point where I know that the viewer has been created as I can access the various html elements that are created via jQuery. However I can't work out if there's any way to create a viewer from an existing reference.
I've tried using the id of the viewer div in:
var viewer = OpenSeadragon(id: "open-seadragon-viewer-id");
but this doesn't seem to work.
Is there any way to do this or can you only get the viewer within the code that initialised it?
Here's one crazy thought... you could monkey-patch some portion of OSD in order to grab the viewer...
var viewer;
var originalIsOpen = OpenSeadragon.Viewer.prototype.isOpen;
OpenSeadragon.Viewer.prototype.isOpen = function() {
// Now we know the viewer!
viewer = this;
// Reinstate the original, since we only need to run our version once
OpenSeadragon.Viewer.prototype.isOpen = originalIsOpen;
// Call the original
return originalIsOpen.call(this);
}
It's kind of tacky, but should work. Note this assumes there is only one viewer on the page... if there are more than one, the same principle could work but you would need to keep track of an array of viewers.
BTW, I'm using isOpen, because it's simple and it gets called every frame. Other functions could work as well.
EDIT: fixed code so we are using the prototype. I still haven't actually tested this code so there may still be bugs!
This solution does not directly answer the question, as it relies on your own code creating the OpenSeaDragon object. It is an implementation of #iangilman's mention of storing the viewer in a global variable. However others may find it useful. (Note that passing a global variable to a function requires a workaround - see Passing a global variable to a function)
The code demonstrates how to use the same OpenSeaDragon object to display different pictures.
var viewer3=null; //global variable
var newURL1='image/imageToDisplay1.png';
var newURL2='image/imageToDisplay2.png';
var elementID='myID';
//the loadScan function will display the picture using openSeaDragon and can be called as many times as you want.
loadScan("viewer3",newURL1,elementID);
loadScan("viewer3",newURL2,elementID);
//the actual function
function loadScan(theViewer,newURL,theID) {
//if object has already been created, then just change the image
if (window[theViewer]!=null) {
window[theViewer].open({
type: 'image',
url: newURL
});
} else {
//create a new OpenSeadragon object
window[theViewer] = OpenSeadragon({
prefixUrl: "/myapp/vendor/openseadragon/images/",
id: theID,
defaultZoomLevel: 1,
tileSources: {
url: newURL,
type: 'image'
}
});
}
}

jquery ui - call methods with same name on different widgets

Suppose I have created several widgets (mywidget1, mywidget2, ...) and that all have a method with the same name (doSomething).
To invoke the method I can use:
$("#elem").widget1("doSomething");
but this way I need to know the name of the widget (in the example widget1).
If I have an array with multiple instances of the various widgets, how can I invoke on each one the method "doSomething" without knowing the name of the widget?
You can't. Two options for you:
Store them in separate arrays (one array for widget1, another for widget2), or
Store objects in the arrays containing the information about which widget relates to that entry
Here's an example of #2
var list = [
{widget: "widget1", element: $("#elem")},
{widget: "widget2", element: $(".some-selector")},
{widget: "widget2", element: $("#another")},
{widget: "widget1", element: $("div .target")}
];
$.each(list, function(i, entry) {
entry.element[entry.widget]("doSomething");
});
In theory I suppose you could do something like the following:
var widgets = [widget1, widget2, widget3]; // etc, will assume they are defined already
// Access each Widget, now widget points to each of the widgets
// So we dont need to know the actual name like widget1, widget2 etc
widgets.forEach(function (widget) {
$('#elem').widget('doSomething'); // Call the doSomething method of this widget
$('#elem').widget.call(null, 'doSomething'); // Try this if the above fails
$('#elem').call(widget, 'doSomething'); // Or maybe this :)
}
Anyway try the above, of the top of my head im not sure which will work. I think what you are trying to do might be a bit difficult to implement, so sorry if it doesnt work. Hopefully it will :)
you can have an object of functions and get the key value dynamically to use the particular function
widgets = {
widget1 : function(value){return value;},
widget2 : function(value){return value+1},
widget3 : function(value){return value+2}
}; //you can have your list of functions say widgets1,2,3....
for(var k in widgets ){
console.log(widgets[k](1));
} //to get the function name you can use the key name in the object
Example on https://gist.github.com/vishnu667/44063d64f2d1210c26c9
you can also call the function if you know the key
widgets['widget2'](10); //to directly call the function if you know the key
to Dynamically add more widgets use
function add_widget(name,widgetFunction){ //function to add widget
widgets[name]=widgetFunction;
}
add_widget("widget4",function(value){return value+10;}); //adds a new widget to the widgets list
I found this solution:
before:
var tmp = $("#elem").widget1();
myarray[i] = tmp;
now:
var tmp = $("#elem").widget1();
myarray[i] = tmp.data(ui-widget1);
in this way it is possible to directly call the method:
myarray[x].doSomething();
what do you think?
can be an efficient solution?
thanks to all

How to update cached jquery object after adding elements via AJAX

I'm trying to write a plugin-like function in jQuery to add elements to a container with AJAX.
It looks like this:
$.fn.cacheload = function(index) {
var $this = $(this);
$.get("cache.php", {{ id: index }).done(function(data) {
// cache.php returns <div class='entry'>Content</div> ...
$(data).insertAfter($this.last());
});
}
and I would like to use it like this:
var entries = $("div.entry"),
id = 28;
entries.cacheload(id);
Think that this would load another "entry"-container and add it to the DOM.
This is works so far. But of course the variable that holds the cached jQuery object (entries) isn't updated. So if there were two divs in the beginning and you would add another with this function it would show in the DOM, but entries would still reference the original two divs only.
I know you can't use the return value of get because the AJAX-call is asynchronous. But is there any way to update the cached object so it contains the elements loaded via AJAX as well?
I know I could do it like this and re-query after inserting:
$.get("cache.php", {{ id: num }).done(function(data) {
$(data).insertAfter($this.last());
entries = $("div.entry");
});
but for this I would have to reference the variable holding the cached objects directly.
Is there any way around this so the function is self-contained?
I tried re-assigning $(this), but got an error. .add() doesn't update the cached object, it creates a new (temporary) object.
Thanks a lot!
// UPDATE:
John S gave a really good answer below. However, I ended up realizing that for me something else would actually work better.
Now the plugin function inserts a blank element (synchronously) and when the AJAX call is complete the attributes of that element are updated. That also ensures that elements are loaded in the correct order. For anyone stumbling over this, here is a JSFiddle: http://jsfiddle.net/JZsLt/2/
As you said yourself, the ajax call is asynchronous. Therefore, your plugin is asynchronous as as well. There's no way for your plugin to add the new elements to the jQuery object until the ajax call returns. Plus, as you discovered, you can't really add to the original jQuery object, you can only create a new jQuery object.
What you can do is have the plugin take a callback function as a second parameter. The callback could be passed a jQuery object that contains the original elements plus the newly inserted ones.
$.fn.cacheload = function(index, callback) {
var $this = this;
$.get('cache.php', { id: index }).done(function(html) {
var $elements = $(html);
$this.last().after($elements);
if (callback) {
callback.call($this, $this.add($elements));
}
});
return $this;
};
Then you could call:
entries.cacheload(id, function($newEntries) { doSomething($newEntries); } );
Of course, you could do this:
entries.cacheload(id, function($newEntries) { entries = $newEntries; } );
But entries will not be changed until the ajax call returns, so I don't see much value in it.
BTW: this inside a plugin refers to a jQuery object, so there's no need to call $(this).

Retrieve existing object into variable?

On my page I initially declare variable var as which creates an object through plugin using createAll... I'm referring to this plugin: http://kolber.github.io/audiojs/docs/
In order for plugin to work this object needs to be created. So after that I load some contact through ajax and plugin needs to be applied to this new content, so again I create this var as to create object, however now there are two similar objects on a page which conflict. I would like to know it there is a way where I can make var as that gets created after ajax call equal to existing object on a page?
I tried looking at what's inside var as by doing alert(as); This returns [Object object]
EDIT
This is what gets printed out with console log (This is original, first object)
Object
duration: 1
element: HTMLAudioElement
loadStartedCalled: false
loadedPercent: 0
mp3: null
playing: false
settings: Object
source: HTMLAudioElement
wrapper: HTMLDivElement
__proto__: Object
This is how objects are created, but I believe you need to know the plugin well to understand this
// Initialize audio js
audiojs.events.ready(function() {
var as = audiojs.createAll({
});
});
I've tested a working solution. Please note, the code is just to show it works - see my explanation below for specifics.
var as;
audiojs.events.ready(function () {
as = audiojs.createAll();
});
$(document).ready(function () {
setTimeout(function () {
var mp3 = "http://s3.amazonaws.com/audiojs/02-juicy-r.mp3"; // audio.js example
// creating new audio element, yours is probably added via ajax
// [0] used to get DOM element instead of jQuery object.
var audio = $('<audio/>', {id: 'test'}).appendTo('body').attr('src', mp3)[0];
var testAS = audiojs.create(audio); // initialise new audio.js player
as.push(testAS); // add "testAS" object to "as" array of objects
console.log(as); // log "as" - now holds the original objects + "testAS"
}, 5000); // timeout used for testing, above code can be in ajax success function instead
});
The mp3 and audio variables are just used as a demonstration as I don't know your ajax function's structure.
Audio.js has a .create() function which takes a single element as it's argument and returns an object. The original as variable is an array containing all the audio objects so you can just push the new object onto the end.
By defining as outside a function you make it global meaning that everything can access it and any new audio.js objects can be appended.

Referencing DOM within object

I have a table where each row contains some data (data-id) and a <div class = "upload">Upload</div>. The uploader needs to be passed an object which contains uploader_obj.button set as the initiating <div>, any parameters such as data-id to be sent to the server, and a bunch of other stuff which I didn't show.
The following script loops over the table, modifies the object to set button and params.id, and creates the uploader on each row.
While a separate upload button is created on each row, they each reference the same params.id which is set to the last row's value (i.e. 222). I need each to be set to the value of their specific row.
One way to fix it is to have each uploader have it's own upload_obj, but this seems like a waste of memory.
Instead, I tried to reference data-id within the uploader_obj. I can do so within onSubmit, however, haven't figured out how to use this value to set param.id. I've tried to set it within param by doing something like params: {'id':$(this.button).parent().parent().data('id')} but this is my document, and not the uploader.
So... Without making a separate uploader_obj for each row, how could I make each row's uploader sent its own param.id to the server? Thank you
PS. Sorry for the weak title. I really tried to think of a better one but couldn't.
<table>
<tr data-id="123"><td>Hello</td><td><div class="upload">Upload</div></td></tr>
<tr data-id="321"><td>Hello</td><td><div class="upload">Upload</div></td></tr>
<tr data-id="222"><td>Hello</td><td><div class="upload">Upload</div></td></tr>
</table>
var uploader_obj = {
button:null,
params: {'id':null},
onSubmit: function(id, fileName) {
var id=$(this.button).parent().parent().data('id')
console.log(id);
},
otherStuff: whatever
};
$('#myTable div.upload').each(function(i,v){
uploader_obj.button=this;
uploader_obj.params.id=$(this).parent().parent().data('id');
new qq.FileUploaderBasic(uploader_obj);
});
You're passing the same object in every iteration, just create the object from the values you have inside the loop instead:
$('#myTable div.upload').each(function(i,ele){
new qq.FileUploaderBasic({
button: ele,
params: {
id: $(ele).closest('tr').data('id')
},
onSubmit: function(id, fileName) {
var id=$(this).closest('tr').data('id')
},
otherStuff: whatever
});
});
I think the problem is that you never create a new object of "uploader_obj". So on every loop-iteration you are overwriting the values of your object.
edit:
var a = new Object();
$('#myTable div.upload').each(function(i,v){
a[i] = uploader_obj;
a[i].button=this;
a[i].params.id=$(this).parent().parent().data('id');
new qq.FileUploaderBasic(a[i]);
});
Instead of making uploader object as a global variable, if you make it local varaible to qq.FileUploaderBasic function and send button_object and data_id as a parameter, may be it will work.
you can try like
$('#myTable div.upload').each(function(i,v){
var button=this;
var id=$(this).parent().parent().data('id');
new qq.FileUploaderBasic(button,id);
});
and keep your object inside your function.

Categories