<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>DaveBouwman.com</title>
    <description>Writing complex Javascript applications has never been a simple matter, but frameworks like EmberJs take much of the pain away. Join me as I share my experiences building large-scale applications on the web.</description>
    <link>http://davebouwman.com/</link>
    <atom:link href="http://davebouwman.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 28 Aug 2017 10:47:01 -0600</pubDate>
    <lastBuildDate>Mon, 28 Aug 2017 10:47:01 -0600</lastBuildDate>
    <generator>Jekyll v3.2.1</generator>
    
      <item>
        <title>Demystifying oAuth and ArcGIS</title>
        <description>&lt;p&gt;Over the years I’ve talked with a lot of developers who find oAuth to be mysterious, and really, when we watch what’s happening “on-the-wire”, it’s not too complicated. In this post we will review how it works, and we will implement it in ~120 lines of commented javascript. Let’s jump in!&lt;/p&gt;

&lt;h2 id=&quot;what-is-oauth&quot;&gt;What is oAuth?&lt;/h2&gt;
&lt;p&gt;Let’s start by recalling the web before oAuth - where you had a login at every different site… and of course you used good security practices and had a different password for every site… (me neither). So, while painful to manage, it was theoretically “ok”. But then we started building applications that integrated with other systems. Now we have a problem - how can application A take actions in system B on a user’s behalf &lt;strong&gt;without having the users credentials?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the problem that oAuth solves&lt;/strong&gt; - it allows applications to use a third-party system for authentication, as well as managing access to services provided by that system.&lt;/p&gt;

&lt;h2 id=&quot;three-components&quot;&gt;Three Components&lt;/h2&gt;
&lt;p&gt;oAuth provides three main things:&lt;/p&gt;

&lt;h3 id=&quot;identity&quot;&gt;Identity&lt;/h3&gt;

&lt;p&gt;We see “Sign In with Facebook” buttons on all manner of sites - from &lt;a href=&quot;http://strava.com&quot;&gt;Strava&lt;/a&gt; to &lt;a href=&quot;http://codepen.io&quot;&gt;CodePen&lt;/a&gt;, millions of applications now leverage the fact that 3+ billion of us have Facebook accounts, and none of us want to create “yet-another-account”. Via oAuth, we can use our Facebook identity on other sites.&lt;/p&gt;

&lt;h3 id=&quot;privileges&quot;&gt;Privileges&lt;/h3&gt;

&lt;p&gt;When you use an oAuth provider to access a site, it will usually ask for some sort of privilege, allowing it to act on your behalf - i.e. Post to your Facebook timeline, or store items in your ArcGIS Online organization. For Facebook there are a wide range of grants that can be requests, but for ArcGIS Online it’s all or nothing, which makes things simpler for us.&lt;/p&gt;

&lt;h3 id=&quot;revokability&quot;&gt;Revokability&lt;/h3&gt;

&lt;p&gt;Should you decide you no longer want the application to have access to your account, you can revoke access at the origin - i.e. at Facebook - instead of having to find the site, login, and flail around trying to delete your account. In my mind, this is a huge feature.&lt;/p&gt;

&lt;h2 id=&quot;arcgis-oauth&quot;&gt;ArcGIS oAuth&lt;/h2&gt;
&lt;p&gt;Similar to Google/Facebook/GitHub and others, ArcGIS Online / Portal / Enterprise support authentication via oAuth - specifically oAuth 2. This article will focus on the “web flow” (also known as the ‘implicit grant’), which is specific to web applications - if you are working with a desktop or native application you should use the “authorization code grant” flow. &lt;a href=&quot;http://resources.arcgis.com/en/help/arcgis-rest-api/#/Authorize/02r300000214000000/&quot;&gt;Documentation here&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-steps&quot;&gt;The Steps&lt;/h2&gt;

&lt;p&gt;Before talking about code, let’s review the basic steps.&lt;/p&gt;

&lt;h3 id=&quot;application-boots&quot;&gt;Application Boots&lt;/h3&gt;
&lt;p&gt;Your application loads in a browser, and shows the user a screen saying they need to sign-in in order to do amazing things.&lt;/p&gt;

&lt;h3 id=&quot;user-clicks-sign-in-button&quot;&gt;User Clicks Sign-In button&lt;/h3&gt;
&lt;p&gt;The application constructs a url to the ArcGIS Online Authorize login page, and it &lt;em&gt;either&lt;/em&gt; opens a pop-up window (what we will do), &lt;em&gt;or&lt;/em&gt; redirects the entire browser to this url.&lt;/p&gt;

&lt;p&gt;This url contains two key pieces of information - a &lt;code class=&quot;highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and a &lt;code class=&quot;highlighter-rouge&quot;&gt;redirect_uri&lt;/code&gt;. More on these later, but they basically tell ArcGIS Online what application the user is authenticating with.&lt;/p&gt;

&lt;h3 id=&quot;user-provides-credentials-to-arcgis-login-screen&quot;&gt;User Provides Credentials to ArcGIS Login Screen&lt;/h3&gt;
&lt;p&gt;This is the key part - &lt;strong&gt;the user provides their credentials to a page hosted by ArcGIS Online&lt;/strong&gt;. Your super-cool application never has access to their credentials.&lt;/p&gt;

&lt;h3 id=&quot;user-submits-credentials-and-arcgis-online-approves-access&quot;&gt;User Submits Credentials, and ArcGIS Online Approves access&lt;/h3&gt;
&lt;p&gt;A number of things happen at this point - and I think this is where it seems somewhat magical…  but bear with me - it’s not that complex:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the login form is POSTed to ArcGIS Online&lt;/li&gt;
  &lt;li&gt;assuming the username &amp;amp; password checkout, it returns a page asking you to allow the app access to your account&lt;/li&gt;
  &lt;li&gt;clicking that button then POSTs another form back to ArcGIS Online, which then responds with a page that has a meta tag that redirects the browse… to the &lt;code class=&quot;highlighter-rouge&quot;&gt;redirect_uri&lt;/code&gt; we passed in.&lt;/li&gt;
  &lt;li&gt;The browser then redirects to that page, which loads up, and &lt;strong&gt;passes the url&lt;/strong&gt; to your application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See that’s not too bad?&lt;/p&gt;

&lt;h3 id=&quot;secret-sauce&quot;&gt;Secret Sauce…&lt;/h3&gt;
&lt;p&gt;So the really crafty bit here is that the &lt;code class=&quot;highlighter-rouge&quot;&gt;redirect_uri&lt;/code&gt; that ArcGIS Online returns to the browser is a hashed-url. This means that the url has a &lt;code class=&quot;highlighter-rouge&quot;&gt;#&lt;/code&gt; in it - followed by the access token. Now, the cool thing about browsers is that they don’t send the hash or anything following that to the server.&lt;/p&gt;

&lt;p&gt;The browser is told to load &lt;code class=&quot;highlighter-rouge&quot;&gt;http://yourcoolapp.com/redirect.html#token=SEEKRET&amp;amp;username=joeuser&lt;/code&gt;, but the only part that goes over the wire is &lt;code class=&quot;highlighter-rouge&quot;&gt;http://yourcoolapp.com/redirect.html&lt;/code&gt;. Once that page is loaded, we can access the entire url (including the hash) in javascript… and thus get the token. Since the authorize page @ ArcGIS is loaded over https, and the hash does not go over the wire, we can have a secured authentication system even when our app runs on http. That said - be sure that all requests sent with a token use &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt;!&lt;/p&gt;

&lt;h2 id=&quot;the-code&quot;&gt;The Code&lt;/h2&gt;
&lt;p&gt;This demo app is about as stripped down as it can get and still be readable and have all the features. &lt;strong&gt;Please note&lt;/strong&gt; - this is &lt;em&gt;not&lt;/em&gt; production code! My point here is to strip back the mysteries so we can see what’s happening. The general process will work for any application, but when building a real application, you’d want to work with an established session management system for whatever framework you are using - i.e. &lt;a href=&quot;http://vestorly.github.io/torii/&quot;&gt;torii&lt;/a&gt; for EmberJs etc.&lt;/p&gt;

&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;allow a user to sign-in&lt;/li&gt;
  &lt;li&gt;use &lt;code class=&quot;highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; to persist authentication info so they can be automatically logged in if they return to the app before their token expires&lt;/li&gt;
  &lt;li&gt;allow a user to sign out, which clears their current session and removes entry in &lt;code class=&quot;highlighter-rouge&quot;&gt;localStorage&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;when a user is authenticated, show some information about them from their AGO user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-1-registering-an-application-with-arcgis-online&quot;&gt;Step 1: Registering an Application with ArcGIS Online&lt;/h2&gt;
&lt;p&gt;Sign in at the &lt;a href=&quot;https://developers.arcgis.com&quot;&gt;ArcGIS for Developers&lt;/a&gt; site, and hit the plus icon at the top, and create a “New Application”. Fill in the basic information, and then go to the “Authentication” tab and leave it open. You will need the &lt;code class=&quot;highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and once you host your app somewhere, you will need to enter some valid &lt;code class=&quot;highlighter-rouge&quot;&gt;redirect_uri&lt;/code&gt;s. You can start with &lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:8080/redirect.html&lt;/code&gt; if you cloned and are running locally with &lt;code class=&quot;highlighter-rouge&quot;&gt;http-server&lt;/code&gt; (see below)&lt;/p&gt;

&lt;h2 id=&quot;step-2-clone-the-repo-and-install-dependencies-or-view-the-demo-app&quot;&gt;Step 2: Clone the Repo and Install Dependencies or View the Demo App&lt;/h2&gt;
&lt;p&gt;The repo for this example is at &lt;a href=&quot;https://github.com/dbouwman/vanilla-arcgis-oauth&quot;&gt;https://github.com/dbouwman/vanilla-arcgis-oauth&lt;/a&gt; - you can either view it on github or clone it and follow along like that. The demo site is at &lt;a href=&quot;http://vanilla-arcgis-oauth.surge.sh/&quot;&gt;http://vanilla-arcgis-oauth.surge.sh/&lt;/a&gt; - all the code is inline in the page, so open the console and drop in breakpoints.&lt;/p&gt;

&lt;h2 id=&quot;step-3-page-scaffolding&quot;&gt;Step 3: Page Scaffolding&lt;/h2&gt;
&lt;p&gt;I simply created a basic page that loads the Bootstrap 3.x css from a CDN. Almost all our javascript is in-line in the page. If you are looking to optimize pageload, you could strip this down to ~30 lines of code, and stuff it in the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; if you wanted to initiate fetching a webmap or some other resource as early as possible (read: before any frameworks have had time to load)&lt;/p&gt;

&lt;h2 id=&quot;step-4-javascipt&quot;&gt;Step 4: Javascipt!&lt;/h2&gt;
&lt;p&gt;I put a lot of comments in the code, so here it is…&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;c1&quot;&gt;// Setup some vars...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Client Id from your Application Registration at developers.arcgis.com&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clientId&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'MReeG1Zt9JCulHLM'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// change this if you are working with porta/enterprise&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;portalBaseUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'https://www.arcgis.com'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// does not matter what this is but it's used twice, so it's in a var&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;localStorageKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'agoauth'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// simple &quot;session&quot; object&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;isAuthenticated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;portalInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// With that setup, when the page loads, it will check for&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// a persisted session and it will automatically log the user in&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// check if the browser supports localStorage...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authObjItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorageKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObjItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// localStorage stores keys with STRING values!&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObjItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// use cookies - you're on your own for this :)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// authObj = someCookieParsetFunction()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// if we got an&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// check if the token has expired&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nowTs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;validUntil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nowTs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// token should be valid...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;validateToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;updateUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ZOMG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;did&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bad&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;occured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// no worries, we assumed the user was not authenicated&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;c1&quot;&gt;// To valudate a token we make a call to the portals/self end-point.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// If this is successful it will return a big fat object with information&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// about the org's portal and the current user.&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;validateToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// We have pulled in fetch via polyfill.io, so this should work in oldish browsers&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/sharing/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// fetch does not auto-parse into json... so...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Esri API's rarely use http error codes, but they may return an error payload&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;// store the authObj in local storage&lt;/span&gt;
          &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorageKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// redirect.html simply calls this function, with the url&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// from there, we parse out the hash into a set of key/values&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// and stuff that into an object that our session then stores&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;checkOAuthResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// url will look like: &quot;http://localhost:8080/redirect.html#access_token=THE-TOKEN&amp;amp;expires_in=7200&amp;amp;username=dbouwman&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// parse the href - this could be tighter, esp if you have something like lodash loaded...&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'#'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'&amp;amp;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'='&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'='&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// the response has an expires_in value that is seconds-from-now...&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// lets turn that into a real date, so we can check if the token is valid later w/o doing an xhr&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;validUntil&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;expires_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;validateToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authObj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;updateUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'error authenticating! Check the console!'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Start the sign-in process by creating the authorize url...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// To make this code easier to deploy, we construct the currentBaseUrl&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// using the browser's current location, and use that to construct the redirect_uri&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Remember - your application registration at ArcGIS Online MUST include the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// redirect_uri that you specify here, or it will barf at you!&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;startSignIn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentBaseUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'//'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authorizeUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/sharing/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oauth2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;redirect_uri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;currentBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/redirect.html`&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;
&lt;/span&gt;      &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authorizeUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'authWindow'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'menubar=no,location=yes,resizable=no,scrollbars=no,status=no,width=500,height=550'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Signing out is simple - just null out the session properties and kill the item&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// from localStorage&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;signOut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;removeItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;localStorageKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;updateUI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Normally, you'd use a framework of some sort for UI updates etc...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// But we're keep'in it real (simple) here today...&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updateUI&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isAuthenticated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signInBlock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'style'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'display:none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signOutBlock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'style'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'display:block'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// ES6 templates ftw! But only on very modern browsers!&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userInfo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;h3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Hello&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/h3&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;You&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;are&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;part&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;portalInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/p&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;        &lt;span class=&quot;err&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'user-info'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signInBlock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'style'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'display:block'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signOutBlock'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'style'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'display:none'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'user-info'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// attach event handlers&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signInBtn'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'click'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;startSignIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'signOutBtn'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'click'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;signOut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;redirecthtml&quot;&gt;Redirect.html&lt;/h2&gt;
&lt;p&gt;This file is super simple - it literally get’s a reference to the window that opened it, and calls the &lt;code class=&quot;highlighter-rouge&quot;&gt;checkOAuthResponse&lt;/code&gt; function, then closes itself.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;closeWindow&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;win&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'_top'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'true'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;win&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opener&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;win&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opener&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkOAuthResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;opener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkOAuthResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;closeWindow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkOAuthResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkOAuthResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And that is it!&lt;/p&gt;

&lt;p&gt;If you cloned the repo, you can run &lt;code class=&quot;highlighter-rouge&quot;&gt;npm install&lt;/code&gt; which will install two packages&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;http-server&lt;/code&gt;, which is a simple command-line http server - great for very simple projects like this&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;surge&lt;/code&gt;, which allows you to host static websites, for free, with a single command. Sweeet.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 27 Aug 2017 00:00:00 -0600</pubDate>
        <link>http://davebouwman.com/2017/08/27/authentication-with-arcgis/</link>
        <guid isPermaLink="true">http://davebouwman.com/2017/08/27/authentication-with-arcgis/</guid>
        
        
      </item>
    
      <item>
        <title>Ember Engines: Helper Not Found</title>
        <description>&lt;p&gt;Today I was doing some research using Ember 2.8 and &lt;a href=&quot;https://github.com/dgeb/ember-engines&quot;&gt;Ember-Engines&lt;/a&gt;, and I ran into an issue that had me stumped for a few hours.&lt;/p&gt;

&lt;p&gt;I had created a new ember engine addon as per the steps listed in the &lt;a href=&quot;https://github.com/dgeb/ember-engines&quot;&gt;Ember-Engines&lt;/a&gt; README, and was consuming the engine in the Dummy app inside the addon. Pretty standard stuff.&lt;/p&gt;

&lt;p&gt;Part of my research was to make sure that the internationalization system we use (&lt;code class=&quot;highlighter-rouge&quot;&gt;ember-intl&lt;/code&gt;) worked with engines. So after getting some basic stuff working, I started to add some internationalization into a template served from my engine.&lt;/p&gt;

&lt;p&gt;Ember intl uses a helper – `` – and as soon as I dropped this into the page, I started to get errors in the console.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Uncaught Error: Assertion Failed: A helper named &quot;t&quot; could not be found
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So - the first thing I did was drop a `` into the index template in the Dummy app itself - and that worked, which told me &lt;code class=&quot;highlighter-rouge&quot;&gt;ember-intl&lt;/code&gt; was installed, and working as far as the Dummy app was concerned.&lt;/p&gt;

&lt;p&gt;After stepping through a bunch of Ember core code, I was able to resolve the issue by simply moving the &lt;code class=&quot;highlighter-rouge&quot;&gt;ember-intl&lt;/code&gt; entry from &lt;code class=&quot;highlighter-rouge&quot;&gt;devDependencies&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;dependencies&lt;/code&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;ripple-effects&quot;&gt;Ripple Effects&lt;/h3&gt;
&lt;p&gt;This got me past my first problem… but then I ran into my next issue. The Ember Intl service gets initialized in the application route of the host app. But&lt;/p&gt;
</description>
        <pubDate>Tue, 04 Oct 2016 00:00:00 -0600</pubDate>
        <link>http://davebouwman.com/2016/10/04/ember-engines-helper-not-found/</link>
        <guid isPermaLink="true">http://davebouwman.com/2016/10/04/ember-engines-helper-not-found/</guid>
        
        
      </item>
    
      <item>
        <title>DevSummit and DevMeetup Videos</title>
        <description>&lt;p&gt;Just a quick note that the video of the Javascript Unit Testing talk from the Esri 2014 Developer Summit is now up. We gave the talk twice, and while there are two recordings of the talk, the first one had some video and audio issues, so the one below is the one to watch (literally!)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://video.esri.com/watch/3432/testing-tools-_and_-patterns-for-javascript-mapping-apps&quot;&gt;Direct link to video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this talk I start things off with the “zen of testing”, then David Spriggs talks about using &lt;a href=&quot;http://theintern.io/&quot;&gt;the Intern&lt;/a&gt;, followed by Tom Wayson discussing &lt;a href=&quot;http://karma-runner.github.io/0.12/index.html&quot;&gt;Karma&lt;/a&gt;, and I close the talk with &lt;a href=&quot;http://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; + &lt;a href=&quot;http://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt; + automation.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href=&quot;http://proceedings.esri.com/library/userconf/devsummit14/papers/dev-058.pdf&quot;&gt;PDF of the slide deck&lt;/a&gt; as well – may be useful in discussions with others.&lt;/p&gt;

&lt;p&gt;All the tools we discuss in the session are listed in &lt;a href=&quot;this%20file&quot;&gt;https://github.com/tomwayson/esri-js-testing-tools-and-patterns&lt;/a&gt; in Tom’s github account.&lt;/p&gt;

&lt;h4 id=&quot;speaker-info&quot;&gt;Speaker Info&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dbouwman&quot;&gt;Dave Bouwman&lt;/a&gt; – &lt;a href=&quot;https://twitter.com/dbouwman&quot;&gt;@dbouwman&lt;/a&gt; – &lt;a href=&quot;http://www.davebouwman.com/&quot;&gt;http://blog.davebouwman.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/davidspriggs&quot;&gt;David Spriggs&lt;/a&gt; – &lt;a href=&quot;https://twitter.com/davidspriggs&quot;&gt;@davidspriggs&lt;/a&gt; – &lt;a href=&quot;http://latitudeagile.com/&quot;&gt;http://latitudeagile.com/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tomwayson&quot;&gt;Tom Wayson&lt;/a&gt; – &lt;a href=&quot;https://twitter.com/tomwayson&quot;&gt;@tomwayson&lt;/a&gt; – &lt;a href=&quot;http://tomwayson.com&quot;&gt;http://tomwayson.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;devmeetup-video&quot;&gt;DevMeetup Video&lt;/h3&gt;

&lt;p&gt;I also gave a quick presentation at the &lt;a href=&quot;http://www.meetup.com/devmeetupcolorado/events/164540862/&quot;&gt;Fort Collins Dev Meetup&lt;/a&gt;, and recorded a screencast. In it, I talk about the soon-to-be-released-in-beta &lt;a href=&quot;http://opendata.arcgis.com/about&quot;&gt;ArcGIS Open Data&lt;/a&gt; project at a high-level, and then demonstrate the front-end developer workflow related to automated linting and unit testing. I then showed some of our integration tests running (using selenium, driven by mocha + wd.js). Finally I talked about some work I’m doing taking “best practices” from the Open Data project and creating &lt;a href=&quot;http://yeoman.io/&quot;&gt;yeoman&lt;/a&gt; scaffolders to help people start off new projects with all the infrastructure in place. For this demo, I scaffolded and published a (really simple) web app up to github pages in ~2 minutes. As this moves forward I’ll be posting about the scaffolder’s themselves as well as how to create scaffolders.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://youtu.be/N0ZeQtNvIQQ&quot;&gt;Direct link to video&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;related-posts&quot;&gt;Related Posts&lt;/h4&gt;

&lt;p&gt;In the video I demonstrate a number of the tools and concepts that I’ve written about in these posts…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.davebouwman.com/2014/03/25/js-code-coverage/&quot;&gt;Chasing Numbers: Pragmatic Javascript Code Coverage&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.davebouwman.com/2014/01/09/javascript-2014/&quot;&gt;Looking Ahead: Javascript in 2014&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.davebouwman.com/2014/01/04/javascript-fu/&quot;&gt;Resources for Leveling up your Javascript-Fu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 18 Apr 2014 10:44:48 -0600</pubDate>
        <link>http://davebouwman.com/2014/04/18/dev-videos/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/04/18/dev-videos/</guid>
        
        
        <category>Uncategorized</category>
        
      </item>
    
      <item>
        <title>Chasing Numbers: Pragmatic Javascript Code Coverage</title>
        <description>&lt;p&gt;When writing unit tests, we want to spend our time wisely, and focus our efforts on areas of the application where test coverage provides the most benefit - typically this means business logic, or other complex “orchestration” code. We can gain insight into this by using “code coverage” tools, which report back information about the lines of code that area executed during your tests.&lt;/p&gt;

&lt;p&gt;Coverage is typically reported as a percentages - percentage of statements, branches, functions and lines covered. Which is great… except what do these numbers really mean? What numbers should we shoot for, and does 100% statement code coverage mean that your code is unbreakable, or that you spent a lot of time writing “low-value” tests? Let’s take a deeper look.&lt;/p&gt;

&lt;p&gt;Here is the output from our automated test system (grunt + jshint + jasmine) on our project. Included is a “coverage summary.&lt;/p&gt;

&lt;p&gt;These numbers have been holding steady throughout the development cycle, but what do these numbers mean? Lets break them down a little&lt;/p&gt;

&lt;h2 id=&quot;coverage-measures&quot;&gt;Coverage Measures&lt;/h2&gt;

&lt;p&gt;The first one is “statements”. In terms of code coverage, a “statement” is the executable code between control-flow statements. On it’s own, it’s not a great metric to focus on because it ignores the control-flow statements (if/else, do/while etc etc). For unit tests to be valuable, we want to execute as many code “paths” as possible, and the statements measure ignores this. But, it’s “free” and thrown up in our faces so it’s good to know what it measures.&lt;/p&gt;

&lt;p&gt;Branches refer to the afore mentioned code-paths, and is a more useful metric. By instrumenting the code under test, the tools measure how many of the various logic “branches” have been executed during the test run.&lt;/p&gt;

&lt;p&gt;Functions is pretty obvious - how many of the functions in the codebase have been executed during the test run.&lt;/p&gt;

&lt;p&gt;Line is by far the easiest to understand, but similar to Statements, high line coverage does not equate to “good coverage”.&lt;/p&gt;

&lt;h2 id=&quot;summary-metrics&quot;&gt;Summary Metrics&lt;/h2&gt;

&lt;p&gt;With that out of the way, let’s look at the numbers.&lt;/p&gt;

&lt;p&gt;Since statements is not a very good indicator, let’s skip that. We notice it, but it’s not something we strive to push upwards.&lt;/p&gt;

&lt;p&gt;On Branches we are at ~42%, which is lower than I’d like, but we’ll get into this in a moment.&lt;/p&gt;

&lt;p&gt;Functions are ~45%, but this is a little skewed because in many controllers and models we add extra functions that abstract calls into the core event bus. We could inline these calls, but that makes it much more complex to create spies and mocks. In contrast, putting them into separate functions greatly simplifies the use of spies and mocks, which makes it much easier to write and maintain the tests. So, although creating these “extra” methods adversely impacts this metric, it’s a trade off we are happy with.&lt;/p&gt;

&lt;p&gt;Where does this leave us? These numbers don’t look that great do they? Yet I’m blogging about it… there must be more.&lt;/p&gt;

&lt;h2 id=&quot;detailed-metrics&quot;&gt;Detailed Metrics&lt;/h2&gt;

&lt;p&gt;These summary numbers tell very little of the story. They are helpful in that they let us know at a glance if the coverage is heading in the right direction, but as “pragmatic programmers” our goal is to build great software, not maximize a particular set of metics. So, we dig into the detailed reports to check where we have acceptable coverage.&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;DetailedCoverage.png&quot; src=&quot;/img/2014/03/DetailedCoverage.png&quot; alt=&quot;Detailed Code Coverage&quot; width=&quot;600&quot; height=&quot;570&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The report is organized around folders in the code tree, and summarizes the metrics at that level. I’ve sorted the report by “Branches”, and while we can see a fair bit of “red” (0-50% coverage) in that table, the important thing is that we know what has low coverage - as long as we are informed about the coverage, and we decide the numbers are acceptable, the coverage report has done it’s job. &lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;FileLevelCoverage.png&quot; src=&quot;/img/2014/03/FileLevelCoverage.png&quot; alt=&quot;File Level Coverage&quot; width=&quot;600&quot; height=&quot;56&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Diving down to the file level, we can check if we have high-levels of coverage on the parts of the code that really matter. For us, the details controller and view are pretty critical, so we can see that they have high coverage. It should be noted that high coverage numbers don’t always tell the whole tale. For critical parts of our code base, we have many suites of tests that throw all manner of data at the code. We have data fixtures for simulating 404’s from APIs, mangled responses, as well as many “flavors” of good data. By throwing all this different data at our objects we have ‘driven’ much of the code that handles corner cases.&lt;/p&gt;

&lt;p&gt;Here is a look at the Models in our application.&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;ModelCoverage.png&quot; src=&quot;/img/2014/03/ModelCoverage1.png&quot; alt=&quot;Model Coverage&quot; width=&quot;600&quot; height=&quot;146&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can easily tell that our models have very good coverage - and this recently helped us a ton when we refactored our back-end API json structure. Since the models contain the logic to fetch and parse the json from the server, upgrading the application to use this new API was relatively simple: create new &lt;a href=&quot;https://github.com/velesin/jasmine-jquery#json-fixtures&quot;&gt;json fixtures&lt;/a&gt; from the new API, run the tests against the new fixtures, watch as most of the tests fail, update the parser code until the tests pass and shazam, the vast majority of the app “just worked”. Without these unit tests, it would have taken much longer to make this change.&lt;/p&gt;

&lt;p&gt;The system we use allows us to dive down even further - to check the actual line-by-line coverage.&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;line-coverage.jpg&quot; src=&quot;/img/2014/03/line-coverage2.jpg&quot; alt=&quot;Line coverage&quot; width=&quot;500&quot; height=&quot;326&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;adding-code-coverage-reports&quot;&gt;Adding Code Coverage Reports&lt;/h2&gt;

&lt;p&gt;There are a number of different tools that can generate code coverage reports. On our project we are using &lt;a href=&quot;http://gotwarlost.github.io/istanbul/&quot;&gt;istanbul&lt;/a&gt;, integrated with jasmine using a &lt;a href=&quot;https://github.com/maenu/grunt-template-jasmine-istanbul&quot;&gt;template mix-in&lt;/a&gt; for &lt;a href=&quot;https://github.com/gruntjs/grunt-contrib-jasmine&quot;&gt;grunt-contrib-jasmine&lt;/a&gt;. Istanbul can also be used with the &lt;a href=&quot;http://theintern.io/&quot;&gt;intern&lt;/a&gt;, and &lt;a href=&quot;http://karma-runner.github.io/&quot;&gt;karma&lt;/a&gt; test runners. If you are using &lt;a href=&quot;http://visionmedia.github.io/mocha/&quot;&gt;Mocha&lt;/a&gt;, check out &lt;a href=&quot;http://blanketjs.org/&quot;&gt;Blanket.js&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you are just getting into unit testing your javascript code, this is kinda the deep-end of the pool - so I’d recommend checking out jasmine or mocha, and get the general flow of js unit testing going, look at automating things with a runner like karma or jasmine, and then look at adding coverage reporting.&lt;/p&gt;

&lt;p&gt;Hopefully this helps show the benefit of having code coverage as part of your testing infrastructure, and helps you ship great code!&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Mar 2014 16:21:05 -0600</pubDate>
        <link>http://davebouwman.com/2014/03/25/js-code-coverage/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/03/25/js-code-coverage/</guid>
        
        <category>Jasmine</category>
        
        <category>javascript</category>
        
        <category>unit-testing</category>
        
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Transitioning To Javascript</title>
        <description>&lt;p&gt;Over the past few weeks I’ve been getting quite a few people asking questions about transitioning to javascript - perhaps this recent post about &lt;a href=&quot;http://blogs.esri.com/esri/arcgis/2014/02/21/esris-roadmap-for-web-developers&quot;&gt;Esri’s Roadmap for Web Developers&lt;/a&gt; has spurred more people into action - whatever the cause, I thought I’d share a few thoughts and links.&lt;/p&gt;

&lt;p&gt;First off, now is a great time to get into javascript! jQuery has leveled the playing field across browsers, and the truly horrible versions of Internet Explorer are nearly in behind us. The community is exploding, and it seems every day there is some new exciting project in javascript.&lt;/p&gt;

&lt;h2 id=&quot;javascript-application-architecture&quot;&gt;Javascript Application Architecture&lt;/h2&gt;

&lt;p&gt;I’ve got some great news: over the last few years javascript has matured as a language and as a community. No longer are javascript applications “spaghetti” code by default, and cross-browser issues are much less common and painful than in the past. Myriad &lt;a href=&quot;https://www.google.com/search?q=model+view+whatever&quot;&gt;Model-View-Something&lt;/a&gt; frameworks exist to provide structure for your code, and if you’re doing anything more complex than “Hello World” I’d strongly recommend investing in learning one (or more).&lt;/p&gt;

&lt;p&gt;I was going to list out a bunch of frameworks along with pros and cons, but then I remembered this video by &lt;a href=&quot;http://vimeo.com/73913825&quot;&gt;Rob Conery titled “Javascript Infero”&lt;/a&gt;. I really like this talk as it compares 4 javascript frameworks - &lt;a href=&quot;http://knockoutjs.com&quot;&gt;Knockout&lt;/a&gt;, &lt;a href=&quot;http://backbonejs.org&quot;&gt;Backbone&lt;/a&gt;, &lt;a href=&quot;http://angularjs.org/&quot;&gt;Angular&lt;/a&gt;, and &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember&lt;/a&gt;. Go ahead and click through and watch it now… I’ll wait here…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://vimeo.com/73913825&quot; target=&quot;_blank&quot;&gt;&lt;img style=&quot;display: block; margin-left: auto; margin-right: auto;&quot; title=&quot;conery-javascript-inferno.jpg&quot; src=&quot;/img/2014/03/conery-javascript-inferno.jpg&quot; alt=&quot;Conery javascript inferno&quot; width=&quot;500&quot; height=&quot;346&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;additional-framework-thoughts&quot;&gt;Additional Framework Thoughts&lt;/h2&gt;

&lt;h3 id=&quot;backbonejs--marionette&quot;&gt;BackboneJS + Marionette&lt;/h3&gt;

&lt;p&gt;Backbone was the first of the client-side MV* frameworks that really took off. It’s also barely a framework - very un-opinionated, thus allowing you do to virtually anything. &lt;a href=&quot;http://marionettejs.com/&quot;&gt;Marionette&lt;/a&gt; is a backbone extension that helps developers implement additional patterns by adding in formal Modules, Controllers, Layouts, Regions, various types of views, as well as an Application. Leveraging these greatly streamlines development both by reducing repetitive code and enforcing a development pattern. Personally I liked this stack because it give you lots of freedom, while still providing pattern guidance. Coming from ASP.NET / C# on the backend, this resonated with me. Last spring I did a &lt;a href=&quot;http://blog.davebouwman.com/2013/02/20/part-1-app-design-and-page-layout/&quot;&gt;6 part series&lt;/a&gt; on building a mapping app using Marionette, that would be a good intro if Backbone + Marionette sound appealing.&lt;/p&gt;

&lt;p&gt;For what it’s worth, this is what we used to build the &lt;a href=&quot;http://blogs.esri.com/esri/arcgis/2014/02/10/preview-of-open-data/&quot;&gt;ArcGIS for Open Data&lt;/a&gt; application, as it gave us the benefit of solid structural and architectural patterns, while still leaving us lots of flexibility to implement the interface behavior we wanted.&lt;/p&gt;

&lt;h3 id=&quot;polymer-aka-web-components-aka-the-future&quot;&gt;Polymer (aka Web Components aka The Future)&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://polymer-project.org&quot;&gt;Polymer&lt;/a&gt; is a Google project that lets you build applications based on Web Components. The tricky bit is that &lt;a href=&quot;http://www.w3.org/TR/components-intro&quot;&gt;Web Components&lt;/a&gt; is an emerging W3C standard, and no browsers have support for them yet. Undaunted, Polymer provides a set of polyfills (stop-gap code) that let you build and use web components today (IE10+ and evergreen browsers). This project just hit “alpha” in mid-February 2014, so it’s great for experiments, but I’d recommend staying away from this for production. That said, Web Components will be the future of the web, so it is worth getting a general understanding of the concepts. Another side note - both Ember and Angular are aligning themselves to slip-steam their view infrastructure into web components.&lt;/p&gt;

&lt;h2 id=&quot;general-stuff-you-should-know--use&quot;&gt;General Stuff You Should Know / Use&lt;/h2&gt;

&lt;h3 id=&quot;underscore--lo-dash&quot;&gt;Underscore / Lo-Dash&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://underscorejs.org/&quot;&gt;Underscore&lt;/a&gt; is a utility belt of awesome stuff that should be part of javascript but is not (yet). &lt;a href=&quot;http://lodash.com/&quot;&gt;Lo-dash&lt;/a&gt; is the mo-better-faster implementation of the same library (yeah competition!). Get to know one/both of these, as they will save you a ton of time and effort. What is really great is that these libraries are smart enough to use native implementations of functions when they are available in the running browser, so you can use the same “code” in your app and in down-level browsers it will use a javascript implementation, but in newer browsers it will use the underlying C++ implementation.&lt;/p&gt;

&lt;h3 id=&quot;bootstrap&quot;&gt;Bootstrap&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;http://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt; is a css framework that allows you to create a “reasonable” web app in minutes - no wonder it’s the most popular front-end framework! Sensible defaults based on a responsive base means that you can throw markup into a file, and after only a few minutes reading the documentation, have a site that looks good on a 27inch iMac and on your phone.&lt;/p&gt;

&lt;p&gt;What’s more - Bootstrap is so popular that when you want to level up, there are &lt;a href=&quot;http://www.webdesignerdepot.com/2013/07/how-to-modify-bootstrap-simply-and-effectively&quot;&gt;tutorials on creating themes&lt;/a&gt;, or you can skip that and drop two lattes worth of cash on a &lt;a href=&quot;https://wrapbootstrap.com/&quot;&gt;pre-made theme&lt;/a&gt;. Boom. Beautiful, and you don’t have to fight the css. Bootstrap also comes with a bunch of optional javascript helpers. Start by using them, and then as you transition up to using a framework like Angular/Ember/Backbone, you can switch over.&lt;/p&gt;

&lt;h2 id=&quot;getting-started8230&quot;&gt;Getting Started…&lt;/h2&gt;

&lt;p&gt;In the famous words of Nike: Just Do It. Start something - anything. Throw it up on github. A great starting point for working with maps is the &lt;a href=&quot;https://github.com/esri/bootstrap-map-js&quot;&gt;bootstrap-map-js&lt;/a&gt; repo. Slap up something simple. Then build something else.&lt;/p&gt;

&lt;p&gt;Will it break? Yes. Will you have problems with Internet Explorer? Yes. Will you scream at your monitor and rue the day you learned how to spell javascript? Likely.&lt;/p&gt;

&lt;p&gt;But honestly, that’s learning. You did that when learning Silverlight or Flex. And really, if you are going to work on the web, javascript needs to be your new best friend. Think of it like this - you could switch to native app development, and then you’d have to know Objective C for iOS, Java for Android, and C# for Windows Phone/8.&lt;/p&gt;

&lt;p&gt;In comparison, javascript is not so bad!&lt;/p&gt;

&lt;h3 id=&quot;resources&quot;&gt;Resources:&lt;/h3&gt;

&lt;p&gt;Checkout my previous blog post on &lt;a href=&quot;http://blog.davebouwman.com/2014/01/04/javascript-fu/&quot;&gt;Leveling up your Javascript&lt;/a&gt; for lots of links.&lt;/p&gt;

&lt;p&gt;YouTube has ton’s of resources for &lt;a href=&quot;http://www.youtube.com/results?search_query=angularjs&quot;&gt;Angular&lt;/a&gt; and &lt;a href=&quot;http://www.youtube.com/results?search_query=emberjs&quot;&gt;Ember&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.html5rocks.com/en/&quot;&gt;Html5Rocks&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://www.polymer-project.org&quot;&gt;Polymer-Project&lt;/a&gt; - lots of info on Polymer and Web Components&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Mar 2014 09:08:41 -0700</pubDate>
        <link>http://davebouwman.com/2014/03/03/transitioning-to-javascript/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/03/03/transitioning-to-javascript/</guid>
        
        <category>javascript</category>
        
        
        <category>Esri</category>
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Telemetry Part 2: The Code</title>
        <description>&lt;p&gt;In &lt;a href=&quot;http://blog.davebouwman.com/2014/02/07/telemetry-p1/&quot;&gt;part 1&lt;/a&gt; we covered the three most common types of telemetry data we want to collect. In this part we will review how to actually implement tracking within your code.&lt;/p&gt;

&lt;p&gt;For this example we are going to use Google Analytics. Simple, Free, and virtually ubiquitous. Of course you could send this information to another service or a custom back-end, but that’s beyond the scope of this discussion.&lt;/p&gt;

&lt;h2 id=&quot;google-analytics-api&quot;&gt;Google Analytics API&lt;/h2&gt;

&lt;p&gt;The three types of telemetry we want to track - page views, user actions and in-browser performance - all map very nicely to three calls in the Google Analytics API:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/pages&quot;&gt;Page Tracking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/events&quot;&gt;Event Tracking&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings&quot;&gt;User Timings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although analytics.js gives you a means to send this information to the backend service, it’s not the sort of thing that we want littered all over our code. Thus…&lt;/p&gt;

&lt;h3 id=&quot;separation-of-concerns&quot;&gt;Separation of Concerns&lt;/h3&gt;

&lt;p&gt;Before we start into the details, let’s talk application design for a moment. While you could simply litter the code with calls to the analytics API, that would make a mess, and be a huge pain should you want to change to use some other tracking system.&lt;/p&gt;

&lt;p&gt;Instead, we want to centralize the tracking and storing of telemetry in a central service. The specifics depend on your framework (Backbone/Angular/Ember/Dojo/other) but you will likely have some sort of “global event bus”, or a core “Application” object which all elements of your application can access.&lt;/p&gt;

&lt;p&gt;In our application, we are using Backbone and Marionette, and so we have added methods to our &lt;a href=&quot;https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md&quot;&gt;“Application”&lt;/a&gt; object, as that is passed into all the modules.&lt;/p&gt;

&lt;h3 id=&quot;tracking-page-views&quot;&gt;Tracking Page Views&lt;/h3&gt;

&lt;p&gt;Again, your application architecture will dictate where to attach these events, but in frameworks that have the concept of a &lt;a href=&quot;http://backbonejs.org/#Router&quot;&gt;router&lt;/a&gt;, that’s a good place to start. For all “navigation” actions in our application, follow the same pattern and centralize things onto an application method, specifically Application.navigate(). Super handy, because we can just drop in the page view logging in this one function as shown below:&lt;/p&gt;

&lt;p&gt;The actual logPageView function is just a call to the analytics function.&lt;/p&gt;

&lt;h3 id=&quot;tracking-user-actions&quot;&gt;Tracking User Actions&lt;/h3&gt;

&lt;p&gt;As we mentioned in the last post, this is simply a means to track what the user has interacted with. So, any place you have DOM event handlers, we want to assign that action a name, and make a call to Application.logUiEvent() method.&lt;/p&gt;

&lt;p&gt;Depending on the amount of “magic” your application framework comes with (I’m looking at you Ember and Angular) this may be more or less difficult. Even with Marionette in the mix, our Backbone based app is pretty un-magical. All DOM interaction happens in handlers, defined in Views. So, all we do is drop in calls to logUiEvent in these handlers. While this could be made even more elegant by overriding the backbone and marionette view event binding infrastructure, in the interest of keeping the code easy to understand, we opted to just add these calls. Here is an example from one of our views.&lt;/p&gt;

&lt;h3 id=&quot;tracking-in-browser-performance&quot;&gt;Tracking In-Browser Performance&lt;/h3&gt;

&lt;p&gt;This is the trickiest of the bunch. As we mentioned, we need two calls - one to setup the timer, and a second to indicate the event we were tracking has completed. Or failed. We also need to handle the case where it does not complete.&lt;/p&gt;

&lt;p&gt;For this we created a timer object - “Took.js” - which grabs a time stamp when it’s instantiated, and calculates the duration until the stop() method is called. We also have two additional methods - store() and log().&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href=&quot;http://jsbin.com/tafe/1&quot;&gt;jsbin&lt;/a&gt; that you can play with as well (obviously it won’t report to Google Analytics)&lt;/p&gt;

&lt;p&gt;We also expose this via the Application object as two simple methods startTimer(name, category, label, maxDuration) and stopTimer(name).&lt;/p&gt;

&lt;p&gt;Through our code we wrap the various blocks we want timing data on in these two calls. Before we show an example, this brings up another area where we have centralized things - xhr calls. Although Backbone has a dependency on jQuery, and we could use $.ajax or $.getJson anywhere in the application, we have decided to route all requests through a central location - again on our Application object.&lt;/p&gt;

&lt;p&gt;Anyhow - here is an example of a call that fetches rows from a remote feature service.&lt;/p&gt;

&lt;h2 id=&quot;using-telemetry&quot;&gt;Using Telemetry&lt;/h2&gt;

&lt;p&gt;At this point, our project has not gone live, so we just have telemetry from dev and test environments in Google Analytics. That said, having this setup well before launch has already helped us re-arrange some of our naming conventions, and validated some ideas about the types of reports we can get out of the system.&lt;/p&gt;

&lt;p&gt;Since we know that javascript performance varies greatly between browsers (orders of magnitude between recent browsers and older versions of Internet Explorer), we really wanted to make sure we could segment our performance data by browser and version.&lt;/p&gt;

&lt;p&gt;Turns out that segmenting the data like this is not “built-in”, but it’s not hard to setup. Basically you create new “segments” and in the UI for that choose “Technology”, and then Browser &amp;amp; Browser Version.&lt;/p&gt;

&lt;p&gt;With this in place, we can now easily compare performance of specific actions between different browsers. NOTE: data in this screen shot is from development - the actual performance is *much* better in production where all the code is combined and minified.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;We hope this helps you get started with telemetry in your javascript applications. This is extremely useful information to have, and now more than ever, it’s very easy to get. Happy coding!&lt;/p&gt;
</description>
        <pubDate>Wed, 12 Feb 2014 13:00:56 -0700</pubDate>
        <link>http://davebouwman.com/2014/02/12/telemetry-part-2-the-code/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/02/12/telemetry-part-2-the-code/</guid>
        
        <category>javascript</category>
        
        
        <category>HTML5</category>
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Truth in Numbers: Telemetry for Javascript Apps</title>
        <description>&lt;p&gt;&lt;img style=&quot;display:block; margin-left:auto; margin-right:auto;&quot; src=&quot;/img/2014/02/telemetry.jpg&quot; alt=&quot;Telemetry&quot; title=&quot;telemetry.jpg&quot; border=&quot;0&quot; width=&quot;600&quot; height=&quot;150&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Designing an application is really a set of educated guesses about what features users will actually use. We do interviews, conduct user testing sessions, and use our existing understanding of the “ideal user”, but without data coming in from real users, we just don’t know. In this two part series we will show you how to get this sort of data.&lt;/p&gt;

&lt;h2 id=&quot;telemetry-the-raw-numbers&quot;&gt;Telemetry: The Raw Numbers&lt;/h2&gt;

&lt;p&gt;The idea here is simple: track user actions in your application and send that information (“telemetry”) to a service that will aggregate it. Most analytics packages do this, but they are oriented towards so called ‘normal’ web pages, and the information they provide for javascript heavy pages (aka apps) is very limited without doing some additional work.&lt;/p&gt;

&lt;h2 id=&quot;types-of-telemetry&quot;&gt;Types of Telemetry&lt;/h2&gt;

&lt;p&gt;We are interested in three classes of telemetry data - Page Views, User Actions, and in-browser performance. In this post we will introduce these classes, give some ideas of what to track, and how to organize things. Next post we will review how to actually implement the tracking.&lt;/p&gt;

&lt;h3 id=&quot;page-views&quot;&gt;Page Views&lt;/h3&gt;

&lt;p&gt;While there are many “page view” tracking techniques, they usually revolve around full-page refreshes. Of course, modern javascript applications eschew the full-page refreshes in favor of a more immersive applications that manipulate the url via &lt;a href=&quot;http://diveintohtml5.info/history.html&quot;&gt;html5 push-state&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Put another way, a user may spend 40 minutes using your web app, but traditional “page view” measurement may only register a single “hit” - when the page first loads.&lt;/p&gt;

&lt;p&gt;Thus, when building rich javascript application, we need to help out and manually collect this information as the user changes pages, or “context” within your application. While most “single-page applications” don’t have clear boundaries between “pages”, we can usually break things down into reasonable units. Most applications will have some “home” or landing view, a search view, one or more types of search results views, item detail views etc etc.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/img/2014/02/server-requests.png&quot; alt=&quot;server-requests&quot; width=&quot;380&quot; height=&quot;219&quot; class=&quot;aligncenter size-full wp-image-1862&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The main thing is to come up with a naming convention for these “pages” that is consistent and makes logical sense.&lt;/p&gt;

&lt;p&gt;We will look at the details of how to integrate this sort of tracking in the next post, but if your application has a “router”, that is likely a good place to attach page view logging.&lt;/p&gt;

&lt;h3 id=&quot;user-actions&quot;&gt;User Actions&lt;/h3&gt;

&lt;p&gt;Being able to track User Action is the core to being able to tell which features are actually being used. Since you are instrumenting the code yourself, you can track virtually anything that raises an event.&lt;/p&gt;

&lt;p&gt;In our case, we want to measure the percentage of users change the base map, the size of the map, and virtually every other interface interaction.&lt;/p&gt;

&lt;p&gt;The really boils down to adding code into every DOM event handler in the app, so how we structure our telemetry helpers will be really important - we don’t want to have brittle code littered all over the app. Think about having a central “telemetry service” that is available to all views or DOM events in the application.&lt;/p&gt;

&lt;p&gt;Having a good naming convention is even more important for this type of tracking since these will likely be added by more than one developer and a mish-mash of naming will make the telemetry data a mess to work with. On the upside, it’s pretty easy to tweak&lt;/p&gt;

&lt;h3 id=&quot;in-browser-performance&quot;&gt;In-Browser Performance&lt;/h3&gt;

&lt;p&gt;The third type of information we want to track is related to performance. For our team, we develop on &lt;a href=&quot;https://www.google.com/intl/en/chrome/browser/canary.html&quot;&gt;Chrome Canary&lt;/a&gt;, on maxed out Retina Macbook Pros while using high-speed internet. Unfortunately not all our users will have such an optimized environment. Add the fact that we are supporting IE8/9/10/11, Chrome, Firefox, Safari and Opera, virtually the only way to get realistic performance information for all those platforms is to harvest it from real users.&lt;/p&gt;

&lt;p&gt;The end-goal of course is to help improve the real-world performance of the application. But, before we start wildly poking around the code base tweaking things we &lt;em&gt;think&lt;/em&gt; may be performance bottle-necks, we want to have the system instrumented so we &lt;em&gt;know&lt;/em&gt; where the real bottle necks are, and that when we deploy changes, we really do see &lt;em&gt;improved&lt;/em&gt; performance.&lt;/p&gt;

&lt;p&gt;So - what do we want to time? Initially, for our project, we want to track basic page load times, network calls (xhr’s), and computationally intensive code blocks (client-side filtering). Some specific timers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;how long did it take to load the page and initialize the app?&lt;/li&gt;
  &lt;li&gt;how long did it take to initialize the map?&lt;/li&gt;
  &lt;li&gt;how long did it take to execute a search?&lt;/li&gt;
  &lt;li&gt;how long did it take to display a layer?&lt;/li&gt;
  &lt;li&gt;how long did it take to sort a table?&lt;/li&gt;
  &lt;li&gt;how long did it take to filter a table?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While page view and events are essentially single calls, tracking timing requires two actions - one to start a timer, and a second to stop it and record the duration. Once again, having sensible, consistent naming is really helpful.&lt;/p&gt;

&lt;p&gt;In the second part of this post, I will talk about how to integrate telemetry into an application.&lt;/p&gt;

&lt;p&gt;Radio Telescope photo modified from &lt;a href=&quot;http://www.flickr.com/photos/shanafin/4142534038/sizes/l/&quot;&gt;Stephen Hanafin&lt;/a&gt;’s Flickr stream. &lt;a href=&quot;http://creativecommons.org/licenses/by-sa/2.0/&quot;&gt;cc by-sa&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 10 Feb 2014 13:00:49 -0700</pubDate>
        <link>http://davebouwman.com/2014/02/10/telemetry-p1/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/02/10/telemetry-p1/</guid>
        
        <category>javascript</category>
        
        
        <category>HTML5</category>
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Working Around Min/Max Scale</title>
        <description>&lt;p&gt;Another quick trick when using the Esri Javascript API. If you run into a scenario where the service you are accessing has min/max scales applied, but you need the data outside that scale range, here is a trick that can help out.&lt;/p&gt;

&lt;p&gt;Before we get to the how, there are a few things you should know about this trick:&lt;/p&gt;

&lt;h3 id=&quot;data-holes-when-max-record-count-bites-back&quot;&gt;Data Holes: When Max Record Count Bites Back&lt;/h3&gt;

&lt;p&gt;Even with the gridded queries that dynamic mode feature layers use, at small scales (zoomed way out) it is common that you will be requesting more than the max record count number of features. When this happens, you get “holes” in the data returned, as shown below.&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;data-holes.png&quot; src=&quot;/img/2014/01/data-holes.png&quot; alt=&quot;data holes due to max record count&quot; width=&quot;600&quot; height=&quot;343&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The default for max record count is usually 1000 features, but I think the latest release bumps this up to 2000. Regardless of the default, this value can be changed as part of the server configuration, so unless you control the server, it’s not something you can change. &lt;/p&gt;

&lt;h3 id=&quot;performance-may-suffer&quot;&gt;Performance May Suffer&lt;/h3&gt;

&lt;p&gt;Scale ranges are commonly used to avoid sending very detailed or dense data over the wire. So, even if the layer you are working with only has a few dozen features, if they are really detailed geometries, things may get really slow, so perhaps reconsider.&lt;/p&gt;

&lt;h3 id=&quot;how-to&quot;&gt;How To&lt;/h3&gt;

&lt;p&gt;It’s actually really easy. Create a feature layer, then in it’s “load” event, reset the min/max scale properties. Then add it to the map.&lt;/p&gt;

&lt;p&gt;Here is a link to a &lt;a href=&quot;http://jsbin.com/EXEwADO/6/edit?html,output&quot;&gt;JSBIN you can play with&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The map below shows a &lt;a href=&quot;http://sampleserver6.arcgisonline.com/arcgis/rest/services/RedlandsEmergencyVehicles/FeatureServer/0&quot;&gt;feature layer&lt;/a&gt; from a demo server that has a minScale of 100,000 shown on a map that is at 1:36,978,595&lt;/p&gt;

&lt;p&gt;[Another quick trick when using the Esri Javascript API. If you run into a scenario where the service you are accessing has min/max scales applied, but you need the data outside that scale range, here is a trick that can help out.&lt;/p&gt;

&lt;p&gt;Before we get to the how, there are a few things you should know about this trick:&lt;/p&gt;

&lt;h3 id=&quot;data-holes-when-max-record-count-bites-back-1&quot;&gt;Data Holes: When Max Record Count Bites Back&lt;/h3&gt;

&lt;p&gt;Even with the gridded queries that dynamic mode feature layers use, at small scales (zoomed way out) it is common that you will be requesting more than the max record count number of features. When this happens, you get “holes” in the data returned, as shown below.&lt;/p&gt;

&lt;p&gt;&lt;img title=&quot;data-holes.png&quot; src=&quot;/img/2014/01/data-holes.png&quot; alt=&quot;data holes due to max record count&quot; width=&quot;600&quot; height=&quot;343&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The default for max record count is usually 1000 features, but I think the latest release bumps this up to 2000. Regardless of the default, this value can be changed as part of the server configuration, so unless you control the server, it’s not something you can change. &lt;/p&gt;

&lt;h3 id=&quot;performance-may-suffer-1&quot;&gt;Performance May Suffer&lt;/h3&gt;

&lt;p&gt;Scale ranges are commonly used to avoid sending very detailed or dense data over the wire. So, even if the layer you are working with only has a few dozen features, if they are really detailed geometries, things may get really slow, so perhaps reconsider.&lt;/p&gt;

&lt;h3 id=&quot;how-to-1&quot;&gt;How To&lt;/h3&gt;

&lt;p&gt;It’s actually really easy. Create a feature layer, then in it’s “load” event, reset the min/max scale properties. Then add it to the map.&lt;/p&gt;

&lt;p&gt;Here is a link to a &lt;a href=&quot;http://jsbin.com/EXEwADO/6/edit?html,output&quot;&gt;JSBIN you can play with&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The map below shows a &lt;a href=&quot;http://sampleserver6.arcgisonline.com/arcgis/rest/services/RedlandsEmergencyVehicles/FeatureServer/0&quot;&gt;feature layer&lt;/a&gt; from a demo server that has a minScale of 100,000 shown on a map that is at 1:36,978,595&lt;/p&gt;

&lt;p&gt;](http://jsbin.com/EXEwADO/6/embed?output){.jsbin-embed}&lt;/p&gt;

&lt;p&gt;And of course you can use the same technique with MapService layers. Shown below is a layer that has a maxScale of 1,000,000 and we are zoomed in well past that.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://jsbin.com/EXEwADO/8/embed?output&quot;&gt;MapService as FeatureService&lt;/a&gt;{.jsbin-embed}&lt;/p&gt;
</description>
        <pubDate>Mon, 03 Feb 2014 08:00:55 -0700</pubDate>
        <link>http://davebouwman.com/2014/02/03/minmax-scale/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/02/03/minmax-scale/</guid>
        
        <category>Esri</category>
        
        <category>javascript</category>
        
        
        <category>ArcGIS Server</category>
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Feature Layers from Map Services</title>
        <description>&lt;p&gt;Here is a lesser known fact: using the &lt;a href=&quot;https://developers.arcgis.com/javascript/&quot;&gt;ArcGIS Javascript API&lt;/a&gt;, you can create a &lt;a href=&quot;https://developers.arcgis.com/javascript/jsapi/featurelayer-amd.html&quot;&gt;FeatureLayer&lt;/a&gt; from any vector layer in a &lt;a href=&quot;http://resources.arcgis.com/en/help/rest/apiref/mapserver.html&quot;&gt;MapService&lt;/a&gt;. Of course it will be read-only, but you still have all the usual control over it in terms of styling and interaction. If that works for you, it’s really simple: just drop the full url to the layer in the map, into the FeatureLayer constructor, and you’re up an running.&lt;/p&gt;

&lt;p&gt;Of course you should be careful with this technique. Many times MapServices are used to display very dense data, so you may end up pulling a lot of data over the wire. But, if you happen to need more interactivity from a layer that’s already published as a MapService, this is a great way to avoid having to publish a FeatureService.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href=&quot;http://jsbin.com/ONIBUtI/3/edit?html,output&quot;&gt;JSBin with the example running&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://jsbin.com/ONIBUtI/3/embed?output&quot;&gt;MapService Layer as FeatureLayer&lt;/a&gt;{.jsbin-embed}&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Jan 2014 22:04:34 -0700</pubDate>
        <link>http://davebouwman.com/2014/01/27/fl-map-services/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/01/27/fl-map-services/</guid>
        
        <category>Esri</category>
        
        <category>javascript</category>
        
        
        <category>Esri</category>
        
        <category>javascipt</category>
        
      </item>
    
      <item>
        <title>Looking Ahead: Javascript in 2014</title>
        <description>&lt;p&gt;&lt;img title=&quot;js-2014-post.png&quot; src=&quot;/img/2014/01/js-2014-post.png&quot; alt=&quot;Javascript in 2014&quot; width=&quot;600&quot; height=&quot;150&quot; border=&quot;0&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ah 2013, what a great year you were – returning to full-time development has been a fantastic change of pace, and I’m super excited about what the next year will bring. In that spirit, here are a few things that the Esri DC team is looking forward to investigating.&lt;/p&gt;

&lt;h2 id=&quot;developer-tooling--automation&quot;&gt;Developer Tooling &amp;amp; Automation&lt;/h2&gt;

&lt;p&gt;This year our team is looking at adding &lt;a href=&quot;http://yeoman.io/&quot;&gt;Yeoman&lt;/a&gt; and &lt;a href=&quot;http://bower.io/&quot;&gt;Bower&lt;/a&gt; into the mix. We’ve been using &lt;a href=&quot;http://gruntjs.com&quot;&gt;grunt&lt;/a&gt; since we started our project, and I can’t imagine doing any front-end development with out it. Yeoman and Bower are companion tools that bring more awesome workflow options for front-end developers. Yeoman focuses on scaffolding out applications or components for you (think Rails or ASP.NET MVC scaffolding but for your javascript). Bower is a dependency management tool which makes it easy to pull down all the front end libraries you use, and easily update them over time. Grunt of course is a javascript task runner that can &lt;a href=&quot;http://gruntjs.com/plugins&quot;&gt;orchestrate things&lt;/a&gt;, and you should be using it. Or &lt;a href=&quot;http://gulpjs.com/&quot;&gt;gulp.js&lt;/a&gt;, which is a “streaming build system”.&lt;/p&gt;

&lt;h2 id=&quot;web-frameworks&quot;&gt;Web Frameworks&lt;/h2&gt;

&lt;p&gt;Although our team, and our primary project uses &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; for a back-end web framework, as javascript developers, we can’t ignore all the activity around &lt;a href=&quot;http://expressjs.com/&quot;&gt;Express.js&lt;/a&gt; &amp;amp;&lt;a href=&quot;http://sailsjs.org/#!&quot;&gt;Sails.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Personally, in switching over to OSX, I’ve been feeling a little off the back in terms of full-stack chops, so I’ve been hacking around with building simple web apps using &lt;a href=&quot;http://expressjs.com/&quot;&gt;Express&lt;/a&gt;, which is a back-end framework built on node.js. Express is the most mature of the node.js “frameworks” and thus the &lt;a href=&quot;http://expressjs.com/api.html&quot;&gt;documentation&lt;/a&gt; is pretty good, and there are good &lt;a href=&quot;http://www.packtpub.com/express-web-application-development/book&quot;&gt;intro&lt;/a&gt; and &lt;a href=&quot;http://www.packtpub.com/advanced-express-web-application-development/book&quot;&gt;advanced&lt;/a&gt; books available. There are also &lt;a href=&quot;https://nodejsmodules.org/tags/expressjs&quot;&gt;a lot of modules&lt;/a&gt; that can be used to extend express.&lt;/p&gt;

&lt;p&gt;Taking it up a level, &lt;a href=&quot;http://sailsjs.org/#!&quot;&gt;Sails.js&lt;/a&gt; is a set of Express middleware that adds “stronger opinions” to Express. It is crazy quick to get started, and makes building out single-page style apps, and APIs a breeze. It’s similar to Rails in that it comes with scaffolding tools – so it’s a simple command to whip up a model and controller at the same time, and have that auto-magically become a REST and Socket.io based API. Again similar to Rails, it also has a plug-able data store model, allowing you to switch between database technologies as your project progresses. Sweet. That said, development is on-going and documentation is limited, so expect to fall back on some Express chops when you start digging into things. From a team perspective, Chris Helm (@chelm) has built the &lt;a href=&quot;https://github.com/Esri/koop&quot;&gt;Koop&lt;/a&gt; project on top of Sails, so stay tuned for some interesting things coming on that front.&lt;/p&gt;

&lt;h2 id=&quot;front-end-javascript&quot;&gt;Front-End Javascript&lt;/h2&gt;

&lt;p&gt;If you are paying any amount of attention to the front-end web development space, you’ve likely heard people raving about &lt;a href=&quot;http://angularjs.org/&quot;&gt;Angular JS&lt;/a&gt;. While I am a big fan of &lt;a href=&quot;http://backbonejs.org/&quot;&gt;Backbone&lt;/a&gt; &amp;amp; &lt;a href=&quot;http://marionettejs.com/&quot;&gt;Marionette&lt;/a&gt;, the more I read about Angular, the more interested I am. Backbone is great when you want “extreme control” of the user experience (as is the case in our project), but in other scenarios, frameworks like Angular are a big win, as they can off-load a lot of work from the developer. On top of that, Angular seems to be able to play-nice with server-rendered markup, which is a big plus for building apps with really fast initial page-load times. Finally, being built by Google, using solid software engineering principles (&lt;a href=&quot;http://docs.angularjs.org/guide/di&quot;&gt;dependency injection&lt;/a&gt; anyone?), with an eye towards our web-componety future (see below), I’d expect Angular to be relevant for quite some time.&lt;/p&gt;

&lt;p&gt;I also found this &lt;a href=&quot;http://blog.nebithi.com/backbone-and-angular-demystifying-the-myths/&quot;&gt;very interesting post about Backbone and Angular &lt;/a&gt;. Not sure I completely agree with everything in there, but if you use backbone, it’s worth reading.&lt;/p&gt;

&lt;p&gt;Speaking of looking forward over the next few years, the &lt;a href=&quot;http://www.w3.org/TR/components-intro/&quot;&gt;proposed web components standard&lt;/a&gt; is extremely interesting, and will be a huge shift in front-end development. At it’s core are 4 technologies (&lt;a href=&quot;https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html&quot;&gt;Templates&lt;/a&gt;, &lt;a href=&quot;http://www.w3.org/TR/shadow-dom/&quot;&gt;Shadow DOM&lt;/a&gt;, &lt;a href=&quot;https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html&quot;&gt;Custom Elements&lt;/a&gt; and &lt;a href=&quot;https://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html#external-custom-elements-and-decorators&quot;&gt;Packaging&lt;/a&gt;) which combined will allow developers to create re-usable “components” for applications. What’s this boils down to is that we will be able to create new html tags and bake in behavior – just like built-in UI components like the &lt;select&gt;. Only much bigger and better. What will be particularly interesting to watch is how application frameworks (Backbone, Ember, Angular) adapt to web components.&lt;/select&gt;&lt;/p&gt;

&lt;p&gt;Thanks to Google, this future is available today. &lt;a href=&quot;https://www.google.com/intl/en/chrome/browser/canary.html&quot;&gt;Chrome Canary&lt;/a&gt; has support for many of the underlying technologies, and the &lt;a href=&quot;http://www.polymer-project.org/&quot;&gt;Polymer Project&lt;/a&gt; is a set of polyfills that allow web components to be used on all modern browsers (IE10+)&lt;/p&gt;

&lt;p&gt;To learn more about Web Components…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.w3.org/TR/components-intro/&quot;&gt;W3C Web Components Draft&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.polymer-project.org/&quot;&gt;Polymer-Project.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.html5rocks.com/en/search?q=web+components&quot;&gt;Html5Rocks.com Web Components Articles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.youtube.com/watch?v=fqULJBBEVQE&quot;&gt;Google I/O 2013 Web Components Talk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.youtube.com/watch?v=0g0oOOT86NY&quot;&gt;Google I/O 2013 Web Components in Action Talk&lt;/a&gt;&lt;span style=&quot;font-size: 1.17em;&quot;&gt; &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div&gt;
  If nothing else, this year will be a ton of fun as we all try to build the next awesome thing.
&lt;/div&gt;
</description>
        <pubDate>Thu, 09 Jan 2014 12:02:30 -0700</pubDate>
        <link>http://davebouwman.com/2014/01/09/javascript-2014/</link>
        <guid isPermaLink="true">http://davebouwman.com/2014/01/09/javascript-2014/</guid>
        
        <category>javascript</category>
        
        
        <category>Esri</category>
        
        <category>javascipt</category>
        
      </item>
    
  </channel>
</rss>
