http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/directives/onblur.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/directives/onblur.js 
b/ocw-ui/frontend/app/scripts/directives/onblur.js
new file mode 100644
index 0000000..313dbe0
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/directives/onblur.js
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name ocwUiApp.directive:onBlur
+ * @description
+ * # onBlur
+ */
+angular.module('ocwUiApp')
+.directive('onBlur', function() {
+       return {
+        restrict: 'A',
+        link: function($scope, $elem, $attrs) {
+            $elem.bind('blur', function() {
+                               $scope.$eval($attrs.onBlur);
+                       });
+        },
+    };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/directives/predictivefilebrowserinput.js
----------------------------------------------------------------------
diff --git 
a/ocw-ui/frontend/app/scripts/directives/predictivefilebrowserinput.js 
b/ocw-ui/frontend/app/scripts/directives/predictivefilebrowserinput.js
new file mode 100644
index 0000000..7142c15
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/directives/predictivefilebrowserinput.js
@@ -0,0 +1,316 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name ocwUiApp.directive:predictiveFileBrowserInput
+ * @description
+ * # predictiveFileBrowserInput
+ */
+angular.module('ocwUiApp')
+.directive('predictiveFileBrowserInput', function() {
+       var link = function($scope, $elem, $attrs) {
+               $scope.autocomplete = [];
+               
+               // Set id to use this directive correctly in multiple places
+               $scope.id = 'autoCompletePath'+ $elem.context.id
+               /*
+                * We need a place to dump our auto-completion options
+                */
+               $($elem).parent().append('<ul id="' + $scope.id +'"><ul>');
+
+               // Handle user clicks on auto-complete path options
+               $(document).on('click', '#' +$scope.id+ ' li span', function(e) 
{
+                       // Set the input text box's value to that of the user 
selected path
+                       var val = $(e.target).text();
+                       $($elem).val(val);
+                       // Need to trigger the input box's "input" event so 
Angular updates the model!
+                       $elem.trigger('input'); 
+                       
+                       // If the user selected a directory, find more results..
+                       if (val[val.length - 1] == '/') {
+                               $scope.fetchFiles($($elem).val());
+                       // Otherwise, remove the auto-complete options...
+                       } else {
+                               $('#' +$scope.id+ ' li').remove();
+                       }
+               });
+
+               /*
+                * Handle key-down events on the input box
+                *
+                * We need to ignore <TAB> presses here so we can auto-complete 
with <TAB>.
+                * If we don't ignore here then <TAB> will move the user to the 
next field
+                * in the form and our common-prefix-fill won't work later.
+                */
+               $($elem).keydown(function(e) {
+                       var code = e.keyCode || e.which;
+                       var BACKSPACE = 8,
+                               TAB = 9;
+
+                       if (code == TAB)
+                               return false;
+               });
+
+               /*
+                * Handle key-up events on the input box
+                */
+               $($elem).keyup(function(e) {
+                       var code = e.keyCode || e.which;
+                       var BACKSPACE = 8,
+                               TAB = 9,
+                               FORWARD_SLASH = 191;
+
+                       if (code === FORWARD_SLASH) {
+                               // Fetch new directory information from the 
server.
+                               $scope.fetchFiles($(this).val());
+                       } else if (code === TAB) {
+                               // Attempt to auto-fill for the user.
+                               $scope.handleTabPress();
+                       } else if (code == BACKSPACE) {
+                               // Need to properly handle the removal of 
directory information
+                               // and the displaying of auto-complete options
+                               $scope.handleBackSpace();
+                       } else {
+                               // Filter auto-complete options based on user 
input..
+                               $scope.handleMiscKeyPress();
+                       }
+
+                       // This is being used so we can handle backspacing. The 
user might hold
+                       // down the backspace key or select a section of text 
and delete. This allows
+                       // us to compare the result to its prior state, which 
makes handling
+                       // backspaces easier.
+                       $scope.lastInputContents = $elem.val();
+               });
+
+               /*
+                * Grab additional path information from the web-server
+                *
+                * Params:
+                *              path - The path to get a directory listing of.
+                */
+               // TODO Make this use $HTTP
+               $scope.fetchFiles = function(path) {
+                       $.get($scope.baseURL + '/dir/list/' + path, {},
+                                function(data) {
+                                        data = data['listing']
+                                        $scope.setNewData(data);
+                                        $scope.updateAutoComplete();
+                                }, 'json');
+               };
+
+               /*
+                * Grab additional path information from the web-server and 
filter the
+                * results based on the current input text.
+                *
+                * Params:
+                *              path - The path to get a directory listing of.
+                *
+                * This is needed to handle deletion of selected text. It is 
possible that
+                * the user will select text and delete only part of a word. 
The results
+                * need to be filtered based on this partial input.
+                */
+               // TODO Why isn't this using $http?!?!?! Because I copy and 
pasted!!!!
+               $scope.fetchFilesAndFilter = function(path) {
+                       $.get($scope.baseURL + '/dir/list/' + path, {},
+                                function(data) {
+                                        data = data['listing']
+                                        $scope.setNewData(data);
+                                        $scope.filterResults();
+                                        $scope.updateAutoComplete();
+                                }, 'json');
+               };
+
+               /*
+                * Handle directory data from the server.
+                *
+                * We store the entire directory information along with the 
remaining
+                * possible options given the users current input. This lets us 
avoid
+                * unnecessary calls to the server for directory information 
every time
+                * the user deletes something.
+                */
+               $scope.setNewData = function(data) {
+                       $scope.autocomplete = data.sort();
+                       $scope.possibleCompletes = $scope.autocomplete;
+               };
+
+               /* 
+                * Handle <TAB> presses.
+                *
+                * Attempt to auto-complete options when the user presses <TAB>.
+                */
+               $scope.handleTabPress = function() {
+                       // If there's only one option available there's no 
points in trying to
+                       // find a common prefix! Just set the value!
+                       if ($scope.possibleCompletes.length === 1) {
+                               $elem.val($scope.possibleCompletes[0]);
+
+                               // Make sure more options are displayed if a 
directory was selected.
+                               $scope.checkForMoreOptions();
+                               $scope.updateAutoComplete();
+                               return;
+                       }
+
+                       // Find the greatest common prefix amongst the 
remaining choices and set
+                       // the input text to it.
+                       var prefix = 
$scope.getLargestCommonPrefix($scope.possibleCompletes);
+                       $elem.val(prefix);
+                       $scope.updateAutoComplete();
+               };
+
+               /*
+                * Handle Backspacing and option displaying.
+                *
+                * The auto-complete options needs to be displayed correctly 
when the user
+                * removes directory information.
+                */
+               $scope.handleBackSpace = function() {
+                       var curInputVal = $elem.val();
+
+                       // If the user deletes everything in the input box all 
we need to do
+                       // is make sure that the auto-complete options aren't 
displayed.
+                       if (curInputVal.length === 0) {
+                               $('#' +$scope.id+ ' li').remove();
+                               return;
+                       }
+
+                       // Figure out how much text the user removed from the 
input box.
+                       var lengthDiff = $scope.lastInputContents.length - 
curInputVal.length;
+                       // Grab the removed text.
+                       var removedText = 
$scope.lastInputContents.substr(-lengthDiff);
+
+                       // If the user deleted over a directory we need to 
fetch information on the
+                       // previous directory for auto-completion.
+                       if (removedText.indexOf('/') !== -1) {
+                               var lastSlashIndex = 
curInputVal.lastIndexOf('/');
+
+                               // If the remaining path still contains a 
directory...
+                               if (lastSlashIndex !== -1) {
+                                       // Grab the section of the path that 
points to a valid directory,
+                                       // fetch the listing, and update the 
results.
+                                       var pathToSearch = curInputVal.slice(0, 
lastSlashIndex + 1);
+                                       
$scope.fetchFilesAndFilter(pathToSearch);
+                               } else {
+                                       // Delete the old auto-complete 
information in the case where the user
+                                       // completely removed path information.
+                                       $('#' +$scope.id+ ' li').remove();
+                               }
+                       } else {
+                               // Otherwise, we just need to filter results 
based on the remaining input.
+                               $scope.filterResults();
+                               $scope.updateAutoComplete();
+                       }
+               };
+
+               /* 
+                * Handle all other key presses in the input box
+                *
+                * Filter the auto-complete options as the user types to ensure 
that only options
+                * which are possible given the current input text are still 
displayed.
+                */
+               $scope.handleMiscKeyPress = function() {
+                       // Safely exit when there are no options available.
+                       if ($scope.autocomplete === [])
+                               return;
+
+                       // Otherwise, filter the results.
+                       $scope.filterResults();
+                       $scope.updateAutoComplete();
+               };
+
+               /* 
+                * When a path is auto-completed with <TAB> we need to check to 
see if it points
+                * to a directory. If it does, we still need to fetch results!
+                */
+               $scope.checkForMoreOptions = function() {
+                       var path = $elem.val();
+                       if (path[path.length - 1] === '/') {
+                               $scope.fetchFiles(path);
+                       }
+               };
+
+               /* 
+                * Calculate the greatest common prefix of the passed options.
+                *
+                * Params:
+                *              Options - An array of strings in which the 
greatest common prefix
+                *                                should be found
+                *
+                * Returns:
+                *              The greatest common prefix of the strings.
+                *
+                *
+                * Note - For us, there will always be a prefix of at least '/' 
since this can't
+                * possible be called without the users entering a starting 
directory. As a result,
+                * we don't explicitly handle the case where there is 0 length 
common prefix.
+                */
+               $scope.getLargestCommonPrefix = function(options) {
+                       var index = 1;
+                       var shortestString = options.reduce(function(a, b) { 
return a.length < b.length ? a : b; });
+                       var longestString = options.reduce(function(a, b) { 
return a.length > b.length ? a : b; });
+                       var     substringToCheck = shortestString[0];
+
+                       while (longestString.indexOf(substringToCheck) !== -1) {
+                               substringToCheck = shortestString.slice(0, 
++index);
+                       }
+
+                       return longestString.slice(0, index - 1);
+               };
+
+               /* 
+                * Filter the auto-complete options based on the current input.
+                */
+               $scope.filterResults = function() {
+                       $scope.possibleCompletes = 
$scope.autocomplete.filter(function(item, index, array) {
+                               return (~item.indexOf($($elem).val()));
+                       });
+
+                       $scope.possibleCompletes.sort();
+               };
+
+               /*
+                * Update the display of auto-complete options.
+                */
+               $scope.updateAutoComplete = function() {
+                       // Remove all the existing options
+                       $('#' +$scope.id+ ' li').remove();
+
+                       // We don't need to show anything if the user has 
completely selected
+                       // the only existing option available.
+                       if ($scope.possibleCompletes.length === 1) {
+                               if ($scope.possibleCompletes[0] === 
$elem.val()) {
+                                       return;
+                               }
+                       }
+
+                       // Display all the possible completes
+                       $.each($scope.possibleCompletes, function(i, v) {
+                               $('#' +$scope.id+ 
'').append($('<li>').html($('<span>').text(v)));
+                       });
+               };
+       };
+
+       return {
+               link: link,
+               scope: true,
+               restrict: 'A'
+       };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/directives/previewmap.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/directives/previewmap.js 
b/ocw-ui/frontend/app/scripts/directives/previewmap.js
new file mode 100644
index 0000000..78aae8c
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/directives/previewmap.js
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name ocwUiApp.directive:previewMap
+ * @description
+ * # previewMap
+ */
+angular.module('ocwUiApp')
+.directive('previewMap', function($rootScope) {
+       return {
+               restrict: 'A',
+               scope: {dataset: '=previewMap', index: '=index'},
+               template: '<div id="{{dataset.name}}" 
class="preview-map"></div>',
+               replace: true,
+               link: function(scope, element, attrs) {
+
+                       // Any attribute that contains {{}} interpolation will 
be set to null in the attrs
+                       // parameter during the link function since the first 
$digest since the compilation
+                       // has yet to run to evaluate it! We can't run a 
$digest in the middle of compilation,
+                       // so using an $observe (or $watch) is the best way to 
get the values.
+                       attrs.$observe('id', function(newId) {
+                               var map = L.map(attrs.id, {
+                                       zoom: 0,
+                                       scrollWheelZoom: false,
+                                       zoomControl: false,
+                                       attributionControl: false,
+                                       worldCopyJump: true,
+                               });
+
+                               
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {}).addTo(map);
+
+                               // Zoom the map to the dataset bound regions 
(or at least try our best to do so)
+                               var datasetBounds = 
[[scope.dataset.latlonVals.latMax, scope.dataset.latlonVals.lonMin], 
+                                                                        
[scope.dataset.latlonVals.latMin, scope.dataset.latlonVals.lonMax]];
+                               map.fitBounds(datasetBounds, {});
+
+                               // Draw a colored overlay on the region of the 
map
+                               var maplatlon = scope.dataset.latlonVals;
+                               var bounds = [[maplatlon.latMax, 
maplatlon.lonMin], [maplatlon.latMin, maplatlon.lonMax]];
+
+                               var polygon = L.rectangle(bounds,{
+                                       stroke: false,
+                                       fillColor: $rootScope.fillColors[1],
+                                       fillOpacity: 0.6
+                               });
+
+                               // Add layer to Group
+                               var rectangleGroup = L.layerGroup();
+                               rectangleGroup.addLayer(polygon);
+
+                               // Add the overlay to the map
+                               rectangleGroup.addTo(map);
+                       });
+               }
+       };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/directives/timeline.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/directives/timeline.js 
b/ocw-ui/frontend/app/scripts/directives/timeline.js
new file mode 100644
index 0000000..760a819
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/directives/timeline.js
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc directive
+ * @name ocwUiApp.directive:timeline
+ * @description
+ * # timeline
+ */
+angular.module('ocwUiApp')
+.directive('timeline', function($rootScope, $window) {
+       return {
+               restrict: 'C',
+               replace: true,
+               transclude: true,
+               template: '<div id="OCWtimeline"></div>',
+               link: function(scope, element, attrs) {
+                       // Instantiate timeline object.
+                       $rootScope.timeline = new 
links.Timeline(document.getElementById('OCWtimeline'));
+
+                       // Redraw the timeline whenever the window is resized
+                       angular.element($window).bind('resize', function() {
+                               $rootScope.timeline.checkResize();
+                       });
+
+                       var options = {
+                               "width": "100%",
+                               "showCurrentTime": false,
+                               "moveable": false,
+                               "zoomable": false
+                       };
+
+                       $rootScope.timeline.draw([], options);
+               }
+       }
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/filters/isodatetomiddleendian.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/filters/isodatetomiddleendian.js 
b/ocw-ui/frontend/app/scripts/filters/isodatetomiddleendian.js
new file mode 100644
index 0000000..7fcd2a9
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/filters/isodatetomiddleendian.js
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc filter
+ * @name ocwUiApp.filter:ISODateToMiddleEndian
+ * @function
+ * @description
+ * # ISODateToMiddleEndian
+ * Filter in the ocwUiApp.
+ */
+angular.module('ocwUiApp')
+.filter('ISODateToMiddleEndian', function() {
+       return function(input) {
+               var original = input;
+
+               // Strip whitespace from the start and end of the string
+               input = input.replace(/(^\s+|\s+$)/g, '');
+
+               // ISO Standard says time is separated from Date with a 'T'. 
Our timestamps
+               // slightly modify that and use a space. We'll check for both 
here and prefer
+               // to split on a 'T' if it's available.
+               if (input.indexOf('T') != -1 || input.indexOf(' ') != -1) {
+                       input = (input.indexOf('T') != -1) ? 
input.split('T')[0] : input.split(' ')[0];
+               } 
+               
+               // The components of the date should be split with hyphens. If 
we can't find them
+               // then the string is poorly formed.
+               if (input.indexOf('-') == -1 || input.split('-').length - 1 != 
2) {
+                       return original;
+               }
+
+               // At this point the date is probably valid and we should try 
to convert it!
+               var components = input.split('-');
+               return (components[1] + "/" + components[2] + "/" + 
components[0]);
+       };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/services/evaluationsettings.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/services/evaluationsettings.js 
b/ocw-ui/frontend/app/scripts/services/evaluationsettings.js
new file mode 100644
index 0000000..b53842c
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/services/evaluationsettings.js
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc service
+ * @name ocwUiApp.evaluationSettings
+ * @description
+ * # evaluationSettings
+ * Service in the ocwUiApp.
+ */
+angular.module('ocwUiApp')
+  .service('evaluationSettings', function($rootScope, $http) {
+    $http.get($rootScope.baseURL + '/processing/metrics/').then(function(data) 
{
+        var metrics_data = data['data']['metrics'];
+        var metrics = [];
+
+        for (var i = 0; i < metrics_data.length; ++i) {
+            metrics.push({'name': metrics_data[i], 'select': false});
+        }
+
+        settings['metrics'] = metrics;
+    });
+
+    var settings = {
+      'metrics': [],
+      'temporal': {
+        'options': ['daily', 'monthly', 'yearly'],
+        'selected': 'yearly',
+      },
+      'spatialSelect': null,
+    };
+
+    return {
+      getSettings: function() {
+        return settings;
+      }
+    };
+  });

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/services/regionselectparams.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/services/regionselectparams.js 
b/ocw-ui/frontend/app/scripts/services/regionselectparams.js
new file mode 100644
index 0000000..cb3f4f8
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/services/regionselectparams.js
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc service
+ * @name ocwUiApp.regionSelectParams
+ * @description
+ * # regionSelectParams
+ * Service in the ocwUiApp.
+ */
+angular.module('ocwUiApp')
+.service('regionSelectParams', function() {
+       var parameters = {
+               "areValid" : true,
+               "latMin"   : "",
+               "latMax"   : "",
+               "lonMin"   : "",
+               "lonMax"   : "",
+               "start"    : "",
+               "end"      : "",
+       };
+
+       return {
+               getParameters: function() {
+                       return parameters;
+               },
+       };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/scripts/services/selecteddatasetinformation.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/scripts/services/selecteddatasetinformation.js 
b/ocw-ui/frontend/app/scripts/services/selecteddatasetinformation.js
new file mode 100644
index 0000000..613cfb8
--- /dev/null
+++ b/ocw-ui/frontend/app/scripts/services/selecteddatasetinformation.js
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use strict';
+
+/**
+ * @ngdoc service
+ * @name ocwUiApp.selectedDatasetInformation
+ * @description
+ * # selectedDatasetInformation
+ * Service in the ocwUiApp.
+ */
+angular.module('ocwUiApp')
+.service('selectedDatasetInformation', function() {
+       var datasets = [];
+
+       return {
+               getDatasets: function() {
+                       return datasets;
+               },
+               getDatasetCount: function() {
+                       return datasets.length;
+               },
+               // TODO: Define the structure of the objects that are added 
with addDataset.
+               addDataset: function(dataset) {
+                       // All datasets need a shouldDisplay attribute that is 
used when rendering
+                       // the overlays on the map!
+                       dataset.shouldDisplay = false;
+                       // The regrid attribute indicates which dataset should 
be used for spatial regridding
+                       dataset.regrid = false;
+
+                       datasets.push(dataset);
+               },
+               removeDataset: function(index) {
+                       datasets.splice(index, 1);
+               },
+               clearDatasets: function() {
+                       datasets.length = 0;
+               },
+       };
+});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/styles/main.css
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/styles/main.css 
b/ocw-ui/frontend/app/styles/main.css
new file mode 100644
index 0000000..fb42ccd
--- /dev/null
+++ b/ocw-ui/frontend/app/styles/main.css
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *    http: *www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+body {
+       
+}
+
+#header-img {
+       
+}
+
+#header-title {
+       
+}
+
+#OCW-powered {
+       
+}
+
+#main-container {
+    margin-top: 20px;
+       min-height: 400px;
+       height: auto !important;
+       height: 400px;
+}
+
+#datasetDiv {
+       height: 750px;
+       overflow-y: auto;
+       overflow-x: hidden;
+}
+
+#ocw-navbar {
+    margin-bottom: 0;
+}
+
+#map { height: 500px; }
+
+/* Small preview map that is displayed alongside dataset information */
+.preview-map {
+       height: 100px;
+       width: 100px;
+}
+
+.small-alert {
+       font-size: 12px;
+       color: green;
+       margin-top: 4px;
+       margin-left: 10px;
+}
+
+.colorSquare {
+       margin-top: 3px;
+       height: 10px;
+       width: 10px;
+}
+
+ul { list-style-type: none; }
+
+.no-color-link { color: #000000; }
+.no-color-link:hover { color: #000000; text-decoration: none; }
+.no-color-link:visited { color: #000000; }
+.no-color-link:active { color: #000000; }
+
+/* Remove the grayed out close button in modal headers */
+.modal-header .close { opacity: 1; }
+
+/* Remove the grayed out close button in modal footers */
+.modal-footer .close { opacity: 1; }
+
+/** 
+  * Timeline 
+  */
+div#OCWtimeline {
+       margin-top: 20px;
+       padding-bottom: 20px;
+}
+
+div.timeline-event {
+       border: none;
+       background: none;
+}
+
+div.timeline-event-content { margin: 0; }
+
+div.ocw-bar { height: 5px; }
+
+/**
+  * Results
+  */
+#results-sidebar {
+       min-height: 400px;
+       height: auto !important;
+       height: 400px;
+}
+
+#results-sidebar-header { font-size: 14px; }
+
+/* Helpers for vertical offsets */
+.top3 { margin-top: 3%; }
+.top7 { margin-top: 7%; }
+.top14 { margin-top:14%; }
+.top21 { margin-top:21%; }
+.top42 { margin-top:42%; }

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/main.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/main.html 
b/ocw-ui/frontend/app/views/main.html
new file mode 100644
index 0000000..2f428e5
--- /dev/null
+++ b/ocw-ui/frontend/app/views/main.html
@@ -0,0 +1,274 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<!-- Modal for evaluation settings -->
+<div class="modal fade" id="evaluationSettingsModal" role="dialog" 
aria-labelledby="datasetSelectModalLabel" aria-hidden="true">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button class="close" data-dismiss="modal">&times;</button>
+        <h3>Settings</h3>
+      </div>
+      <div class="modal-body" ng-controller="SettingsCtrl">
+        <h4>Select the metrics you would like to run.</h4>
+        <div ng-repeat="metric in settings.metrics">
+          <label><input type="checkbox" ng-model="metric.select"> 
{{metric.name}}</label>
+        </div>
+        <hr />
+        <h4>Select how you would like to temporally re-grid the datasets.</h4>
+        <select class="form-control" ng-model="settings.temporal.selected" 
ng-options="opt for opt in settings.temporal.options"></select>
+        <hr />
+        <h4>Select which dataset to use as the reference.</h4>
+        <select class="form-control" ng-model="settings.spatialSelect" 
ng-options="dataset as dataset.name for dataset in datasets"></select>
+        <hr />
+        <!-- Temporarily hidden for work on CLIMATE-365.-->
+        <div ng-hide=true>
+        <h4>Select a file which will define the bounds of subregions.</h4>
+        <form class="form-inline" autocomplete="off">
+          <input id="subregionFileInput" predictive-file-browser-input 
ng-model="settings.subregionFile" type="text" class="input-xlarge" 
autocomplete="off" />
+        </form>
+        </div>
+        <!--End hidden section for CLIMATE-365-->
+      </div>
+      <div class="modal-footer">
+        <button class="btn btn-warning cancel" 
data-dismiss="modal">Close</button>
+      </div>
+    </div>
+  </div>
+</div>
+<!-- END - Modal for evaluation settings -->
+
+<div class="row">
+  <div class="col-md-12">
+    <div class="row">
+      <!-- Dataset Select and Display Column -->
+      <div class="col-md-6">
+        <!--Dataset Select Controls-->
+        <div ng-controller="DatasetSelectCtrl">
+          <div class="row">
+            <div class="col-md-1 col-md-offset-10">
+              <button class="btn btn-link no-color-link" 
ng-click="clearDatasets()" ng-disabled="shouldDisableClearButton()">
+                <span tooltip-placement="left" tooltip-popup-delay="700" 
tooltip="Clear Datasets">
+                  <i class="fa fa-trash-o fa-2x"></i>
+                </span>
+              </button>
+            </div>
+            <div class="col-md-1">
+              <button class="btn btn-link no-color-link" data-toggle="modal" 
data-target="#datasetSelect">
+                <span tooltip-placement="left" tooltip-popup-delay="700" 
tooltip="Add Dataset">
+                  <i class="fa fa-plus fa-2x"></i>
+                </span>
+              </button>
+            </div>
+          </div>
+          <!-- Modal for dataset selection -->
+          <div class="modal fade" id="datasetSelect" role="dialog" 
aria-labelledby="datasetSelectModalLabel" aria-hidden="true">
+            <div class="modal-dialog">
+              <div class="modal-content">
+                <div class="modal-header">
+                  <h3>Dataset Select</h3>
+                </div>
+                <div class="modal-body">
+                  <tabset>
+                    <tab ng-repeat="tab in templates" heading="{{tab.title}}" 
active="tab.active" disabled="tab.disabled">
+                      <div ng-include src="tab.url"></div>
+                    </tab>
+                    <li class="pull-right">Queued Datasets: 
{{datasetCount.length}}</li>
+                  </tabset>
+                </div>
+                <div class="modal-footer">
+                  <button class="btn btn-warning cancel" 
data-dismiss="modal">Close</button>
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- END - Modal for dataset selection -->
+          <div class="row">
+            <div class="col-md-12">
+            <hr />
+            </div>
+          </div>
+        </div>
+        <!--Dataset display-->
+        <div ng-controller="DatasetDisplayCtrl" id="datasetDiv">
+            <div ng-repeat="dataset in datasets">
+              <div class="row">
+                <!--Data section-->
+                <div class="col-md-8 col-md-offset-1 muted">
+                  {{dataset.name}}
+                </div>
+                <div class="col-md-1 col-md-offset-2">
+                  <span tooltip-placement="left" tooltip-popup-delay="700" 
tooltip="Remove Dataset">  
+                    <a class="no-color-link" href="#" 
ng-click="removeDataset($index)">
+                      <i class="fa fa-remove"></i>
+                    </a>
+                  </span>
+                </div>
+              </div>
+              <!--Time Values!-->
+              <div class="row">
+                <!--Dataset Info Section-->
+                <div class="col-md-9">
+                  <div class="row">
+                    <div class="col-md-2 col-md-offset-1 
text-center">Start:</div>
+                    <div class="col-md-2">
+                      <div class="col-md-2 
text-center">{{dataset.timeVals.start | ISODateToMiddleEndian}}</div>
+                    </div>
+                    <div class="col-md-2 text-center">End:</div>
+                    <div class="col-md-2">
+                      <div class="col-md-2 text-center">{{dataset.timeVals.end 
| ISODateToMiddleEndian}}</div>
+                    </div>
+                  </div>
+                  <!--Lat/Long Values!-->
+                  <div class="row">
+                    <div class="col-md-2 col-md-offset-1 
text-center">North:</div>
+                    <div class="col-md-2 text-center">
+                      {{dataset.latlonVals.latMax | number:2}}
+                    </div>
+                    <div class="col-md-2 text-center">West:</div>
+                    <div class="col-md-2 text-center">
+                      {{dataset.latlonVals.lonMin | number:2}}
+                    </div>
+                  </div>
+                  <div class="row">
+                    <div class="col-md-2 col-md-offset-1 
text-center">South:</div>
+                    <div class="col-md-2 text-center">
+                      {{dataset.latlonVals.latMin | number:2}}
+                    </div>
+                    <div class="col-md-2 text-center">East:</div>
+                    <div class="col-md-2 text-center">
+                      {{dataset.latlonVals.lonMax | number:2}}
+                    </div>
+                  </div>
+                </div>
+                <!--Preview Map Section-->
+                <div class="col-md-3">
+                  <!--If the dataset is global we show a picture of a globe 
instead of the actual map-->
+                  <div ng-hide="dataset.latlonVals.lonMin == -180 && 
dataset.latlonVals.lonMax == 180 && 
+                                dataset.latlonVals.latMin == -90 && 
dataset.latlonVals.latMax == 90" 
+                                preview-map="dataset" index="$index"></div>
+                  <div ng-show="dataset.latlonVals.lonMin == -180 && 
dataset.latlonVals.lonMax == 180 &&
+                                dataset.latlonVals.latMin == -90 && 
dataset.latlonVals.latMax == 90">
+                    <img src="img/globe.png" class="preview-map">
+                  </div>
+                </div>
+              </div>
+              <div class="row">
+                <div class="col-md-6 col-md-offset-3"><hr /></div>
+              </div>
+            </div>
+          </div>
+      </div>
+
+      <!-- Map, Timeline, and Parameter Control Column -->
+      <div class="col-md-6">
+        <!--Map-->
+        <div class="row"  ng-controller="WorldMapCtrl">
+          <div class="col-md-12">
+            <leaflet-map id="map"></leaflet-map>
+          </div>
+        </div>
+        
+        <!--Timeline-->
+        <div class="row">
+          <div class="col-md-12" ng-controller="TimelineCtrl">
+            <div class="timeline"></div>
+          </div>
+        </div>
+        
+        <div class="row">
+          <div class="col-md-12" ng-controller="ParameterSelectCtrl">
+            <div class="row top3">
+              <div class="col-md-2 text-center">Start Date:</div>
+              <div class="col-md-4">
+                <form>
+                  <!--This styling HAD to be done inline. Using a class 
wouldn't work and for some -->
+                  <!--reason the input boxes refused to be 100% wide when 
their span size was set.-->
+                  <input ng-disabled="shouldDisableControls()" 
on-blur="checkParameters();" ng-model="displayParams.start" 
ui-date="datepickerSettings" ui-date-format="yy-mm-dd" type="text" 
class="col-md-4 text-center" style="width:100%" />
+                </form>
+              </div>
+              <div class="col-md-2 text-center">End Date:</div>
+              <div class="col-md-4">
+                <form>
+                  <!--This styling HAD to be done inline. Using a class 
wouldn't work and for some -->
+                  <!--reason the input boxes refused to be 100% wide when 
their span size was set.-->
+                  <input ng-disabled="shouldDisableControls()" 
on-blur="checkParameters();" ng-model="displayParams.end" 
ui-date="datepickerSettings" ui-date-format="yy-mm-dd" type="text" 
class="col-md-4 text-center" style="width:100%"/>
+                </form>
+              </div>
+            </div>
+            <div class="row top3">
+              <div class="col-md-2 text-center">North:</div>
+              <div class="col-md-4">
+                <form action="">
+                  <input ng-disabled="shouldDisableControls()" 
ng-model="displayParams.latMax"  on-blur="checkParameters();" type="text" 
class="col-md-4 text-center" style="width:100%"/>
+                </form>
+              </div>
+              <div class="col-md-2 text-center">South:</div>
+              <div class="col-md-4">
+                <form action="">
+                  <!--This styling HAD to be done inline. Using a class 
wouldn't work and for some -->
+                  <!--reason the input boxes refused to be 100% wide when 
their span size was set.-->
+                  <input ng-disabled="shouldDisableControls()" 
ng-model="displayParams.latMin" on-blur="checkParameters();" type="text" 
class="col-md-4 text-center" style="width:100%"/>
+                </form>
+              </div>
+            </div>
+            <div class="row top3">
+              <div class="col-md-2 text-center">East:</div>
+              <div class="col-md-4">
+                <form>
+                  <!--This styling HAD to be done inline. Using a class 
wouldn't work and for some -->
+                  <!--reason the input boxes refused to be 100% wide when 
their span size was set.-->
+                  <input ng-disabled="shouldDisableControls()" 
ng-model="displayParams.lonMax" on-blur="checkParameters();" type="text" 
class="col-md-4 text-center" style="width:100%"/>
+                </form>
+              </div>
+              <div class="col-md-2 text-center">West:</div>
+              <div class="col-md-4">
+                <form>
+                  <!--This styling HAD to be done inline. Using a class 
wouldn't work and for some -->
+                  <!--reason the input boxes refused to be 100% wide when 
their span size was set.-->
+                  <input ng-disabled="shouldDisableControls()" 
ng-model="displayParams.lonMin" on-blur="checkParameters();"; type="text" 
class="col-md-4 text-center" style="width:100%"/>
+                </form>
+              </div>
+            </div>
+            <div class="row top3">
+              <div class="col-md-2 col-md-offset-6">
+                <!--<button class="btn btn-link no-color-link pull-right" 
bootstrap-modal-open="evaluationSettings">-->
+                <button class="btn btn-link no-color-link pull-right" 
data-toggle="modal" data-target="#evaluationSettingsModal">
+                  <span tooltip-placement="left" tooltip-popup-delay="700" 
tooltip="Settings">
+                    <span class="fa-stack fa-lg">
+                      <i class="fa fa-square-o fa-stack-2x"></i>
+                      <i class="fa fa-cogs fa-stack-1x"></i>
+                    </span>
+                  </span>
+                </button>
+              </div>
+              <div class="col-md-4">
+                <button ng-click="runEvaluation()" 
ng-disabled="shouldDisableEvaluateButton()" class="btn btn-block btn-primary">
+                  <div ng-hide="runningEval">Evaluate</div>
+                  <div ng-show="runningEval"><i class="fa fa-spinner 
fa-spin"></i></div>
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      </div>
+  </div>
+</div>
+

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/modelselect.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/modelselect.html 
b/ocw-ui/frontend/app/views/modelselect.html
new file mode 100644
index 0000000..9bc128f
--- /dev/null
+++ b/ocw-ui/frontend/app/views/modelselect.html
@@ -0,0 +1,87 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div class="container">
+  <div class="row">
+    <div class="span10 offset1 columns">
+      <div class="row">
+        <div class="span10">
+          <center>
+            <form id="modelSelectorForm">
+              <input type="file" name="modelSelector">
+            </form>
+          </center>
+        </div>
+      </div>
+      <div class="row">
+        <div class="span10 offset2">
+          <form class="form-horizontal" id="parameterSelectorForm">
+            <div class="control-group">
+              <label class="control-label" for="paramSelect">Parameter 
Value</label>
+              <div class="controls">
+                <select id="paramSelect">
+                  <option ng-repeat="param in modelParameters">
+                    {{param.text}}
+                  </option>
+                </select>
+              </div>
+            </div>
+            <div class="control-group">
+              <label class="control-label" for="latSelect">Latitude 
Variable</label>
+              <div class="controls">
+                <select id="latSelect">
+                  <option ng-repeat="lat in latVariables">
+                    {{lat.text}}
+                  </option>
+                </select>
+              </div>
+            </div>
+            <div class="control-group">
+              <label class="control-label" for="lonSelect">Longitude 
Variable</label>
+              <div class"controls">
+                <select id="lonSelect">
+                  <option ng-repeat="lon in lonVariables">
+                    {{lon.text}}
+                  </option>
+                </select>
+              </div>
+            </div>
+            <div class="control-group">
+              <label class="control-label" for="dateTimeSelect">Date/Time 
Variable</label>
+              <div class="controls">
+                <select id="dateTimeSelect">
+                  <option ng-repeat="dateTime in dateTimeVariables">
+                    {{dateTime.text}}
+                  </option>
+                </select>
+              </div>
+            </div>
+            <div class="control-group">
+              <div class="controls">
+                <button type="submit" class="btn btn-warn">Cancel</button>
+                <button type="submit" class="btn">Add Model</button>
+              </div>
+            </div>
+          </form>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/results.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/results.html 
b/ocw-ui/frontend/app/views/results.html
new file mode 100644
index 0000000..432bba1
--- /dev/null
+++ b/ocw-ui/frontend/app/views/results.html
@@ -0,0 +1,34 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div class="row">
+  <div class="col-md-3">
+    <div id="results-sidebar" class="pa-sidebar well well-small">
+      <ul class="nav nav-list">
+        <li id="results-sidebar-header" class="nav-header">Latest Run 
Results</li>
+        <li ng-repeat="result in results"
+            ng-class="{ active: $state.includes('results.detail') && 
$stateParams.resultId == result }">
+          <a href="#/results/{{result.replace('/', '')}}" >{{result}}</a>
+        </li>
+      </ul>
+      <div ui-view="menu"></div>
+    </div>
+  </div>
+  <div class="col-md-9" ui-view ng-animate="{enter:'fade-enter'}"></div>
+</div>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/resultsdetail.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/resultsdetail.html 
b/ocw-ui/frontend/app/views/resultsdetail.html
new file mode 100644
index 0000000..4083ff3
--- /dev/null
+++ b/ocw-ui/frontend/app/views/resultsdetail.html
@@ -0,0 +1,30 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div>
+  <h2>{{result}}</h2>
+  <div class="row text-center">
+    <div class="{{alertClass}}">{{alertMessage}}</div>
+    <ul>
+      <li ng-repeat="figure in figures">
+        <img class="img-responsive" 
ng-src="{{baseURL}}/static/eval_results/{{figure}}" alt="" />
+      </li>
+    </ul>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/resultslist.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/resultslist.html 
b/ocw-ui/frontend/app/views/resultslist.html
new file mode 100644
index 0000000..f4664c6
--- /dev/null
+++ b/ocw-ui/frontend/app/views/resultslist.html
@@ -0,0 +1,21 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<h4>Select one of your previous runs. If none are displayed, please return
+  to the main page and start one!</h4>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/selectobservation.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/selectobservation.html 
b/ocw-ui/frontend/app/views/selectobservation.html
new file mode 100644
index 0000000..fd10ff8
--- /dev/null
+++ b/ocw-ui/frontend/app/views/selectobservation.html
@@ -0,0 +1,80 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div ng-controller="ObservationSelectCtrl">
+  <div class="row">
+    <div class="col-md-12">
+      <div class="row">
+        <!--<form class="form-inline" autocomplete="off">-->
+        <form class="form" autocomplete="off">
+          <div class="form-group">
+            <div class="col-md-9">
+              <input id="observationFileInput" predictive-file-browser-input 
ng-model="filePathInput" type="text" class="form-control" autocomplete="off" 
placeholder="Navigate to a .nc file. Start by typing '/'" />
+            </div>
+            <div class="col-md-2">
+              <button class="btn" ng-click="uploadLocalFile()" 
ng-disabled="shouldDisableLoadButton()">
+                <div ng-hide="loadingFile">Parse File</div>
+                <div ng-show="loadingFile"><i class="icon-spinner 
icon-spin"></i></div>
+              </button>
+            </div>
+          </div>
+        </form>
+      </div>
+      <hr />
+      <div class="row top3">
+        <div class="col-md-5 text-center">
+          Evaluation Variable
+        </div>
+        <div class="col-md-6">
+          <select class="form-control" ng-model="paramSelect" 
ng-options="param for param in params"></select>
+        </div>
+      </div>
+      <div class="row top3">
+        <div class="col-md-5 text-center">
+          Latitude Variable
+        </div>
+        <div class="col-md-6">
+          <select class="form-control" ng-model="latsSelect" ng-options="lat 
for lat in lats"></select>
+        </div>
+      </div>
+      <div class="row top3">
+        <div class="col-md-5 text-center">
+          Longitude Variable
+        </div>
+        <div class="col-md-6">
+          <select class="form-control" ng-model="lonsSelect" ng-options="lon 
for lon in lons"></select>
+        </div>
+      </div>
+      <div class="row top3">
+        <div class="col-md-5 text-center">
+          Date/Time Variable
+        </div>
+        <div class="col-md-6">
+          <select class="form-control" ng-model="timeSelect" ng-options="time 
for time in times"></select>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="row top3">
+    <div class="col-md-3 col-md-offset-1">
+      <button class="btn btn-primary btn-block" ng-click="addDataSet()">Add 
Dataset</button>
+    </div>
+    <div class="pull-left small-alert" ng-show="fileAdded">Successfully added 
dataset...</div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/app/views/selectrcmed.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/app/views/selectrcmed.html 
b/ocw-ui/frontend/app/views/selectrcmed.html
new file mode 100644
index 0000000..6b09f48
--- /dev/null
+++ b/ocw-ui/frontend/app/views/selectrcmed.html
@@ -0,0 +1,40 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<div ng-controller="RcmedSelectionCtrl">
+  <div class="row">
+    <div class="col-md-10 col-md-offset-1">
+      <div class="row top3">Select the RCMED dataset that you would like 
use.</div>
+      <div class="row top3">
+        <select ng-change="dataSelectUpdated()" class="form-control" 
ng-model="datasetSelection" ng-options="obs as obs.longname for obs in 
availableObs"></select>
+      </div>
+      <div class="row top3">Select the dataset parameter that you would like 
to test.</div>
+      <div class="row top3">
+        <select class="form-control" ng-model="parameterSelection" 
ng-options="param as param.shortname for param in retrievedObsParams"></select>
+      </div>
+
+      <div class="row top3">
+        <div class="col-md-3">
+          <button class="btn btn-primary btn-block-left" 
ng-click="addObservation()">Add Observation</button>
+        </div>
+        <div class="pull-left small-alert" ng-show="fileAdded">Successfully 
added dataset...</div>
+      </div>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/bower.json
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/bower.json b/ocw-ui/frontend/bower.json
new file mode 100644
index 0000000..d288a19
--- /dev/null
+++ b/ocw-ui/frontend/bower.json
@@ -0,0 +1,25 @@
+{
+  "name": "ocw-ui",
+  "version": "0.0.0",
+  "dependencies": {
+    "angular": "1.2.16",
+    "json3": "~3.3.1",
+    "es5-shim": "~3.1.0",
+    "bootstrap": "~3.2.0",
+    "angular-resource": "1.2.16",
+    "angular-cookies": "1.2.16",
+    "angular-animate": "1.2.16",
+    "angular-route": "1.2.16",
+    "angular-ui-router": "angular-ui/ui-router#~0.2.10",
+    "leaflet": "~0.7.3",
+    "chap-links-timeline": "~2.6.1",
+    "components-font-awesome": "~4.1.0",
+    "angular-bootstrap": "~0.11.0",
+    "angular-ui-date": "~0.0.3"
+  },
+  "devDependencies": {
+    "angular-mocks": "1.2.16",
+    "angular-scenario": "1.2.16"
+  },
+  "appPath": "app"
+}

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/config/karma-e2e.conf.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/config/karma-e2e.conf.js 
b/ocw-ui/frontend/config/karma-e2e.conf.js
deleted file mode 100755
index 746879e..0000000
--- a/ocw-ui/frontend/config/karma-e2e.conf.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
-**/
-basePath = '../';
-
-files = [
-  ANGULAR_SCENARIO,
-  ANGULAR_SCENARIO_ADAPTER,
-  'test/e2e/**/*.js'
-];
-
-autoWatch = false;
-
-browsers = ['Chrome'];
-
-singleRun = true;
-
-proxies = {
-  '/': 'http://localhost:8000/'
-};
-
-junitReporter = {
-  outputFile: 'test_out/e2e.xml',
-  suite: 'e2e'
-};

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/config/karma.conf.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/config/karma.conf.js 
b/ocw-ui/frontend/config/karma.conf.js
deleted file mode 100755
index 65b5013..0000000
--- a/ocw-ui/frontend/config/karma.conf.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
-**/
-basePath = '../';
-
-files = [
-  JASMINE,
-  JASMINE_ADAPTER,
-  'app/js/lib/jquery/jquery-1.10.1.js',
-  'app/js/lib/jquery/jquery-ui/jquery-ui-1.10.3.min.js',
-  'app/js/lib/bootstrap/bootstrap.js',
-  'app/js/lib/angular/angular.js',
-  'app/js/lib/angular/angular-*.js',
-  'app/js/lib/angular-ui/*.js',
-  'test/lib/angular/angular-mocks.js',
-  'app/js/lib/jquery/jquery-ui/datepicker-wrapper/date.js',
-  'app/js/lib/leaflet/leaflet-0.5.js',
-  'app/js/app.js',
-  'app/js/controllers/*.js',
-  'app/js/directives/*.js',
-  'app/js/services/*.js',
-  'app/js/filters/*.js',
-  'test/unit/**/*.js'
-];
-
-autoWatch = true;
-
-browsers = ['Chrome'];
-
-junitReporter = {
-  outputFile: 'test_out/unit.xml',
-  suite: 'unit'
-};

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/package.json
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/package.json b/ocw-ui/frontend/package.json
new file mode 100644
index 0000000..be2f597
--- /dev/null
+++ b/ocw-ui/frontend/package.json
@@ -0,0 +1,40 @@
+{
+  "name": "ocwui",
+  "version": "0.0.0",
+  "dependencies": {},
+  "devDependencies": {
+    "grunt": "^0.4.1",
+    "grunt-autoprefixer": "^0.7.3",
+    "grunt-concurrent": "^0.5.0",
+    "grunt-contrib-clean": "^0.5.0",
+    "grunt-contrib-concat": "^0.4.0",
+    "grunt-contrib-connect": "^0.7.1",
+    "grunt-contrib-copy": "^0.5.0",
+    "grunt-contrib-cssmin": "^0.9.0",
+    "grunt-contrib-htmlmin": "^0.3.0",
+    "grunt-contrib-imagemin": "^0.7.0",
+    "grunt-contrib-jshint": "^0.10.0",
+    "grunt-contrib-uglify": "^0.4.0",
+    "grunt-contrib-watch": "^0.6.1",
+    "grunt-filerev": "^0.2.1",
+    "grunt-google-cdn": "^0.4.0",
+    "grunt-karma": "^0.8.3",
+    "grunt-newer": "^0.7.0",
+    "grunt-ngmin": "^0.0.3",
+    "grunt-svgmin": "^0.4.0",
+    "grunt-usemin": "^2.1.1",
+    "grunt-wiredep": "^1.8.0",
+    "jshint-stylish": "^0.2.0",
+    "karma": "^0.12.17",
+    "karma-jasmine": "^0.1.5",
+    "karma-phantomjs-launcher": "^0.1.4",
+    "load-grunt-tasks": "^0.4.0",
+    "time-grunt": "^0.3.1"
+  },
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "scripts": {
+    "test": "grunt test"
+  }
+}

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/scripts/e2e-test.sh
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/scripts/e2e-test.sh 
b/ocw-ui/frontend/scripts/e2e-test.sh
deleted file mode 100755
index dfca333..0000000
--- a/ocw-ui/frontend/scripts/e2e-test.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-BASE_DIR=`dirname $0`
-
-echo ""
-echo "Starting Karma Server"
-echo "-------------------------------------------------------------------"
-
-karma start $BASE_DIR/../config/karma-e2e.conf.js $*

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/scripts/test.sh
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/scripts/test.sh b/ocw-ui/frontend/scripts/test.sh
deleted file mode 100755
index 7094550..0000000
--- a/ocw-ui/frontend/scripts/test.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-BASE_DIR=`dirname $0`
-
-echo ""
-echo "Starting Karma Server"
-echo "-------------------------------------------------------------------"
-
-karma start $BASE_DIR/../config/karma.conf.js $*

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/scripts/web-server.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/scripts/web-server.js 
b/ocw-ui/frontend/scripts/web-server.js
deleted file mode 100755
index e137ad2..0000000
--- a/ocw-ui/frontend/scripts/web-server.js
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/usr/bin/env node
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
-**/
-
-var util = require('util'),
-    http = require('http'),
-    fs = require('fs'),
-    url = require('url'),
-    events = require('events');
-
-var DEFAULT_PORT = 8000;
-
-function main(argv) {
-  new HttpServer({
-    'GET': createServlet(StaticServlet),
-    'HEAD': createServlet(StaticServlet)
-  }).start(Number(argv[2]) || DEFAULT_PORT);
-}
-
-function escapeHtml(value) {
-  return value.toString().
-    replace('<', '&lt;').
-    replace('>', '&gt;').
-    replace('"', '&quot;');
-}
-
-function createServlet(Class) {
-  var servlet = new Class();
-  return servlet.handleRequest.bind(servlet);
-}
-
-/**
- * An Http server implementation that uses a map of methods to decide
- * action routing.
- *
- * @param {Object} Map of method => Handler function
- */
-function HttpServer(handlers) {
-  this.handlers = handlers;
-  this.server = http.createServer(this.handleRequest_.bind(this));
-}
-
-HttpServer.prototype.start = function(port) {
-  this.port = port;
-  this.server.listen(port);
-  util.puts('Http Server running at http://localhost:' + port + '/');
-};
-
-HttpServer.prototype.parseUrl_ = function(urlString) {
-  var parsed = url.parse(urlString);
-  parsed.pathname = url.resolve('/', parsed.pathname);
-  return url.parse(url.format(parsed), true);
-};
-
-HttpServer.prototype.handleRequest_ = function(req, res) {
-  var logEntry = req.method + ' ' + req.url;
-  if (req.headers['user-agent']) {
-    logEntry += ' ' + req.headers['user-agent'];
-  }
-  util.puts(logEntry);
-  req.url = this.parseUrl_(req.url);
-  var handler = this.handlers[req.method];
-  if (!handler) {
-    res.writeHead(501);
-    res.end();
-  } else {
-    handler.call(this, req, res);
-  }
-};
-
-/**
- * Handles static content.
- */
-function StaticServlet() {}
-
-StaticServlet.MimeMap = {
-  'txt': 'text/plain',
-  'html': 'text/html',
-  'css': 'text/css',
-  'xml': 'application/xml',
-  'json': 'application/json',
-  'js': 'application/javascript',
-  'jpg': 'image/jpeg',
-  'jpeg': 'image/jpeg',
-  'gif': 'image/gif',
-  'png': 'image/png',
-  'svg': 'image/svg+xml'
-};
-
-StaticServlet.prototype.handleRequest = function(req, res) {
-  var self = this;
-  var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, 
function(match, hex){
-    return String.fromCharCode(parseInt(hex, 16));
-  });
-  var parts = path.split('/');
-  if (parts[parts.length-1].charAt(0) === '.')
-    return self.sendForbidden_(req, res, path);
-  if (~path.indexOf("dirlist"))
-    return self.getDirList_(req, res, path);
-  fs.stat(path, function(err, stat) {
-    if (err)
-      return self.sendMissing_(req, res, path);
-    if (stat.isDirectory())
-      return self.sendDirectory_(req, res, path);
-    return self.sendFile_(req, res, path);
-  });
-}
-
-StaticServlet.prototype.sendError_ = function(req, res, error) {
-  res.writeHead(500, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>Internal Server Error</title>\n');
-  res.write('<h1>Internal Server Error</h1>');
-  res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
-  util.puts('500 Internal Server Error');
-  util.puts(util.inspect(error));
-};
-
-StaticServlet.prototype.sendMissing_ = function(req, res, path) {
-  path = path.substring(1);
-  res.writeHead(404, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>404 Not Found</title>\n');
-  res.write('<h1>Not Found</h1>');
-  res.write(
-    '<p>The requested URL ' +
-    escapeHtml(path) +
-    ' was not found on this server.</p>'
-  );
-  res.end();
-  util.puts('404 Not Found: ' + path);
-};
-
-StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
-  path = path.substring(1);
-  res.writeHead(403, {
-      'Content-Type': 'text/html'
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>403 Forbidden</title>\n');
-  res.write('<h1>Forbidden</h1>');
-  res.write(
-    '<p>You do not have permission to access ' +
-    escapeHtml(path) + ' on this server.</p>'
-  );
-  res.end();
-  util.puts('403 Forbidden: ' + path);
-};
-
-StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
-  res.writeHead(301, {
-      'Content-Type': 'text/html',
-      'Location': redirectUrl
-  });
-  res.write('<!doctype html>\n');
-  res.write('<title>301 Moved Permanently</title>\n');
-  res.write('<h1>Moved Permanently</h1>');
-  res.write(
-    '<p>The document has moved <a href="' +
-    redirectUrl +
-    '">here</a>.</p>'
-  );
-  res.end();
-  util.puts('301 Moved Permanently: ' + redirectUrl);
-};
-
-StaticServlet.prototype.getDirList_ = function(req, res, path) {
-  res.writeHead(200, {
-       'Content-Type': 'json'
-  });
-
-  // Grab the passed path value
-  var pathQuery = url.parse(req.url, true).query.path
-  // Using the supplied path, grab directory information
-  var dirList = fs.readdirSync(pathQuery);
-
-  // Filter out any hidden files or current/previous directory references
-  dirList = dirList.filter(function(item, index, array) {
-       return (item[0] !== ".");
-  });
-  
-  // Generate the full path names for all the items found when 'ls'-ing 
-  // the passed directory.
-  dirList = dirList.map(function(item, index, array) {
-    var temp = item; 
-
-       // Make sure the path is joined properly. Sometimes there will be a 
trailing
-       // '/' in the path and sometimes there won't. Don't want to end up with 
'//'.
-    if (pathQuery[pathQuery.length - 1] === "/") {
-      temp = pathQuery + item;
-    } else {
-         temp = pathQuery + "/" + item;
-    }
-  
-       // We want the directories that are found to have a trailing '/'. Let's 
make sure
-       // that we do that!
-    var ret = temp;
-    if (fs.existsSync(temp + "/")) {
-     ret = temp + "/";
-    }  
-
-    return ret;
-  });
-  
-  // Sort all the results alphabetically ignoring case.
-  dirList = dirList.sort(function(a, b) {
-    if (a.toLowerCase() < b.toLowerCase()) return -1;
-    if (a.toLowerCase() > b.toLowerCase()) return 1;
-    return 0;
-  });
-
-  res.write(JSON.stringify(dirList));
-  res.end();
-}
-
-StaticServlet.prototype.sendFile_ = function(req, res, path) {
-  var self = this;
-  var file = fs.createReadStream(path);
-  res.writeHead(200, {
-    'Content-Type': StaticServlet.
-      MimeMap[path.split('.').pop()] || 'text/plain'
-  });
-  if (req.method === 'HEAD') {
-    res.end();
-  } else {
-    file.on('data', res.write.bind(res));
-    file.on('close', function() {
-      res.end();
-    });
-    file.on('error', function(error) {
-      self.sendError_(req, res, error);
-    });
-  }
-};
-
-StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
-  var self = this;
-  if (path.match(/[^\/]$/)) {
-    req.url.pathname += '/';
-    var redirectUrl = url.format(url.parse(url.format(req.url)));
-    return self.sendRedirect_(req, res, redirectUrl);
-  }
-  fs.readdir(path, function(err, files) {
-    if (err)
-      return self.sendError_(req, res, error);
-
-    if (!files.length)
-      return self.writeDirectoryIndex_(req, res, path, []);
-
-    var remaining = files.length;
-    files.forEach(function(fileName, index) {
-      fs.stat(path + '/' + fileName, function(err, stat) {
-        if (err)
-          return self.sendError_(req, res, err);
-        if (stat.isDirectory()) {
-          files[index] = fileName + '/';
-        }
-        if (!(--remaining))
-          return self.writeDirectoryIndex_(req, res, path, files);
-      });
-    });
-  });
-};
-
-StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) 
{
-  path = path.substring(1);
-  res.writeHead(200, {
-    'Content-Type': 'text/html'
-  });
-  if (req.method === 'HEAD') {
-    res.end();
-    return;
-  }
-  res.write('<!doctype html>\n');
-  res.write('<title>' + escapeHtml(path) + '</title>\n');
-  res.write('<style>\n');
-  res.write('  ol { list-style-type: none; font-size: 1.2em; }\n');
-  res.write('</style>\n');
-  res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
-  res.write('<ol>');
-  files.forEach(function(fileName) {
-    if (fileName.charAt(0) !== '.') {
-      res.write('<li><a href="' +
-        escapeHtml(fileName) + '">' +
-        escapeHtml(fileName) + '</a></li>');
-    }
-  });
-  res.write('</ol>');
-  res.end();
-};
-
-// Must be last,
-main(process.argv);

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/test/.jshintrc
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/test/.jshintrc b/ocw-ui/frontend/test/.jshintrc
new file mode 100644
index 0000000..b1be025
--- /dev/null
+++ b/ocw-ui/frontend/test/.jshintrc
@@ -0,0 +1,36 @@
+{
+  "node": true,
+  "browser": true,
+  "esnext": true,
+  "bitwise": true,
+  "camelcase": true,
+  "curly": true,
+  "eqeqeq": true,
+  "immed": true,
+  "indent": 2,
+  "latedef": true,
+  "newcap": true,
+  "noarg": true,
+  "quotmark": "single",
+  "regexp": true,
+  "undef": true,
+  "unused": true,
+  "strict": true,
+  "trailing": true,
+  "smarttabs": true,
+  "globals": {
+    "after": false,
+    "afterEach": false,
+    "angular": false,
+    "before": false,
+    "beforeEach": false,
+    "browser": false,
+    "describe": false,
+    "expect": false,
+    "inject": false,
+    "it": false,
+    "jasmine": false,
+    "spyOn": false
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/test/e2e/runner.html
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/test/e2e/runner.html 
b/ocw-ui/frontend/test/e2e/runner.html
deleted file mode 100755
index 73c7733..0000000
--- a/ocw-ui/frontend/test/e2e/runner.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Licensed to the Apache Software Foundation (ASF) under one
- ~ or more contributor license agreements.  See the NOTICE file
- ~ distributed with this work for additional information
- ~ regarding copyright ownership.  The ASF licenses this file
- ~ to you under the Apache License, Version 2.0 (the
- ~ "License"); you may not use this file except in compliance
- ~ with the License.  You may obtain a copy of the License at
- ~
- ~    http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing,
- ~ software distributed under the License is distributed on an
- ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- ~ KIND, either express or implied.  See the License for the
- ~ specific language governing permissions and limitations
- ~ under the License.
- -->
-<!doctype html>
-<html lang="en">
-  <head>
-    <title>End2end Test Runner</title>
-    <script src="../lib/angular/angular-scenario.js" ng-autotest></script>
-    <script src="scenarios.js"></script>
-  </head>
-  <body>
-  </body>
-</html>

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/test/e2e/scenarios.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/test/e2e/scenarios.js 
b/ocw-ui/frontend/test/e2e/scenarios.js
deleted file mode 100755
index c7c0e6c..0000000
--- a/ocw-ui/frontend/test/e2e/scenarios.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
-**/
-'use strict';
-
-/* http://docs.angularjs.org/guide/dev_guide.e2e-testing */
-
-describe('my app', function() {
-
-  beforeEach(function() {
-    browser().navigateTo('../../app/index.html');
-  });
-
-
-  it('should automatically redirect to /view1 when location hash/fragment is 
empty', function() {
-    expect(browser().location().url()).toBe("/view1");
-  });
-
-
-  describe('view1', function() {
-
-    beforeEach(function() {
-      browser().navigateTo('#/view1');
-    });
-
-
-    it('should render view1 when user navigates to /view1', function() {
-      expect(element('[ng-view] p:first').text()).
-        toMatch(/partial for view 1/);
-    });
-
-  });
-
-
-  describe('view2', function() {
-
-    beforeEach(function() {
-      browser().navigateTo('#/view2');
-    });
-
-
-    it('should render view2 when user navigates to /view2', function() {
-      expect(element('[ng-view] p:first').text()).
-        toMatch(/partial for view 2/);
-    });
-
-  });
-});

http://git-wip-us.apache.org/repos/asf/climate/blob/652ea657/ocw-ui/frontend/test/karma.conf.js
----------------------------------------------------------------------
diff --git a/ocw-ui/frontend/test/karma.conf.js 
b/ocw-ui/frontend/test/karma.conf.js
new file mode 100644
index 0000000..744d927
--- /dev/null
+++ b/ocw-ui/frontend/test/karma.conf.js
@@ -0,0 +1,86 @@
+// Karma configuration
+// http://karma-runner.github.io/0.12/config/configuration-file.html
+// Generated on 2014-07-15 using
+// generator-karma 0.8.3
+
+module.exports = function(config) {
+  'use strict';
+
+  config.set({
+    // enable / disable watching file and executing tests whenever any file 
changes
+    autoWatch: true,
+
+    // base path, that will be used to resolve files and exclude
+    basePath: '../',
+
+    // testing framework to use (jasmine/mocha/qunit/...)
+    frameworks: ['jasmine'],
+
+    // list of files / patterns to load in the browser
+    files: [
+      'bower_components/angular/angular.js',
+      'bower_components/angular-mocks/angular-mocks.js',
+      'bower_components/angular-animate/angular-animate.js',
+      'bower_components/angular-cookies/angular-cookies.js',
+      'bower_components/angular-resource/angular-resource.js',
+      'bower_components/angular-route/angular-route.js',
+      'bower_components/angular-animate/angular-animate.js',
+      'bower_components/angular-bootstrap/ui-bootstrap.js',
+      // For some reason this causes the tests to completely fail to run
+      // if it is uncommented.
+      //'bower_components/angular-scenario/angular-scenario.js',
+      'bower_components/angular-ui-date/src/date.js',
+      'bower_components/angular-ui-router/release/angular-ui-router.js',
+      'bower_components/chap-links-timeline/timeline.js',
+      'bower_components/jquery/dist/jquery.js',
+      'bower_components/jquery-ui/jquery-ui.js',
+      'bower_components/leaflet/dist/leaflet.js',
+      'app/scripts/**/*.js',
+      'test/mock/**/*.js',
+      'test/spec/**/*.js'
+    ],
+
+    // list of files / patterns to exclude
+    exclude: [],
+
+    // web server port
+    port: 8080,
+
+    // Start these browsers, currently available:
+    // - Chrome
+    // - ChromeCanary
+    // - Firefox
+    // - Opera
+    // - Safari (only Mac)
+    // - PhantomJS
+    // - IE (only Windows)
+    browsers: [
+      'PhantomJS',
+      //'Chrome'
+    ],
+
+    // Which plugins to enable
+    plugins: [
+      'karma-phantomjs-launcher',
+      'karma-chrome-launcher',
+      'karma-jasmine'
+    ],
+
+    // Continuous Integration mode
+    // if true, it capture browsers, run tests and exit
+    singleRun: false,
+
+    colors: true,
+
+    // level of logging
+    // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || 
LOG_DEBUG
+    logLevel: config.LOG_INFO,
+
+    // Uncomment the following lines if you are using grunt's server to run 
the tests
+    // proxies: {
+    //   '/': 'http://localhost:9000/'
+    // },
+    // URL root prevent conflicts with the site root
+    // urlRoot: '_karma_'
+  });
+};

Reply via email to