Displaying hash in URL using location.replace(hash) - javascript

Tech Stack
AngularJS v1.2.28 - cannot change this
Angular Leaflet Directive
Leaflet 1.0.0-beta.2
Esri Leaflet v2.0.0-beta.6
Intro
I've run into some trouble. I'm trying to use leaflet-hash.js as a way to allow users to share their location with others. The problem is that I can get the hash to appear in the URL if I change location.replace(hash). But a few problems occur depending on what I change it to. Below I outline what I'm trying to achieve and what I've already tried and the outcome.
Desired Behavior
The user should be able to access the map page in 4 ways:
Type an address on the homepage and click the search button.
Click a county name on the homepage.
Enter a URL with the map coordinates (e.g.
http://www.myapp.com/map#10/39.4378/-76.5841).
Enter the URL for the map page without the coordinates (e.g.
http://www.myapp.com/map).
The user should also be able to zoom/move around on the map page and the coordinates will change in the URL hash.
If the user only enters the generic map URL (http://www.myapp.com/map) they are automatically taken to the statewide zoom level (http://www.myapp.com/map#8/38.903/-76.776).
The Problem
My problem only concerns numbers 3 and 4 above.
Using the plugin as-is I am redirected back to the homepage regardless of which of the 4 methods above I use. I believe this bit of code is the culprit:
location.replace(hash);
If I change it to:
location.hash = hash;
the hash will appear in the URL but I get the following error and constant page refreshing when I try to access the map page using method 4:
[$rootScope:infdig] 10 $digest() iterations reached. Aborting!
If I change the line to:
location.hash.replace(hash);
I won't get the digest error or the constant page refreshing but the hash isn't in the URL like it should be
(e.g. http://www.myapp.com/map instead of http://www.myapp.com/map#10/39.4378/-76.5841)
This happens regardless of whether I use access method 3 or 4.
My Code
On map load I check for the presence of # and then determine what, if any, geometry I should also be displaying:
leafletData.getMap("map").then(function(map) {
$scope.map = map;
var hash = new L.Hash($scope.map);
var checkLocation = window.location.href;
//No # in URL
if (checkLocation.indexOf("#") <= 0) {
if (searchResults.geojson) {
// dealing w/ polygon
// CODE TO DISPLAY POLYGON
} else if (searchResults.latlng) {
// dealing w/ point
// CODE TO DISPLAY POINT
} else {
// no geometry so go with default view
// CODE TO DISPLAY DEFAULT VIEW
}
}
// # in URL
else {
var url = decodeURIComponent(window.location.href);
if (searchResults.geojson) {
// dealing w/ polygon
// CODE TO DISPLAY POLYGON
} else if (searchResults.latlng) {
// dealing w/ point
// CODE TO DISPLAY POINT
} else {
// no geometry so go with default view
var n = url.lastIndexOf('#');
var hashOnly = url.substring(n);
var mapView = parseUrlHash(hashOnly);
console.log(mapView);
$scope.map.setView([mapView.lat, mapView.lng], mapView.zoom);
}
}
});
Leaflet Hash Plugin Code
I've only posted the part I think is relevant along with my comments. The full code can be found here:
leaflet-hash.js
onMapMove: function() {
// bail if we're moving the map (updating from a hash),
// or if the map is not yet loaded
if (this.movingMap || !this.map._loaded) {
return false;
}
var hash = this.formatHash(this.map);
hash = decodeURIComponent(hash);
if (this.lastHash != hash) {
//[A] Original code
// Automatically redirects you back to the home page
//location.replace(hash);
//[B] Suggestion from GitHub
//https://github.com/mlevans/leaflet-hash/issues/45
// Shows hash in URL but causes this: Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
// and constant page refreshing when trying to access map page using method 4.
location.hash = hash;
//[C] Fixes errors in [B] but hash does not appear in the URL
// I need it to be in the URL so users can share location
//location.hash.replace();
//[D] Same as [C]
//location.hash.replace(hash);
this.lastHash = hash;
}
},
I'm not sure where I'm going wrong. I thought the problem might be in the plugin but perhaps my code is what's causing it?
UPDATE
In app.js I have $locationProvider.html5Mode(true);. Could this be causing a conflict?

perhaps
var curHash = location.hash?location.hash.substring(1);
if (hash!=curHash) location.replace(
curHash?location.href.split("#")[0]+"#"+hash:location.href+"#"+hash
);

Related

Need to be make a billboard entity have selectable call back function and maybe pop up info

I have a Cesium map with a billboard entity which contains an icon. We periodically update the location of the icon.
I need to make the icon selectable on the map, and be able call a callback function, and also maybe pop up some information about the entity.
Is there a clean way of doing this?
updateMover: function (aoMover, aoPosition, aoHeading, aoYaw, aoPitch, aoRoll, aoView) {
aoMover.point = undefined;
aoMover.label.pixelOffset = new Cesium.Cartesian2(0, -50);
if (aoMover.billboard === undefined) {
// If it moves, its not a fixed radar
aoMover.billboard = {
// image: '../images/Green.png', // default: undefined
//...
An entity's name and description fields can be used to pop-up information about the entity.
aoMover.name = 'Plaintext human-readable short name';
aoMover.description = 'Full <strong>HTML</strong> description...';
The HTML can contain any styling or markup, but by default will be placed in a sandboxed iframe to offer some XSS protection from user-supplied data. By default it is shown in a Cesium InfoBox owned by the viewer, which owns the sandboxed iframe. Here's a screenshot, showing the default InfoBox on the right-hand side:
Getting notified when the selection changes is a little trickier, currently this is considered "private" behavior and may change without notice in a future version of Cesium (for example by introducing an official Event to fire). But until an official path exists, you can use something like this:
Cesium.knockout.getObservable(viewer, '_selectedEntity').subscribe(function (entity) {
if (Cesium.defined(entity)) {
console.log('Selected ' + (entity.name || entity.id));
} else {
console.log('De-selected.');
}
});

Disable view picker DIV is not accessible in MS CRM

I am trying to disable view picker on account lookup in 2016.
The code works fine I mean I am not getting any error but the code not able to find the particular DIV of "parentaccountid".
Where as I can see it in Elements:
But it's returns {null} when the code try to get the elements of DIV - document.getElementByid("parentaccountid");
My code runs on load of opportunity page.
Even at this step I can see the particular DIV element- id: parentaccountid
Where as I still get {null} value:
Where as in 2013 I have seen it working pretty fine.
Sorry I don't have data to share here but it works fine.
Here is the code below:
function DisablePick()
{
VULoader();
//call this function from OnLoad handler
function VULoader(){
var myLookup1;
alert("Hello..I am Here");
var fetch = "<fetch mapping='logical'>"
+"<entity name='account'>"
+"<attribute name='name'/>"
+"<filter type='and'>"
+"<condition attribute='name' operator='eq' value='Blue Yonder Airlines (sample)' />"
+"</filter>"
+"</link-entity>"
+"</entity>"
+"</fetch>";
myLookup1 = new XrmLookupField("parentaccountid");
myLookup1.AddLockedView(
//sViewId
myLookup1.NewGuid() ,
//sEntityName
"account",
//sViewDisplayName
"My Locked Custom View",
//sFilterXml
fetch,
//sFilterLayout
layout(1, "name", "accountid")
.column("name", 200)
.toString()
);
}
function XrmLookupField(sId) {
var xlf = this;
//control instance
xlf.Ctl = Xrm.Page.getControl(sId);
//dom instance
xlf.Dom = document.getElementById(sId);
//jquery instance
xlf.$ = $(xlf.Dom);
/* 2013 addition --- Inline Control instance --- */
xlf.$i = $("#" + sId + "_i");
//use that to disable the view picker
xlf.DisableViewPicker = function () {
/* 2013 addition --- The attribute capitalization changed */
xlf.SetParameter("disableviewpicker", "1");
}
//use that to enable the view picker
xlf.EnableViewPicker = function () {
/* 2013 addition --- The attribute capitalization changed */
xlf.SetParameter("disableviewpicker", "0");
}
//set undocumented attributes
xlf.SetParameter = function (sName, vValue) {
xlf.$.attr(sName, vValue);
/* 2013 addition --- Also change the inline contorl value */
xlf.$i.attr(sName, vValue);
}
//add locked view
xlf.AddLockedView = function (sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout) {
//first enable the view picker
xlf.EnableViewPicker();
//add the custom view (last parameter set the view as default)
xlf.Ctl.addCustomView(sViewId, sEntityName, sViewDisplayName, sFilterXml, sFilterLayout, true);
//lock the view picker
xlf.DisableViewPicker();
}
//create new guid
xlf.NewGuid = function () {
var d = new Date().getTime();
var guid = '{xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx}'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x7 | 0x8)).toString(16);
});
return guid;
}
}
}
Few points to mention:
I am calling DisablePicker method on load of an opportunity page.
Yes, I have already checked whether any duplicate id is there or not but I didn't find any.
I tried to run this method on load of Account field as well but nope no response.
If I change the property disableviewpicker value from 0 to 1 in DIV itself manually from browser after page gets loaded & open the lookup then it will take the effect of the changed value.
Really I am getting no clue why it's behaving this manner, I just need to know where exactly I am going wrong or it's a product bug but I don't think it is. As its a very basic behavior.
PS: For my non MS CRM friends I cannot change the DIV or anything except my JavaScript code.
Your form code executes in a frame that is separate from the actual CRM form; it runs in the ClientApiWrapper.aspx page. Therefore, to access the form element you want to modify from your form script, you form code should do parent.document.getElementById instead of document.getElementById.
When you use your browser's dev tools, the default frame that the console executes against is also not the frame that has your form elements, and it's also not the frame that has your form's code. The simplest way to execute against the correct frame is to use the function in your dev tools to switch frames. In Firefox, that button looks like this:
It's easiest to set your frame to the ClientApiWrapper.aspx one, as it provides access to both the libraries loaded on your form as well as the CRM client-side API.

Can't figure out History JS

I am trying to implement a navigation to my ajax controlled site, and I am encountering some strange errors.
I am using History JS, the HTML5 only version.
This is how I initialize it:
function initializeHistory() {
var History = window.History;
if ( !History.enabled ) {
console.log("Not enabled!");
return false;
}
// Changing the main page state to -1.
History.replaceState({id:-1}, baseTitle, baseUrl);
// Bind to StateChange Event
History.Adapter.bind(window,'statechange',function(){
var State = History.getState();
console.log(History.savedStates);
if (historyData.manualStateChange)
{
if (State.data.id=='-1') alert('History start');
historyData.allowHistoryPushPop=false;
var gotoState=historyData.data[State.data.id];
var currentState=historyData.currentNavigation;
/* Some code here to revert to the previous state */
historyData.allowHistoryPushPop=true;
}
History.log(State.data, State.title, State.url);
});
};
I am using a global object, named historyData, in which I store the following things:
var historyData={
data: new Array(), //an array which contains objects that refer to the data that needs to be shown, when back or forward button is pushed
manualStateChange: true, //using this to figure out if the back or forward button was pressed in the browser, or if I am adding or removing a state from History programmatically
allowHistoryPushPop: true, //using this to prevent history from being changed in certain situations
currentNavigation: {
page: 'dashboard',
pageid: null,
module: null,
moduleid: null
}, // stores the current object from the historyData.data array
status: true // boolean that enables or disables History on my page
}
Whenever I click on a link in my page, a function, called navigation fires, which changes the History's state, and eventually runs a chain of functions to display the page which was asked for by the user. The relevant parts from the navigation function are as follows:
function navigation(gotofunc, navData) {
if ((historyData.allowHistoryPushPop) && (historyData.status))
{
if (navData['urlData']==null) // if this is null, then the title and the url for the asked page, will be returned by the server, after an ajax call, so we set it to the current url and current title
{
var curState=History.getState();
urlData={
title: curState.title,
url: curState.url
};
} else {
urlData=navData['urlData'];
if (!urlData['title']) urlData['title']=curState.title;
if (!urlData['url']) urlData['url']=curState.url;
}
navData['parameters']=new Array();
if (arguments.length>2) for (i=2;i<arguments.length ;i++) navData['parameters'].push(arguments[i]);
historyData.manualStateChange=false; // we are making a programmatic change, so we don't want the binded 'statechange' to fire
historyData.data.push(navData); // store the history data in our own array
History.pushState({id:historyData.data.length-1}, urlData['title'], urlData['url']); // push the History state, and set it's id to point to our newly added array element
historyData.manualStateChange=true; // re-enable the manual state change
}
}
If I don't know up front what the URL will be after I fetch the data via ajax, my Ajax call, replaces the current state with the correct data, this way:
if ((dataArray['navDat']) && (historyData.status))
{
historyData.manualStateChange=false;
var State = History.getState();
History.replaceState(State.data, dataArray['navDat']['title'], dataArray['navDat']['url']);
historyData.manualStateChange=true;
}
This works fine for the most part. If I navigate forward a few pages, and then go backwards, everything works greatly, if I then go forward once again(all by using the browsers back and forward button), it works greatly. There is only one exception: if I load the page, load a subpage, then try to click on the back button, this line never fires:
if (State.data.id=='-1') alert('History start');
It basicaly won't figure out that I arrived back at the front page, when I only navigate one page forward.
The other strange thing is the following(perhaps this is what's causing my original problem also): I tried fetching the savedStates of the History object, to see what is going on, and strangely, when I use the replaceState event, it adds a new state in savedStates. One of the objects that is added, is with the correct data id, the other one is with the previous data id.
What could be causing the problem, is there an error in my script somewhere? Or is this completly normal? (the part of adding multiple objects to History.savedStates after replaceState)
Thanks for the help in advance!
If I replace the url, or the title of the page with the first replaceState, the program realizes when I return to the front page, I dunno why, but till then this is the best solution I can figure.

Backbone router not triggering view change

I've been trying to solve a pretty irritating for the last couple of days now, and I'm finally admitting defeat, and appealing to SO.
First I'll give an overview of what I'm trying to do, then I'll give you specifics of where I'm running into issues.
The Goal:
A user is filling out a form, and if any element is changed on the form and the user tries to leave the form without saving the changes, a modal should display with the following message:
You have made changes to this record. Do you want to save the changes?
The user gets a Yes/No/Cancel option.
If the user selects:
Yes: The record should save, then navigate to the originally intended route.
No: The record should not save, and the user should be navigated to the intended route.
Cancel: The modal should close and the user will remain on the current page with no route change.
I am using the backbone.routefilter library to detect route changes before the route change happens.
The Problem:
To solve the problem, I put a route change listener within my initialize method in the view that contains the form. Below is that code:
// If the user tries go to a different section but the record has been changed, confirm the user action
app.circulationRouter.before = function( route, params ) {
app.fn.clearNotifications();
if(app.recordChanged){
var confirmView = new app.views.confirm({
header:"Patron Record Changed",
bodyText:"You have made changes to this record. Do you want to save the changes?",
trueLabel:"Yes",
falseLabel:"No",
cancelLabel:"Cancel",
hasTrue:true,
hasFalse:true,
hasCancel:true,
trueCallback:function(){
// Ignore the patron record change
_this.saveRecord();
// Render the patron section AFTER the save
_this.model.on({
'saved' : function(){
// Render the new seciton
app.circulationRouter.renderPatronSection(_this.model, params[1]);
app.currentPatronSection = params[1];
// Set the record changed to false
app.recordChanged = false;
// Remove the 'before' listener from the circulationRouter
app.circulationRouter.before = function(){};
if(con)console.log('in saved');
// Remove the listener
_this.model.off('saved');
},
'notsaved' : function(){
// Stay on the patron record page, keep the url at record
app.circulationRouter.navigate("patrons/"+_this.model.get('PatronID')+"/record",false);
_this.model.off('notsaved');
}
}, _this);
},
falseCallback:function(){
if(con)console.log(params);
if(params.length){
// Ignore the patron record change
app.circulationRouter.renderPatronSection(_this.model, params[1]);
app.currentPatronSection = params[1];
}else{
if(con)console.log('blah');
setTimeout(function(){
if(con)console.log('should navigate');
app.circulationRouter.navigate("", true);
}, 5000);
}
app.recordChanged = false;
app.circulationRouter.before = function(){};
},
cancelCallback:function(){
// Stay on the patron record page, keep the url at record
app.circulationRouter.navigate("patrons/"+_this.model.get('PatronID')+"/record",false);
}
});
app.el.append(confirmView.render().el);
return false;
}
};
Each of the three options has a callback that gets called within the initialize function that will control the outcome. The Cancel button always behaves correctly. The issue I'm running into is when the user wants to navigate to the home page, ie when this line is called: app.circulationRouter.navigate("", true);. Everything else works correctly if there is a new, defined route to navigate to.
Here is the sequence of events that creates the issue:
1) Modify a record
2) Try to navigate to the home page
3) Route is changed home page route, but record is still in view
4) Modal is automatically displayed with three options
5) Select the No button
6) falseCallback is triggered
7) Modal is closed and view remains on record page, but route displayed in browser is for home page
The expected behavior for #7 was to display the view for home page, but only the url reflects that.
You can see in the falseCallback I even tried delaying the trigger for the redirection to make sure it wasn't a DOM issue, but that didn't work.
Does anyone know what may be happening?
Here's the problem. Once the route is changed, if you re-navigate to the same route, even if you set the param {trigger:true}, the page won't reload. There is a long discussion about it here.
Based off of the discussion listed in github, I am using this solution and adding the refresh option. If the refresh option is set to true, then the view will reload, even if the url doesn't change. Otherwise, the functionality remains the same.
_.extend(Backbone.History.prototype, {
refresh: function() {
this.loadUrl(this.fragment);
}
});
var routeStripper = /^[#\/]/;
var origNavigate = Backbone.History.prototype.navigate;
Backbone.History.prototype.navigate = function (fragment, options) {
var frag = (fragment || '').replace(routeStripper, '');
if (this.fragment == frag && options.refresh)
this.refresh();
else
origNavigate.call(this, fragment, options);
};

Determining creation date of an image in Javascript?

I'm attempting to create a page that displays and refreshes an image from a webcam (camimg.jpg), but displays a static image (notlive.jpg) if the image hasn't been updated recently. I've found the AJAXCam script, which serves the purpose of reloading the "live" image at a set interval (15 secs), yet with no Javascript experience I can't figure out how to determine when the image was last updated in order to decide whether or not camimg.jpg or notlive.jpg should be displayed.
My limited experience with programming has me thinking it should be something along the lines of:
pageLoaded = time page loaded in browser
imageUpdated = time the image was uploaded to server
if imageUpdated is not within 20 seconds of pageLoaded:
display notlive.jpg
else:
run AJAXCam
The AJAXCam code (initially called by <body onLoad="holdUp()">) is as follows:
<script type="text/javascript">
<!--
//
//AJAXCam v0.8b (c) 2005 Douglas Turecek http://www.ajaxcam.com
//
function holdUp()
{
//
// This function is first called either by an onLoad event in the <body> tag
// or some other event, like onClick.
//
// Set the value of refreshFreq to how often (in seconds)
// you want the picture to refresh
//
refreshFreq=15;
//
//after the refresh time elapses, go and update the picture
//
setTimeout("freshPic()", refreshFreq*1000);
}
function freshPic()
{
//
// Get the source value for the picture
// e.g. http://www.mysite.com/doug.jpg and
//
var currentPath=document.campic.src;
//
// Declare a new array to put the trimmed part of the source
// value in
//
var trimmedPath=new Array();
//
// Take everything before a question mark in the source value
// and put it in the array we created e.g. doug.jpg?0.32234 becomes
// doug.jpg (with the 0.32234 going into the second array spot
//
trimmedPath=currentPath.split("?");
//
// Take the source value and tack a qustion mark followed by a random number
// This makes the browser treat it as a new image and not use the cached copy
//
document.campic.src = trimmedPath[0] + "?" + Math.random();
//
// Go back and wait again.
holdUp();
}
// -->
</script>
Is what I'm trying to accomplish even possible? And if so, how would I go about implementing it? Thanks in advance for the help!
There is no way to do it in javascript.
Your only option is, in case you have access to server side languages like PHP you can send a simple HEAD request and check Last Modified header of the image then report it back to the browser via Ajax.

Categories