Browse Source

AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)

Yusaku Sako 11 năm trước cách đây
mục cha
commit
e81d705b77
60 tập tin đã thay đổi với 2504 bổ sung1495 xóa
  1. 18 0
      ambari-web/app/assets/test/tests.js
  2. 15 0
      ambari-web/app/messages.js
  3. 5 11
      ambari-web/app/templates/main/dashboard.hbs
  4. 1 1
      ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs
  5. 1 1
      ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs
  6. 1 1
      ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
  7. 2 2
      ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs
  8. 5 0
      ambari-web/app/views.js
  9. 3 6
      ambari-web/app/views/main/dashboard.js
  10. 14 15
      ambari-web/app/views/main/dashboard/widget.js
  11. 25 0
      ambari-web/app/views/main/dashboard/widgets/cluster_metrics_widget.js
  12. 10 54
      ambari-web/app/views/main/dashboard/widgets/datanode_live.js
  13. 10 49
      ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js
  14. 12 15
      ambari-web/app/views/main/dashboard/widgets/hbase_links.js
  15. 15 78
      ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
  16. 7 94
      ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
  17. 10 45
      ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js
  18. 25 81
      ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
  19. 10 15
      ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
  20. 26 78
      ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js
  21. 11 76
      ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js
  22. 6 55
      ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js
  23. 7 93
      ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js
  24. 49 0
      ambari-web/app/views/main/dashboard/widgets/links_widget.js
  25. 10 15
      ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js
  26. 7 8
      ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js
  27. 2 8
      ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js
  28. 2 8
      ambari-web/app/views/main/dashboard/widgets/metrics_load.js
  29. 2 8
      ambari-web/app/views/main/dashboard/widgets/metrics_memory.js
  30. 2 8
      ambari-web/app/views/main/dashboard/widgets/metrics_network.js
  31. 26 78
      ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
  32. 29 89
      ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
  33. 8 56
      ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
  34. 7 94
      ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
  35. 6 51
      ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
  36. 140 0
      ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
  37. 11 75
      ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
  38. 7 92
      ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
  39. 8 52
      ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
  40. 66 0
      ambari-web/app/views/main/dashboard/widgets/text_widget.js
  41. 149 0
      ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
  42. 10 83
      ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
  43. 73 0
      ambari-web/test/views/main/dashboard/widget_test.js
  44. 69 0
      ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
  45. 103 0
      ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
  46. 95 0
      ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
  47. 102 0
      ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
  48. 107 0
      ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
  49. 95 0
      ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
  50. 45 0
      ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
  51. 107 0
      ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
  52. 107 0
      ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
  53. 95 0
      ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
  54. 97 0
      ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
  55. 169 0
      ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
  56. 95 0
      ambari-web/test/views/main/dashboard/widgets/resource_manager_uptime_test.js
  57. 69 0
      ambari-web/test/views/main/dashboard/widgets/tasktracker_live_test.js
  58. 84 0
      ambari-web/test/views/main/dashboard/widgets/text_widget_test.js
  59. 90 0
      ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
  60. 122 0
      ambari-web/test/views/main/dashboard_test.js

+ 18 - 0
ambari-web/app/assets/test/tests.js

@@ -63,6 +63,24 @@ require('test/utils/config_test');
 require('test/utils/string_utils_test');
 require('test/views/common/chart/linear_time_test');
 require('test/views/common/filter_view_test');
+require('test/views/main/dashboard_test');
+require('test/views/main/dashboard/widget_test');
+require('test/views/main/dashboard/widgets/text_widget_test');
+require('test/views/main/dashboard/widgets/uptime_text_widget_test');
+require('test/views/main/dashboard/widgets/node_managers_live_test');
+require('test/views/main/dashboard/widgets/datanode_live_test');
+require('test/views/main/dashboard/widgets/tasktracker_live_test');
+require('test/views/main/dashboard/widgets/hbase_average_load_test');
+require('test/views/main/dashboard/widgets/hbase_regions_in_transition_test');
+require('test/views/main/dashboard/widgets/jobtracker_rpc_test');
+require('test/views/main/dashboard/widgets/namenode_rpc_test');
+require('test/views/main/dashboard/widgets/hbase_master_uptime_test');
+require('test/views/main/dashboard/widgets/jobtracker_uptime_test');
+require('test/views/main/dashboard/widgets/namenode_uptime_test');
+require('test/views/main/dashboard/widgets/resource_manager_uptime_test');
+require('test/views/main/dashboard/widgets/links_widget_test');
+require('test/views/main/dashboard/widgets/pie_chart_widget_test');
+require('test/views/main/dashboard/widgets/namenode_cpu_test');
 require('test/views/common/configs/services_config_test');
 require('test/models/host_test');
 require('test/models/rack_test');

+ 15 - 0
ambari-web/app/messages.js

@@ -1142,6 +1142,9 @@ Em.I18n.translations = {
   'charts.heatmap.metrics.MRMemHeapUsed' :'MapReduce JVM Heap Memory Used',
   'charts.heatmap.metrics.YarnMemHeapUsed' :'YARN JVM Heap Memory Used',
   'charts.heatmap.metrics.reducesRunning' :'MapReduce Reduces Running',
+
+  'charts.heatmap.metrics.YarnResourceUsed' :'YARN Resource used %',
+
   'charts.heatmap.metrics.memoryUsed' :'Host Memory Used %',
   'charts.heatmap.metrics.processRun' :'Total Running Processes',
   'charts.heatmap.metrics.YarnResourceUsed' :'YARN Resource used %',
@@ -1207,6 +1210,18 @@ Em.I18n.translations = {
   'dashboard.widgets.NodeManagersLive': 'NodeManagers Live',
   'dashboard.widgets.YARNMemory': 'YARN Memory',
 
+  'dashboard': {
+    'widgets': {
+      'popupHeader': 'Customize Widget',
+      'hintInfo': {
+        'common': 'Edit the percentage thresholds to change the color of current pie chart. <br />Enter two numbers between 0 to {0}',
+        'hint1': 'Edit the percentage of thresholds to change the color of current widget. <br />Assume all components UP is 100, and all DOWN is 0. <br /> So enter two numbers between 0 to {0}',
+        'hint2': 'Edit the thresholds to change the color of current widget.<br /><br />So enter two numbers larger than 0.',
+        'hint3': 'Edit the thresholds to change the color of current widget.<br />The unit is milli-second. <br />So enter two numbers larger than 0. '
+      }
+    }
+  },
+
   'dashboard.services':'Services',
   'dashboard.services.hosts':'Hosts',
   'dashboard.services.uptime':'{0}',

+ 5 - 11
ambari-web/app/templates/main/dashboard.hbs

@@ -88,13 +88,13 @@
                   </button>
                   <ul class="dropdown-menu right-align-dropdown">
                     <li>
-                      <a {{action "resetAllWidgets" target="view"}}>
+                      <a href="#" {{action "resetAllWidgets" target="view"}}>
                         <i class="icon-refresh"></i>
                         {{t dashboard.button.reset}}
                       </a>
                     </li>
                     <li>
-                      <a {{action "switchToClassic" target="view"}}>
+                      <a href="#" {{action "switchToClassic" target="view"}}>
                         <i class="icon-backward"></i>
                         {{t dashboard.button.switch}}
                       </a>
@@ -114,15 +114,9 @@
             <div class="thumbnails row-fluid" id="sortable">
               {{#if view.visibleWidgets.length}}
                 {{#each widgetClass in view.visibleWidgets}}
-                  {{#if widgetClass.isProgressBar}}
-                    <div class="span4p8">
-                      {{view widgetClass }}
-                    </div>
-                  {{else}}
-                    <div class="span2p4">
-                      {{view widgetClass }}
-                    </div>
-                  {{/if}}
+                  <div {{bindAttr class="widgetClass.class"}}>
+                    {{view widgetClass }}
+                  </div>
                 {{/each}}
               {{/if}}
             </div>

+ 1 - 1
ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs

@@ -18,7 +18,7 @@
 <form class="form-horizontal" autocomplete="off">
     <div class="each-row">
         <div class="alert alert-info">
-          {{view.configPropertyObj.hintInfo}}
+          {{{view.configPropertyObj.hintInfo}}}
         </div>
     </div>
 

+ 1 - 1
ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs

@@ -38,7 +38,7 @@
         </tr>
         <!--region servers-->
         <tr>
-            <td><a href="#" {{action filterHosts view.regionServerComponent}}>{{view.model.regionServers.length}} {{t dashboard.services.hbase.regionServers}}</a></td>
+            <td><a href="#" {{action filterHosts view.component}}>{{view.model.regionServers.length}} {{t dashboard.services.hbase.regionServers}}</a></td>
         </tr>
         <!--hbase master Web UI-->
         <tr>

+ 1 - 1
ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs

@@ -36,7 +36,7 @@
         <!--Data Nodes-->
         <tr>
           <td>
-              <a href="#" {{action filterHosts view.dataNodeComponent}}>{{view.model.dataNodes.length}} {{t dashboard.services.hdfs.datanodes}}</a>
+              <a href="#" {{action filterHosts view.component}}>{{view.model.dataNodes.length}} {{t dashboard.services.hdfs.datanodes}}</a>
           </td>
         </tr>
       </table>

+ 2 - 2
ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs

@@ -31,11 +31,11 @@
         </tr>
         <!--taskTrackers-->
         <tr>
-          <td><a href="#" {{action filterHosts view.taskTrackerComponent}}>{{view.model.taskTrackers.length}} {{t dashboard.services.mapreduce.taskTrackers}}</a></td>
+          <td><a href="#" {{action filterHosts view.component}}>{{view.model.taskTrackers.length}} {{t dashboard.services.mapreduce.taskTrackers}}</a></td>
         </tr>
         <!--jobTracker Web UI-->
         <tr>
-          <td><a {{bindAttr href="view.jobTrackerWebUrl"}} target="_blank">{{t services.service.summary.jobTrackerWebUI}}</a></td>
+          <td><a {{bindAttr href="view.webUrl"}} target="_blank">{{t services.service.summary.jobTrackerWebUI}}</a></td>
         </tr>
       </table>
     </div>

+ 5 - 0
ambari-web/app/views.js

@@ -99,6 +99,11 @@ require('views/main/dashboard/cluster_metrics/memory');
 require('views/main/dashboard/cluster_metrics/network');
 
 require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/uptime_text_widget');
+require('views/main/dashboard/widgets/links_widget');
+require('views/main/dashboard/widgets/pie_chart_widget');
+require('views/main/dashboard/widgets/cluster_metrics_widget');
 require('views/main/dashboard/widgets/namenode_heap');
 require('views/main/dashboard/widgets/namenode_cpu');
 require('views/main/dashboard/widgets/hdfs_capacity');

+ 3 - 6
ambari-web/app/views/main/dashboard.js

@@ -125,8 +125,8 @@ App.MainDashboardView = Em.View.extend({
       }, this);
     }
     var obj = this.get('initPrefObject');
-    obj.visible = visibleFull;
-    obj.hidden = hiddenFull;
+    obj.set('visible', visibleFull);
+    obj.set('hidden', hiddenFull);
   },
   
   hdfs_model: null,
@@ -491,10 +491,7 @@ App.MainDashboardView = Em.View.extend({
         },
         templateName: require('templates/main/dashboard/alert_notification_popup')
       }),
-      primary: 'Close',
-      onPrimary: function() {
-        this.hide();
-      },
+      primary: Em.I18n.t('common.close'),
       secondary : null,
       didInsertElement: function () {
         this.$().find('.modal-footer').addClass('align-center');

+ 14 - 15
ambari-web/app/views/main/dashboard/widget.js

@@ -33,11 +33,11 @@ App.DashboardWidgetView = Em.View.extend({
     } else if (this.get('model_type') == 'yarn') {
       return this.get('parentView').get('yarn_model');
     }
-  }.property(''), //data bind from parent view
+  }.property(), //data bind from parent view
 
   id: null, // id 1-10 used to identify
   viewID: function(){ // used by re-sort
-    return 'widget-' + this.id;
+    return 'widget-' + this.get('id');
   }.property('id'),  //html id bind to view-class: widget-(1)
   attributeBindings: ['viewID'],
 
@@ -64,12 +64,12 @@ App.DashboardWidgetView = Em.View.extend({
       //update view on dashboard
       var objClass = parent.widgetsMapper(this.id);
       parent.get('visibleWidgets').removeObject(objClass);
-      parent.get('hiddenWidgets').pushObject(Em.Object.create({displayName: this.title, id: this.id, checked: false}));
+      parent.get('hiddenWidgets').pushObject(Em.Object.create({displayName: this.get('title'), id: this.get('id'), checked: false}));
     } else {
       //reconstruct new persist value then post in persist
       parent.getUserPref(parent.get('persistKey'));
       var oldValue = parent.get('currentPrefObject');
-      var deletedId = this.id;
+      var deletedId = this.get('id');
       var newValue = Em.Object.create({
         dashboardVersion: oldValue.dashboardVersion,
         visible: [],
@@ -81,7 +81,7 @@ App.DashboardWidgetView = Em.View.extend({
           newValue.visible.push(oldValue.visible[i]);
         }
       }
-      newValue.hidden.push([deletedId, this.title]);
+      newValue.hidden.push([deletedId, this.get('title')]);
       parent.postUserPref(parent.get('persistKey'), newValue);
       parent.translateToReal(newValue);
     }
@@ -93,8 +93,7 @@ App.DashboardWidgetView = Em.View.extend({
     var configObj = Ember.Object.create({
       thresh1: self.get('thresh1') + '',
       thresh2: self.get('thresh2') + '',
-      hintInfo: 'Edit the percentage thresholds to change the color of current pie chart. ' + ' '+
-        ' Enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.common').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -143,7 +142,7 @@ App.DashboardWidgetView = Em.View.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget' ],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -161,17 +160,13 @@ App.DashboardWidgetView = Em.View.extend({
             var parent = self.get('parentView');
             parent.getUserPref(parent.get('persistKey'));
             var oldValue = parent.get('currentPrefObject');
-            oldValue.threshold[parseInt(self.id)] = [configObj.get('thresh1'), configObj.get('thresh2')];
+            oldValue.threshold[parseInt(self.get('id'))] = [configObj.get('thresh1'), configObj.get('thresh2')];
             parent.postUserPref(parent.get('persistKey'), oldValue);
           }
 
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
@@ -198,7 +193,7 @@ App.DashboardWidgetView = Em.View.extend({
             }
           });
 
-          function updateColors (handlers) {
+          function updateColors(handlers) {
             var colorstops = colors[0] + ", "; // start with the first color
             for (var i = 0; i < handlers.length; i++) {
               colorstops += colors[i] + " " + handlers[i]*100/max_tmp + "%,";
@@ -255,8 +250,12 @@ App.DashboardWidgetView = Em.View.extend({
     } else if (lineNum == 5) {
       return "simple-text-hidden-five-line";
     }
-  }.property('this.hiddenInfo.length')
+    return '';
+  }.property('hiddenInfo.length')
 
 });
 
 
+App.DashboardWidgetView.reopenClass({
+  class: 'span2p4'
+});

+ 25 - 0
ambari-web/app/views/main/dashboard/widgets/cluster_metrics_widget.js

@@ -0,0 +1,25 @@
+/**
+ * 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 App = require('app');
+
+App.ClusterMetricsDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/cluster_metrics')
+
+});

+ 10 - 54
ambari-web/app/views/main/dashboard/widgets/datanode_live.js

@@ -18,9 +18,8 @@
 
 var App = require('app');
 
-App.DataNodeUpView = App.DashboardWidgetView.extend({
+App.DataNodeUpView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.DataNodeUp'),
   id: '4',
 
@@ -32,31 +31,14 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
   hiddenInfo: function () {
     var result = [];
     result.pushObject( App.HostComponent.find().filterProperty('componentName', 'DATANODE').filterProperty("workStatus","STARTED").length
-      + ' ' + this.t('dashboard.services.hdfs.nodes.live'));
+      + ' ' + Em.I18n.t('dashboard.services.hdfs.nodes.live'));
     result.pushObject( App.HostComponent.find().filterProperty('componentName', 'DATANODE').filterProperty("workStatus","INSTALLED").length
-      + ' ' + this.t('dashboard.services.hdfs.nodes.dead'));
-    result.pushObject(this.get('model.decommissionDataNodes.length')+ ' ' + this.t('dashboard.services.hdfs.nodes.decom'));
+      + ' ' + Em.I18n.t('dashboard.services.hdfs.nodes.dead'));
+    result.pushObject(this.get('model.decommissionDataNodes.length')+ ' ' + Em.I18n.t('dashboard.services.hdfs.nodes.decom'));
     return result;
   }.property('model', 'model.decommissionDataNodes.length'),
   hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen'],
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-
   thresh1: 40,
   thresh2: 70,
   maxValue: 100,
@@ -75,9 +57,7 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the percentage of thresholds to change the color of current widget. ' +
-        ' Assume all data nodes UP is 100, and all DOWN is 0. '+
-        ' So enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint1').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -126,7 +106,7 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -148,10 +128,6 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
@@ -166,36 +142,17 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
             max: max_tmp,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             },
             slide: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
               configObj.set('thresh1', ui.values[0] + '');
               configObj.set('thresh2', ui.values[1] + '');
             },
             change: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
             }
           });
-
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', false);
@@ -203,5 +160,4 @@ App.DataNodeUpView = App.DashboardWidgetView.extend({
       }
     });
   }
-
-})
+});

+ 10 - 49
ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js

@@ -18,41 +18,27 @@
 
 var App = require('app');
 
-App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
+App.HBaseAverageLoadView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.HBaseAverageLoad'),
   id: '21',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hbase',
   hiddenInfo: function () {
     var avgLoad = this.get('model.averageLoad');
     if (avgLoad == null) {
-      avgLoad = this.t('services.service.summary.notAvailable');
+      avgLoad = Em.I18n.t('services.service.summary.notAvailable');
     }
     var result = [];
-    result.pushObject(this.t('dashboard.services.hbase.averageLoadPerServer').format(avgLoad));
+    result.pushObject(Em.I18n.t('dashboard.services.hbase.averageLoadPerServer').format(avgLoad));
     return result;
   }.property("model.averageLoad"),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
   isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
+    return this.get('data') <= this.get('thresh1');
   }.property('data','thresh1','thresh2'),
   isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
+    return this.get('data') > this.get('thresh2');
   }.property('data','thresh1','thresh2'),
   isNA: function (){
     return this.get('data') === null || isNaN(this.get('data'));
@@ -70,7 +56,7 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
     if(this.get('data') || this.get('data') == 0){
       return this.get('data') + "";
     }else{
-      return this.t('services.service.summary.notAvailable');
+      return Em.I18n.t('services.service.summary.notAvailable');
     }
   }.property('model.averageLoad'),
 
@@ -79,9 +65,7 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the thresholds to change the color of current widget. ' +
-
-        ' So enter two numbers larger than 0. ',
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint2'),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -125,7 +109,7 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show( {
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -149,10 +133,6 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function() {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var colors = ['#95A800', '#FF8E00', '#B80000']; //color green, orange ,red
@@ -168,28 +148,9 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
             max: 100,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             }
           });
-
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', true);
@@ -198,4 +159,4 @@ App.HBaseAverageLoadView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

+ 12 - 15
ambari-web/app/views/main/dashboard/widgets/hbase_links.js

@@ -18,18 +18,18 @@
 
 var App = require('app');
 
-App.HBaseLinksView = App.DashboardWidgetView.extend({
+App.HBaseLinksView = App.LinkDashboardWidgetView.extend({
 
   templateName: require('templates/main/dashboard/widgets/hbase_links'),
   title: Em.I18n.t('dashboard.widgets.HBaseLinks'),
   id: '19',
 
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-  isLinks: true,
   model_type: 'hbase',
 
+  port: '60010',
+
+  componentName: 'HBASE_REGIONSERVER',
+
   /**
    * All master components
    */
@@ -70,18 +70,15 @@ App.HBaseLinksView = App.DashboardWidgetView.extend({
     }
   }.property('activeMaster'),
 
-  regionServerComponent: function () {
-    return App.HostComponent.find().findProperty('componentName', 'HBASE_REGIONSERVER');
-  }.property(),
-
   hbaseMasterWebUrl: function () {
     if (this.get('activeMaster.host') && this.get('activeMaster.host').get('publicHostName')) {
-      return "http://" + this.get('activeMaster.host').get('publicHostName') + ":60010";
+      return "http://" + this.get('activeMaster.host').get('publicHostName') + ':' + this.get('port');
     }
-  }.property('activeMaster')
+    return '';
+  }.property('activeMaster'),
 
-})
+  calcWebUrl: function() {
+    return '';
+  }
 
-App.HBaseLinksView.reopenClass({
-  isLinks: true
-})
+});

+ 15 - 78
ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js

@@ -19,17 +19,15 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.HBaseMasterHeapPieChartView = App.DashboardWidgetView.extend({
+App.HBaseMasterHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.HBaseMasterHeap'),
   id: '20',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'hbase',
 
+  modelFieldMax: 'heapMemoryMax',
+  modelFieldUsed: 'heapMemoryUsed',
   hiddenInfo: function () {
     var heapUsed = this.get('model').get('heapMemoryUsed');
     var heapMax = this.get('model').get('heapMemoryMax');
@@ -40,78 +38,17 @@ App.HBaseMasterHeapPieChartView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.heapMemoryUsed', 'model.heapMemoryMax'),
 
-  thresh1: null,
-  thresh2: null,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.heapMemoryMax') * 1000000;
-    return total > 0 ;
-  }.property('model.heapMemoryMax'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-hbase-heap', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: null, //bind from parent
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var heapUsed = this.get('model').get('heapMemoryUsed');
-      var heapMax = this.get('model').get('heapMemoryMax');
-      var percent = heapMax > 0 ? (100 * heapUsed / heapMax).toFixed() : 0;
-      return [percent, 100-percent];
-    }.property('model.heapMemoryUsed', 'model.heapMemoryMax'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
-
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      if(old_svg){
-        old_svg.remove();
-      }
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-
-  })
-
-})
+  widgetHtmlId: 'widget-hbase-heap',
 
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
 
+  calcDataForPieChart: function() {
+    var used = this.get('model').get(this.get('modelFieldUsed'));
+    var total = this.get('model').get(this.get('modelFieldMax'));
+    var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
+    return [ percent, 100 - percent];
+  }
+});

+ 7 - 94
ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js

@@ -17,107 +17,20 @@
  */
 
 var App = require('app');
-var date = require('utils/date');
 
-App.HBaseMasterUptimeView = App.DashboardWidgetView.extend({
+App.HBaseMasterUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.HBaseMasterUptime'),
   id: '23',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hbase',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-    return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'Hbase Master',
+  modelField: 'masterStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function () {
-    var uptime = this.get('model.masterStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]) {
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', []);
-    this.set('hiddenInfo', ['Hbase Master','Not Running']);
-    return null;
-  }.property('model.masterStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.masterStartTime'),
-
-  timeConverter: function (timestamp){
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if (start == -1) { // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for (var i = 4; i < arr.length; i++) {
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    } else { // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});

+ 10 - 45
ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js

@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
+App.HBaseRegionsInTransitionView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.HBaseRegionsInTransition'),
   id: '22',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hbase',
   hiddenInfo: function () {
     var result = [];
@@ -37,20 +33,14 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
 
   classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
   isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
+    return this.get('data') <= this.get('thresh1');
+  }.property('data','thresh1'),
   isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
+    return (this.get('data') <= this.get('thresh2') && this.get('data') > this.get('thresh1') );
   }.property('data','thresh1','thresh2'),
   isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
+    return this.get('data') > this.get('thresh2');
+  }.property('data','thresh2'),
   isNA: function () {
     return this.get('data') === null;
   }.property('data'),
@@ -72,9 +62,7 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the thresholds to change the color of current widget. ' +
-
-        ' So enter two numbers larger than 0. ',
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint2'),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -117,7 +105,7 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -141,10 +129,6 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var colors = ['#95A800', '#FF8E00', '#B80000']; //color green, orange ,red
@@ -160,28 +144,9 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
             max: 100,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             }
           });
-
-          function updateColors (handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', true);
@@ -190,4 +155,4 @@ App.HBaseRegionsInTransitionView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

+ 25 - 81
ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js

@@ -19,19 +19,30 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.NameNodeCapacityPieChartView = App.DashboardWidgetView.extend({
+App.NameNodeCapacityPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.NameNodeCapacity'),
   id: '2',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'hdfs',
 
-  hiddenInfo: function () {
+  modelFieldMax: 'capacityTotal',
+  /**
+   * HDFS model has 'remaining' value, but not 'used'
+   */
+  modelFieldUsed: 'capacityRemaining',
+
+  widgetHtmlId: 'widget-nn-capacity',
+
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
+
+  calcHiddenInfo: function () {
     var text = this.t("dashboard.services.hdfs.capacityUsed");
+    var total = this.get('model').get(this.get('modelFieldMax')) + 0;
+    var used = total - this.get('model').get(this.get('modelFieldUsed')) + 0;
     var total = this.get('model.capacityTotal');
     var remaining = this.get('model.capacityRemaining');
     var used = total !== null && remaining !== null ? total - remaining : null;
@@ -43,80 +54,13 @@ App.NameNodeCapacityPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(percent + '% used');
     result.pushObject(numberUtils.bytesToSize(used, 1, 'parseFloat') + ' of ' + numberUtils.bytesToSize(total, 1, 'parseFloat'));
     return result;
-  }.property('model.capacityUsed', 'model.capacityTotal'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.capacityTotal') + 0;
-    return total > 0 ;
-  }.property('model.capacityTotal'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-nn-capacity', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,  // can be customized later
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var total = this.get('model.capacityTotal') + 0;
-      var remaining = this.get('model.capacityRemaining') + 0;
-      var used = total - remaining;
-      var percent = total > 0 ? ((used * 100) / total).toFixed() : 0;
-      if (percent == "NaN" || percent < 0) {
-        percent = Em.I18n.t('services.service.summary.notAvailable') + " ";
-      }
-      return [ percent, 100 - percent];
-    }.property('model.capacityUsed', 'model.capacityTotal'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
-
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  },
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
+  calcDataForPieChart: function() {
+    var total = this.get('model').get(this.get('modelFieldMax')) * 1024 * 1024;
+    var used = total - this.get('model').get(this.get('modelFieldUsed')) * 1024 * 1024;
+    var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
+    return [ percent, 100 - percent];
+  }
 
-})
+});

+ 10 - 15
ambari-web/app/views/main/dashboard/widgets/hdfs_links.js

@@ -18,28 +18,23 @@
 
 var App = require('app');
 
-App.HDFSLinksView = App.DashboardWidgetView.extend({
+App.HDFSLinksView = App.LinkDashboardWidgetView.extend({
 
   templateName: require('templates/main/dashboard/widgets/hdfs_links'),
   title: Em.I18n.t('dashboard.widgets.HDFSLinks'),
   id: '17',
 
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-  isLinks: true,
   model_type: 'hdfs',
 
-  dataNodeComponent: function () {
-    return App.HostComponent.find().findProperty('componentName', 'DATANODE');
-  }.property(),
+  port: '50070',
 
-  nodeWebUrl: function () {
-    return "http://" + this.get('model').get('nameNode').get('publicHostName') + ":50070";
-  }.property('model.nameNode')
+  componentName: 'DATANODE',
 
-})
+  modelField: 'nameNode',
 
-App.HDFSLinksView. reopenClass({
-  isLinks: true
-})
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
+
+});

+ 26 - 78
ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js

@@ -18,22 +18,29 @@
 
 var App = require('app');
 
-App.JobTrackerCpuPieChartView = App.DashboardWidgetView.extend({
+App.JobTrackerCpuPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.JobTrackerCpu'),
   id: '7',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'mapreduce',
-  hiddenInfo: function () {
-    var value = this.get('model.jobTrackerCpu');
+
+  widgetHtmlId: 'widget-jt-cpu',
+
+  modelFieldUsed: 'jobTrackerCpu',
+
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
+
+  calcHiddenInfo: function() {
+    var value = this.get('model').get(this.get('modelFieldUsed'));
     var obj1;
     if( value == null) {
       obj1 = Em.I18n.t('services.service.summary.notAvailable');
-    }else{
+    }
+    else {
       value = value >= 100 ? 100: value;
       obj1 = (value + 0).toFixed(2) + '%';
     }
@@ -41,76 +48,17 @@ App.JobTrackerCpuPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(obj1);
     result.pushObject('CPU wait I/O');
     return result;
-  }.property('model.jobTrackerCpu'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.jobTrackerCpu');
-    return total !== null ;
-  }.property('model.jobTrackerCpu'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-jt-cpu', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: 50,  // can be customized later
-    thresh2: 80,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var value = this.get('model.jobTrackerCpu');
-      value = value >= 100 ? 100: value;
-      var percent = (value + 0).toFixed(1);
-      return [ percent, 100 - percent];
-    }.property('model.jobTrackerCpu'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'thresh1', 'thresh2'),
+  },
 
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  calcIsPieExists: function() {
+    return (this.get('model').get(this.get('modelFieldUsed')) != null);
+  },
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('model.jobTrackerCpu', 'thresh1', 'thresh2')
-  })
+  calcDataForPieChart: function() {
+    var value = this.get('model').get(this.get('modelFieldUsed'));
+    value = value >= 100 ? 100: value;
+    var percent = (value + 0).toFixed(1);
+    return [ percent, 100 - percent];
+  }
 
-})
+});

+ 11 - 76
ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js

@@ -19,18 +19,14 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.JobTrackerHeapPieChartView = App.DashboardWidgetView.extend({
+App.JobTrackerHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.JobTrackerHeap'),
   id: '6',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'mapreduce',
 
-  hiddenInfo: function () {
+ /* hiddenInfo: function () {
     var heapUsed = this.get('model').get('jobTrackerHeapUsed');
     var heapMax = this.get('model').get('jobTrackerHeapMax');
     var percent = heapMax > 0 ? 100 * heapUsed / heapMax : 0;
@@ -38,75 +34,14 @@ App.JobTrackerHeapPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(percent.toFixed(1) + '% used');
     result.pushObject(numberUtils.bytesToSize(heapUsed, 1, "parseFloat") + ' of ' + numberUtils.bytesToSize(heapMax, 1, "parseFloat"));
     return result;
-  }.property('model.jobTrackerHeapUsed', 'model.jobTrackerHeapMax'),
+  }.property('model.jobTrackerHeapUsed', 'model.jobTrackerHeapMax'),*/
+  modelFieldMax: 'jobTrackerHeapMax',
+  modelFieldUsed: 'jobTrackerHeapUsed',
 
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
+  widgetHtmlId: 'widget-jt-heap',
 
-  isPieExist: function () {
-    var total = this.get('model.jobTrackerHeapMax') * 1000000;
-    return total > 0 ;
-  }.property('model.jobTrackerHeapMax'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-jt-heap', // id in html
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var used = this.get('model.jobTrackerHeapUsed') * 1000000;
-      var total = this.get('model.jobTrackerHeapMax') * 1000000;
-      var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.jobTrackerHeapUsed', 'model.jobTrackerHeapMax'),
-
-    contentColor: function (){
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
-
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
-
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
-
-})
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
+});

+ 6 - 55
ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js

@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.JobTrackerRpcView = App.DashboardWidgetView.extend({
+App.JobTrackerRpcView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.JobTrackerRpc'),
   id:'9',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'mapreduce',
   hiddenInfo: function (){
     var result = [];
@@ -35,26 +31,6 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.jobTrackerRpc'),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-  isNA: function () {
-     return this.get('data') === null;
-  }.property('data'),
-
   thresh1: 0.5,
   thresh2: 2,
   maxValue: 'infinity',
@@ -75,7 +51,7 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
     if (this.get('data') || this.get('data') == 0) {
       return this.get('data') + " ms";
     } else {
-      return this.t('services.service.summary.notAvailable');
+      return Em.I18n.t('services.service.summary.notAvailable');
     }
   }.property('model.jobTrackerRpc'),
 
@@ -84,9 +60,7 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the thresholds to change the color of current widget. ' +
-        ' The unit is milli-second. '+
-        ' So enter two numbers larger than 0. ',
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint3'),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -128,7 +102,7 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -152,10 +126,6 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var colors = ['#95A800', '#FF8E00', '#B80000']; //color green, orange ,red
@@ -171,28 +141,9 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
             max: 100,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             }
           });
-
-          function updateColors (handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', true);
@@ -201,4 +152,4 @@ App.JobTrackerRpcView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

+ 7 - 93
ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js

@@ -17,106 +17,20 @@
  */
 
 var App = require('app');
-var date = require('utils/date');
 
-App.JobTrackerUptimeView = App.DashboardWidgetView.extend({
+App.JobTrackerUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.JobTrackerUptime'),
   id: '16',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'mapreduce',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-   return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'JobTracker',
+  modelField: 'jobTrackerStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function(){
-    var uptime = this.get('model.jobTrackerStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]){
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', ['JobTracker','Not Running']);
-    return null;
-  }.property('model.jobTrackerStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.jobTrackerStartTime'),
-
-  timeConverter: function (timestamp) {
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if(start == -1){ // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for(var i = 4; i < arr.length; i++){
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    }else{ // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});

+ 49 - 0
ambari-web/app/views/main/dashboard/widgets/links_widget.js

@@ -0,0 +1,49 @@
+/**
+ * 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 App = require('app');
+var date = require('utils/date');
+
+App.LinkDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  componentName: null,
+
+  component: function() {
+    return App.HostComponent.find().findProperty('componentName', this.get('componentName'));
+  }.property(),
+
+  port: null,
+
+  modelField: null,
+
+  webUrl: null,
+
+  didInsertElement: function() {
+    this._super();
+    this.addObserver('model.' + this.get('modelField'), this, this.calc);
+  },
+  calc: function() {
+    this.set('webUrl', this.calcWebUrl());
+  },
+  calcWebUrl: function() {
+    if (this.get('model')) {
+      return "http://" + this.get('model').get(this.get('modelField')).get('publicHostName') + ':' + this.get('port');
+    }
+    return '';
+  }
+});

+ 10 - 15
ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js

@@ -18,28 +18,23 @@
 
 var App = require('app');
 
-App.MapReduceLinksView = App.DashboardWidgetView.extend({
+App.MapReduceLinksView = App.LinkDashboardWidgetView.extend({
 
   templateName: require('templates/main/dashboard/widgets/mapreduce_links'),
   title: Em.I18n.t('dashboard.widgets.MapReduceLinks'),
   id: '18',
 
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-  isLinks: true,
   model_type: 'mapreduce',
 
-  taskTrackerComponent: function () {
-    return App.HostComponent.find().findProperty('componentName', 'TASKTRACKER');
-  }.property(),
+  port: '50030',
 
-  jobTrackerWebUrl: function () {
-    return "http://" + this.get('model').get('jobTracker').get('publicHostName') + ":50030";
-  }.property('model.jobTracker')
+  componentName: 'TASKTRACKER',
 
-})
+  modelField: 'jobTracker',
 
-App.MapReduceLinksView. reopenClass({
-  isLinks: true
-})
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
+
+});

+ 7 - 8
ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js

@@ -24,18 +24,17 @@ App.MapReduceSlotsView = App.DashboardWidgetView.extend({
   title: Em.I18n.t('dashboard.widgets.MapReduceSlots'),
   id:'10',
 
-  isPieChart: false,
-  isText: false,
   isProgressBar: true,
   model_type: 'mapreduce',
-  hiddenInfo: function (){
+  hiddenInfo: function () {
     var result = [];
-    if(this.get('isViewExist')){
+    if(this.get('isViewExist')) {
       var line1 = "Map: " + this.get('model.mapSlotsOccupied') + " Occupied / " + this.get('model.mapSlotsReserved') + " Reserved / " + this.get('model.mapSlots') + " Total";
       result.pushObject(line1);
       var line2 = "Reduce: " + this.get('model.reduceSlotsOccupied') + " Occupied / " + this.get('model.reduceSlotsReserved') + " Reserved / " + this.get('model.reduceSlots') + " Total";
       result.pushObject(line2);
-    }else{
+    }
+    else {
       result.pushObject('MapReduce Not Started');
     }
     return result;
@@ -81,8 +80,8 @@ App.MapReduceSlotsView = App.DashboardWidgetView.extend({
     return this.get('model.reduceSlotsOccupied') + "/" + this.get('model.reduceSlotsReserved') + "/" + this.get('model.reduceSlots');
   }.property('model.reduceSlotsReserved','model.reduceSlotsOccupied','model.reduceSlots')
 
-})
+});
 
 App.MapReduceSlotsView.reopenClass({
-  isProgressBar: true
-})
+  class: 'span4p8'
+});

+ 2 - 8
ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js

@@ -18,19 +18,13 @@
 
 var App = require('app');
 
-App.ChartClusterMetricsCPUWidgetView = App.DashboardWidgetView.extend({
+App.ChartClusterMetricsCPUWidgetView = App.ClusterMetricsDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/cluster_metrics'),
   title: Em.I18n.t('dashboard.clusterMetrics.cpu'),
   id: '13',
 
-  isClusterMetrics: true,
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-
   content: App.ChartClusterMetricsCPU.extend({
     noTitleUnderGraph: true,
     inWidget: true
   })
-})
+});

+ 2 - 8
ambari-web/app/views/main/dashboard/widgets/metrics_load.js

@@ -18,19 +18,13 @@
 
 var App = require('app');
 
-App.ChartClusterMetricsLoadWidgetView = App.DashboardWidgetView.extend({
+App.ChartClusterMetricsLoadWidgetView = App.ClusterMetricsDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/cluster_metrics'),
   title: Em.I18n.t('dashboard.clusterMetrics.load'),
   id: '14',
 
-  isClusterMetrics: true,
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-
   content: App.ChartClusterMetricsLoad.extend({
     noTitleUnderGraph: true,
     inWidget: true
   })
-})
+});

+ 2 - 8
ambari-web/app/views/main/dashboard/widgets/metrics_memory.js

@@ -18,19 +18,13 @@
 
 var App = require('app');
 
-App.ChartClusterMetricsMemoryWidgetView = App.DashboardWidgetView.extend({
+App.ChartClusterMetricsMemoryWidgetView = App.ClusterMetricsDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/cluster_metrics'),
   title: Em.I18n.t('dashboard.clusterMetrics.memory'),
   id: '11',
 
-  isClusterMetrics: true,
-  isPieChart: false,
-  isText: false,
-  isProgressBar: false,
-
   content: App.ChartClusterMetricsMemory.extend({
     noTitleUnderGraph: true,
     inWidget: true
   })
-})
+});

+ 2 - 8
ambari-web/app/views/main/dashboard/widgets/metrics_network.js

@@ -18,19 +18,13 @@
 
 var App = require('app');
 
-App.ChartClusterMetricsNetworkWidgetView = App.DashboardWidgetView.extend({
+App.ChartClusterMetricsNetworkWidgetView = App.ClusterMetricsDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/cluster_metrics'),
   title: Em.I18n.t('dashboard.clusterMetrics.network'),
   id: '12',
 
-  isClusterMetrics: true,
-  isPieChart: false,
-  isText:false,
-  isProgressBar:false,
-
   content: App.ChartClusterMetricsNetwork.extend({
     noTitleUnderGraph: true,
     inWidget: true
   })
-})
+});

+ 26 - 78
ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js

@@ -18,22 +18,29 @@
 
 var App = require('app');
 
-App.NameNodeCpuPieChartView = App.DashboardWidgetView.extend({
+App.NameNodeCpuPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.NameNodeCpu'),
   id: '3',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'hdfs',
-  hiddenInfo: function () {
-    var value = this.get('model.nameNodeCpu');
+
+  widgetHtmlId: 'widget-nn-cpu',
+
+  modelFieldUsed: 'nameNodeCpu',
+
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
+
+  calcHiddenInfo: function() {
+    var value = this.get('model').get(this.get('modelFieldUsed'));
     var obj1;
     if( value == null) {
       obj1 = Em.I18n.t('services.service.summary.notAvailable');
-    }else{
+    }
+    else {
       value = value >= 100 ? 100: value;
       obj1 = (value + 0).toFixed(2) + '%';
     }
@@ -41,76 +48,17 @@ App.NameNodeCpuPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(obj1);
     result.pushObject('CPU wait I/O');
     return result;
-  }.property('model.nameNodeCpu'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.nameNodeCpu');
-    return total !== null ;
-  }.property('model.nameNodeCpu'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-nn-cpu', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,  // can be customized later
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette ({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var value = this.get('model.nameNodeCpu');
-      value = value >= 100 ? 100: value;
-      var percent = (value + 0).toFixed(1);
-      return [ percent, 100 - percent];
-    }.property('model.nameNodeCpu'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'thresh1', 'thresh2'),
+  },
 
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  calcIsPieExists: function() {
+    return (this.get('model').get(this.get('modelFieldUsed')) != null);
+  },
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('model.nameNodeCpu', 'thresh1', 'thresh2')
-  })
+  calcDataForPieChart: function() {
+    var value = this.get('model').get(this.get('modelFieldUsed'));
+    value = value >= 100 ? 100: value;
+    var percent = (value + 0).toFixed(1);
+    return [ percent, 100 - percent];
+  }
 
-})
+});

+ 29 - 89
ambari-web/app/views/main/dashboard/widgets/namenode_heap.js

@@ -19,98 +19,38 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.NameNodeHeapPieChartView = App.DashboardWidgetView.extend({
+App.NameNodeHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.NameNodeHeap'),
   id: '1',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'hdfs',
 
-  hiddenInfo: function () {
-  var memUsed = this.get('model').get('jvmMemoryHeapUsed');
-  var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
-  var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
-  var result = [];
-  result.pushObject(percent.toFixed(1) + '% used');
-  result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1000000) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1000000));
-  return result;
-  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-  thresh1: null,
-  thresh2: null,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-    return total > 0 ;
-  }.property('model.jvmMemoryHeapCommitted'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-nn-heap', // html id
-    stroke: '#D6DDDF', //light grey
-    thresh1: null, //bind from parent
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var used = this.get('model.jvmMemoryHeapUsed') * 1000000;
-      var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-      var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-    contentColor: function () {
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
-
-    // refresh text and color when data in model changed
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      if(old_svg){
-        old_svg.remove();
-      }
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
-
-})
-
-
+ /* hiddenInfo: function () {
+    var memUsed = this.get('model').get('jvmMemoryHeapUsed');
+    var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
+    var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
+    var result = [];
+    result.pushObject(percent.toFixed(1) + '% used');
+    result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1024 * 1024));
+    return result;
+  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),*/
+
+  modelFieldMax: 'jvmMemoryHeapCommitted',
+  modelFieldUsed: 'jvmMemoryHeapUsed',
+
+  widgetHtmlId: 'widget-nn-heap',
+
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  },
+
+  getUsed: function() {
+    return this.get('model').get(this.get('modelFieldUsed')) * 1024 * 1024 || 0;
+  },
+
+  getMax: function() {
+    return this.get('model').get(this.get('modelFieldMax')) * 1024 * 1024 || 0;
+  }
+});

+ 8 - 56
ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js

@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.NameNodeRpcView = App.DashboardWidgetView.extend({
+App.NameNodeRpcView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.NameNodeRpc'),
   id: '5',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hdfs',
   hiddenInfo: function () {
     var result = [];
@@ -35,26 +31,6 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.nameNodeRpc'),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-  isNA: function () {
-    return this.get('data') === null;
-  }.property('data'),
-
   thresh1: 0.5,
   thresh2: 2,
   maxValue: 'infinity',
@@ -74,8 +50,9 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
   content: function () {
     if (this.get('data') || this.get('data') == 0) {
       return this.get('data') + " ms";
-    } else {
-      return this.t('services.service.summary.notAvailable');
+    }
+    else {
+      return Em.I18n.t('services.service.summary.notAvailable');
     }
   }.property('model.nameNodeRpc'),
 
@@ -84,9 +61,7 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the thresholds to change the color of current widget. ' +
-        ' The unit is milli-second. '+
-        ' So enter two numbers larger than 0. ',
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint3'),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -129,7 +104,7 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: ['sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -153,10 +128,6 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var colors = ['#95A800', '#FF8E00', '#B80000']; //color green, orange ,red
@@ -172,28 +143,9 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
             max: 100,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             }
           });
-
-          function updateColors (handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', true);
@@ -202,4 +154,4 @@ App.NameNodeRpcView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

+ 7 - 94
ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js

@@ -17,107 +17,20 @@
  */
 
 var App = require('app');
-var date = require('utils/date');
 
-App.NameNodeUptimeView = App.DashboardWidgetView.extend({
+App.NameNodeUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.NameNodeUptime'),
   id: '15',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'hdfs',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-    return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'NameNode',
+  modelField: 'nameNodeStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function () {
-    var uptime = this.get('model.nameNodeStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]) {
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', []);
-    this.set('hiddenInfo', ['NameNode','Not Running']);
-    return null;
-  }.property('model.nameNodeStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.nameNodeStartTime'),
-
-  timeConverter: function (timestamp){
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if (start == -1) { // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for (var i = 4; i < arr.length; i++) {
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    } else { // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});

+ 6 - 51
ambari-web/app/views/main/dashboard/widgets/node_managers_live.js

@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.NodeManagersLiveView = App.DashboardWidgetView.extend({
+App.NodeManagersLiveView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.NodeManagersLive'),
   id: '26',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'yarn',
 
   hiddenInfo: function () {
@@ -46,23 +42,6 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
     'model.nodeManagersCountUnhealthy', 'model.nodeManagersCountRebooted', 'model.nodeManagersCountDecommissioned'),
   hiddenInfoClass: "hidden-info-five-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen'],
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-
   thresh1: 40,
   thresh2: 70,
   maxValue: 100,
@@ -85,9 +64,7 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the percentage of thresholds to change the color of current widget. ' +
-        ' Assume all task trackers UP is 100, and all DOWN is 0. '+
-        ' So enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint1').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -158,12 +135,9 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
+        var self = this;
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
         var colors = ['#B80000', '#FF8E00', '#95A800']; //color red, orange, green
 
@@ -176,36 +150,17 @@ App.NodeManagersLiveView = App.DashboardWidgetView.extend({
             max: max_tmp,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers,colors);
             },
             slide: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values,colors);
               configObj.set('thresh1', ui.values[0] + '');
               configObj.set('thresh2', ui.values[1] + '');
             },
             change: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values,colors);
             }
           });
-
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', false);

+ 140 - 0
ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js

@@ -0,0 +1,140 @@
+/**
+ * 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 App = require('app');
+var numberUtils = require('utils/number_utils');
+
+App.PieChartDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/pie_chart'),
+
+  maxValue: 100,
+
+  modelFieldMax: null,
+  modelFieldUsed: null,
+
+  hiddenInfo: null,
+
+  isPieExist: null,
+
+  dataForPieChart: null,
+
+  widgetHtmlId: null,
+
+  getUsed: function() {
+    return this.get('model').get(this.get('modelFieldUsed')) || 0;
+  },
+
+  getMax: function() {
+    return this.get('model').get(this.get('modelFieldMax')) || 0;
+  },
+
+  calcHiddenInfo: function() {
+    var used = this.getUsed();
+    var max = this.getMax();
+    var percent = max > 0 ? 100 * used / max : 0;
+    var result = [];
+    result.pushObject(percent.toFixed(1) + '% used');
+    result.pushObject(numberUtils.bytesToSize(used, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(max, 1, 'parseFloat', 1024 * 1024));
+    return result;
+  },
+
+  calcIsPieExists: function() {
+    return (this.get('model').get(this.get('modelFieldMax')) > 0);
+  },
+
+  calcDataForPieChart: function() {
+    var used = this.get('model').get(this.get('modelFieldUsed'));
+    var total = this.get('model').get(this.get('modelFieldMax'));
+    var percent = total > 0 ? ((used)*100 / total).toFixed() : 0;
+    return [ percent, 100 - percent];
+  },
+
+  calc: function() {
+    this.set('hiddenInfo', this.calcHiddenInfo());
+    this.set('isPieExist', this.calcIsPieExists());
+    this.set('dataForPieChart', this.calcDataForPieChart());
+  },
+
+  didInsertElement: function() {
+    this._super();
+    this.addObserver('model.' + this.get('modelFieldMax'), this, this.calc);
+    this.addObserver('model.' + this.get('modelFieldUsed'), this, this.calc);
+  },
+
+  content: App.ChartPieView.extend({
+    model: null,  //data bind here
+    id: function() {
+      return this.get('parentView.widgetHtmlId');
+    }.property('parentView.widgetHtmlId'), // html id
+    stroke: '#D6DDDF', //light grey
+    thresh1: null, //bind from parent
+    thresh2: null,
+    innerR: 25,
+
+    existCenterText: true,
+    centerTextColor: function () {
+      return this.get('contentColor');
+    }.property('contentColor'),
+
+    palette: new Rickshaw.Color.Palette({
+      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
+    }),
+
+    data: function() {
+      return this.get('parentView.dataForPieChart');
+    }.property('parentView.dataForPieChart'),
+
+    contentColor: function () {
+      var used = parseFloat(this.get('data')[0]);
+      var thresh1 = parseFloat(this.get('thresh1'));
+      var thresh2 = parseFloat(this.get('thresh2'));
+      var color_green = '#95A800';
+      var color_red = '#B80000';
+      var color_orange = '#FF8E00';
+      if (used <= thresh1) {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_green  ].reverse()
+        }));
+        return color_green;
+      } else if (used <= thresh2) {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_orange  ].reverse()
+        }));
+        return color_orange;
+      } else {
+        this.set('palette', new Rickshaw.Color.Palette({
+          scheme: [ '#FFFFFF', color_red  ].reverse()
+        }));
+        return color_red;
+      }
+    }.property('data', 'thresh1', 'thresh2'),
+
+    // refresh text and color when data in model changed
+    refreshSvg: function () {
+      // remove old svg
+      var old_svg =  $("#" + this.get('id'));
+      if(old_svg){
+        old_svg.remove();
+      }
+
+      // draw new svg
+      this.appendSvg();
+    }.observes('data', 'thresh1', 'thresh2')
+  })
+});

+ 11 - 75
ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js

@@ -19,18 +19,13 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.ResourceManagerHeapPieChartView = App.DashboardWidgetView.extend({
+App.ResourceManagerHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.ResourceManagerHeap'),
   id: '24',
-
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
   model_type: 'yarn',
 
-  hiddenInfo: function () {
+  /*hiddenInfo: function () {
     var memUsed = this.get('model').get('jvmMemoryHeapUsed');
     var memCommitted = this.get('model').get('jvmMemoryHeapCommitted');
     var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
@@ -38,75 +33,16 @@ App.ResourceManagerHeapPieChartView = App.DashboardWidgetView.extend({
     result.pushObject(percent.toFixed(1) + '% used');
     result.pushObject(numberUtils.bytesToSize(memUsed, 1, "parseFloat", 1000000) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, "parseFloat", 1000000));
     return result;
-  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.jvmMemoryHeapCommitted') * 1000000;
-    return total > 0 ;
-  }.property('model.jvmMemoryHeapCommitted'),
-
-  content: App.ChartPieView.extend({
-
-    model: null,  //data bind here
-    id: 'widget-rm-heap', // id in html
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var memUsed = this.get('model').get('jvmMemoryHeapUsed') * 1000000;
-      var memCommitted = this.get('model').get('jvmMemoryHeapCommitted') * 1000000;
-      var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),
+  }.property('model.jvmMemoryHeapUsed', 'model.jvmMemoryHeapCommitted'),*/
 
-    contentColor: function (){
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
+  modelFieldMax: 'jvmMemoryHeapCommitted',
+  modelFieldUsed: 'jvmMemoryHeapUsed',
 
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  widgetHtmlId: 'widget-rm-heap',
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
 
-})
+});

+ 7 - 92
ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js

@@ -19,104 +19,19 @@
 var App = require('app');
 var date = require('utils/date');
 
-App.ResourceManagerUptimeView = App.DashboardWidgetView.extend({
+App.ResourceManagerUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/uptime'),
   title: Em.I18n.t('dashboard.widgets.ResourceManagerUptime'),
   id: '25',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'yarn',
-  hiddenInfo: [],
-  hiddenInfoClass: "hidden-info-three-line",
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
-  isGreen: function () {
-    return this.get('data') != null;
-  }.property('data'),
-  isOrange: function () {
-    return false;
-  }.property('data'),
-  isRed: function () {
-    return false;
-  }.property('data'),
-  isNA: function () {
-    return this.get('data') == null;
-  }.property('data'),
+  component: 'ResourceManager',
+  modelField: 'resourceManagerStartTime',
 
-  thresh1: 5,
-  thresh2: 10,
-  maxValue: 'infinity',
-
-  data: function(){
-    var uptime = this.get('model.resourceManagerStartTime');
-    if (uptime && uptime > 0) {
-      var uptimeString = this.timeConverter(uptime);
-      var diff = (new Date()).getTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff); //17.67 days
-      var timeUnit = null;
-      switch (formatted.split(" ")[1]){
-        case 'secs':
-          timeUnit = 's';
-          break;
-        case 'hours':
-          timeUnit = 'hr';
-          break;
-        case 'days':
-          timeUnit = 'd';
-          break;
-        case 'mins':
-          timeUnit = 'min';
-          break;
-        default:
-          timeUnit = formatted.split(" ")[1];
-      }
-      this.set('timeUnit', timeUnit);
-      this.set('hiddenInfo', []);
-      this.get('hiddenInfo').pushObject(formatted);
-      this.get('hiddenInfo').pushObject(uptimeString[0]);
-      this.get('hiddenInfo').pushObject(uptimeString[1]);
-      return parseFloat(formatted.split(" ")[0]);
-    }
-    this.set('hiddenInfo', ['ResourceManager','Not Running']);
-    return null;
-  }.property('model.resourceManagerStartTime'),
-
-  timeUnit: null,
-
-  content: function () {
-    var data = this.get('data');
-    if (data) {
-      return data.toFixed(1) + ' '+ this.get('timeUnit');
-    } else {
-      return this.t('services.service.summary.notAvailable');
-    }
-  }.property('model.jobTrackerStartTime'),
-
-  timeConverter: function (timestamp) {
-    var origin = new Date(timestamp);
-    origin = origin.toString();
-    var result = [];
-    var start = origin.indexOf('GMT');
-    if(start == -1){ // ie
-      var arr = origin.split(" ");
-      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
-      var second = '';
-      for(var i = 4; i < arr.length; i++){
-        second = second + " " + arr[i];
-      }
-      result.pushObject(second);
-    }else{ // other browsers
-      var end = origin.indexOf(" ", start);
-      result.pushObject(origin.slice(0, start-10));
-      result.pushObject(origin.slice(start-9));
-    }
-    return result;
+  didInsertElement: function() {
+    this._super();
+    this.calc();
   }
 
-})
+});

+ 8 - 52
ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js

@@ -18,15 +18,11 @@
 
 var App = require('app');
 
-App.TaskTrackerUpView = App.DashboardWidgetView.extend({
+App.TaskTrackerUpView = App.TextDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/simple_text'),
   title: Em.I18n.t('dashboard.widgets.TaskTrackerUp'),
   id: '8',
 
-  isPieChart: false,
-  isText: true,
-  isProgressBar: false,
   model_type: 'mapreduce',
 
   hiddenInfo: function () {
@@ -39,23 +35,6 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     return result;
   }.property('model.aliveTrackers.length', 'model.taskTrackers.length'),
 
-  classNameBindings: ['isRed', 'isOrange', 'isGreen'],
-  isRed: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') <= thresh1? true: false;
-  }.property('data','thresh1','thresh2'),
-  isOrange: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return (this.get('data') <= thresh2 && this.get('data') > thresh1 )? true: false;
-  }.property('data','thresh1','thresh2'),
-  isGreen: function () {
-    var thresh1 = this.get('thresh1');
-    var thresh2 = this.get('thresh2');
-    return this.get('data') > thresh2? true: false;
-  }.property('data','thresh1','thresh2'),
-
   thresh1: 40,
   thresh2: 70,
   maxValue: 100,
@@ -74,9 +53,7 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     var configObj = Ember.Object.create({
       thresh1: parent.get('thresh1') + '',
       thresh2: parent.get('thresh2') + '',
-      hintInfo: 'Edit the percentage of thresholds to change the color of current widget. ' +
-        ' Assume all task trackers UP is 100, and all DOWN is 0. '+
-        ' So enter two numbers between 0 to ' + max_tmp,
+      hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint1').format(max_tmp),
       isThresh1Error: false,
       isThresh2Error: false,
       errorMessage1: "",
@@ -124,7 +101,7 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
 
     var browserVerion = this.getInternetExplorerVersion();
     App.ModalPopup.show({
-      header: 'Customize Widget',
+      header: Em.I18n.t('dashboard.widgets.popupHeader'),
       classNames: [ 'sixty-percent-width-modal-edit-widget'],
       bodyClass: Ember.View.extend({
         templateName: require('templates/main/dashboard/edit_widget_popup'),
@@ -147,10 +124,6 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
           this.hide();
         }
       },
-      secondary : Em.I18n.t('common.cancel'),
-      onSecondary: function () {
-        this.hide();
-      },
 
       didInsertElement: function () {
         var handlers = [configObj.get('thresh1'), configObj.get('thresh2')];
@@ -165,36 +138,19 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
             max: max_tmp,
             values: handlers,
             create: function (event, ui) {
-              updateColors(handlers);
+              parent.updateColors(handlers, colors);
             },
             slide: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
               configObj.set('thresh1', ui.values[0] + '');
               configObj.set('thresh2', ui.values[1] + '');
             },
             change: function (event, ui) {
-              updateColors(ui.values);
+              parent.updateColors(ui.values, colors);
             }
           });
 
-          function updateColors(handlers) {
-            var colorstops = colors[0] + ", "; // start with the first color
-            for (var i = 0; i < handlers.length; i++) {
-              colorstops += colors[i] + " " + handlers[i] + "%,";
-              colorstops += colors[i+1] + " " + handlers[i] + "%,";
-            }
-            // end with the last color
-            colorstops += colors[colors.length - 1];
-            var css1 = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
-            $('#slider-range').css('background-image', css1);
-            var css2 = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
-            $('#slider-range').css('background-image', css2);
-            //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
-            var css3 = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
-            $('#slider-range').css('background-image', css3);
-
-            $('#slider-range .ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
-          }
+
         } else {
           configObj.set('isIE9', true);
           configObj.set('isGreenOrangeRed', false);
@@ -203,4 +159,4 @@ App.TaskTrackerUpView = App.DashboardWidgetView.extend({
     });
   }
 
-})
+});

+ 66 - 0
ambari-web/app/views/main/dashboard/widgets/text_widget.js

@@ -0,0 +1,66 @@
+/**
+ * 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 App = require('app');
+var date = require('utils/date');
+
+App.TextDashboardWidgetView = App.DashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/simple_text'),
+
+  classNameBindings: ['isRed', 'isOrange', 'isGreen', 'isNA'],
+
+  isRed: function () {
+    return (this.get('data') <= this.get('thresh1'));
+  }.property('data','thresh1'),
+
+  isOrange: function () {
+    return (this.get('data') <= this.get('thresh2') && this.get('data') > this.get('thresh1'));
+  }.property('data','thresh1','thresh2'),
+
+  isGreen: function () {
+    return (this.get('data') > this.get('thresh2'));
+  }.property('data','thresh2'),
+
+  isNA: function () {
+    return this.get('data') === null;
+  }.property('data'),
+
+  hiddenInfo: [],
+  thresh1: null,
+  thresh2: null,
+  maxValue: null,
+  updateColors: function(handlers, colors) {
+    var colorstops = colors[0] + ", "; // start with the first color
+    for (var i = 0; i < handlers.length; i++) {
+      colorstops += colors[i] + " " + handlers[i] + "%,";
+      colorstops += colors[i+1] + " " + handlers[i] + "%,";
+    }
+    colorstops += colors[colors.length - 1];
+    var cssForChromeAndSafari = '-webkit-linear-gradient(left,' + colorstops + ')'; // chrome & safari
+    var slider = $('#slider-range');
+    slider.css('background-image', cssForChromeAndSafari);
+    var cssForIE = '-ms-linear-gradient(left,' + colorstops + ')'; // IE 10+
+    slider.css('background-image', cssForIE);
+    //$('#slider-range').css('filter', 'progid:DXImageTransform.Microsoft.gradient( startColorStr= ' + colors[0] + ', endColorStr= ' + colors[2] +',  GradientType=1 )' ); // IE 10-
+    var cssForFireFox = '-moz-linear-gradient(left,' + colorstops + ')'; // Firefox
+    slider.css('background-image', cssForFireFox);
+
+    slider.find('.ui-widget-header').css({'background-color': '#FF8E00', 'background-image': 'none'}); // change the  original ranger color
+  }
+});

+ 149 - 0
ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js

@@ -0,0 +1,149 @@
+/**
+ * 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 App = require('app');
+var date = require('utils/date');
+
+App.UptimeTextDashboardWidgetView = App.TextDashboardWidgetView.extend({
+
+  templateName: require('templates/main/dashboard/widgets/uptime'),
+
+  timeUnit: null,
+
+  hiddenInfoClass: "hidden-info-three-line",
+
+  thresh1: 5,
+  thresh2: 10,
+  maxValue: 'infinity',
+
+  component: null,
+  /**
+   * Model's field that used to calculate data and content
+   * Should be defined in every child
+   */
+  modelField: null,
+
+  data: null,
+  content: null,
+
+  isGreen: function () {
+    return this.get('data') != null;
+  }.property('data'),
+
+  isOrange: function () {
+    return false;
+  }.property('data'),
+
+  isRed: function () {
+    return false;
+  }.property('data'),
+
+  timeConverter: function (timestamp) {
+    var origin = new Date(timestamp);
+    origin = origin.toString();
+    var result = [];
+    var start = origin.indexOf('GMT');
+    if (start == -1) { // ie
+      var arr = origin.split(" ");
+      result.pushObject(arr[0] + " " + arr[1] + " " + arr[2] + " " + arr[3]);
+      var second = '';
+      for (var i = 4; i < arr.length; i++) {
+        second = second + " " + arr[i];
+      }
+      result.pushObject(second);
+    }
+    else { // other browsers
+      var end = origin.indexOf(" ", start);
+      result.pushObject(origin.slice(0, start-10));
+      result.pushObject(origin.slice(start-9));
+    }
+    return result;
+  },
+
+  /**
+   * All children should have such code
+   * <code>
+   * didInsertElement: function() {
+   *   this._super();
+   *   this.calc();
+   * }
+   * </code>
+   */
+  didInsertElement: function() {
+    this._super();
+    this.addObserver('model.' + this.get('modelField'), this, this.calc);
+  },
+
+  calc: function() {
+    this.set('data', this.calcData());
+    this.set('content', this.calcContent());
+  },
+
+  uptimeProcessing: function(uptime) {
+    var uptimeString = this.timeConverter(uptime);
+    var diff = (new Date()).getTime() - uptime;
+    if (diff < 0) {
+      diff = 0;
+    }
+    var formatted = date.timingFormat(diff); //17.67 days
+    var timeUnit = null;
+    switch (formatted.split(" ")[1]) {
+      case 'secs':
+        timeUnit = 's';
+        break;
+      case 'hours':
+        timeUnit = 'hr';
+        break;
+      case 'days':
+        timeUnit = 'd';
+        break;
+      case 'mins':
+        timeUnit = 'min';
+        break;
+      default:
+        timeUnit = formatted.split(" ")[1];
+    }
+    this.set('timeUnit', timeUnit);
+    this.set('hiddenInfo', []);
+    this.get('hiddenInfo').pushObject(formatted);
+    this.get('hiddenInfo').pushObject(uptimeString[0]);
+    this.get('hiddenInfo').pushObject(uptimeString[1]);
+    return formatted;
+  },
+
+  calcData: function () {
+    var field = this.get('modelField');
+    var uptime = this.get('model').get(field);
+    if (uptime && uptime > 0) {
+      var formatted = this.uptimeProcessing(uptime);
+      return parseFloat(formatted.split(" ")[0]);
+    }
+    this.set('hiddenInfo', [this.get('component'),'Not Running']);
+    return null;
+  },
+
+  calcContent: function () {
+    var data = this.get('data');
+    if (data) {
+      return data.toFixed(1) + ' '+ this.get('timeUnit');
+    }
+    else {
+      return Em.I18n.t('services.service.summary.notAvailable');
+    }
+  }
+});

+ 10 - 83
ambari-web/app/views/main/dashboard/widgets/yarn_memory.js

@@ -19,94 +19,21 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.YARNMemoryPieChartView = App.DashboardWidgetView.extend({
+App.YARNMemoryPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  templateName: require('templates/main/dashboard/widgets/pie_chart'),
   title: Em.I18n.t('dashboard.widgets.YARNMemory'),
   id: '27',
 
-  isPieChart: true,
-  isText: false,
-  isProgressBar: false,
-  model_type: 'yarn',
-
-  hiddenInfo: function () {
-    var memUsed = this.get('model').get('yarnMemoryAllocated');
-    var memCommitted = this.get('model').get('yarnMemoryAvailable');
-    var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted) : 0;
-    var result = [];
-    result.pushObject(percent.toFixed(1) + '% used');
-    result.pushObject(numberUtils.bytesToSize(memUsed, 1, 'parseFloat', 1024 * 1024) + ' of ' + numberUtils.bytesToSize(memCommitted, 1, 'parseFloat', 1024 * 1024));
-    return result;
-  }.property('model.yarnMemoryAllocated', 'model.yarnMemoryAvailable'),
-
-  thresh1: 40,// can be customized
-  thresh2: 70,
-  maxValue: 100,
-
-  isPieExist: function () {
-    var total = this.get('model.yarnMemoryAvailable');
-    return total > 0 ;
-  }.property('model.yarnMemoryAvailable'),
-
-  content: App.ChartPieView.extend({
+  widgetHtmlId: 'widget-yarn-memory',
 
-    model: null,  //data bind here
-    id: 'widget-yarn-memory', // id in html
-    stroke: '#D6DDDF', //light grey
-    thresh1: null,
-    thresh2: null,
-    innerR: 25,
-
-    existCenterText: true,
-    centerTextColor: function () {
-      return this.get('contentColor');
-    }.property('contentColor'),
-
-    palette: new Rickshaw.Color.Palette({
-      scheme: [ '#FFFFFF', '#D6DDDF'].reverse()
-    }),
-
-    data: function () {
-      var memUsed = this.get('model').get('yarnMemoryAllocated') * 1000000;
-      var memCommitted = this.get('model').get('yarnMemoryAvailable') * 1000000;
-      var percent = memCommitted > 0 ? ((100 * memUsed) / memCommitted).toFixed() : 0;
-      return [ percent, 100 - percent];
-    }.property('model.yarnMemoryAllocated', 'model.yarnMemoryAvailable'),
-
-    contentColor: function (){
-      var used = parseFloat(this.get('data')[0]);
-      var thresh1 = parseFloat(this.get('thresh1'));
-      var thresh2 = parseFloat(this.get('thresh2'));
-      var color_green = '#95A800';
-      var color_red = '#B80000';
-      var color_orange = '#FF8E00';
-      if (used <= thresh1) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_green  ].reverse()
-        }))
-        return color_green;
-      } else if (used <= thresh2) {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_orange  ].reverse()
-        }))
-        return color_orange;
-      } else {
-        this.set('palette', new Rickshaw.Color.Palette({
-          scheme: [ '#FFFFFF', color_red  ].reverse()
-        }))
-        return color_red;
-      }
-    }.property('data', 'this.thresh1', 'this.thresh2'),
+  model_type: 'yarn',
 
-    refreshSvg: function () {
-      // remove old svg
-      var old_svg =  $("#" + this.id);
-      old_svg.remove();
+  modelFieldUsed: 'yarnMemoryAllocated',
+  modelFieldMax: 'yarnMemoryAvailable',
 
-      // draw new svg
-      this.appendSvg();
-    }.observes('this.data', 'this.thresh1', 'this.thresh2')
-  })
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
 
-})
+});

+ 73 - 0
ambari-web/test/views/main/dashboard/widget_test.js

@@ -0,0 +1,73 @@
+/**
+ * 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 App = require('app');
+require('views/main/dashboard/widget');
+
+describe('App.DashboardWidgetView', function() {
+  var dashboardWidgetView = App.DashboardWidgetView.create();
+
+  describe('#viewID', function() {
+    it('viewID is computed with id', function() {
+      dashboardWidgetView.set('id', 5);
+      expect(dashboardWidgetView.get('viewID')).to.equal('widget-5');
+    });
+  });
+
+  describe('#hoverContentTopClass', function() {
+    var tests = [
+      {
+        h: ['', ''],
+        e: 'simple-text-hidden-two-line',
+        m: '2 lines'
+      },
+      {
+        h: ['', '', ''],
+        e: 'simple-text-hidden-three-line',
+        m: '3 lines'
+      },
+      {
+        h: [''],
+        e: '',
+        m: '1 line'
+      },
+      {
+        h: [],
+        e: '',
+        m: '0 lines'
+      },
+      {
+        h: ['', '', '', '', ''],
+        e: 'simple-text-hidden-five-line',
+        m: '5 lines'
+      },
+      {
+        h: ['', '', '', ''],
+        e: 'simple-text-hidden-four-line',
+        m: '4 lines'
+      }
+    ];
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        dashboardWidgetView.set('hiddenInfo', test.h);
+        expect(dashboardWidgetView.get('hoverContentTopClass')).to.equal(test.e);
+      });
+    });
+  });
+
+});

+ 69 - 0
ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js

@@ -0,0 +1,69 @@
+/**
+ * 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 App = require('app');
+
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/datanode_live');
+
+describe('App.DataNodeUpView', function() {
+
+  var tests = [
+    {
+      data: 100,
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true
+      }
+    },
+    {
+      data: 0,
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false
+      }
+    },
+    {
+      data: 50,
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('', function() {
+      var dataNodeUpView = App.DataNodeUpView.create({model_type:null, data: test.data, content: test.data.toString()});
+      it('isRed', function() {
+        expect(dataNodeUpView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(dataNodeUpView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(dataNodeUpView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+    });
+  });
+
+});

+ 103 - 0
ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js

@@ -0,0 +1,103 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/hbase_average_load');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseAverageLoadView', function() {
+
+  var tests = [
+    {
+      model: {
+        averageLoad: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1'
+      }
+    },
+    {
+      model: {
+        averageLoad: 10
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '10'
+      }
+    },
+    {
+      model: {
+        averageLoad: 0
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '0'
+      }
+    },
+    {
+      model: {
+        averageLoad: null
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable')
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('averageLoad - ' + test.model.averageLoad, function() {
+      var hBaseAverageLoadView = App.HBaseAverageLoadView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(hBaseAverageLoadView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseAverageLoadView.get('data')).to.equal(test.model.averageLoad);
+      });
+      it('isRed', function() {
+        expect(hBaseAverageLoadView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseAverageLoadView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseAverageLoadView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseAverageLoadView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 95 - 0
ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js

@@ -0,0 +1,95 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/hbase_master_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseMasterUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        masterStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model:  Em.Object.create({
+        masterStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model:  Em.Object.create({
+        masterStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    var hBaseMasterUptimeView = App.HBaseMasterUptimeView.create({model_type:null, model: test.model});
+    hBaseMasterUptimeView.calc();
+    describe('masterStartTime - ' + test.model.masterStartTime, function() {
+      it('content', function() {
+        expect(hBaseMasterUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseMasterUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(hBaseMasterUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseMasterUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseMasterUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseMasterUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 102 - 0
ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js

@@ -0,0 +1,102 @@
+/**
+ * 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 App = require('app');
+
+require('views/main/dashboard/widgets/hbase_regions_in_transition');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.HBaseRegionsInTransitionView', function() {
+
+  var tests = [
+    {
+      model: {
+        regionsInTransition: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: 10
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '10'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: 0
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '0'
+      }
+    },
+    {
+      model: {
+        regionsInTransition: null
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: true,
+        content: 'null'
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('regionsInTransition - ' + test.model.regionsInTransition, function() {
+      var hBaseRegionsInTransitionView = App.HBaseRegionsInTransitionView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(hBaseRegionsInTransitionView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(hBaseRegionsInTransitionView.get('data')).to.equal(test.model.regionsInTransition);
+      });
+      it('isRed', function() {
+        expect(hBaseRegionsInTransitionView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(hBaseRegionsInTransitionView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(hBaseRegionsInTransitionView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(hBaseRegionsInTransitionView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 107 - 0
ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js

@@ -0,0 +1,107 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/jobtracker_rpc');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.JobTrackerRpcView', function() {
+
+  var tests = [
+    {
+      model: {
+        jobTrackerRpc: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1.00 ms',
+        data: '1.00'
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: 10
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '10.00 ms',
+        data: '10.00'
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: 0
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0 ms',
+        data: 0
+      }
+    },
+    {
+      model: {
+        jobTrackerRpc: null
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('jobTrackerRpc - ' + test.model.jobTrackerRpc, function() {
+      var jobTrackerRpcView = App.JobTrackerRpcView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(jobTrackerRpcView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerRpcView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerRpcView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerRpcView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerRpcView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerRpcView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 95 - 0
ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js

@@ -0,0 +1,95 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/jobtracker_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.JobTrackerUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model: Em.Object.create({
+        jobTrackerStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('jobTrackerStartTime - ' + test.model.jobTrackerStartTime, function() {
+      var jobTrackerUptimeView = App.JobTrackerUptimeView.create({model_type:null, model: test.model});
+      jobTrackerUptimeView.calc();
+      it('content', function() {
+        expect(jobTrackerUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 45 - 0
ambari-web/test/views/main/dashboard/widgets/links_widget_test.js

@@ -0,0 +1,45 @@
+/**
+ * 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 App = require('app');
+
+require('models/host_component');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/links_widget');
+
+describe('App.LinkDashboardWidgetView', function() {
+
+  var model = Em.Object.create({
+    field: Em.Object.create({
+      publicHostName: 'host1'
+    })
+  });
+  var linkDashboardWidgetView = App.LinkDashboardWidgetView.create({
+    model_type: null,
+    model: model,
+    port: 1234,
+    modelField: 'field'
+  });
+  linkDashboardWidgetView.calc();
+  describe('#webUrl', function() {
+    it('calc', function() {
+      expect(linkDashboardWidgetView.get('webUrl')).to.equal('http://host1:1234');
+    });
+  });
+
+});

+ 107 - 0
ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js

@@ -0,0 +1,107 @@
+/**
+ * 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 App = require('app');
+
+require('utils/helper');
+require('views/common/chart/pie');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/pie_chart_widget');
+require('views/main/dashboard/widgets/namenode_cpu');
+
+describe('App.NameNodeCpuPieChartView', function() {
+
+  var model = Em.Object.create({
+    used: null,
+    max: null
+  });
+  var nameNodeCpuPieChartView = App.NameNodeCpuPieChartView.create({
+    model_type: null,
+    model: model,
+    modelFieldUsed: 'used',
+    modelFieldMax: 'max',
+    widgetHtmlId: 'fake'
+  });
+
+  nameNodeCpuPieChartView.calc();
+
+  describe('#calcIsPieExists', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          used: 1
+        }),
+        e: true,
+        m: 'Exists'
+      },
+      {
+        model: Em.Object.create({
+          used: 0
+        }),
+        e: true,
+        m: 'Exists'
+      },
+      {
+        model: Em.Object.create({}),
+        e: false,
+        m: 'Not exists'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        nameNodeCpuPieChartView.set('model', test.model);
+        expect(nameNodeCpuPieChartView.calcIsPieExists()).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('calcDataForPieChart', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          used: 0
+        }),
+        e: ['0.0', 100],
+        m: 'Nothing is used'
+      },
+      {
+        model: Em.Object.create({
+          used: 100
+        }),
+        e: ['100.0', 0],
+        m: 'All is used'
+      },
+      {
+        model: Em.Object.create({
+          used: 50
+        }),
+        e: ['50.0', 50],
+        m: 'Half is used'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        nameNodeCpuPieChartView.set('model', test.model);
+        expect(nameNodeCpuPieChartView.calcDataForPieChart()).to.eql(test.e);
+      });
+    });
+  });
+
+});

+ 107 - 0
ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js

@@ -0,0 +1,107 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/namenode_rpc');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.NameNodeRpcView', function() {
+
+  var tests = [
+    {
+      model: {
+        nameNodeRpc: 1
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '1.00 ms',
+        data: '1.00'
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: 10
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '10.00 ms',
+        data: '10.00'
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: 0
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0 ms',
+        data: 0
+      }
+    },
+    {
+      model: {
+        nameNodeRpc: null
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('nameNodeRpc - ' + test.model.nameNodeRpc, function() {
+      var jobTrackerRpcView = App.NameNodeRpcView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(jobTrackerRpcView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(jobTrackerRpcView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(jobTrackerRpcView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(jobTrackerRpcView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(jobTrackerRpcView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(jobTrackerRpcView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 95 - 0
ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js

@@ -0,0 +1,95 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widgets/namenode_uptime');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widget');
+
+describe('App.NameNodeUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        nameNodeStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model:  Em.Object.create({
+        nameNodeStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model:  Em.Object.create({
+        nameNodeStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    var nameNodeUptimeView = App.NameNodeUptimeView.create({model_type:null, model: test.model});
+    nameNodeUptimeView.calc();
+    describe('nameNodeStartTime - ' + test.model.nameNodeStartTime, function() {
+      it('content', function() {
+        expect(nameNodeUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(nameNodeUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(nameNodeUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(nameNodeUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(nameNodeUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(nameNodeUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 97 - 0
ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js

@@ -0,0 +1,97 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/node_managers_live');
+
+describe('App.NodeManagersLiveView', function() {
+
+  var tests = [
+    {
+      model: {
+        nodeManagerNodes: [{}, {}, {}],
+        nodeManagerLiveNodes: [{}, {}]
+      },
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false,
+        content: '2/3',
+        data: 67
+      }
+    },
+    {
+      model: {
+        nodeManagerNodes: [{},{}],
+        nodeManagerLiveNodes: [{},{}]
+      },
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '2/2',
+        data: 100
+      }
+    },
+    {
+      model: {
+        nodeManagerNodes: [{}, {}],
+        nodeManagerLiveNodes: []
+      },
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false,
+        content: '0/2',
+        data: 0.00
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('nodeManagerNodes length - ' + test.model.nodeManagerNodes.length + ' | nodeManagerLiveNodes length' + test.model.nodeManagerLiveNodes.length, function() {
+      var nodeManagersLiveView = App.NodeManagersLiveView.create({model_type:null, model: test.model});
+      it('content', function() {
+        expect(nodeManagersLiveView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(nodeManagersLiveView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(nodeManagersLiveView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(nodeManagersLiveView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(nodeManagersLiveView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(nodeManagersLiveView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 169 - 0
ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js

@@ -0,0 +1,169 @@
+/**
+ * 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 App = require('app');
+
+require('views/common/chart/pie');
+require('utils/helper');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/pie_chart_widget');
+
+describe('App.PieChartDashboardWidgetView', function() {
+
+  var model = Em.Object.create({
+    used: null,
+    max: null
+  });
+  var pieChartDashboardWidgetView = App.PieChartDashboardWidgetView.create({
+    model_type: null,
+    model: model,
+    modelFieldUsed: 'used',
+    modelFieldMax: 'max',
+    widgetHtmlId: 'fake'
+  });
+
+  pieChartDashboardWidgetView.calc();
+
+  describe('#getUsed', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          used: 1
+        }),
+        e: 1,
+        m: '"Used" is set'
+      },
+      {
+        model: Em.Object.create({
+          used: null
+        }),
+        e: 0,
+        m: '"Used" is not set'
+      },
+      {
+        model: Em.Object.create({}),
+        e: 0,
+        m: '"Used" is not defined'
+      }
+    ];
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        pieChartDashboardWidgetView.set('model', test.model);
+        expect(pieChartDashboardWidgetView.getUsed()).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('#getMax', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          max: 1
+        }),
+        e: 1,
+        m: '"Max" is set'
+      },
+      {
+        model: Em.Object.create({
+          max: null
+        }),
+        e: 0,
+        m: '"Max" is not set'
+      },
+      {
+        model: Em.Object.create({}),
+        e: 0,
+        m: '"Max" is not defined'
+      }
+    ];
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        pieChartDashboardWidgetView.set('model', test.model);
+        expect(pieChartDashboardWidgetView.getMax()).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('#calcIsPieExists', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          max: 1
+        }),
+        e: true,
+        m: 'Exists'
+      },
+      {
+        model: Em.Object.create({
+          max: 0
+        }),
+        e: false,
+        m: 'Not exists'
+      },
+      {
+        model: Em.Object.create({}),
+        e: false,
+        m: 'Not exists'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        pieChartDashboardWidgetView.set('model', test.model);
+        expect(pieChartDashboardWidgetView.calcIsPieExists()).to.equal(test.e);
+      });
+    });
+  });
+
+  describe('calcDataForPieChart', function() {
+    var tests = [
+      {
+        model: Em.Object.create({
+          max: 10,
+          used: 0
+        }),
+        e: ['0', 100],
+        m: 'Nothing is used'
+      },
+      {
+        model: Em.Object.create({
+          max: 10,
+          used: 10
+        }),
+        e: ['100', 0],
+        m: 'All is used'
+      },
+      {
+        model: Em.Object.create({
+          max: 10,
+          used: 5
+        }),
+        e: ['50', 50],
+        m: 'Half is used'
+      }
+    ];
+
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        pieChartDashboardWidgetView.set('model', test.model);
+        expect(pieChartDashboardWidgetView.calcDataForPieChart()).to.eql(test.e);
+      });
+    });
+  });
+
+});

+ 95 - 0
ambari-web/test/views/main/dashboard/widgets/resource_manager_uptime_test.js

@@ -0,0 +1,95 @@
+/**
+ * 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 App = require('app');
+
+require('messages');
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/resource_manager_uptime');
+
+describe('App.ResourceManagerUptimeView', function() {
+
+  var tests = [
+    {
+      model: Em.Object.create({
+        resourceManagerStartTime: ((new Date()).getTime() - 192.1*24*3600*1000)
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false,
+        content: '192.1 d',
+        data: 192.1
+      }
+    },
+    {
+      model:  Em.Object.create({
+        resourceManagerStartTime: 0
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    },
+    {
+      model:  Em.Object.create({
+        resourceManagerStartTime: null
+      }),
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: false,
+        isNA: true,
+        content: Em.I18n.t('services.service.summary.notAvailable'),
+        data: null
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    var resourceManagerUptimeView = App.ResourceManagerUptimeView.create({model_type:null, model: test.model});
+    resourceManagerUptimeView.calc();
+    describe('resourceManagerStartTime - ' + test.model.resourceManagerStartTime, function() {
+      it('content', function() {
+        expect(resourceManagerUptimeView.get('content')).to.equal(test.e.content);
+      });
+      it('data', function() {
+        expect(resourceManagerUptimeView.get('data')).to.equal(test.e.data);
+      });
+      it('isRed', function() {
+        expect(resourceManagerUptimeView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(resourceManagerUptimeView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(resourceManagerUptimeView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(resourceManagerUptimeView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 69 - 0
ambari-web/test/views/main/dashboard/widgets/tasktracker_live_test.js

@@ -0,0 +1,69 @@
+/**
+ * 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 App = require('app');
+
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/tasktracker_live');
+
+describe('App.TaskTrackerUpView', function() {
+
+  var tests = [
+    {
+      data: 100,
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true
+      }
+    },
+    {
+      data: 0,
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false
+      }
+    },
+    {
+      data: 50,
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('', function() {
+      var taskTrackerUpView = App.TaskTrackerUpView.create({model_type:null, data: test.data, content: test.data.toString()});
+      it('isRed', function() {
+        expect(taskTrackerUpView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(taskTrackerUpView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(taskTrackerUpView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+    });
+  });
+
+});

+ 84 - 0
ambari-web/test/views/main/dashboard/widgets/text_widget_test.js

@@ -0,0 +1,84 @@
+/**
+ * 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 App = require('app');
+
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+
+describe('App.TextDashboardWidgetView', function() {
+
+  var tests = [
+    {
+      data: 100,
+      e: {
+        isRed: false,
+        isOrange: false,
+        isGreen: true,
+        isNA: false
+      }
+    },
+    {
+      data: 1,
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: false
+      }
+    },
+    {
+      data: 50,
+      e: {
+        isRed: false,
+        isOrange: true,
+        isGreen: false,
+        isNA: false
+      }
+    },
+    {
+      data: null,
+      e: {
+        isRed: true,
+        isOrange: false,
+        isGreen: false,
+        isNA: true
+      }
+    }
+  ];
+
+  tests.forEach(function(test) {
+    describe('data - ' + test.data + ' | thresh1 - 40 | thresh2 - 70', function() {
+      var textDashboardWidgetView = App.TextDashboardWidgetView.create({thresh1:40, thresh2:70});
+      textDashboardWidgetView.set('data', test.data);
+      it('isRed', function() {
+        expect(textDashboardWidgetView.get('isRed')).to.equal(test.e.isRed);
+      });
+      it('isOrange', function() {
+        expect(textDashboardWidgetView.get('isOrange')).to.equal(test.e.isOrange);
+      });
+      it('isGreen', function() {
+        expect(textDashboardWidgetView.get('isGreen')).to.equal(test.e.isGreen);
+      });
+      it('isNA', function() {
+        expect(textDashboardWidgetView.get('isNA')).to.equal(test.e.isNA);
+      });
+    });
+  });
+
+});

+ 90 - 0
ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js

@@ -0,0 +1,90 @@
+/**
+ * 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 App = require('app');
+
+require('views/main/dashboard/widget');
+require('views/main/dashboard/widgets/text_widget');
+require('views/main/dashboard/widgets/uptime_text_widget');
+
+describe('App.UptimeTextDashboardWidgetView', function() {
+
+  describe('#timeConverter', function() {
+    var timestamps = [
+      {
+        t: 1358245370553,
+        e: {
+          l: 2,
+          f: 'Tue Jan 15 2013'
+        }
+      },
+      {
+        t: 0,
+        e: {
+          l: 2,
+          f: 'Thu Jan 01 1970'
+        }
+      }
+    ];
+    timestamps.forEach(function(timestamp) {
+      var uptimeTextDashboardWidgetView = App.UptimeTextDashboardWidgetView.create({thresh1:40, thresh2:70});
+      it('timestamp ' + timestamp.t, function() {
+        var result = uptimeTextDashboardWidgetView.timeConverter(timestamp.t);
+        expect(result.length).to.equal(timestamp.e.l);
+        expect(result[0]).to.equal(timestamp.e.f);
+      });
+    });
+  });
+
+  describe('#uptimeProcessing', function() {
+    var timestamps = [
+      {
+        t: (new Date()).getTime() - 10*1000,
+        e: {
+          timeUnit: 's'
+        }
+      },
+      {
+        t: (new Date()).getTime() - 3600*1000,
+        e: {
+          timeUnit: 'hr'
+        }
+      },
+      {
+        t: (new Date()).getTime() - 24*3600*1000,
+        e: {
+          timeUnit: 'd'
+        }
+      },
+      {
+        t: (new Date()).getTime() - 1800*1000,
+        e: {
+          timeUnit: 'min'
+        }
+      }
+    ];
+    timestamps.forEach(function(timestamp) {
+      var uptimeTextDashboardWidgetView = App.UptimeTextDashboardWidgetView.create({thresh1:40, thresh2:70});
+      it('timestamp ' + timestamp.t + '. timeUnit should be ' + '"' + timestamp.e.timeUnit + '"', function() {
+        var result = uptimeTextDashboardWidgetView.uptimeProcessing(timestamp.t);
+        expect(uptimeTextDashboardWidgetView.get('timeUnit')).to.equal(timestamp.e.timeUnit);
+      });
+    });
+  });
+
+});

+ 122 - 0
ambari-web/test/views/main/dashboard_test.js

@@ -0,0 +1,122 @@
+/**
+ * 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 App = require('app');
+require('messages');
+var filters = require('views/common/filter_view');
+require('views/main/dashboard');
+
+describe('App.MainDashboardView', function() {
+
+  var mainDashboardView = App.MainDashboardView.create();
+
+  describe('#setInitPrefObject', function() {
+    var hdfs_widgets_count = 7;
+    var mapreduce_widgets_count = 7;
+    var hbase_widgets_count = 4;
+    var yarn_widgets_count = 4;
+    var total_widgets_count = 27;
+    var tests = [
+      {
+        models: {
+          hdfs_model: null,
+          mapreduce_model: null,
+          hbase_model: null,
+          yarn_model: null
+        },
+        e: {
+          visibleL: total_widgets_count - hdfs_widgets_count - mapreduce_widgets_count - hbase_widgets_count - yarn_widgets_count - 1,
+          hiddenL: 0
+        },
+        m: 'All models are null'
+      },
+      {
+        models: {
+          hdfs_model: {},
+          mapreduce_model: null,
+          hbase_model: null,
+          yarn_model: null
+        },
+        e: {
+          visibleL: total_widgets_count  - mapreduce_widgets_count - hbase_widgets_count - yarn_widgets_count - 1,
+          hiddenL: 0
+        },
+        m: 'mapreduce_model, hbase_model, yarn_model are null'
+      },
+      {
+        models: {
+          hdfs_model: {},
+          mapreduce_model: {},
+          hbase_model: null,
+          yarn_model: null
+        },
+        e: {
+          visibleL: total_widgets_count - hbase_widgets_count - yarn_widgets_count - 1,
+          hiddenL: 0
+        },
+        m: 'hbase_model and yarn_model are null'
+      },
+      {
+        models: {
+          hdfs_model: {},
+          mapreduce_model: {},
+          hbase_model: {},
+          yarn_model: null
+        },
+        e: {
+          visibleL: total_widgets_count - yarn_widgets_count - 1,
+          hiddenL: 1
+        },
+        m: 'yarn_model is null'
+      },
+      {
+        models: {
+          hdfs_model: {},
+          mapreduce_model: {},
+          hbase_model: {},
+          yarn_model: {}
+        },
+        e: {
+          visibleL: total_widgets_count - 1,
+          hiddenL: 1
+        },
+        m: 'All models are not null'
+      }
+    ];
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        mainDashboardView.set('hdfs_model', test.models.hdfs_model);
+        mainDashboardView.set('mapreduce_model', test.models.mapreduce_model);
+        mainDashboardView.set('hbase_model', test.models.hbase_model);
+        mainDashboardView.set('yarn_model', test.models.yarn_model);
+        mainDashboardView.setInitPrefObject();
+        expect(mainDashboardView.get('initPrefObject.visible.length')).to.equal(test.e.visibleL);
+        expect(mainDashboardView.get('initPrefObject.hidden.length')).to.equal(test.e.hiddenL);
+      });
+    });
+  });
+
+  describe('#persistKey', function() {
+    App.router.set('loginName', 'tdk');
+    it('Check it', function() {
+      expect(mainDashboardView.get('persistKey')).to.equal('user-pref-tdk-dashboard');
+    });
+  });
+
+});