Prog282 Final2014

Elvenware Logo

TOC

#Prog282 Final Spring 2014

Goals:

We will define a set of rules or structure for our JSON document that will allow us to use it as a guide, or meta-document, for a page that we show to the user. We read in the JSON document, and then parse it to discover what we want to show to the user. We will call this file MetaDocument.json, or just the meta document.

##Overview

Part of your grade will be a review of how closely you follow the following guidelines:

More discussion of this can be found on Elvenware on the JavaScript Patterns page.

On the meta level, the guiding principles are:

And of course we should unit test everything as much as possible so we feel confident that our refactorings don't break our code.

##Expectations

NOTE: To fully understand this section you may need to read it once, then read the next section, called requirements, then come back and read this section again.

To do well on the final, some students will not have to complete all the sections. There are students of varying levels of experience in this course. Everyone is expected to complete certain core features. Others will have to meet differing requirements depending on their experience level.

I reserve the right to fix my own grading criteria while going through the finals. Nevertheless, I think giving a few general guidelines might be helpful. These guidelines will help students focus on realizable goals. Everyone is expected to complete at least part of the final if they want to get a passing grade. However, some students will probably only get confused if they try to complete some of the more difficult sections. They should focus instead on core, category 0, features. If they can complete them, then they should attempt other features.

Consider the following grid:

Cat Who
0 Everyone
1 Top Performers
2 Experienced
3 Experts
4 Extra Credit
---- -------------

Key:

Probably you can figure out what category you belong to, but if you have questions, let me know.

It will be difficult, perhaps even impossible, to get above a B without completing at least category 1. To get an A, you need to at least try to complete everything. It may be possible for some students to get a C even if they have only completed FileList.json, address and About.

##Requirements

Before we begin to describing the midterm itself, here are some base line requirements.

There are several main objects you need to show:
Document or Item Cat Has Own Page Log in URL MongoDb Reader Display Bootstrap
FileList.json 0 No No NA No JsonReader DisplayFileList Optional
address 0 No No NA No JsonReader DisplayAddress Optional
documentData 0 Optional No Document No JsonReader DisplayDocumentData Optional
pictureCaption 1 Optional No Pictures No JsonReader DisplayPictureCaption Optional
Markdown File 2 Optional Yes Markdown Yes MarkdownReader MarkShow Optional
HTML View 3 Optional No HtmlView Yes None NA Optional
Position Map 2 Optional No Position No None NA Optional No
Login 3 Yes No Login No None NA Yes
Account Info 3 Yes Yes Account No None NA Yes
About 0 Yes No About No None NA Yes
AWS 0 NA NA NA NA NA NA NA
Upstart 4 NA NA NA NA NA NA NA
GitHub IO 1 NA NA NA NA NA NA NA
Bootstrap 3 NA NA NA NA NA NA NA

The Document or Item column specifies the trigger for launching a view. FileList.json should be loaded by default when you start the page. This is sometimes called simply the File List. The next three views are based on document descriptors called address, documentData and pictureCaption. These are custom Json files described below.

Markdown File and HTML View are triggered when you click on a markdown file in the File List. The remaining views are triggered by a the selection of a button and/or hyperlink.

The Has Own Page column specifies whether or not the view for the page must appear in its own page. Some views can appear in place on their own page, and some pages must have their own Jade view and appear under their own URL. For instance, they would appear if you naviagted to localhost:30025:/UserPage.

Log In means the user must be logged in to view the page. If they are not logged in, they should be taken to the login page.

You have a good deal of freedom to implement this as you prefer.

Address

This view displays addresses.

Sample Document

{
    "title": "Presidents01",
    "type": "address",
    "version": "v0.0.1",
    "keywords": "images, shapes",
    "License": "Creative Commons",
    "authorInfo": {
        "name": "Claudio Coder",
        "website": "http://agilemanifesto.org/principles.html",
        "email": "a@b.com"
    },
    "content": {
        "firstName": "George",
        "lastName": "Washington",
        "address": "101 June Street",
        "city": "Bellevue",
        "state": "WA"
    }
}

##DocumentData

Display a set of headings and accompanying paragraphs.

Sample document descriptor:

{
    "title": "Paragraphs01",
    "type": "documentData",
    "version": "v0.0.1",
    "keywords": "images, shapes",
    "License": "Creative Commons",
    "authorInfo": {
        "name": "Claudio Coder",
        "website": "http://agilemanifesto.org/principles.html",
        "email": "a@b.com"
    },
    "content": [
        {
            "title": "Deception01",
            "level": 1,
            "content": "Deception Falls near Stevens Pass."
        },
        {
            "title": "Deception02",
            "level": 2,
            "content": "Deception Falls on Highway 2"
        },
        {
            "title": "Deception03",
            "level": 1,
            "content": "Deception Falls runs high in the spring rains."
        }
    ]
}

##PictureCaption

Display a set of headings, pictures and captions.

Sample Document Descriptor:

{
    "title": "Pictures01",
    "type": "pictureCaption",
    "version": "v0.0.1",
    "keywords": "images, shapes",
    "License": "Creative Commons",
    "authorInfo": {
        "name": "Claudio Coder",
        "website": "http://agilemanifesto.org/principles.html",
        "email": "a@b.com"
    },
    "content": [
        {
            "title": "Deception01",
            "winfile": "C:\\Src\\Git\\Writing\\Data\\Images\\Deception01.jpg",
            "url": "images/Deception01.jpg",
            "caption": "Deception Falls near Stevens Pass"
        },
        {
            "title": "Deception02",
            "winfile": "C:\\Src\\Git\\Writing\\Data\\Images\\Deception02.jpg",
            "url": "images/Deception02.jpg",
            "caption": "Deception Falls on Highway 2"
        },
        {
            "title": "Deception03",
            "winfile": "C:\\Src\\Git\\Writing\\Data\\Images\\Deception03.jpg",
            "url": "images/Deception03.jpg",
            "caption": "Deception Falls runs high in the spring rains."
        }
    ]
}

##Markdown File

When you click on a Markdown file, the file should open in the markdown editor. You should be able to:

##HTML View

Place a checkbox on the main page near the file list. The label for the checkbox should read:

If the checkbox is checked, and you click on a markdown file in the FileList, then the HTML for that markdown file should be pulled from the database and displayed in its own page. You can use SetPick and GetPick to store the name of the file to display in the HTML view page. You should have a div or similar control specified in your HtmlView.jade file that will host this html.

NOTE: Because we are using SetPick and GetPick in multiple places, we will probably have to keep them in routes/index.js. This despite what I said in a previous assignment. If you tried to moved them to Markdown.js, you will probably want to move them back.

##Position Page

This is the Position Page from the midterm. The only difference is that you should now display the current position in a Google Map or some other mapping tool of your choice.

LogIn

The login page for Google Passport. The user will use this page to log in via their Google account.

About

This is mostly so that you can make clear to prospective employers or others that you are the author of this web application. You can put whatever you want on the page, but it must include:

Account Info

Display the user name of the current logged in user. You must be signed in to get this information.

Required Libraries

You must use the following libraries in your program.

###Tests and Maintainence

Required Patterns

All of your code, except no more than three lines in Main.js, must be inside a pattern. You must use at least all of the following patterns:

##Objects

Your program will have the following objects in it.

I've included the project file. The point I'm trying to make is that you should include an Eclipse project file with the name field set to Week12Final-LastName, which LastName is your last name.

Your code should be arranged in directories, a bit like this:

public/javascript/Main
public/javascript/Factories/ReaderFactory.js
public/javascript/Factories/BridgeFactory.js
public/javascript/Bridges/ReaderBridge.js
public/javascript/Bridges/FancyReaderBridge.js
etc...

The point is that you create sub-directories for:

The Packages

##The Document Descriptor

This document will likely go through multiple iterations over the course of the quarter, but here is what it will look like on the first try. As a Category 2 assignment, the fields in this document should be shown with the help of the FancyBridge object.

{
    "title": "Favorite Shapes",
    "type": "ImageDisplay",
    "version": "v0.0.1",
    "keywords": "images, shapes",
    "license": "Creative Commons",
    "authorInfo": {
        "name": "Claudio Coder",
        "website": "http://agilemanifesto.org/principles.html",
        "email": "a@b.com"
    },
    "content": [
        {
            "title": "Square",
            "image": "Square.png",
            "caption": "Squares are symetrical and have right angles"
        },
        {
            "title": "Circle",
            "image": "Circle.png",
            "caption": "Circles are symetrical and have curves"
        }
    ]
}

To sum up, the main fields should include:

{
    "title": ,
    "type": <MetaDocumentType>,
    "version": <VersionNumber>,
    "authorInfo": <AuthorInfo>,
    "keywords": <Keywords>,
    "copyright": <License>,
    "content": <DocumentObject>
}
</code></pre><p>You can put other fields in the document if you wish.</p>
<p>###Types</p>
<p>We will be able to define new types as needed. These types are specified in the <strong><type></strong> field of our metaDocument. At first, we will work with three types:</p>
<ul class="collection"><li class="collection-item">pictureCaption: Images and captions</li><li class="collection-item">documentData: Captions and text</li><li class="collection-item">address: By default these are our Presidents01.json files. I wanted to have us put these fields in the database. Oh well, some other course....</li></ul><h2 id="two-sections-on-one-page">Two Sections on One Page</h2><p>Some students have opted to have two sections on the main page:</p>
<ul class="collection"><li class="collection-item">FileList.json is display on top</li><li class="collection-item">The selected file is displayed on the bottom</li></ul><p>This a good solution. Don't, however, allow it to be an excuse for having two parallel sets of code in Control.js. That would be a mistake because it likely violates DRY (Don't repeat yourself). The solution is simple: the same code that displays <strong>FileList.json</strong> at the top can also display the selected file on the bottom.</p>
<p>Your Document Descriptor, ie in <strong>FileList.json</strong> and similar files, could include a field called targetDiv. If the user selects FileList.json, then the target DIV as specified in FileList.json is set to topDiv, which is the place in your HTML where you currently display FileList.json. If the users selects some other file, say Presidents01.json, then the targetDiv listed in Presidents01.json is set to bottomDiv, and so the contents of that file displays in the lower part of your main HTML page. Now you can solve the problem with one set of code, rather than two. That one set of code reads the targetDiv to decide where to display the code. Then move all the code that sorts these kinds of things into their own object, something like FileTypeSorter.js, and out of Control.js.</p>
<h2 id="aws-githubio-and-upstart">AWS, GitHubIO and Upstart</h2><p>You should have an instance on AWS that:</p>
<ul class="collection"><li class="collection-item">Runs Node</li><li class="collection-item">Supports Git</li><li class="collection-item">Has an elastic IP</li><li class="collection-item">Has ports 22 and 30025 open</li></ul><p>Use Git to upload your code. Run your application either as a standard</p>
<p>##Hints</p>
<p>Some hints</p>
<p>###TinyPubSub and jQuery</p>
<p>If you get the "jquery undefined" error with TinyPubSub, try using shim with TinyPubSub. The point is that we have done nothing to make <strong>TinyPubSub</strong> work with require. That is, we don't wrap it in a require <strong>define</strong> function. As a result, we need to <strong>shim</strong> it into memory.</p>
<pre class="elf-scroll"><code>require.config({
    paths: {
        "jquery": "jquery-2.1.1"
    },
    shim: {
        'TinyPubSub': {
            deps: ['jquery'],
            exports: 'TinyPubSub'
        }
    }
});

require(['TinyPubSub'],

    function(tiny) {
        'use strict';
        console.log("Main called");
</code></pre><p>###Bootstrap and Markdown</p>
<p>If you want to use bootstrap on your markdown page...</p>
<p>First edit <strong>MarkdownLayout.jade</strong> to include the bootstrap CSS and a definition for your navigation bar:</p>
<pre class="elf-scroll"><code>doctype html
html
  head
    title= title
    link(rel="stylesheet" href="/stylesheets/bootstrap.min.css")
    link(rel="stylesheet" href="/stylesheets/bootstrap-theme.min.css")
    link(rel='stylesheet', href='/stylesheets/markdown.css')
    link(rel='stylesheet', href='/stylesheets/prettify.css')
    script(src="javascripts/require.js" data-main="javascripts/Main")
  body
    div.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
      div.container
        div.navbar-header
          button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
            span.sr-only Toggle navigation
            span.icon-bar
            span.icon-bar
            span.icon-bar
          a.navbar-brand(href='/') Prog282-Calvert

    block content
</code></pre><p>You can now delete the button that takes you to the home page as the navigator does the job for you. You will, however, find one more small problem: the top of your page disappears underneath the navigation bar. To fix that, you could edit <strong>markdown.css</strong> to add a little padding at the top of the body:</p>
<pre class="elf-scroll"><code>body
{
    background-color: White;
    font-family: sans-serif;
    padding-top: 50px;
    padding-left: 10px;
}
</code></pre><p>To spice up the table, you could entirely optionally add a few borders:</p>
<pre class="elf-scroll"><code>td, th {
    border: solid black 1px;    
    padding: 3px;
}


th {
    background: #D0FFE0;
}

td {
    background: #D0FFF0;
}

table {
    border: solid black 2px;    
    margin: 25px;
}
</code></pre><p>The end result should be a page with navigation bar, some nicer fonts, and a border around any tables you draw:</p>
<p><img class="normal-image" src=http://elvenware.com/charlie/books/CloudNotes/Images/Prog282Final01.png alt="Markdown Css"></p>
<p>###Bootstrap on the Left</p>
<p>Some folks had mentioned that bootstrap is putting the menu in near the middle of the page, that it is not aligned on the left. I put this in <strong>style.css</strong> and it seemed to help:</p>
<pre class="elf-scroll"><code>.container {
    margin-left: 25px;
}
</code></pre><p>You can set the left margin closer in if your want. For instance, it could be set to <strong>10px</strong>. This changes the location of the container found in these lines from index.jade:</p>
<pre class="elf-scroll"><code>block content
  div.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
    div.container
      div.navbar-header
</code></pre><p>###Bootstrap Layout in other Pages</p>
<p>Above we put all the code for for the Markdown page in one jade file. But usually we will want to work with a layout.jade where we set the HEAD section for the HTML, and a second page where we define the HTML BODY.</p>
<p>When converting the other pages to support bootstrap, I stuck fairly closely to the original bootstrap assignment. The one change I needed to make was to include a slightly different bootstrap navigation bar in each file. To make that work, I took the definition of the navigation bar out of <strong>bootstraplayout.jade</strong>:</p>
<pre class="elf-scroll"><code>doctype html
html
  head
    title= title
    link(rel="stylesheet" href="/stylesheets/bootstrap.min.css")
    link(rel="stylesheet" href="/stylesheets/bootstrap-theme.min.css")
    link(rel='stylesheet', href='/stylesheets/style.css')
    script(src="javascripts/require.js" data-main="javascripts/Main")

  body

  block content
</code></pre><p>Then  I included a bootstrap navigation bar definition in each of the pages where I wanted bootstrap to appear:</p>
<pre class="elf-scroll"><code>extends BootStrapLayout

block content
  div.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
    div.container
      div.navbar-header
        button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
          span.sr-only Toggle navigation
          span.icon-bar
          span.icon-bar
          span.icon-bar
        a.navbar-brand(href='/') Prog282-Calvert

  h1= title
  p Welcome to #{title}
</code></pre><p>The bootstrap navigation bar for my main page at this stage looks like this:</p>
<pre class="elf-scroll"><code>extends BootStrapLayout

block content
  div.navbar.navbar-inverse.navbar-fixed-top(role='navigation')
    div.container
      div.navbar-header
        button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
          span.sr-only Toggle navigation
          span.icon-bar
          span.icon-bar
          span.icon-bar
        a.navbar-brand(href='/') Prog282-Calvert

      div#bs-example-navbar-collapse-1.collapse.navbar-collapse
        ul.nav.navbar-nav
          li
            a(href='MapDisplay') Map
          li
            a(href='About') About
          li
            a(href='UnitTests') Test
</code></pre><p>You can change the definition of navbar in each page depending on your needs. With this approach we have <strong>bootstraplayout</strong> only be concerned with loading the CSS, and leave it up to each page to decide exactly how you want the navigation bar to look.</p>
<p><img class="normal-image" src=http://elvenware.com/charlie/books/CloudNotes/Images/Prog282Final02.png alt="Bootstrap 01"></p>
<p>And though my version of the final is not yet complete, here is at least a sample of what the main page might look like when you click on Presidents01.json:</p>
<p><img class="normal-image" src=http://elvenware.com/charlie/books/CloudNotes/Images/Prog282Final04.png alt="Bootstrap 03"></p>
<p>You don't have to follow this design. Do what you want, if you need some idea of what pages should look like, here is my map page at this stage. When you click on the map button in the screen shot above, this is what you see. If you click on <strong>Prog282-Calvert</strong> you go back to the main page, shown above:</p>
<p><img class="normal-image" src=http://elvenware.com/charlie/books/CloudNotes/Images/Prog282Final03.png alt="Bootstrap 02"></p>
<p>###Document Descriptors and the Fancy Bridge</p>
<p>The document descriptors have a number of details that we are supposed to display with the help of the <strong>FancyReaderBridge</strong>. These include the following fields seen in files such as <strong>Presidents01.json</strong> and <strong>FileList.json</strong>:</p>
<pre class="elf-scroll"><code>    "title": "Main File List",
    "type": "fileList",
    "version": "v0.0.1",
    "keywords": "images, shapes",
    "license": "Creative Commons",
</code></pre><p>The act of displaying this data poses two problems:</p>
<ul class="collection"><li class="collection-item">If the <strong>FancyReaderBridge</strong> is to know about the document descriptor, how will it gain that knowledge. The only objects with any know about these fields are the readers (JsonReader, etc) and display objects (DisplayAddress, etc). How can we get the information to the <strong>FancyReaderBridge</strong> from these remote outposts?</li><li class="collection-item">The second problem is a more an aesthetic delimna. Is it wrong from the FancyReaderBridge to directly change the user interface?</li></ul><p>Let's begin with the first question. How do we get the information to the FancyReaderBridge? We've actually faced this problem before when we had to hook up button response methods to objects on the main page. We solved that problem with the Observer Pattern, with our TinyPubSub class. I think we should do that same thing here. In fact, we can use the same event. When our display object publish a message <strong>pageRefresh</strong> event, we can tack on the whole document descriptor record:</p>
<pre class="elf-scroll"><code>$.publish('pageRefresh', {
    message: "Refreshed Address",
    serverData: serverData
}
</code></pre><p>Here <strong>serverData</strong> is the entire contents of a file such as <strong>Presidents01.json</strong> or <strong>FileList.json</strong>. These files include the fields we are interested in such as <strong>title</strong>, <strong>type</strong> or <strong>keywords</strong>.</p>
<p>Then we can have <strong>FancyBridgeReader</strong> subscribe to the <strong>pageRefresh</strong> event:</p>
<blockquote>
<p>$.subscribe("pageRefresh", displayData);</p>
</blockquote>
<p>As you recall, the callbacks for these functions look like this:</p>
<pre class="elf-scroll"><code>var displayData = function(event, data) {
    // YOUR CODE HERE
};
</code></pre><p>So that solves our first problem: how to get data from the display object to the reader bridge. The key point is that we moved the data without ever disrupting the loose coupling in the application. The <strong>FancyReaderBridge</strong> and the <strong>DisplayAddress</strong> object still do not know one another in the Biblical sense, and yet they are able to communicate.</p>
<p>This leaves us with the second question: Is it okay for the <strong>FancyReaderBridge</strong> to directly modify the UI? There are two arguments here that hold weight with me, one from each side of the debate:</p>
<ul class="collection"><li class="collection-item">The FancyReaderBridge follows the decorator pattern. As such, it could be argued that it would be okay to modify the <strong>bridge</strong> pattern with non-bridge-like behavior.</li><li class="collection-item">If we let the <strong>FancyReaderBridge</strong> modify the UI, how is anyone trying to maintain the app ever going to think to look in <strong>FancyReaderBridge.js</strong> to solve a UI issue?</li></ul><p>I find the first argument mildly compeling, but the second one is, in my opinion, overwhelming. We have a <strong>Display</strong> directory, and in it are a series of display objects. These objects should, as much as possible, be responsible for handling our display. As a result, the code to update the UI with metadata from our Document Descriptors should be handled in a display object.</p>
<p>So we can create a simple <strong>Display Object</strong> and put it in the <strong>Display</strong> folder:</p>
<p>/**</p>
<ul class="collection"><li class="collection-item">DisplayMetaData.js</li><li class="collection-item">/</li></ul><p>define(function() {</p>
<pre class="elf-scroll"><code>var DisplayMetaData = (function() {

    function DisplayMetaData() {
    }

    DisplayMetaData.prototype.show = function(serverData) {
        // YOUR METADATA DISPLAY CODE HERE
    };

    DisplayMetaData.prototype.publish = function(serverData) {
        $.publish('pageRefresh', {
            message: "Refreshed Address",
            serverData: serverData,
            okToDisplay: function(serverData) {
                show(serverData);
            }
        });
    };

    return DisplayMetaData;

}());

return DisplayMetaData;  // require
</code></pre><p>});</p>
<p>It might make sense to have the display object create the DisplayMetaData object, but still let <strong>FancyMetaData</strong> make the decision as to whether or not to display it. You could do that like this:</p>
<pre class="elf-scroll"><code>$.publish('pageRefresh', {
    message : "Refreshed Address",
    serverData : serverData,
    okToDisplay: function(serverData) {
        var displayMetaData = new DisplayMetaData();
        displayMetaData.display(serverData);                
    }
});
</code></pre><p>It may not be a perfect solution, but it gets the job done, and it keeps the act of playing with the user interface in the <strong>Display</strong> directory, where it belongs.</p>
<p>###The Elf and the Map</p>
<p>I declared an elf object <strong>public/javascripts</strong>:</p>
<pre class="elf-scroll"><code>/**
 * Elf
 */

define(function() {
    'use strict';

    var elf = {
        position : function() {
            elf.displayMap.position();
        }
    };

    return elf;

});
</code></pre><p>Then in main we need to declare a global instance of elf. This is our only global variable: we are allowed only one!</p>
<pre class="elf-scroll"><code>var elf;

require([ 'bootstrap', 'Elf', 'MarkdownExtra', 'Control', 'MarkShow', 'DisplayMap' ],
    function(bootstrap, elfInit, MarkdownExtra, Control, MarkShow, DisplayMap) {
        'use strict';
        console.log("Main called.");
        prettyPrint();

        elf = elfInit; // We need something in the global space

        $(document).ready(function() {
            if (endsWith(document.URL, "Markdown")) {
                elf.markShow = new MarkShow();
                elf.markShow.getPick();
            }
            if (endsWith(document.URL, "MapDisplay")) {
                elf.displayMap = new DisplayMap();   // elf.displayMap.position is public
            } else {
                elf.control = new Control();
            }
        });
    });
</code></pre><p>Then you take the code from the <strong>Control.js</strong> file in the MapExpress example and you put it in <strong>DisplayMap</strong>.</p>
<p>I believe you can write either this:</p>
<pre class="elf-scroll"><code>var elf = {
    position : function() {
        elf.displayMap.position();
    }
};
</code></pre><p>or this:</p>
<pre class="elf-scroll"><code>var elf = {
    position : function() {
        this.displayMap.position();
    }
};
</code></pre><p>We are doing all this work to make this one line which is now in <strong>DisplayMap</strong> work:</p>
<pre class="elf-scroll"><code>script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=elf.position';
</code></pre><p>This is telling Google maps that the callback method that it can use to reach our code is called <strong>elf.position</strong>. Of course Google Maps can't see our private objects, it can only see global objects. So we create one global object, called <strong>elf</strong>, and we make sure that it has a property called <strong>position</strong> that points at our code. In effect, <strong>elf.position</strong> is the entry point, the <strong>main</strong> for our map object. That is the role it plays in our <strong>MapExpress</strong> example, so nothing really new is going on here. It's just that we have to do a little dance to make the <strong>elf</strong> object global so that Google maps can see it.</p>
<p>Just to review. A key piece of code is this one:</p>
<pre class="elf-scroll"><code>script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=elf.position';
</code></pre><p>Do you see the <strong>callback=elf.position</strong> at the end of that URL? What we are doing is telling Google that once they have loaded their map for us, they need to call the elf.position method in our code in order to kick of our custom code:</p>
<ul class="collection"><li class="collection-item">They set up the map and provide the Map API, the Mapping Web Service</li><li class="collection-item">We call the method in the web service.</li></ul><p>The quote unquote "problem" with our code is that we went way out of our way to make sure that we put nothing in the global name space. Our default code leaves 0 footprint. There are no global, public entry points into our code. Which is a very, very good thing -- unless someone like Google needs to call one of the methods of our code.</p>
<p>So our goal is to find a way to put something in the global namespace, to intentionally break the rule that we have tried so hard to follow. (Remember, early in the class, I said that we are "allowed" to put one, and only one, variable in the global name space.) So I ask you to declare a variable called <strong>elf</strong> in the global namespace. Then we point that global variable at our elf object:</p>
<pre class="elf-scroll"><code>elf = elfInit;
</code></pre><p>Then we set things up so that <strong>elf.position</strong> points at the the <strong>elf.displayMap.position()</strong> method. So, we want to fix up the elf code and tie it in with the code that we created in Main:</p>
<pre class="elf-scroll"><code>elf.displayMap = new DisplayMap();
</code></pre><p>And here is the updated elf object that works with it:</p>
<pre class="elf-scroll"><code>var elf = {
    position : function() {
    elf.displayMap.position();
}
</code></pre><p>##More on Databases</p>
<p>Folks have pointed out that the code we have now just keeps inserting new data into the database rather than updating a record if it already exists. In other words, we end up with multiple copies of the same record in the database. This is to be expected, since our code is doing an <strong>insert</strong> each time. The fix, of course, is to do an <strong>update</strong> rather than an <strong>insert</strong>.</p>
<p>We are using the <strong>MongoClient</strong> object from the <a href="https://www.npmjs.org/package/mongodb">mongodb</a> package.</p>
<blockquote>
<p>var MongoClient = mongodb.MongoClient;</p>
</blockquote>
<p>If we dig into the <a href="http://docs.mongodb.org/manual/reference/method/db.collection.update/">docs</a> a bit we see that there is an optional <strong>upsert</strong> parameter we passed to <strong>update</strong>. If we set this to true we can automatically turn an update into an insert if there is no existing record to update. We can also pass in a query parameters that specifies which record we want to update.</p>
<p>Here is the whole function, which you should use to overright the existing <strong>QueryMongo.UpdateCollection</strong> method:</p>
<pre class="elf-scroll"><code>    // Will create collection if it does not exist, will create record if does
    // not exist. From QueryMongo.js
    QueryMongo.prototype.updateCollection = function(objectToInsert, callback) {
        console.log("QueryMongo.updateCollection called");
        getDatabase(function getCol(database) {
            console.log("In the update callback");
            var collection = database.collection(collectionName);
            collection.update(objectToInsert.query,
                objectToInsert.update,
                { upsert: true },
                function(err, docs) {
                    if (err) {
                        throw err;
                    }
                    if (callClose) {
                        closeDatabase();
                    }
                    console.log("update succeeded");
                    callback({
                        result : "Success",
                        mongoDocument : docs
                    });
                }
            );
        });
    };
</code></pre><p>Notice the first three parameters to the update call:</p>
<pre class="elf-scroll"><code>collection.update(
  objectToInsert.query,    // Query specifying the record to update
  objectToInsert.update,   // The data we want to put in the database
  { upsert: true },        // Do an insert if no data matches our query
  etc...
</code></pre><p>The first is the query that identifies the record we want to update. The second is the data we want to put in the database, the third is the flag specifying our desire that a record be created if there is none that matches our query.</p>
<p>We can call it from <strong>routes/Markdown.js</strong> like this:</p>
<pre class="elf-scroll"><code>var markdownData = {
    query : {
        fileName : request.query.fileName
    },
    update : {
        dataType : "BridgeReader",
        markdown : request.query.markdown,
        html : request.query.html,
        fileName : request.query.fileName
    }
};

queryMongo.updateCollection(markdownData, handleSuccess);
</code></pre><p>Right now I'm specifying all records of this type as having a <strong>dataType</strong> of <strong>BridgeReader</strong>. This field allows us to write a query that discovers all the records in the database that were inserted by our program using this technique. Right now my query to discover the record we want to update is simply to use the fileName as a unique string.</p>
<p>Using the fileName as unique identifier is an idea that has numerous holes in it, but I just want to get this hint up here so I'm going with that for now. One possible solution to this issue is to associate a GUID with each record:</p>
<blockquote>
<p><a href="https://www.npmjs.org/package/guid">https://www.npmjs.org/package/guid</a></p>
</blockquote>
<p>GUIDs]<a href="http://en.wikipedia.org/wiki/Universally_unique_identifier">8</a> are guaranteed to be globally unique throughout the <a href="http://en.wikipedia.org/wiki/Globally_unique_identifier">entire universe</a>. How's that for a claim? I'll see if I can get that going, but not right now.</p>
<p>##The Config File</p>
<p>We are going to use MongoTalk.config for more than just the database.</p>
<pre class="elf-scroll"><code>{
    "urls": [
        "mongodb://127.0.0.1:27017/test",
        "mongodb://127.0.0.1:27017/bar",
        "mongodb://csc:cscFoiBar2314@ds049848.mongolab.com:49848/elvenlab01"
    ],
    "selectedUrl": 0,
    "bridgeStart": [
        "Documents/Data/FileList.json",
        "Documents/Data/FileListWin.json"
    ],
    "selectedBridgeStart": 0,
    "passport": [
        {
            "returnURL": "http://localhost:30025/Login/auth/google/return",
            "realm": "http://localhost:30025/"
        },
        {
            "returnURL": "http://[ELASTICIP]:30025/Login/auth/google/return",
            "realm": "http://[ELASTICIP]:30025/"
        }
    ],
    "selectedPassport": 0
}
</code></pre><p>And then load it like this:</p>
<pre class="elf-scroll"><code>router.get('/getConfig', function(request, response) {
    loadConfig(function(configFile) {
        var mongoTalkJson = JSON.parse(configFile);
        var fileList = mongoTalkJson.bridgeStart[mongoTalkJson.selectedBridgeStart];
        console.log("The the fileList URL:" + fileList);
        response.send({fileList: process.env.HOME + path.sep + fileList});
    });
});
</code></pre><p>Don't forget to include the following at the top of "routes/index.js", or "routes/Login.js" or wherever you use loadConfig:</p>
<pre class="elf-scroll"><code>var loadConfig = require('./LoadConfig.js').loadConfig;
</code></pre><p>And in Control, we have to refactor the constructor to call this new method before we try to load FileList:</p>
<pre class="elf-scroll"><code> $.getJSON('/getConfig', function(fileList) {
    options.defaultFileName = fileList.fileList;
    run();
});
</code></pre><p>##Upstart</p>
<p>Some references:</p>
<ul class="collection"><li class="collection-item">In JsObjects <a href="https://github.com/charliecalvert/JsObjects/blob/master/JavaScript/NodeCode/ExpressSend/README.md">ExpressSend</a></li><li class="collection-item">In <a href="http://www.elvenware.com/charlie/development/web/JavaScript/NodeJs.html#upstart">Elvenwareo NodeJs Page</a></li></ul><p>Here is the line I use to start my app:</p>
<pre class="elf-scroll"><code>exec /usr/bin/nodejs /home/charlie/bin/BridgeReader/bin/www >> /var/log/bridgeReader.log 2>&1
</code></pre><p>Don't forget to also change the home directory:</p>
<pre class="elf-scroll"><code>script
    export HOME="/home/ubuntu"
</code></pre><p>##New Login with OAuth</p>
<p>First install the bearer-http</p>
<pre class="elf-scroll"><code>npm install passport-http-bearer --save
</code></pre><p>The reference:</p>
<ul class="collection"><li class="collection-item"><a href="https://github.com/jaredhanson/passport-http-bearer">Git Repo</a></li></ul><p>And here is the code for <strong>routes/index.js</strong> or some similar place such as <strong>routes/Login.js</strong>. Start near the top of the file by linking in Bearer Strategy:</p>
<pre class="elf-scroll"><code>var express = require('express');
var router = express.Router();
var passport = require('passport');
var BearerStrategy = require('passport-http-bearer').Strategy;
</code></pre><p>Now you are ready to start coding. We begin by simulating two database entries that define a set of default users:</p>
<pre class="elf-scroll"><code>var users = [ {
    id : 1,
    username : 'sue',
    token : '123456789',
    email : 'sue@susan.com'
}, {
    id : 2,
    username : 'paul',
    token : 'abcdefghi',
    email : 'paul@paolo.com'
} ];
</code></pre><p>The next lines of code give us a way to look up the user in the database. If we get passed a token, which is like a password, we want to confirm that it is associated with a real user. We could, of course, make requirments for passing the test more stringent. For instance, we could ensure that a particular token is associated with a particular email address. Then the user would have to pass in two things: email and token. But for now, we are concerned only with the token:</p>
<pre class="elf-scroll"><code>function findByToken(token, fn) {
    'use strict';
    console.log("find by token called: " + JSON.stringify(token));
    for (var i = 0, len = users.length; i < len; i++) {
        var user = users[i];
        if (user.token === token.token) {
            console.log("token found");
            return fn(null, user);
        }
    }
    return fn(null, null);
}

passport.use(new BearerStrategy(function(token, done) {
    'use strict';
    // User.findOne({
    findByToken({
        token : token
    }, function(err, user) {
        if (err) {
            return done(err);
        }
        if (!user) {
            return done(null, false);
        }
        return done(null, user, {
            scope : 'read'
        });
    });
}));
</code></pre><p>And finally, here is code to call from the client when we want to authorize a particular user. Again, this is server side code, but this is the method that is called from the client:</p>
<pre class="elf-scroll"><code>router.get('/authorize', passport.authenticate('bearer', {
    session : true
}), function(request, response) {
    'use strict';
    console.log("authorize called");
    console.log(request.user);
    //console.log(request.email);
    // response.redirect('/');
    response.send({'result': 'success'});
});
</code></pre><p>So you would call it like this from the client side:</p>
<pre class="elf-scroll"><code>var loginClick = function() {
    // '123456789'
    var userInput = $("#userInput").val();
    var token = {access_token: userInput};
    $.getJSON('/Login/authorize', token, function(serverData) {
        console.log(serverData);
    }).error = utilities.handleError;
};
</code></pre><p>##Server side Errrors</p>
<p>in app.js Express generates this code:</p>
<pre class="elf-scroll"><code>if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}
</code></pre><p>Change it to look like this:</p>
<pre class="elf-scroll"><code>if (app.get('env') === 'development') {
    console.log('env error handler is active');
    app.use(function(err, req, res, next) {
        console.log('env error thrown');
        console.error(err);
        console.error(JSON.stringify(err));
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}
</code></pre><p>Now you should get better error messages. You can test it like this:</p>
<pre class="elf-scroll"><code>router.get('/anyRouter', function(request, response) {
    console.log(requester);  // requester doesn't exist, and will throw an exception.
});
</code></pre><p>The above code should trigger an exception, and cause your newly revamped exception handler to show what it can do.</p>
<p>##HTML Show</p>
<ul class="collection"><li class="collection-item">Markdown/MarkHtmlShow.js</li></ul><p>##Turn It In</p>
<p>There are three parts:</p>
<ul class="collection"><li class="collection-item">Put the assignment in your respository in a folder called <strong>Week12Final</strong>.</li><li class="collection-item">Leave your app running on Aws, preferrably with <strong>upstart</strong>.</li><li class="collection-item">On Google Drive provide screenshots and a brief description of your final in a shared folder called <strong>Week12Final</strong>.</li></ul><p>You must provide at least two screenshots, but four or five might be more reasonable. Show your app running on AWS or on your local machine. Describe the level of completeness of your application. Describe which features you completed and which you left undone. If you have known bugs, you can list them.</p>
<p>If you want, you can send me your Mongo config:</p>
<pre class="elf-scroll"><code>"mongodb://USERNAME:PASSWORD@dsXXXX.mongolab.com:XXXX/DATABASE",
</code></pre></div><footer class="page-footer teal"><div class="container"><div class="row"><div class="col l6 s12"><h5 class="white-text">Elvenware</h5><p class="grey-text text-lighten-4">Elvenware is the site Charlie and Margie Calvert use to publish their various writings.</p></div><div class="col l3 s12"><h5 class="white-text">Overviews</h5><ul><li><a class="white-text" href="/">Home</a></li><li><a class="white-text" href="/core-links.html">In Depth View</a></li><li><a class="white-text" href="/git-guide">Git</a></li></ul></div><div class="col l3 s12"><h5 class="white-text">Basics</h5><ul><li><a class="white-text" href="/javascript-guide">JavaScript</a></li><li><a class="white-text" href="/html-guide">HTML</a></li><li><a class="white-text" href="/css-guide">CSS</a></li></ul></div></div></div><div class="footer-copyright"><div class="container">Copyright © 2019 by<a class="brown-text text-lighten-3" href="/about.html">  Charlie Calvert</a></div></div></footer></body></html>