google earth plugin does not respond to appendchild and removechild - google-earth-plugin

I am dynamically adding kml files to google earth. For this, I have written javascript functions to add a kml and to remove a kml. These functions work fine for the first time for a kml. But if called again they do not respond. This happens for each kml that I try to add or remove. If I keep the page on browser for some time, then these functions again respond once and again become unresponsive.
function add(id, fileurl)
{
var link = ge.createLink('');
var href= fileurl;
link.setHref(href);
var networkLink = ge.createNetworkLink("'" + id + "'");
networkLink.set(link, true, true);
ge.getFeatures().appendChild(networkLink);
}
function remove(id)
{
for(var i=0; i<ge.getFeatures().getChildNodes().getLength(); i++)
{
if(ge.getFeatures().getChildNodes().item(i).getId() == id || ge.getFeatures().getChildNodes().item(i).getId() == "'" + id + "'")
{
id = ge.getFeatures().getChildNodes().item(i).getId();
ge.getFeatures().removeChild(ge.getElementById(id));
break;
}
}

The issue is that you can't re-add a feature using an ID that you have already used until all references to it have been released. This is usually done by the internal garbage collector - but you can also force it by calling release() on the object you are deleting. This ...
Permanently deletes an object, allowing its ID to be reused.
Attempting to access the object once it is released will result in an
error.
Also when an object is created with the API the object does not have a base address. In this case, the object can be returned by passing only its ID to getElementById(). This can then be used to remove the feature.
e.g.
function remove(id) {
ge.getElementById(id).release();
}
Really though I would look to avoid using IDs altogether and would simply keep a variable that points to the feature, then use that to remove. e.g.
function add(fileurl) {
var link = ge.createLink(''); //no id
link.setHref(fileurl);
var networkLink = ge.createNetworkLink(''); //no id
networkLink.set(link, true, true);
ge.getFeatures().appendChild(networkLink);
return networkLink;
}
var link1 = add("http://yoursite.com/file.kml");
var link2 = add("http://yoursite.com/file2.kml"); // etc...
// then to remove, simply...
link1.release();
link2.release();

OK. So I figured out that if you remove an object from GE, and then try to add another object with the same id, GE complains and won't create the object - unless some time (approx. 30 seconds in my case) has passed. This time actually is required by JavaScript to garbage collect the object.
Setting the object to null doesn't give immediate result but may help Garbage Collector.
Also release() method offered by GE does not help.

Related

Cannot injectScript in Custom Variable Template

I'm trying to injectScript via Custom Variable Template (not tag).
Here is simplified code:
const log = require('logToConsole');
const setTimeout = require('callLater');
const setInWindow = require('setInWindow');
const copyFromWindow = require('copyFromWindow');
const copyFromDataLayer = require('copyFromDataLayer');
const injectScript = require('injectScript');
const pixelSend = function(eventType, eventParams, tries) {
// logic
log('success')
};
log('event - ', copyFromDataLayer('event'));
if (copyFromDataLayer('event') === 'gtm.js') {
injectScript('https://vk.com/js/api/openapi.js', // this one should create **"VK"** object in global scope, used to actually send the events
pixelSend(),
data.gtmOnFailure);
}
return true;
Unfortunately openapi.js never gets injected (checking in network tab) and thus VK object never gets created and I cannot use it.
If I just run in console:
var head = document.getElementsByTagName('head')[0]
var js = document.createElement('script');
js.src = 'https://vk.com/js/api/openapi.js';
head.appendChild(js);
It gets injected and VK object becomes available.
What am I doing wrong?
Just in case:
queryPermission('inject_script', 'https://vk.com/js/api/openapi.js') = true
I tried this, and there were just a few minor bugs - line 11 is missing a semicolon, and you did not mention if you allowed access to read the "event" key from the datalayer.
After that was fixed, the script worked as expected.
Obviously it will only work on the page view trigger (since this is the only case when event equals gtm.js. I probably would move the condition from the tag to the trigger).
Instead of "return true" you should end this will a call to data.gtmOnSuccess(), else you might have trouble using this tag in a tag sequence.
If in the template UI you hit the "run code" switch you will actually get information on all error in your code (alas one at a time, since execution stops at the first error). You can also write tests with mock input, for templates that require settings via input fields.

AFRAME: Event on completion of dynamic adding of component

My use-case is as follows:
In a loop, entities are being created and components are being set up. This is via a json-object that is being passed to the function. The question I have is how best to get an event that the whole set of entities and their components are being initialised. The code is something like this
var parent = document.querySelector('#parent');
var ent = document.createElement('a-entity');
parent.appendChild(ent);
for(var i =0; i = components.length; i++) {
var arr = components[i];
var cl = arr[0]; // class name
var attr = arr[2]; // component name
var attrV = arr[3]; // component data
ent.setAttribute('class', cl);
AFRAME.utils.entity.setComponentProperty(ent, attr, attrV);
//ent.setAttribute(attr, attrV); tried with this too
}
console.log('loop completed')
The loop completed gets logged before the completion of the loading of some of the components. I would like to have some sort of a call back to know that all the components have been completed loaded.
There seems to be an event componentinitialized but it sends a return for only 1 component. My real requirement (not reflected in above code) is that an entity can have multiple components added.
To use the above, I may have to set this event for every component and keep track of whether it has been completed or not. Just wondering if there is a more elegant way to do it. Thanks
Entities emit the "loaded" event. It should be easier than listening for each component initialization within the entity.
Try out:
entity.addEventListener("loaded", (e) => {
console.log(e)
})
like i did here.

Relational Query - 2 degrees away

I have three models:
Timesheets
Employee
Manager
I am looking for all timesheets that need to be approved by a manager (many timesheets per employee, one manager per employee).
I have tried creating datasources and prefetching both Employee and Employee.Manager, but I so far no success as of yet.
Is there a trick to this? Do I need to load the query and then do another load? Or create an intermediary datasource that holds both the Timesheet and Employee data or something else?
You can do it by applying a query filter to the datasource onDataLoad event or another event. For example, you could bind the value of a dropdown with Managers to:
#datasource.query.filters.Employee.Manager._equals
- assuming that the datasource of the widget is set to Timesheets.
If you are linking to the page from another page, you could also call a script instead of using a preset action. On the link click, invoke the script below, passing it the desired manager object from the linking page.
function loadPageTimesheets(manager){
app.showPage(app.pages.Timesheets);
app.pages.Timesheets.datasource.query.filters.Employee.Manager._equals = manager;
app.pages.Timesheets.datasource.load();
}
I would recommend to redesign your app a little bit to use full power of App Maker. You can go with Directory Model (Manager -> Employees) plus one table with data (Timesheets). In this case your timesheets query can look similar to this:
// Server side script
function getTimesheets(query) {
var managerEmail = query.parameters.ManagerEmail;
var dirQuery = app.models.Directory.newQuery();
dirQuery.filters.PrimaryEmail._equals = managerEmail;
dirQuery.prefetch.DirectReports._add();
var people = dirQuery.run();
if (people.length === 0) {
return [];
}
var manager = people[0];
// Subordinates lookup can look fancier if you need recursively
// include everybody down the hierarchy chart. In this case
// it also will make sense to update prefetch above to include
// reports of reports of reports...
var subortinatesEmails = manager.DirectReports.map(function(employee) {
return employee.PrimaryEmail;
});
var tsQuery = app.models.Timesheet.newQuery();
tsQuery.filters.EmployeeEmail._in = subortinatesEmails;
return tsQuery.run();
}

How to set the bookmarking (cmi.location) for SCORM 1.2?

I tried to bookmarking for flash SCORM 1.2 packages. I'm properly capturing the last visited data(cmi.loation, suspend data), but when I'm trying to reset the data for next launch, SCO is not relocating, it is starting from beginning.
And I set the hard coded values in LMSInitilization() function in javascript.
I used bellow code for setting the location variable to SCO.
// cmi data model storing object
var cmiobj = new Object();
function LMSInitialize(dummyString) {
// already initialized or already finished
if ((flagInitialized) || (flagFinished)) { return "false"; }
// set initialization flag
flagInitialized = true;
this.cmiobj["cmi.core.lesson_location"]="6";
this.cmiobj['cmi.core.lesson_status']='incomplete';
this.cmiobj['cmi.core.session_time']='00:00:50';
this.cmiobj['cmi.suspend_data']='FA1Enon ... ";
// return success value
return "true";
}
Hope you help.
You need to set cmi.core.exit to "suspend" too - otherwise it will not supply any of the old data for you to continue with next time.

Ideas on making a javascript object name unique in ASP.Net?

I've created an ASP.Net user control that will get placed more than once inside of web page. In this control I've defined a javascript object such as:
function MyObject( options )
{
this.x = options.x;
}
MyObject.prototype.someFunction=function someFunctionF()
{
return this.x + 1;
}
In the code behind I've created MyObject in a startup script --
var opts = { x: 99 };
var myObject = new MyObject( opts );
When a certain button in the control is pressed it will call myObject.someFunction(). Now lets say the value of x will be 99 for one control but 98 for another control. The problem here is that the var myObject will be repeated and only the last instance will matter. Surely there's a way to make the var myObject unique using some concept I've haven't run across yet. Ideas?
Thanks,
Craig
Your Javascript like this:-
function MyObject(options) { this.x = options.x; }
MyObject.prototype.someFunction = function() { return this.x + 1; }
MyObject.create(id, options) {
if (!this._instances) this._instances = {};
return this._instances[id] = new MyObject(options);
}
MyObject.getInstance(id) { return this._instances[id]; }
Your startup javascript like this:-
MyObject.create(ClientID, {x: 99});
Other code that needs to use an instance (say in the client-side onclick event)
String.Format("onclick=\"MyObject.getInstance('{0}').someFunction()\", ClientID);
Note the low impact on the clients global namespace, only the MyObject identifier is added to the global namespace, regardless of how many instances of your control are added to the page.
If it is just one value, why not have the function take it as a parameter and build your onclick handler so that it puts the correct value in for each control. If it is more complex than that, then consider making options an array and, for each control, insert the correct options into the spot in the array that corresponds to each particular control. Then pass the proper index into the array into the function.
I do this by using ScriptManager.RegisterClientScriptBlock to register a string as a JavaScript block on the client side. I can then modify my script string using {0}, {1}..,{n} place holders to inject necessary ids. It depends on the structure of your code as to if this is the most elegant fashion, but it works in a pinch. You could then inject variable names using references to Me.ClientID.
You can make the value of "x" static and access it anywhere in the code, such as:
function MyObject( options ) { MyObject.x = options.x; }
MyObject.x = 99; // static
MyObject.prototype.someFunction = function () { return MyObject.x + 1; }
This way you can access MyObject.x anywhere in your code, even without re-instanciating MyObject.
Excellent solution Anthony. The other solutions offered were as good and I did consider them but I was looking for something a little more elegant like this solution.
Thanks!

Resources