Category Archives: Javascript and jQuery

Design Manager and The Return of the Snazzy Looking 15 Minute Weather Web Part


You may also be interested in: fpweb.net


 

Editor’s note: Contributor Erik Abderhalden is a consultant with Rightpoint. Follow him @erikboderek

They say there’s only two seasons in Chicago: construction and winter. Thankfully most of the major highway and tolls are construction free now (sans I-90 west of 290), and winter is a while away. Or is it? Chicago weather is downright bizarre. How do you keep tabs on what it’s like outside? 15 minute weather web part to the rescue!

What I love about the 15 minute weather web part is how easy it is to style. Unlike other weather web parts, you can really get into this one and style it however you want. In my initial post this would just be another ol’ web part sitting pretty in a zone. What if I’m too cool for zones? OK Fonzie, chill. We can create the weather web part as a snippet and place it anywhere we want and style it however we want. Since we’re too cool for zones, we can even embedded in – wait for it – the master page.

Thanks to the HTML snippet generator in SharePoint 2013 you can place it anywhere you want in the master page. Here’s how. Make sure Publishing is enabled on your site first.

  1. Download jQuery Tools here and zWeatherFeed here and place them in your site. Download jQuery too – especially if your master page isn’t already using it.
  2. Follow the configuration steps in my original post (stop after the first code block)
  3. Save the code as a text file
  4. Upload the text file to your Style Library and publish it
  5. Follow steps 1-5 here.
  6. In the Design tab select Media and Content > Content Editor
  7. 2013-11-12-WeatherWebpart-Part02-01.png

  8. In the content link property, enter the URL of where the text file from step 4 was uploaded
  9. Expand the Appearance section and set Chrome Type to None
  10. Click the Update button right of the web part properties
  11. 2013-11-12-WeatherWebpart-Part02-02.png

  12. Click Copy to Clipboard. Don’t worry that the preview is empty.
  13. Open up your master page in SharePoint Designer
  14. Make sure you open up the HTML version of your master page and not the .master
  15. Look for SharePoint: AspMenu ID="TopNavigationMenu". A line or two after it there should be a / asp: ContentPlaceHolder> and a / SharePoint: AjaxDelta> . Create a div with the class "weather".
  16. Paste the content copied from the snippet generator inside that div. It should look something like this:
  17. 2013-11-12-WeatherWebpart-Part02-03.png

  18. Save the master page and check out your site

OK – so it looks a little wonky. Let me help you with some CSS. Throw this in a CEWP or reference it via an external stylesheet in your master page. This won’t be perfect because the position of the classes depends on other elements in your master page, but this should whet your appetite.



When done, your web part should look like this. If you have multiple locations in your text file, the web part will rotate through them as well.

2013-11-12-WeatherWebpart-Part02-04.png

I wanted to share one caveat. If you’re using design manager to package up your publishing assets to move between environments or create a boilerplate site template, leaving the 15 minute weather web part, or any other web part embedded in the master page, is a bad idea. Strange things happen when you import the package. I’ll save you the headache now instead of later.

Finally I need to share some credit where credit is due. This post wouldn’t be possible without the help of my awesome coworker Liz Sdregas.

SharePoint: Create a Snazzy Looking Weather Web Part in 15 Minutes or Less


You may also be interested in: fpweb.net


 

Editor’s note: Contributor Erik Abderhalden is a consultant with Rightpoint. Follow him @erikboderek

When people ask me what’s the weather is like outside, I think of Good Morning Vietnam when Robin Williams asks his fictional weather reporter Roosevelt E. roosevelt what the weather’s like. Roosevelt snaps back, "You got a window? Open it."

When it comes to intranet sites, one of the more frequent requests is the ability to display weather. Not everyone in corporate America has the ability to open a window, nay even sit by a window. Thus a weather widget, or in the case of SharePoint a web part, is utilized to showcase the current temperature and give workers something to look forward to when they leave work or plan their weekend.

If you Google "SharePoint weather web part" you get a slew of solutions and they all have different functionality. What if instead of downloading a web part you could use a content editor web part and some JavaScript, CSS and accomplish the same functionality for free? It’s easy to set up and takes about 15 minutes from start to finish.

In this solution, I’ll be utilizing Zazar’s zWeatherFeed JavaScript and some CSS. zWeatherFeed utilizes Yahoo weather and is easily customizable to meet your requirements.

First, download the zWeatherFeed JavaScript here. If you’re like me and reside in the United States, we don’t use Celsius like the rest of the world, so we need to change the script to use Farenheit instead of Celsius. In your favorite script editing program, open up the script you just downloaded. Do a search for "unit" and replace the value of "c" to "f". The location varies if you downloaded the .min.js or .js file. Here’s what you need to look for:

zweather.min.js
unit:"c"
zweather.js
var defaults = { unit: ‘f’,

Great. Upload the JS to a safe place on your SharePoint site.

Now create a new text file. In the text file we’re going to place our code to call the JavaScript, and set up the HTML formatting for the weather.

The code is as follows. Be sure to update line 3 to reflect the JavaScript’s actual location.



In line 7 of the code are all the zip codes the web part will diplay the weather for. You can use up to 10 zip codes so update the code to be reflect all the zip codes you wish to display. When done, upload the file to your site.

Now you’ll need some styling. First things first, download this image and add it to your SharePoint site. This will be used to toggle between the different weather forecasts and displays at the bottom of the web part.

We’re not doing anything fancy here other than following the instructions about styling the .day and .night classes so the web part’s background will reflect if it’s presently day or night in the currently location. You can add the stylesheet to the page via your prefered MO: another CEWP, in the same text file as the JS, an external stylesheet etc.,

However you place the stylesheet, be sure to update line 53 to reference the image you downloaded in the previous step. If you don’t include the reference, no worries, but you won’t have the nice navigation in the web part because that’s what truly defines this web part as snazzy opposed to all those non-snazzy weather web parts.



Be sure to upload the CSS to your site. Now that the fun stuff is done, it’s just configuring the page. Add a content editor web part to the page and reference the JavaScript in the web part’s content link property. Repeat if applicable for the CSS. And voila! You’re now the proud owner of a snazzy looking weather web part!

2013-11-05-WeatherWebpart-01.png

SharePoint 2013 – User Profile Properties through JSOM


You may also be interested in: fpweb.net


 

Editor’s note: Contributor Tahir Naveed is a Microsoft SharePoint Specialist in the New York City region

Within an organization, users are created in an Active Directory and then imported to SharePoint through the User Profile Service. This service creates User Profiles in SharePoint which have properties like name, email, phone number, manager etc as well as some custom properties.

The following script will access four User Profile properties (Title, Department, Office location and Phone) through the JavaScript Object Model:



Example:

1. Create a blank ASPX page.

2. Add a Content Editor Web Part to the page.

3. Copy the above JavaScript on the page to get the following output

2013-10-16-UserProfileJSOM-01.png

Ref1: http://technet.microsoft.com/en-us/library/ee721054.aspx
Ref2: http://technet.microsoft.com/en-us/library/hh147510.aspx

Use KnockoutJs in SharePoint 2013


You may also be interested in: O’Reilly – SharePoint 2010 at Work


 

Editor’s note: Contributor Veena Sarda is a SharePoint Consultant at Tata Consultancy Services. Follow her @writerpurple

In this article we will see how to use Knockout.js in SharePoint 2013. You will not need server side coding, Visual Studio or SharePoint Designer to build this user interface (UI). Only knowledge of knockout.js is required. Before going into details, I am pasting a screenshot of how the UI will look once this sample is built.

2013-10-11-KnockoutJS-01.jpg

We will need to refer to 3 js libraries. I have uploaded them in the document library but as a best practice, create a different JavaScript library for them – jquery-2.0.3.min.js, knockout-3.0.0rc.js and ko.sp-1.0.min.Ex.js

ko.sp-1.0.min.Ex.js can be downloaded from kosp.codeplex.com and has binders of knockout with SharePoint. Some SharePoint data types such as images and hyperlinks need special processing, to get the correct results, which is handled by this library.

We will use a simple example of a product list. Build a SharePoint List – ProductList – with the following columns and add a few records in this list.

2013-10-11-KnockoutJS-02.jpg

Create a Site Page and Insert a Script Editor Web Part from the Media and Content category on to the page.

2013-10-11-KnockoutJS-03.jpg

Click on Edit Snippet and paste the code as below



Click on Insert and Stop Editing. If all goes well you will see the results as shown in first screenshot.

A couple of things to note:

  1. $.getJSON(_spPageContextInfo.webAbsoluteUrl + "/_vti_bin/listdata.svc/ProductList"
    is the main data returning element. Change to your list name and you can add filters at the JSON request to get specific records.
  2. Pagination is controlled in
    self.currentPage = ko.computed(function () {
                                                        var startPageIndex = self.pageNumber();
                                                         var endPageIndex = self.pageNumber() + self.nbPerPage;
                                                             return self.Products().slice(startPageIndex ,endPageIndex );
  3. For image data binding use spSrc (kosp library handles this)

This program can be extended for many other visual effects and checking boundary conditions. This is one of the powerful ways to write a UI without requiring server side coding and without Visual Studio and SharePoint Designer.

Create a ‘Save My Searches’ Feature in SharePoint 2010 with JavaScript/jQuery


You may also be interested in: Creating insightful dashboards in SharePoint – Collabion Charts for SharePoint


 

Editor’s note: Contributor Craig Pilkenton is a Senior Microsoft Consultant for Slalom Consulting.

Summary

In working on SharePoint enhancement projects for clients, I’m eventually asked "How can I save my searches in SharePoint?". After getting ‘that look’ when mentioning how they could use the Alerts and RSS Feed features to have new items pushed to their Inbox, I start into a wistful spiel of how the ‘My Links’ feature used to be available in SharePoint 2007’s Site Actions dropdown, hooked directly to the My Sites for storing and accessing them. I then finish the tale with the sad ending that the link was removed from Site Actions in SharePoint 2010 and only available by going directly to your My Site (if even provisioned!).

While the feature is available in a users My Site, many companies still aren’t giving out those sites to their employees, or even creating that Web Application for us to utilize with a custom Site Action link that could load the hidden Application Page. So what’s a developer to do? That’s right, create a solution with JavaScript/jQuery!

Body

I recently had to come up with such a solution where it all had be client-side code for future migration to SharePoint 2013. Based on their requirements, it needed to have a small visual footprint on the Results page, launch an existing search or quickly save the current search with a custom name tied to the individual saving it, and be able to get to a "My Searches" List on the Global Navigation Bar for launching, editing, or deleting those saved searches. A later feature was added so that when dropped on the Advanced Search Page it would fill in previously chosen values back to their appropriate filter boxes, something missing in normal SharePoint Search.

2013-08-08-SharePointSaveSearch-01.png
1) App overview

What to Save?

When doing a search in SharePoint, we type a keyword into whichever search textbox is being used and after a few seconds our Results page appears with the items most relevant from the Index and some Refiners down the left side. If you look at the URL that was returned for the Results page, you will notice something like this: ("…/Results.aspx?k=workflow"). SharePoint takes our keyword(s) and posts them to the Results page using the QueryString variable called "k=", filling in that pages search textbox with our original query for consistency. Since SharePoint Search is URL-driven, meaning that whatever is after that QueryString variable are the results returned from the index, all we have to do is save that URL to a Custom List for easy access and post it for the user when they want to re-run the search

2013-08-08-SharePointSaveSearch-02.png
2) SharePoint Search URL’s

Where to Save?

So for designing how to save these searches, I started first with the "data layer". For my Custom List to store’s the Search Results URL’s, what columns do I need to create. That way it is easily manageable and accessible. I needed a Hyperlink column for the URL, a Single Line of Text column for the custom title, and a Person or Group column to create a view for only their links. For a nicer-looking link in the List View, I also added a Calculated Column to combine the URL with the custom title that included Path to SharePoint’s "Show Calculated Column HTML" tool.

2013-08-08-SharePointSaveSearch-03.png

2013-08-08-SharePointSaveSearch-04.png
3) My Searches Custom List

How to Save?

Now that we have a place to save the searches in our Site Collection we can start building out our code. Lines 1-25 are the necessary script includes and HTML for the requirements. Starting on line 27-36 are the variables I need to set for future use, including a reference to the hidden User Information List using REST on line 31 we’ll need to link the Search Result to the current user. Lines 38-41 are the $(document).ready() section with the 2 main setup functions running on each page load, getSPuser() & getDropdownData().

Since we can’t directly get the logged-in username, Lines 42- 64 grab the Site Collection ID assigned to our login that SharePoint stores in a special DOM element (Line 42), then queries the User Information List for our NT Logon, along with any other data we’d like. While we’ll just be saving the Site Collection ID into our My Searches List to hook the query to the user, we may need nice names and emails later on.

To get started saving this Search, the user clicks on the right arrows (Image 1) which fires activateSave() (lines 108-121 below) to toggle CSS classes for hiding and unhiding the name textbox and save icon controls. This way the user just sees the saved searches dropdown until they want to save the current Search Result. The code also pre-populates the name textbox with the search keyword for faster saving.

setSave(), which starts on line 65-90, is hooked to the image button from line 21 and fires from the OnClick() event gathering the Title, URL, and SharePoint User ID into a JSON object that is then posted to the My Searches List REST feed using the $.ajax() method (Lines 76-89). This Post saves the data back to the SharePoint List and on success checks the list for new saved searches by user, the getDropdownData() function detailed next, which we just created and then fires activateSave() to toggle the controls again back to their hidden state.

Show the Saved Searches!

With our code in-place to save our searches, we can now focus on the getDropdownData() function (Lines 91-107) which is called as mentioned above during page loads and on success of our save to show searches we already have saved by user. On Line 92 we create a standard REST URL (e.g. "http://myfarm.com/Search/_vti_bin/listdata.svc/MySearches?$filter=(SearchUserId eq xx)&$orderby=Title", etc.) against our My Searches List, limited by the current SharePoint user Id, and use the $.getJSON() command to return JSON data, looping through the results and adding the name & URL’s to our dropdown of saved searches with the Search Result URL as the dropdown item value’s.

Lines 108-121 contain the activateSave() function mentioned above for toggling CSS classes to hide or unhide controls. The fireSavedSearch() function on Lines 122-127 is hooked to the Searches dropdown onclick event (Image 1, Line 10 HTML) and grabs the currently selected value, then setting the browsers window.location equal to that URL. For the setupControls() function located on Lines 128-142, the code is evaluating whether this is a Search Results page to allow the saving functionality, or to just show the My Searches dropdown. This way the Web Part can be added to any page for showing saved searches, but only allow saving from a page that will have our "k=" QueryString variable. This function was later enhanced to evaluate if added to the Advanced Search page (Lines 135-137) and if so, fire the setupAdvancedSearch() function and its children functions detailed below to fill in previously chosen values back to their appropriate filter boxes.

Finally, Lines 143-263 are all the functions necessary to parse the different QueryString variables into the correct textbox or dropdown controls to enhance the Advance Search Page. While not required for the My Saved Searches feature, it helped increase the usefulness of the saved searches by putting the chosen metadata back where it was created.


<script src="../../SiteAssets/JavaScript/jquery-1.9.1.min.js"></script>
<script src="../../../SiteAssets/JavaScript/gRED_Overrides.js"></script>
<link rel="stylesheet" href="/Search/SiteAssets/Scripts/MySearches_Toolbox.css" />

<div id='selectionarea' class='rounded-corners'>
	<table class="csTableFilters">
		<tr>
			<td class="csTdFilters">
				<label id="lblMessages"> </label>
				<SELECT id="ddlFilteMySearches" class="csFilterDropdowns" onclick="fireSavedSearch()">
				</SELECT>
			</td>
			<td class="csTdFilters">
				<INPUT TYPE="BUTTON" VALUE="" ONCLICK="activateSave()" id="btnActivateSave" class="csSearchesBtn">
				</br><label id="lblActivateSave" class="csSearchesLabel">Save Search</label>
			</td>
			<td class="csTdFilters">
				<input type="text" id="txtSearchTitle" class="csTxtFilter hideControl" maxlength="50" />
			</td>
			<td class="csTdFilters">
				<INPUT TYPE="BUTTON" VALUE="" ONCLICK="setSave()" id="btnSetSave" class="csSearchesBtn hideControl">
			</td>
		</tr>
	</table>
</div>
<script type="text/javascript">
var currUrl = window.location.href.toString();
var rootSiteUrl = currUrl.split("Search")[0];
var dataSvc = "_vti_bin/listdata.svc/";
var searchSite = "Search/";
var listUserInfo = "UserInformationList?$filter=(Id eq xx)";
var listMySearches = "MySearches?$filter=(SearchUserId eq xx)&$orderby=Title";
var countMySearches = 0;
var spUserId = "";
var spUserAccount = "";
var spUserName = "";

$(document).ready(function() {
	getSPuser();
	getDropdownData();
});
function getSPuser() {
	spUserId = _spPageContextInfo.userId;
	getSPuserById(spUserId);	
}
function getSPuserById(userId) {
	var strRestFullPath = rootSiteUrl + dataSvc + listUserInfo.replace('xx',userId.toString());
	var foundName = "";
	$.getJSON(strRestFullPath,function(data) {
		
		$.each(data.d.results, function(i,result) {
			var arrNodes = JSON.stringify(result).split(':');
			for (var nde in arrNodes) {
				var currNode = arrNodes[nde];
				if(currNode.indexOf("Account") > -1) {
					spUserName = currNode.toString().split(',')[0].replace(/"/g, "");
				}
				if(currNode.indexOf("WorkEMail") > -1) {
					spUserAccount = currNode.toString().split(',')[0].replace(/"/g, "");
				}
			}
		});
	});
}
function setSave() {
	var newSearch = {
		Title: $("#txtSearchTitle").val().trim(),
		SearchUrl: currUrl.toString(),
		SearchUserId: spUserId,
		AdvancedParamaters: currUrl.split('?')[1]
	};
	var body = JSON.stringify(newSearch);
	var postUrl = rootSiteUrl + searchSite + dataSvc + listMySearches.split('?')[0];
	console.log(postUrl);
	console.log(body);
	$.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        processData: false,
        url: postUrl,
        data: body,
        dataType: "json",
        success: function () {
        	$("#lblMessages").html(" - <font color='black'>Search saved</font>");
        	getDropdownData();
        	activateSave();
        },
        error: function (xhr, status, error) { $("#lblMessages").html(" - <font color='red'>Error: " + xhr.responseText + "</font>"); }
    });
}
function getDropdownData() {
	var restTarget = rootSiteUrl + searchSite + dataSvc + listMySearches.replace('xx',spUserId.toString());
	var elem = $("#ddlFilteMySearches");
	elem.empty();
	
	$.getJSON(restTarget,function(data) {
		$.each(data.d.results, function(i,result) {
			var strItem = result.Title.toString();
			var strVal = result.SearchUrl.toString();
			$('<option />', {value: strVal, text: strItem}).appendTo(elem);
			countMySearches++;
		});
	});
	setupControls();
	elem = $("#ddlFilteMySearches");
	elem.prepend("<option>Select to fire a Search...</option>");
}
function activateSave() {
	if($('#btnSetSave').hasClass('hideControl')) {
		$('#lblFilterSearchTitle').removeClass('hideControl').addClass('showControl');
		$('#txtSearchTitle').removeClass('hideControl').addClass('showControl');
		$('#btnSetSave').removeClass('hideControl').addClass('showControl');
		$("#lblActivateSave").text("Undo Save");
	}
	else {
		$('#lblFilterSearchTitle').removeClass('showControl').addClass('hideControl');
		$('#txtSearchTitle').removeClass('showControl').addClass('hideControl');
		$('#btnSetSave').removeClass('showControl').addClass('hideControl');
		$("#lblActivateSave").text("Save Search");
	}
}
function fireSavedSearch() {
	var ddlVal = $("#ddlFilteMySearches");
	if(ddlVal.get(0).selectedIndex != 0) {
		window.location = ddlVal.val();
	}
}
function setupControls() {
	if(currUrl.toLowerCase().indexOf("/search/pages/results.aspx") == -1) {		
		$('#btnActivateSave').removeClass('showControl').addClass('hideControl');
		$('#lblActivateSave').removeClass('showControl').addClass('hideControl');
		$('.csTableFilters').css('margin-top','10px').css('margin-bottom','-0px').css('margin-right','-100px');
		$('#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036').css('margin-top','-40px');
	}	
	if(currUrl.toLowerCase().indexOf("/search/pages/advanced.aspx") > -1) {
		setupAdvancedSearch();
	}
	$("#txtSearchTitle").val($("#ctl00_m_g_b4ca66bd_4c70_41c3_acff_1f8e44679522_SE86CCFBC_InputKeywords").val());
	var setupLink = "<u><a href='"+rootSiteUrl + searchSite + "Lists/" + listMySearches.split('?')[0]+"' alt='My Searches'>";
	setupLink += "My Searches</a></u>";
	$(".csDivHeaderMsg").html(setupLink);
}
function setupAdvancedSearch() {
	if(currUrl.indexOf("?") > -1) {
		var arrQueryParams = currUrl.split('?k=')[1].split(' ');
		var mainDdlProp = $("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_PS_plb_0");
		var firstTxtProp = $("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_PS_pvtb_0");
		mainDdlProp.change(function () {
			var blCreateDdl = false;
			switch($(this).val()) {
				case "gredprojectname":
				case "gredprotocolid":
				case "gredfunctionalareaname":
					blCreateDdl = true;
					break;
			}
			if(blCreateDdl) {setupKeyDropdown(firstTxtProp,$(this).val(),""); }
			else { removeKeyDropdown($("#ddlProperty_0"),firstTxtProp); }
		});
		
		for(var param in arrQueryParams) {
			var paramVal = arrQueryParams[param];
			var formVal = "";
			if(paramVal.indexOf("ALL") > -1) {
				formVal = fixAsciiToChar(fixAsciiToChar(paramVal,"("),")").split('(')[1].replace(')','');
				$("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_TQS_AndQ_tb").val(formVal);
			}
			if(paramVal.indexOf(""") > -1) {
				formVal = fixAsciiToChar(paramVal,""");
				$("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_TQS_PhraseQ_tb").val(formVal);
			}
			if(paramVal.indexOf("ANY") > -1) {
				formVal = fixAsciiToChar(fixAsciiToChar(paramVal,"("),")").split('(')[1].replace(')','');
				$("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_TQS_OrQ_tb").val(formVal);
			}
			if(paramVal.indexOf("NONE") > -1) {
				formVal = fixAsciiToChar(fixAsciiToChar(paramVal,"("),")").split('(')[1].replace(')','');
				$("#ctl00_m_g_3982de3b_d4aa_46fc_a672_9a8f33c63036_ASB_TQS_NotQ_tb").val(formVal);
			}
			if(paramVal.indexOf(":") >-1) {
				formVal = fixAsciiToChar(paramVal,":").split(':');
				mainDdlProp.val(formVal[0]);
				setupKeyDropdown(firstTxtProp,formVal[0],fixAsciiToChar(formVal[1],"-"));
			}
		}
	}
}
function setupKeyDropdown(cntrlInsertBefore,strKey,strDefaultVal) {
	$("#ddlProperty_0").remove();
	var keywordDropdown = $('<select/>')
    .attr('id', 'ddlProperty_0')
    .attr('class', 'ms-advsrchPropertyDDL csFilterDropdowns')
    .change(function () {
		cntrlInsertBefore.val($(this).val());
	});
    setupDropdowns(keywordDropdown,strKey,strDefaultVal);
    keywordDropdown.insertBefore(cntrlInsertBefore);
    cntrlInsertBefore.val(strDefaultVal).attr('disabled','true');
}
function removeKeyDropdown(cntrlRemove,cntrlToEnable) {
	cntrlRemove.remove();
	cntrlToEnable.val("").removeAttr('disabled');
}
function setupDropdowns(ddlElem,strKey,strDefaultVal) {
	var listTarget = "";
	switch(strKey) {
		case "gredprojectname":
			listTarget = "GREDListProjectName";
			break;
		case "gredprotocolid":
			listTarget = "GREDListProtocolID";
			break;
		case "gredfunctionalareaname":
			listTarget = "GREDListFunctionalAreaName";
			break;
	}
	
	getDropData(listTarget,ddlElem,strDefaultVal);
}
function getDropData(listTarget,targetDropdown,strDefaultVal) {
	var restTarget = rootSiteUrl + dataSvc + listTarget;
	$(targetDropdown).append("<option> </option>");
	$.getJSON(restTarget,function(data) {
		$.each(data.d.results, function(i,result) {
			var strItem = result.Value.toString();
			targetDropdown.append("<option>"+strItem.trim()+"</option>");
		});
        targetDropdown.val(strDefaultVal);
	});
}
function fixAsciiToChar(fullString, asciiTarget) {
	var newString = fullString;
	var counter = 1;
	if(fullString.indexOf(asciiTarget) > -1) {
		var asciiReplacer = "";
		switch(asciiTarget) {
			case '(':
				asciiReplacer = "(";
				break;
			case ')':
				asciiReplacer = ")";
				break;
			case ':':
				asciiReplacer = ":";
				break;
			case '-':
				asciiReplacer = "-";
				break;
			case ':':
				asciiReplacer = ":";
				break;
			case '"':
				counter++;
				asciiReplacer = "";
				break;
		}
		for(var aa = 0; aa < counter; aa++) {
			fullString= fullString.replace(asciiTarget,asciiReplacer);
		}
		newString = fullString;
	}	
	return newString;
}
</script>

Summary

Although I’ve built out a solution using server-side code to put back the ‘missing’ Site Actions link for popping the My Links Application Page, without the availability of My Sites to store those links, creating a custom client-side solution was the only way to deliver this Search enhancement. The end-result is a Web Part that can be dropped on any page for quick access to their saved searches while having a small UI profile and doesn’t require navigating away from the core idea; seeing their favorite Search Results.

Reference Links

SharePoint: I am following


You may also be interested in: Documentation Toolkit for SharePoint


 

Editor’s note: Contributor Nicki Borell is a SharePoint Evangelist & Consultant for Experts Inside. Follow him @NickiBorell.

A new feature in SharePoint 2013 is „Following“. Users can follow nearly everything in SharePoint 2013. Unfortunately you can only get an overview of WHAT you are following using your MySite. The Web Parts / Lists used to store and aggregate that data are not usable in any Team Site etc.

My first thought was that the new and hyped SearchDriven technique could be the way to aggregate all the content a person followed including outside the MySite. FAILURE. The content, for example, stored under “Site contents -> Social” in every MySite cannot be found using search. Also, the suggestion part of the Following Feature is not based on Search Analytics. More details about Suggestions in the context of Following can be found here: https://www.nothingbutsharepoint.com/sites/eusp/Pages/SharePoint-Online-2013-Suggested-Sites-to-Follow.aspx

The facts that matter are:

<!–[if !supportLists]–>· <!–[endif]–>Sites that are being followed by your colleagues.

<!–[if !supportLists]–>· <!–[endif]–>Sites that are being followed by people you’re following will tend to be recommended to you.

<!–[if !supportLists]–>· <!–[endif]–>Sites that are being followed by a large number of people in your organization.

<!–[if !supportLists]–>· <!–[endif]–>Sites that you have modified recently.

To get information about what a user is following we need to use a REST API call. MSDN documentation which can be found here: http://msdn.microsoft.com/en-us/library/dn194080.aspx

Using, for example, that method:

GET http://<siteCollection>/<site>/_api/social.following/my/followed(types=15)

You get a JSON result set with information about which documents, persons and sites the user who fired the query is following.

Using this JSON result and some jQuery magic we can generate a Web Part containing this information in a list view:

2013-07-30-SharePointIAmFollowing-01.jpg

Or the same information in METRO style:

2013-07-30-SharePointIAmFollowing-02.jpg

For the jQuery Script to create that list, go to: https://iamfollowing.codeplex.com//. The solution works with SharePoint 2013 only, on-prem or with O365 / SharePoint Online.

You can use other REST endpoints to feed the jQuery script to get other information’s, too.

A New Concept in SharePoint Collaboration: Self-Publishing Books

 

2013-07-18-SharePointBlackMagicSolutions-02corrected.pngA team of crack writers (that means “very good at what they do”, not “pipe smokers”) from EUSP have been heads down for two months putting together a new type of book: self-published, multiple author SharePoint book. Instead of one person slaving over a hot computer for 9 months to a year, we decided to split up the chores, allocating one chapter per author. Instead of one person proofreading for accuracy, we had 90+ volunteers reading daily, making suggestions and commenting on how to improve the solutions.

Thus was born: Black Magic Solutions for White Hat SharePoint.

How We Did It

We used SPYam as a platform to help us organize the first stage of the project. By using a private channel in Yammer, we were able to hash out ideas, agree on deadlines and exchange the resources that were needed to get started.

A private channel was setup on Yammer for each chapter. A call for community volunteers was made and we were overwhelmed with responses (over 200). Each author was allocated 20 reviewers, who had access to their chapter’s private SPYam channel and could participate in the day-to-day writing and review of the content.

In retrospect, this was one of the smartest things we did. By having input from the volunteers after the initial draft, each chapter had multiple feedback loops that greatly improved the content.

The End Result

The book is in its final stages of production and will be available in the first part of August. I’m very proud to announce pre-sale pricing for Black Magic Solutions for White Hat SharePoint. If you’ll support us by purchasing the book before its release, we’ll treat you right. We’re selling the electronic version of the book for $12.95, but for those that trust in us, believe in us and what we’re doing, we’ll get it to you for $7.95.

So run over to the purchase page, punch the “Buy” button and let’s get this party started! Over 140 of your friends already have.

Thanks in advance for your support.

Table of Contents

  • Preface – Mark Miller
  • Forward – Mark Fidelman
  • If a Brit stumbles in a jQuery forest, does anyone hear his cries? – Dave Coleman
  • Building Solid Script Libraries for your Enterprise – Marc D. Anderson
  • Build a Content Slider Web Part: Dynamic Display of Pictures and Text – Wendy Neal
  • Build an HTML5 Video Gallery – Ben Tedder
  • Modify Your SharePoint 2013 Navigation Menu with a jQuery Plugin – Eric Overfield
  • Creating a Mobile-friendly SharePoint Blog with jQuery Mobile – Josh McCarty
  • Create a Team Site Solution for Running Agile Projects – Paul Tavares
  • Acknowledgements
  • 2013-07-18-SharePointBlackMagicSolutions-01corrected.png
    Pre-publication price: $7.95

HTML5 and SharePoint 2010 – Part 5: Modernizr


You may also be interested in: Documentation Toolkit for SharePoint


 

Editor’s note: Contributor Eric Overfield, a leading expert in SharePoint Branding and User Interface design, is President and co-founder of PixelMill. Follow him @ericoverfield

In my previous articles on HTML5/CSS3 and SharePoint 2010 we inspected HTML5 and CSS3 and endeavored to integrate these into the SharePoint environment. Mostly a success with some caveats. We browsed through the basic requirements necessary to use HTML5 in your SharePoint sites although I referenced a few HTML5 master page templates that have already been built by outstanding SharePoint Branding community members and recommended them as your basis so you could begin with a solid HTML5/SharePoint foundation without having to start from scratch. Let us continue with a look at more caveats.

But many of my users are still on IE7 or IE8

Yes, unfortunately may of our user base is still using IE8 or older. We can complain all we want, but such is life. We saw how we could use html5him to enable HTML5 elements such as <nav> and <header> but what about other features of HTML5 including <canvas>, or how about CSS3 properties such as rounded borders or text shadowing?

Modernizr to the Rescue

What we need is a tool that can provide us the knowledge of what features are available on each client, and if possible provide shims for backward compatibility, and that tool is Modernizr. Modernizr is a lean JavaScript library that can detect HTML5 and CSS3 features available at the client side. After the Modernizr library is loaded by the client during a page view, we can use simple conditional feature detection statements to see if a client’s browser allows a particular feature, say if borderradius (rounded borders) is allowed, or if the video or canvas tag is supported. You can even use Modernizr to detect what kind of video is supported (currently Modernizr checks for ogg, webm and h264 support). There is a large set of features you can check for; for an entire list, review their feature set list.

The point of this is to allow you to use JavaScript/jQuery/CSS to provide different experiences based on each particular client’s browsers available features, without having to keep a master list of user agents handy. This is going to become even more important as we start moving towards responsive sites. Say that you are looking to move your SharePoint videos to the <video> tag. Well, if that tag is not supported in a client’s browser then you could use JavaScript/jQuery to grab the <video> source value and load it into a Flash player, or provide some other fallback measure. Or, say that rounded corners are unavailable then have those clients load a different CSS style sheet that provides different styling. Sure, you could use the <!–[if lt eIE 7]> <![endif]–> tag in your head to load a different css file in for IE7 users but that might not help in the case of video, audio or canvas tags.

Another advantage of Modernizr available to you is the use of polyfills. Polyfills are JavaScript shims that help replicate “modern” api’s in older browsers including shims for tags such as video, audio and canvas. Interesting. There is a polyfill for almost all HTML5 apis and you can learn what is currently available at https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills. If we take the case above of what to do when the <video> element is unavailable, we could use a prebuilt polyfill to replicate the functionality.

Installing It

Enabling the Modernizr library in your SharePoint site is easy but as always with SharePoint, there are tradeoffs. Let’s not forget that SharePoint 2010 was not built with IE9 or HTML5 in mind.We will continue with our sample HTML5 SharePoint site we have been working with.

1. Download the most recent stable Modernizr library from www.modernizr.com. When you download a production build, you get to decide which elements you actually want to check for. Select only those elements which you will provide conditions for to keep the Modernizr footprint to a minimum. Under “Extra” make sure to include Modernizr.load as we will use that for polyfills among other reasons.

2. Add your custom modernizr library to your SharePoint site. In my case I placed this in /Style Library/html5/scripts, and named my custom library modernizr.custom.js.

2013-07-09-SharePoint2010HTML5-Part05-01.png

If you have publishing enabled don’t forget to publish a major version.

2013-07-09-SharePoint2010HTML5-Part05-02.png

3. Open your custom master page and in the head section, you will need to add a <script> tag to link to the Modernizr library. By default a Modernizr script will include html5shiv/html5shim, so we will need to remove the existing html5hiv/html5shiv reference (if you already included it).

2013-07-09-SharePoint2010HTML5-Part05-03.png

Note: In my screenshot you might also notice that I am using a <script> tag instead of a <SharePoint:ScriptLink> tag. I have found that as soon as you use the <SharePoint:ScriptLink> tag, the very large core.js script is loaded as well, even if it is not used, say for an anonymous publishing site.

Note: There have been issues reported about older versions of Modernizr not working well with SharePoint in IE7 and IE8. The primary reason is because Modernizr extends the Array Prototype. Bryan Gulley wrote a blog post a few month back, “How to make Modernizer and other libraries compatible with SharePoint” I recommend you read his post if you run into problems with Modernizr and SharePoint.

4. Finally, use the Modernizr Library. Just by including the reference to the library, it will be loaded by the client on page load. To use Modernizr functions, as an example, check for audio and if supported play the proper audio file:


if (Modernizr.audio) {
    var audio = new Audio();
    audio.src = Modernizr.audio.ogg ? 'background.ogg' :
        Modernizr.audio.mp3 ? 'background.mp3' :
                                  'background.m4a';
   audio.play();
}

Or how about using Modernizr.Load and a polyfill for the canvas:


Modernizr.load([
  {
    test : Modernizr.canvas,
    nope : ['canvas-polyfill.js', ‘canvas-polyfill.css']
  }
]);

This tag will check to see if the canvas tag is supported, and if it is not, load another js and css file. Possibly your canvas-polyfill.js file will use FlashCanvas (http://flashcanvas.net/) to provide a shim so that your canvas will still work. Cool huh? Please note that I have not included the canvas-polyfill.hs or canvas-polyfill.css at this time, they are being referenced for demo purposes only.

2013-07-09-SharePoint2010HTML5-Part05-04.png

For an exhaustive list of properties available to you, please reference the Modernizr feature set list.

Another neat trick worth knowing

One of the outcomes of loading the Modernizr scripts is what it adds to your <html> tag. This is something that you cannot see using the IE Developer tool bar as it will only show you the html tags and their properties at load time, not what is stored in memory. Nor will you see it in the html source file. Let’s take a quick peek.

The <html> tag before Modernizr is added as viewed in the HTML view in Firebug for Firefox. Notice there is no “class” assigned to the <html> tag.

2013-07-09-SharePoint2010HTML5-Part05-05.png

The <html> tag after Modernizr is added. Wow, that is quite a lot of new classes made available to us.

2013-07-09-SharePoint2010HTML5-Part05-06.png

Nice huh? So we can use those additional classes as well to provide simple to complex styles based on what is available. Want to set all video tags to display: none, if the <video> element is unavailable? Add the following to your stylesheet. It’s that quick.


html.no-video video {
                    display: none;
}

I know that I gave a very quick introduction to Modernizr, but it is a simple yet extremely powerful tool. What you need to remember is that Modernizr provides a quick way to check to see if a client browser supports a particular feature. If it does or does not you can then execute additional code. If you are looking for tutorials run a quick search at Google: modernizr tutorial.

What’s Next?

Now that we have a strong foundation to build HTML5 sites, let’s start digging into some of the cool features of HTML5. Why don’t we check out how to use the audio, video or canvas elements with polyfills? Or how about neat CCS3 tricks? Next time my friends. Until then, happy coding.

How to Get Login Name and Display Name using SharePoint 2013 REST API


You may also be interested in: O’Reilly – SharePoint 2010 at Work


 

Editor’s note: Contributor Alex Choroshin is a Sharepoint Team Leader at Bank Leumi. Follow him @choroshin

Working with REST API is quite simple and straight forward. For example when you need to fetch data from a list you can use the following JQuery Ajax code snippet:


jQuery.ajax({
               url: "http://YourSite/_api/web/lists/getbytitle('ListName')/items",
               type: "GET",
               headers: { "Accept": "application/json;odata=verbose" },
               success: function(data, textStatus, xhr) {
                var dataResults = data.d.results;
                alert(dataResults[0].Title);     
                },
               error: function(xhr, textStatus, errorThrown) {
               alert("error:"+JSON.stringify(xhr));
               }
            });

Another good example is when you need to get specific fields like “Title”, “ID” or “Modified” , you can use the "$select keyword.

For example:


url: "http://YourSite/_api/web/lists/getbytitle('ListName')/items$select= Title ,ID, Modified",
            type: "GET",
            headers: { "Accept": "application/json;odata=verbose" },
            success: function(data, textStatus, xhr) {
             var dataResults = data.d.results;
             alert(dataResults[0].Modified);     
                },
            error: function(xhr, textStatus, errorThrown) {
            alert("error:"+JSON.stringify(xhr));
            }
         });   

But what happens when you need to get a user/group field like “Author” ? well, things are not as obvious as they seem.

Unfortunately you can’t use /getbytitle(‘ListName’)/items or /getbytitle(‘ListName’)/items?filter=Author to get the user field since this field does not exist in the response data, but luckily for us we have an “AuthorId” field that (as you already guessed) will get us the user id.

2013-05-06-SharePoint2013REST-01.png

So, after getting the user id from your list you need to make another Ajax call to get the user login name/display name by using /_api/Web/GetUserById method .

Example:


function getUser(id){
var returnValue;
  jQuery.ajax({
   url: "http://YourSite/_api/Web/GetUserById(" + id + ")",
   type: "GET",
   headers: { "Accept": "application/json;odata=verbose" },
   success: function(data) {
           var dataResults = data.d;
      //get login name  
      var loginName  = dataResults.LoginName.split('|')[1];
      alert(loginName);     
      //get display name
      alert(dataResults.Title);
   }
 });
}

2013-05-06-SharePoint2013REST-02.png

Full Example:


jQuery.ajax({
    url: "/SiteName/_api/web/lists/getbytitle('ListName')/items",
    type: "GET",
    headers: { "Accept": "application/json;odata=verbose" },
    success: function(data, textStatus, xhr) {
        var dataResults = data.d.results;
        var resultId = dataResults[0].AuthorId.results[0];        
        getUser(resultId)                                                                          
    },
    error: function(xhr, textStatus, errorThrown) {
        alert("error:"+JSON.stringify(xhr));
    }
});                                                                           
 
function getUser(id){
var returnValue;
  jQuery.ajax({
   url: "http://YourSite/_api/Web/GetUserById(" + id + ")",
   type: "GET",
   headers: { "Accept": "application/json;odata=verbose" },
   success: function(data) {
           var dataResults = data.d;
      //get login name  
      var loginName  = dataResults.LoginName.split('|')[1];
      alert(loginName);     
      //get display name
      alert(dataResults.Title);
   }
 });
}

I would like to thank Ofir Elmishali for helping me with this post.

Hope you’ll find this post helpful.

Integrate OpenStreet Map with SharePoint 2013 Using REST and Leaflet.js


You may also be interested in: Sharegate No-Brainer Migration Tools


 

Editor’s note: Follow contributor Jason MacKenzie @Jason_MacKenzie

SharePoint 2013 has introduced powerful new capabilities to interact and integrate with information stored in SharePoint. Using the new REST API it is possible to build powerful client-side applications that leverage your investment in SharePoint.

Prior to getting into the details of this solution I wanted to point out that I originally built this using SPServices which is the brilliant library that I, and many others, have used to deliver SharePoint-based solutions. When I decided to migrate the platform to the new REST API I felt like I was kicking a trusted friend to the curb. I know – what a nerd. I want to take a moment to give a huge shout out to Marc D. Anderson for his work creating this solution and supporting the SharePoint community.

Now that we’ve gotten that out of the way, let’s take a look at the solution. The client’s use case was as follows: they needed a way to be able to plot office locations on a map with contractually agreed to boundaries around them. These boundaries may be a specific radius around an office location or an irregularly shaped polygon. Once these are plotted on the map they required the ability to plot an address on the map to determine whether the address fell within any of the predefined boundaries.

The solution was originally prototyped using the Google Maps API however there were privacy concerns that ruled this option out. This led to the search for a different solution and I eventually settled on OpenStreetMap. Leaflet is an open-source JavaScript library that facilitates interacting with OpenStreetMap. It is absolutely beautiful in its simplicity and capabilities.

Solution Overview

Let’s take a look at the solution and how to manage the information prior to diving into the code. You’ll note that there are 3 locations plotted on the map. The Ivory Tower popup is the head office and there are two other locations with boundaries around them. Each marker also has a popup associated with it that can be viewed by clicking on it.

2013-04-13-OpenStreetSharePoint2013-01.png

2013-04-13-OpenStreetSharePoint2013-02.png

Entering an address into the text box and searching for it will initiate an AJAX call to the Nominatim tool which is a geocoding service for OpenStreetMap. The result will be placed on the map with a nice marker associated with it. The leaf marker was taken directly from the Leaflet tutorial.

There is a small loading image on the page to let the user know something is happening due to the fact that it can occasionally take a few seconds to plot the location.

2013-04-13-OpenStreetSharePoint2013-03.png

2013-04-13-OpenStreetSharePoint2013-04.png

When the location is plotted we also calculate the distance to the various locations which is displayed in a modal dialog when the user clicks the hyperlink next to the Clear Map button.

2013-04-13-OpenStreetSharePoint2013-05.png

I created a few SharePoint lists to store the required information. The first is a configuration list that is self-explanatory:

2013-04-13-OpenStreetSharePoint2013-06.png

The other is the list that stores our locations and pertinent information about them:

2013-04-13-OpenStreetSharePoint2013-07.png

The purpose of the various columns are:

  • Title: Name of the Location used in map popups.
  • ServiceLocationType: Radius or Irregular
  • Radius: The radius in KM
  • Office Address: The address of the office
  • BoundaryLatLong: The various points that make up the polygon used for the irregularly shaped boundaries
  • OfficeLatLong: the Lat and Long of the office location to be used for plotting on the map.

I want to give a shout out to the author of this tool. It makes plotting polygons and getting the latitude and longitude of the points a snap. Very helpful in this scenario.

Code Overview

All the script was placed into a HTML form web part on a SharePoint page. I will review the various areas of the script and then post the entire script at the end of this article.

The first section is all our links to the required libraries and CSS files along with the DIV that will contain our map and some other HTML elements.


<meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="http://leafletjs.com/dist/leaflet.css" />
    <!--[if lte IE 8]><link rel="stylesheet" href="http://leafletjs.com/dist/leaflet.ie.css" /><![endif]-->

    <input type=text id="txtAddress"  style="width:300px" value="Enter an address to search for..." onfocus="inputFocus(this)" onblur="inputBlur(this)" /><input type="Button" id="btnSearch" value="Search" /><input type="Button" id=btnClearMap value="Clear Map">
    <a style="cursor:hand;" id="ShowDistance">Show Distance to Office Locations</a><img id="loadingImage"  src="/sites/development/IALP Demo/Icons/ajax-loader.gif">


    <br><br>

<div id="map" style="width: 1000px; height: 600px"></div><div id="distance" title="Distance to Office Locations"></div>

        <script src="/sites/SharePointResourceCentre/jQuery and Scripts/jquery.js"></script>
        <script src="/sites/SharePointResourceCentre/jQuery and Scripts/jquery.SPServices-0.7.2.js"></script>
        <script src="/sites/SharePointResourceCentre/jQuery and Scripts/jQuery-ui/jquery-ui.js"></script>
        <link rel="stylesheet" href="/sites/SharePointResourceCentre/jQuery and Scripts/Themes/themes/sunny/jquery-ui.css" />
        
    <script src="http://leafletjs.com/dist/leaflet.js"></script>

The next section involves creating a JavaScript object with methods to load the configuration information. You’ll note that we are making a REST call to SharePoint to get the information from the configuration list in JSON format. Once we have the results we’ll initialize and configure the map.


var HeadOfficeLat = '';
    var HeadOfficeLong = '';
    var HeadOfficeName = '';
    var MapZoomLevel;
    var map;

    SPConfiguration = 
    {

        load: function() 
        {
            //Required for cross site scripting in IE
            $.support.cors = true;
            $.ajax(
                {
                    url: _spPageContextInfo.webAbsoluteUrl + "/IALP Demo/_api/web/lists/GetByTitle('Configuration')/items",
                    method: "GET",
                    headers:
                    {
                            "accept": "application/json;odata=verbose",
                    },
                    success: SPConfiguration.onSuccess,
                    error: SPConfiguration.onError
                }
            );
        },

        onSuccess: function(data){

            var temp;
            var results = data.d.results;
            for (var i = 0; i < results.length; i++) {
                HeadOfficeName = results[i].Title;
                 temp = results[i].HeadOfficeLongLat.split(",");
                HeadOfficeLat = temp[0]
                HeadOfficeLong = temp[1]

                MapZoomLevel = results[i].MapZoomLevel;
            }


        //Initialize the Map
         map = L.map('map').setView([HeadOfficeLat, HeadOfficeLong], MapZoomLevel);
        L.tileLayer('http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png', {maxZoom: 18}).addTo(map);

        },

        onError: function (err) {
            alert(JSON.stringify(err));
        }
    }

The following script deals with populating the locations the map along with their boundaries. Again, we are making a REST call into SharePoint and then working with Leaflet to populate the results.


SPLocations =
    {
 
        load: function() 
        {

            $.support.cors = true;
            $.ajax(
                {
                    url: _spPageContextInfo.webAbsoluteUrl + "/IALP Demo/_api/web/lists/GetByTitle('IALP Service Locations')/items",
                    method: "GET",
                    headers:
                    {
                            "accept": "application/json;odata=verbose",
                    },
                    success: SPLocations.onSuccess,
                    error: SPLocations.onError
                }
            );
        },
 
        onSuccess: function (data)
        {


            var results = data.d.results;
            for (var i = 0; i < results.length; i++) {
                  
             
                //Split the Lat and Long of the office into an array
                 var officeLatLongArray = results[i].OfficeLatLong.split(",")
            
                //Add a new marker for the office location on the map
                var officeMarker =  L.marker([officeLatLongArray[0], officeLatLongArray[1]], {title: results[i].Title}).addTo(map).bindPopup(results[i].Title).openPopup();
                allMarkers.push(officeMarker);

                //If this location has a service radius then plot in on the map
                 if(results[i].ServiceLocationType == 'Radius')
                 {
                     L.circle([officeLatLongArray[0], officeLatLongArray[1]], results[i].Radius * 1000, {
                    color: 'red',
                    fillColor: '#f03',
                    fillOpacity: 0.5
                    }).addTo(map);
                 }
                 
                 //Otherwise it will be an irregularly shaped polygon
                 else
                 {


                     //Split the BoundaryLatLong filed into an array, loop through it and plot the points on the map.
                     var polygonArray = [];

                     var boundaryLatLongArray = results[i].BoundaryLatLong.split(";")
                     for (var iPoint = 0; iPoint < boundaryLatLongArray.length; iPoint++) {

                         var boundaryPointLatLong = boundaryLatLongArray[iPoint].split(",");

                         var latlng = new L.LatLng(boundaryPointLatLong[0], boundaryPointLatLong[1]);
                         polygonArray.push(latlng);
                            
                         var polygon = L.polygon(polygonArray, {
                        color: 'red',
                        fillColor: '#f03',
                        fillOpacity: 0.05
                        }).addTo(map);

                }


             }


            }


            //Put the head office marker on the map last so the popup is the one that is currently active
              var headOfficeMarker = L.marker([HeadOfficeLat, HeadOfficeLong], {title: HeadOfficeName}).addTo(map).bindPopup("<b>" + HeadOfficeName +"</b>").openPopup();

            //Add this marker to the array
            allMarkers.push(headOfficeMarker);

             //Fix a rendering glitch;
            map.setZoom(11);
            map.setZoom(MapZoomLevel);

        },
 
        onError: function (err) {
            alert(JSON.stringify(err));
        }
    };

    SPConfiguration.load();

The last important section involves plotting the user entered location on the map and calculating the distance to the various office locations. This is where we make our call to the Nominatim service to geocode the results for placement on the map.


function addr_search() {

    $("#loadingImage").show();

    var destinationMarker;
    var destinationLat;
    var destinationLong;

    var searchValue = $("#txtAddress").val()

    if (searchValue == '')
    {
        alert('Please enter an address');
        $("#txtAddress").focus();
        $("#txtAddress").css({backgroundColor: 'red'});

    }  
    else
    {

        $("#txtAddress").css({backgroundColor: 'white'});        
        var inp = searchValue;

        //Required for cross site scripting in IE
        $.support.cors = true;

        //Make a call to the Nominatum service and get a JSON result back. This is to get the lat and long of the address being searched for
        $.ajax({
            url: 'http://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(inp),
            cache: false,
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            success: function(data){
                 $.each(data, function(key, val) {
                     destinationLat = val.lat;
                     destinationLong = val.lon;
                     destinationMarker = L.marker([val.lat, val.lon], {icon: greenIcon, title: searchValue}).addTo(map).bindPopup("<b>" + searchValue + "</b>").openPopup();  
                 });

                 //Now we'll loop through the marker array and calculate the distance to each office (as the crow flies)
                 $("#distance").append("<ul>");

                 for(i=0;i<allMarkers.length;i++) {
                    var latlng = allMarkers[i].getLatLng();                    
                    var destinationLatLong = destinationMarker.getLatLng();
                    $("#distance").append("<li><b>" + allMarkers[i].options['title'] + "</b> - " + (latlng.distanceTo(destinationLatLong)/1000).toFixed(2) + " km</li>");
                }

                $("#distance").append("</ul>");
                $("#ShowDistance").show('slow');
                $("#loadingImage").hide();
            },
            error: function(request, status, error){
                alert(error);
            }
        });
    }


}

In this article I have demonstrated how to use the SharePoint REST API in a browser to interact with OpenStreetMap to produce a compelling solution. I’m certain you will agree that the possibilities are limitless!

The full solution is located here.