I'm trying to add a network link when someone clicks on a placemark that has been loaded via KML. What I do is attach an event handler to the globe and check to see if the user clicked on a placemark.
On the html, there is a button that when clicked, removes the network link from google earth when clicked (see trackRemoval). Everything seems to work the first time a placemark is clicked.
The problem is when the placemark is clicked a second time (after having the network link removed), the call to createNetworkLink fails. Attached is the relevant snippets of code.
Can someone see what I'm doing wrong?
var ge = new Array(2);
function clickHandler(event) {
if (event.getTarget().getType() == 'KmlPlacemark') {
event.preventDefault();
var placemark = event.getTarget();
var device = placemark.getName();
var networkLink = ge[0].createNetworkLink(device + "link");
var link = ge[0].createLink("");
networkLink.setDescription("Vechicle view for" + device);
networkLink.setName("Track for " + device);
networkLink.setFlyToView(true);
link.setHref("http://x.x.x.x/blah/blah.kml");
link.setRefreshMode(ge[0].REFRESH_ON_INTERVAL);
link.setRefreshInterval(60);
networkLink.setLink(link);
ge[0].getGlobe().getFeatures().appendChild(networkLink);
}
}
function initgeaor(instance) {
google.earth.addEventListener(instance.getGlobe(), 'click', clickHandler);
}
function trackRemoval() {
var device = this.name;
var networklink = ge[0].getElementById(device + "link");
ge[0].getGlobe().getFeatures().removeChild(networklink);
}
See https://developers.google.com/earth/documentation/reference/interface_kml_object
See the note on the method release(). From my experience this "indeterminate amount of time" is difficult to gauge. So 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 that 'indeterminate amount of time' has passed.
You may be able to use a timer after you removeChild() and release() the network link.
You could also give your new KmlNetworkLink a different id each time you create it so there's no collision. I'm not sure if this is an option for you. var networkLink = ge[0].createNetworkLink(device + "link" + idNumber);
Related
I have made a website using asp.net and I have set up a sign-in feature that uses OpenID specifically Steam. I have a web API that gets called and that procs the challenge for the open id. When the user clicks sign in it opens a new tab and browser and direct them to sign in api point. On my local pc, it works perfectly however on a hosted app service on Azure it does not. When the window opens it does not call the API. The only way to call the API is to clear the cache (Shift + Ctrl + R). This then reloades the page and the API get called and it works perfect. If I dont clear the cache its just a blank page.
Here is the code for the API
[HttpPost("api/signin"), HttpGet("api/signin")]
public async Task<IActionResult> SignIn(string type)
{
string provider = type;
// Note: the "provider" parameter corresponds to the external
// authentication provider choosen by the user agent.
if (string.IsNullOrWhiteSpace(provider))
{
return BadRequest();
}
if (!await HttpContext.IsProviderSupportedAsync(provider))
{
return BadRequest();
}
// Instruct the middleware corresponding to the requested external identity
// provider to redirect the user agent to its own authorization endpoint.
// Note: the authenticationScheme parameter must match the value configured in Startup.cs
return Challenge(new AuthenticationProperties { RedirectUri = "api/saveuser" }, provider);
}
Here is the code for the function that opens the new window and points to the API when a button is pressed.
handleLoginSteam = () => {
let winHeight = window.screen.height / 1.5;
let winWidth = window.screen.width / 3;
const win = window.open(
'/api/signin?type=Steam',
'Discord Sign In', "height=" + winHeight + ",width=" + winWidth + ",top=" + ((window.screen.height - winHeight) / 2) + ",left=" + ((window.screen.width - winWidth) / 2));
win.opener.location.reload();
const timer = setInterval(() => {
if (win.closed) {
clearInterval(timer);
this.fetchUserData();
}
}, 500);
}
This is a picture of the new window not calling the api
At this point pressing ctrl shift r is the only way to invoke the steam sign in.
I tried adding a random parameter to the link that gets passed to the window to stop caching but it doesnt work!
If any one can help me out I would be greatly apreciated im quite new to asp.netS.
Went through the code seems you are trying to achieve opens the new window and points to the Signin API when a button is pressed and RefreshParentWindow task. Now, based on your code let’s take a look at your code line-by-line.
This is the code to open a popup window.
const win = window.open(
'/api/signin?type=Steam',
'Discord Sign In', "height=" + winHeight + ",width=" + winWidth + ",top=" + ((window.screen.height - winHeight) / 2) + ",left=" + ((window.screen.width - winWidth) / 2));
On the same page(I assume) you have
win.opener.location.reload(); //this will refresh the parent window
const timer = setInterval(() => {
if (win.closed) {
clearInterval(timer);
this.fetchUserData();
}
}, 500);
}
setInterval() It repeats the statements in the specified intervals REPEATEDLY. Now, you are checking if the window is still opened or not using this line of code.
if (win.closed)
this check every second even if the window is closed!
Reference: https://www.w3schools.com/jsref/prop_win_closed.asp
Now follow these steps:
Open child window using the script on your parent page(you already have it there).
Get rid of the code that checks if the window is closed or not. You don't need it, in any way.
When you are done with adding a new record from you child window call these statements.
opener.location.reload(); //This will refresh parent window.
window.close(); //Close child window. You may also use self.close();
The above two lines will be written on the child page. Maybe on buttons click.
<input type="button" onclick="Signin()" value="Signin" />
function AddRecord(){
//Add newrecord.
opener.location.reload(); //This will refresh parent window.
window.close(); //Close child window. You may also use self.close();
}
Reference : https://www.codeproject.com/Questions/1097734/How-to-reload-refresh-the-parent-window-after-chil
I am calling AMU.loadRecordByKey(widget) in the onAttach event of an edit form (not an insert form) to display the correct record that was previously submitted.
Here is the function from the App Maker University script library:
/**
* Loads a record by Key
* Checks URL first and then uses page parameter RECORD_ID
* Use in the page onAttach, recommend a deticated datasource
* #param {Object} widget Should be the page
*/
AMU.loadRecordByKey = function(widget){
google.script.url.getLocation(function(location) {
var recordId = location.parameter.record_id;
var properties = app.pages[app.currentPage.name].properties;
if (recordId !== undefined && recordId !== null) {
widget.datasource.query.filters._key._equals = recordId;
}else if(properties.RECORD_ID !== null){
widget.datasource.query.filters._key._equals = properties.RECORD_ID;
}else{
// alert('No Record ID Found');
}
widget.datasource.load();
});
};
When the form initially opens its displays fine, but right after the AMU function is called, everything on the form shifts to the left. There is no error in the function (I tested this by putting alerts between every line).
Any ideas?
The script just loads datasource. Once items are loaded rendering engine does its work. So, there is nothing to do with the script, you need to look at your layout.
Keep in mind, that ideally you need to use this script with datasource in manual load mode, to avoid double data loading and blinking.
Side note:
// this
var properties = app.pages[app.currentPage.name].properties;
// can be simplified to this
var properties = app.currentPage.properties;
Sometimes there is a need to have one button which will automatically complete and create a new phone call where some data from the old one are transfered to new one, there is a solution. It is also possible to implement this behaviour on other forms.
Download the Visual Ribbon Editor for CRM 2011/2013 from http://crmvisualribbonedit.codeplex.com/
Create Phone Call Ribbon Scripts which will be used by button created in later steps.
Define the following source of the script created in previous step. The script mark as completed the phone call and open a new phone call - regarding and description fields are sent through url. Therefore the url has to be shortened if it exceeds 2000 chars, otherwise the link does not work.
function SaveAsCompleteAndNew() {
// Attempt to save Activity and Mark it as Complete
SaveAsCompleted();
// If the form is not valid
if (!Xrm.Page.data.getIsValid())
return;
var url = "/main.aspx?etn=phonecall&pagetype=entityrecord&extraqs=";
var regardingString = "";
var regardingValue = Xrm.Page.getAttribute("regardingobjectid").getValue();
if (regardingValue != null) {
regardingString += "®arding_id=" + regardingValue[0].id;
regardingString += "®arding_name=" + regardingValue[0].name;
regardingString += "®arding_type=" + regardingValue[0].entityType;
regardingString = encodeURIComponent(regardingString);
}
var descriptionValue = Xrm.Page.data.entity.attributes.get("description").getValue();
var descriptionString = ((descriptionValue != null) ? encodeURIComponent("description=" + descriptionValue) : "");
// The url length is limited to about 2k chars, otherwise the link cannot be opened. Therefore the length has to be limited.
var maxDescriptionLength = 1970 - (url.length + regardingString.length);
if (descriptionString.length > maxDescriptionLength) {
var shortenedText = descriptionString.substr(0, maxDescriptionLength - 25);
// Patt1 checks if it ends with e.g. %1 and patt2 with %. These are not allowed because they have been reduced by
// substr. Correct format is three chars like %20 for white space. If there are not in correct format, url does not work
var patt1 = new RegExp("%\\d$");
var patt2 = new RegExp("%$");
if (patt1.test(shortenedText))
shortenedText = shortenedText.substr(0, shortenedText.length - 3);
else if (patt2.test(shortenedText))
shortenedText = shortenedText.substr(0, shortenedText.length - 2);
descriptionString = shortenedText + encodeURIComponent("\n...shortened...");
}
var extraqsEncoded = descriptionString + regardingString;
window.open(url + extraqsEncoded);
}
Run Visual Ribbon Editor for CRM 2011/2013, connect to CRM instance, select the Phone Call entity and add a new button "Complete And New" through New button fucntion. Define the following setting on the Details tab:
Note: As you can see there are also icons defined. Load these icons as web resource to the CRM.
Select Action tab and define the action which should be perfomed on click command of "Complete And New button". As a Function Name use the same name as defined in step 3. Library should be a path to the script created also in step 3.
You can also define Display Rules - in our case we show the button only to people who has right to write to the current phone call entry and also if the phone call is in Open status (statuscode = 1).
Save all changes in Visual Ribbon Editor for CRM 2011/2013 and publish them. Also do not forget to publish changes in CRM customization otherwise added webresources are not available.
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.
I would have an openlayers vector layer with features scattered all over the map. I want to be able to click on a feature and have a message display.
I'm not sure if there is a way to add a listener/handler to each feature.
Any ideas?
Add SelectFeture control:
var selectFeature = new OpenLayers.Control.SelectFeature(vector_layer);
map.addControl(selectFeature);
selectFeature.activate();
After that you can listen to select/unselect events on vector layer:
vector_layer.events.on({
'featureselected': function(feature) {
//display your message here
},
'featureunselected': function(feature) {
//hide message
}
});
You need to use a combination of the SelectFeature control and one of the OpenLayers.Popup classes such as OpenLayers.Popup.FramedCloud. Here is an example of just that:
http://openlayers.org/dev/examples/select-feature-openpopup.html
In that example, try using the "draw polygon" option to draw a polygon (double-click on the map to complete the polygon). Then use "select polygon on click" and click on the polygon, and you will get a framed cloud popup.
You can view the source for the page to see how it this is done. Here are the relevant parts of the code. You can, of course, change the message to whatever you want to display in the framed cloud:
var map = <your OpenLayers.Map object>;
var polygonLayer = <your vector layer>;
selectControl = new OpenLayers.Control.SelectFeature(polygonLayer,
{onSelect: onFeatureSelect, onUnselect: onFeatureUnselect});
map.addControl(selectControl); // not in the example, but do this
function onPopupClose(evt) {
selectControl.unselect(selectedFeature);
}
function onFeatureSelect(feature) {
var message = "<div style='font-size:.8em'>Feature: " + feature.id +"<br>Area: " + feature.geometry.getArea()+"</div>";
selectedFeature = feature;
popup = new OpenLayers.Popup.FramedCloud("chicken",
feature.geometry.getBounds().getCenterLonLat(),
null,
message,
null, true, onPopupClose);
feature.popup = popup;
map.addPopup(popup);
}
function onFeatureUnselect(feature) {
map.removePopup(feature.popup);
feature.popup.destroy();
feature.popup = null;
}
Here are the references for the controls you will be using:
http://dev.openlayers.org/apidocs/files/OpenLayers/Control/SelectFeature-js.html
http://dev.openlayers.org/apidocs/files/OpenLayers/Popup/FramedCloud-js.html
If there are many vector layers is it necessary to write "layer_name.events.on ..." for each layer? Is it possible to make a list of layers and assign ".events.on" to all of them?