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'
}
});
}
}
Related
In my project I need to save the data to .txt or .xml or .json file. I could not find any answer from vis.js website/issues blog. It might be simple, do not know. Really helpful if anyone help me out with example code. Thank you so much in advance.
function saveData(data,callback) {
data.id = document.getElementById('node-id').value;
data.label = document.getElementById('node-label').value;
clearPopUp();
callback(data);
}
If I understand you correctly, you are looking for a way to save data and options of a graph. In my graph editor adaptation for TiddlyWiki Classic I use the following method to extract data (the full implementation can be found in the repo, see config.macros.graph.saveDataAndOptions, here's a simplified one):
config.macros.graph.saveDataAndOptions = function(network,newOptions) {
newOptions = newOptions || {};
// get nodes and edges
var nodes = network.body.data.nodes._data; // contains id, label, x,y, custom per-node options and doesn't contain options from options.nodes; presumably contains option values set when network was created, not current ones (it is so for x,y)
// no suitable getter unfortunately
var edges = network.body.data.edges._data; // map; for edges to/from? certain node use network.getConnectedNodes(id)
// network.body.data.edges._data is a hash of { id: , from: , to: }
// get node positions, options
var positions = network.getPositions(), // map
options = // get options stored previously
// merge newOptions into options
for(var nodeId in nodes) {
// nodes[nodeId].x is the initial value, positions[nodeId].x is the current one
if(positions[nodeId]) { // undefined for hidden
nodes[nodeId].x = positions[nodeId].x;
nodes[nodeId].y = positions[nodeId].y;
}
storedNode = copyObjectProperties(nodes[nodeId]);
storedNodes.push(storedNode);
}
//# do whatever you need with storedNodes, edges and options
// (pack them with JSON.stringify, store to a file etc)
};
However, while this works ok for storing data, this only helps to save options passed for storing explicitly which can be not very nice for some cases. I use this method in manipulation helpers and on dragEnd (network.on("dragEnd",this.saveToTiddlerAfterDragging), config.macros.graph.saveToTiddlerAfterDragging = function(stuff) { config.macros.graph.saveDataAndOptions(this,{ physics: false }); };). I haven't recieved any better suggestions, though.
If you need to get data and options reactively and setting such helper to handle certain edit events can't solve your problem, then I suggest wrapping nodes, edges and options as vis.DataSet and save those when needed. This is related too.
To answer the question about events/other ways to use such methods. Here's how I use them:
I save data after drag&drop moving of nodes, this is done using an event handler. Namely, I introduced
config.macros.graph.saveToTiddlerAfterDragging = function(stuff) {
config.macros.graph.saveDataAndOptions(this,{ physics: false });
};
(when drag&drop is used, physics should be switched off, otherwise coordinates won't be preserved anyway) and then I use
network.on("dragEnd",this.saveToTiddlerAfterDragging);
so that changes are saved.
As for saving after adding/editing a node/edge, I apply saving not by an event (although it's nice thinking, and you should try events of DataSet, since there's no special graph events for that). What I do is I add an elaborated hijack to the manipulation methods. Take a look at the source I've linked after the
var mSettings = options.manipulation;
line: for each manipulation method, like options.manipulation.addNode I hijack it so that its callback is hijacked to call config.macros.graph.saveDataAndOptions in the end. Here's a simplified version of what I'm doing:
var nonSaving_addNode = options.manipulation.addNode;
options.manipulation.addNode = function(data,callback) {
// hijack callback to add saving
arguments[1] = function() {
callback.apply(this,arguments); // preserve initial action
config.macros.graph.saveDataAndOptions(network); // add saving
};
nonSaving_addNode.apply(this,arguments);
}
The thing is, addNode is actually called when the add node button is clicked; though, I'm using a customized one to create a popup and apply changes once user is happy with the label they chose.
so i have some code which looks like this :
var my_important_var_x = 'init'; // my global variable for storing one very important piece of info. [screen size/resolution]
function checkmyresolutin() {
// this tests the broswer/window resolution and print to the body [for css purposes]
var resolution_is = '768px';
my_important_var_x = resolution_is; // now the var contains the resolution
return my_important_Var_X;
}
checkmyresolutin(); // for getting the resolutoin on document ready.
$(window).resize(function(){
checkmyresolutin(); // this is the important part,every time the browser is resized i need to know ,certain function load/unload on certain resolutions.
});
function_abc(); // this is my problem,ill explain bellow,to keep this code block clean.
So after the checkmyresolutin() is called when the browser is resized and it returns the resolution/sceensize and is stored in the my_important_var_x variable ,i need to pass that latest information from that variable to the function_abc(); function...anyone ?
EDIT :
Guys,there are typos but ignore them,i did not copy paste my .js,becasuse it would take up to 400 lines,this is just an example ,the "logic" is what's important here.
I just need somehow to pass content of the enter code here to the function_abc() after the checkmyresolutin() has been run by the jQuery resize function()
You have a typo:
var my_important_var_x = 'init'; // my global variable for storing one very important piece of info. [screen size/resolution]
should be
var my_important_Var_X = 'init'; // my global variable for storing one very important piece of info. [screen size/resolution]
See this JSFiddle, with an annoying alert, to show it working.
I also made a different version where function_abc() is called automatically on resize, just to show how that works.
Check out this discussion of variable scope in Javascript.
I am using jQuery and jQuery UI, and I am almost new to JavaScript. I would like to set a global variable in the window object so to keep custom data related to multiple jQuery objects. That is, at this time I am using the following (poor) code:
// Given
window.myDataObject = {};
// Then, in the same file, I run multiple times (one time for each 'tag_id_1',
// 'tag_id_2', ..., 'tag_id_N') the following code
var tag = $('#tag_id_N')
myDataObject = { pagination: { page : 1, per_page: 10 } } // This aims to keep pagination data for tags.
Since I am trying to keep data for multiple tags, running the above code multiple times makes the window.myDataObject to be continuously "re-initialized" (making the keeping process inefficient). Because that, I thought to add "namespaced" properties (maybe, namespaced with "something" related to each tag object) to window.myDataObject so that each tag has its own pagination data.
How can I make that? Is that approach a "good" / "proper" way to proceed?
I think you're just looking for the .data() method:
The .data() method allows us to attach data of any type to DOM
elements in a way that is safe from circular references and therefore
from memory leaks.
We can set several distinct values for a single element and retrieve
them later:
First of all you should be using the window object not windows.
Secondly if you want to use multiple tags you could try to do the following:
// Given
windows.taggedObjects = {};
// Then, in the same file, I run multiple times (one time for each 'tag_id_1',
// 'tag_id_2', ..., 'tag_id_N') the following code
var tagId = 'tag_id_N';
// This aims to keep pagination data for tags.
window.taggedObjects[tagId] = { tag: $('#' + tagId), pagination: { page : 1, per_page: 10 } };
To retrieve your data just use the tag id again like so:
alert(window.taggedObjects[tagId]);
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).
There may be a better way of doing this...so please suggest if there is.
I've got some javascript that calls an AIR function. This AIR functions creates a new HTML element and adds it to the "Stage" like so:
// guid is the ID given to the new window (HTML element) by javascript
private function createNewWindow(guid:String):void {
var frame:HTML = new HTML();
frame.id = guid;
addElement(frame);
}
Now I've also got a function that sets the location of the frame based on its id...this is where I'm struggling.
// set the location of the window referenced by it's id (guid)
private function setLocation(guid:String, location:String):void {
// psuedocode. Obviously it won't work.
stage.getById(guid).location = location;
}
So, how do I "get" my HTML element based on its ID?
Short answer, you don't. This isn't javascript, this is a OO language and as such, you need to change your thought process. What are you trying to do? Create several html windows within an air application? If you want to have an id based approach, you're going to need to store the id and the pointer to the component in an data structure (like a dictionary).
private var _components:Dictionary = new Dictionary();
this._components['someId'] = someComponent;
And from there you can add a function that just saves/returns the components. I'm not entirely sure what's your approach and what you're trying to accomplish, but my gut tells me you're not doing something right.