You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2013/08/08 20:27:12 UTC

[1/3] AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)

Updated Branches:
  refs/heads/trunk d75875cf1 -> e81d705b7


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js b/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
new file mode 100644
index 0000000..05ae6e6
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/resource_manager_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/resource_manager_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/resource_manager_uptime_test.js
new file mode 100644
index 0000000..bf4b637
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/tasktracker_live_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/tasktracker_live_test.js b/ambari-web/test/views/main/dashboard/widgets/tasktracker_live_test.js
new file mode 100644
index 0000000..50d0123
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/text_widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/text_widget_test.js b/ambari-web/test/views/main/dashboard/widgets/text_widget_test.js
new file mode 100644
index 0000000..b598f6b
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js b/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
new file mode 100644
index 0000000..7baec59
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard_test.js b/ambari-web/test/views/main/dashboard_test.js
new file mode 100644
index 0000000..18acf67
--- /dev/null
+++ b/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');
+    });
+  });
+
+});


[3/3] git commit: AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)

Posted by yu...@apache.org.
AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)


Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/e81d705b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/e81d705b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/e81d705b

Branch: refs/heads/trunk
Commit: e81d705b778e7d172de89e6cd1d0d2a37b10bda8
Parents: d75875c
Author: Yusaku Sako <yu...@hortonworks.com>
Authored: Thu Aug 8 11:25:34 2013 -0700
Committer: Yusaku Sako <yu...@hortonworks.com>
Committed: Thu Aug 8 11:25:34 2013 -0700

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |  18 ++
 ambari-web/app/messages.js                      |  15 ++
 ambari-web/app/templates/main/dashboard.hbs     |  16 +-
 .../main/dashboard/edit_widget_popup.hbs        |   2 +-
 .../main/dashboard/widgets/hbase_links.hbs      |   2 +-
 .../main/dashboard/widgets/hdfs_links.hbs       |   2 +-
 .../main/dashboard/widgets/mapreduce_links.hbs  |   4 +-
 ambari-web/app/views.js                         |   5 +
 ambari-web/app/views/main/dashboard.js          |   9 +-
 ambari-web/app/views/main/dashboard/widget.js   |  29 ++--
 .../dashboard/widgets/cluster_metrics_widget.js |  25 +++
 .../main/dashboard/widgets/datanode_live.js     |  64 ++-----
 .../dashboard/widgets/hbase_average_load.js     |  59 ++-----
 .../views/main/dashboard/widgets/hbase_links.js |  27 ++-
 .../main/dashboard/widgets/hbase_master_heap.js |  93 ++--------
 .../dashboard/widgets/hbase_master_uptime.js    | 101 +----------
 .../widgets/hbase_regions_in_transition.js      |  55 ++----
 .../main/dashboard/widgets/hdfs_capacity.js     | 106 +++---------
 .../views/main/dashboard/widgets/hdfs_links.js  |  25 ++-
 .../main/dashboard/widgets/jobtracker_cpu.js    | 104 +++---------
 .../main/dashboard/widgets/jobtracker_heap.js   |  87 ++--------
 .../main/dashboard/widgets/jobtracker_rpc.js    |  61 +------
 .../main/dashboard/widgets/jobtracker_uptime.js | 100 +----------
 .../main/dashboard/widgets/links_widget.js      |  49 ++++++
 .../main/dashboard/widgets/mapreduce_links.js   |  25 ++-
 .../main/dashboard/widgets/mapreduce_slots.js   |  15 +-
 .../views/main/dashboard/widgets/metrics_cpu.js |  10 +-
 .../main/dashboard/widgets/metrics_load.js      |  10 +-
 .../main/dashboard/widgets/metrics_memory.js    |  10 +-
 .../main/dashboard/widgets/metrics_network.js   |  10 +-
 .../main/dashboard/widgets/namenode_cpu.js      | 104 +++---------
 .../main/dashboard/widgets/namenode_heap.js     | 118 ++++---------
 .../main/dashboard/widgets/namenode_rpc.js      |  64 +------
 .../main/dashboard/widgets/namenode_uptime.js   | 101 +----------
 .../dashboard/widgets/node_managers_live.js     |  57 +------
 .../main/dashboard/widgets/pie_chart_widget.js  | 140 +++++++++++++++
 .../dashboard/widgets/resource_manager_heap.js  |  86 ++--------
 .../widgets/resource_manager_uptime.js          |  99 +----------
 .../main/dashboard/widgets/tasktracker_live.js  |  60 +------
 .../views/main/dashboard/widgets/text_widget.js |  66 ++++++++
 .../dashboard/widgets/uptime_text_widget.js     | 149 ++++++++++++++++
 .../views/main/dashboard/widgets/yarn_memory.js |  93 ++--------
 .../test/views/main/dashboard/widget_test.js    |  73 ++++++++
 .../dashboard/widgets/datanode_live_test.js     |  69 ++++++++
 .../widgets/hbase_average_load_test.js          | 103 +++++++++++
 .../widgets/hbase_master_uptime_test.js         |  95 +++++++++++
 .../widgets/hbase_regions_in_transition_test.js | 102 +++++++++++
 .../dashboard/widgets/jobtracker_rpc_test.js    | 107 ++++++++++++
 .../dashboard/widgets/jobtracker_uptime_test.js |  95 +++++++++++
 .../main/dashboard/widgets/links_widget_test.js |  45 +++++
 .../main/dashboard/widgets/namenode_cpu_test.js | 107 ++++++++++++
 .../main/dashboard/widgets/namenode_rpc_test.js | 107 ++++++++++++
 .../dashboard/widgets/namenode_uptime_test.js   |  95 +++++++++++
 .../widgets/node_managers_live_test.js          |  97 +++++++++++
 .../dashboard/widgets/pie_chart_widget_test.js  | 169 +++++++++++++++++++
 .../widgets/resource_manager_uptime_test.js     |  95 +++++++++++
 .../dashboard/widgets/tasktracker_live_test.js  |  69 ++++++++
 .../main/dashboard/widgets/text_widget_test.js  |  84 +++++++++
 .../widgets/uptime_text_widget_test.js          |  90 ++++++++++
 ambari-web/test/views/main/dashboard_test.js    | 122 +++++++++++++
 60 files changed, 2504 insertions(+), 1495 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index aa1e6f2..a3d7947 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/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');
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 266adf6..2836337 100644
--- a/ambari-web/app/messages.js
+++ b/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}',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/templates/main/dashboard.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard.hbs b/ambari-web/app/templates/main/dashboard.hbs
index 6cd35a2..9c27428 100644
--- a/ambari-web/app/templates/main/dashboard.hbs
+++ b/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>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs b/ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs
index 201910f..30cc5be 100644
--- a/ambari-web/app/templates/main/dashboard/edit_widget_popup.hbs
+++ b/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>
 

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs b/ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs
index 309f1aa..ac13928 100644
--- a/ambari-web/app/templates/main/dashboard/widgets/hbase_links.hbs
+++ b/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>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs b/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
index 7f1d88a..edaa684 100644
--- a/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
+++ b/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>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs b/ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs
index 33d6547..63ac451 100644
--- a/ambari-web/app/templates/main/dashboard/widgets/mapreduce_links.hbs
+++ b/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>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index cb804c3..4f7bd1a 100644
--- a/ambari-web/app/views.js
+++ b/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');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard.js b/ambari-web/app/views/main/dashboard.js
index 8e9da3a..a3953e6 100644
--- a/ambari-web/app/views/main/dashboard.js
+++ b/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');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widget.js b/ambari-web/app/views/main/dashboard/widget.js
index 06d8920..2b3828c 100644
--- a/ambari-web/app/views/main/dashboard/widget.js
+++ b/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'
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/cluster_metrics_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/cluster_metrics_widget.js b/ambari-web/app/views/main/dashboard/widgets/cluster_metrics_widget.js
new file mode 100644
index 0000000..d6b2db7
--- /dev/null
+++ b/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')
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/datanode_live.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/datanode_live.js b/ambari-web/app/views/main/dashboard/widgets/datanode_live.js
index 1453712..962b47f 100644
--- a/ambari-web/app/views/main/dashboard/widgets/datanode_live.js
+++ b/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({
       }
     });
   }
-
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js b/ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js
index 795e417..b02c420 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_average_load.js
+++ b/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({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hbase_links.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_links.js b/ambari-web/app/views/main/dashboard/widgets/hbase_links.js
index 1bbe533..bbac1ac 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_links.js
+++ b/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
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js b/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
index da48f7f..b9645ee 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
+++ b/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];
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js b/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
index 3ced6ec..c6e9b67 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
+++ b/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();
   }
 
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js b/ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js
index d583fc7..d93f9e7 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_regions_in_transition.js
+++ b/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({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js b/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
index 9a27d01..9b31557 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
+++ b/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];
+  }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js b/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
index 6c16686..ede0003 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
+++ b/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();
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js b/ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js
index fe62bc7..4bfc6c6 100644
--- a/ambari-web/app/views/main/dashboard/widgets/jobtracker_cpu.js
+++ b/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];
+  }
 
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js b/ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js
index 46d5c07..6df6c40 100644
--- a/ambari-web/app/views/main/dashboard/widgets/jobtracker_heap.js
+++ b/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')
-  })
-
-})
\ No newline at end of file
+  didInsertElement: function() {
+    this._super();
+    this.calc();
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js b/ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js
index 9c046d1..46780b3 100644
--- a/ambari-web/app/views/main/dashboard/widgets/jobtracker_rpc.js
+++ b/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({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js b/ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js
index c7921fd..282e138 100644
--- a/ambari-web/app/views/main/dashboard/widgets/jobtracker_uptime.js
+++ b/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();
   }
 
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/links_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/links_widget.js b/ambari-web/app/views/main/dashboard/widgets/links_widget.js
new file mode 100644
index 0000000..1aabdcb
--- /dev/null
+++ b/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 '';
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js b/ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js
index 443a9f6..afdfbe6 100644
--- a/ambari-web/app/views/main/dashboard/widgets/mapreduce_links.js
+++ b/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();
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js b/ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js
index 8a0c1d6..40c613f 100644
--- a/ambari-web/app/views/main/dashboard/widgets/mapreduce_slots.js
+++ b/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
-})
\ No newline at end of file
+  class: 'span4p8'
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js b/ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js
index 3f7f139..7bc7620 100644
--- a/ambari-web/app/views/main/dashboard/widgets/metrics_cpu.js
+++ b/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
   })
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/metrics_load.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/metrics_load.js b/ambari-web/app/views/main/dashboard/widgets/metrics_load.js
index 23298fb..80360a7 100644
--- a/ambari-web/app/views/main/dashboard/widgets/metrics_load.js
+++ b/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
   })
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/metrics_memory.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/metrics_memory.js b/ambari-web/app/views/main/dashboard/widgets/metrics_memory.js
index ff22cd5..4a30dcc 100644
--- a/ambari-web/app/views/main/dashboard/widgets/metrics_memory.js
+++ b/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
   })
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/metrics_network.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/metrics_network.js b/ambari-web/app/views/main/dashboard/widgets/metrics_network.js
index 17ea447..d82784e 100644
--- a/ambari-web/app/views/main/dashboard/widgets/metrics_network.js
+++ b/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
   })
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js b/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
index 3873776..35708c5 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
+++ b/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];
+  }
 
-})
+});
\ No newline at end of file


[2/3] AMBARI-2829. Dashboard refactor and unit tests. (onechiporenko via yusaku)

Posted by yu...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
index 80029fc..7d283a7 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
+++ b/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;
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
index e994439..749d5d3 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
+++ b/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({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
index 216ef4c..60ad42d 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
+++ b/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();
   }
 
-})
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js b/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
index f94d28a..55d02e0 100644
--- a/ambari-web/app/views/main/dashboard/widgets/node_managers_live.js
+++ b/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);

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
new file mode 100644
index 0000000..502ea69
--- /dev/null
+++ b/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')
+  })
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
index f274fc7..966de98 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
+++ b/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();
+  }
 
-})
\ No newline at end of file
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
index d3b092d..cb7ab84 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
+++ b/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();
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js b/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
index 77469c1..b7cd038 100644
--- a/ambari-web/app/views/main/dashboard/widgets/tasktracker_live.js
+++ b/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({
     });
   }
 
-})
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/text_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/text_widget.js b/ambari-web/app/views/main/dashboard/widgets/text_widget.js
new file mode 100644
index 0000000..85b2d54
--- /dev/null
+++ b/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
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
new file mode 100644
index 0000000..251ef2d
--- /dev/null
+++ b/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');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
index da96be0..e422abb 100644
--- a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
+++ b/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();
+  }
 
-})
\ No newline at end of file
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widget_test.js b/ambari-web/test/views/main/dashboard/widget_test.js
new file mode 100644
index 0000000..f1e0436
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js b/ambari-web/test/views/main/dashboard/widgets/datanode_live_test.js
new file mode 100644
index 0000000..e68a8e3
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_average_load_test.js
new file mode 100644
index 0000000..932ff87
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_master_uptime_test.js
new file mode 100644
index 0000000..1f365cf
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js b/ambari-web/test/views/main/dashboard/widgets/hbase_regions_in_transition_test.js
new file mode 100644
index 0000000..80a292b
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js b/ambari-web/test/views/main/dashboard/widgets/jobtracker_rpc_test.js
new file mode 100644
index 0000000..c1ef319
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/jobtracker_uptime_test.js
new file mode 100644
index 0000000..820c653
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js b/ambari-web/test/views/main/dashboard/widgets/links_widget_test.js
new file mode 100644
index 0000000..4796bde
--- /dev/null
+++ b/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');
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_cpu_test.js
new file mode 100644
index 0000000..5852c2b
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
new file mode 100644
index 0000000..14016e8
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js b/ambari-web/test/views/main/dashboard/widgets/namenode_uptime_test.js
new file mode 100644
index 0000000..607ecfd
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/e81d705b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js
new file mode 100644
index 0000000..ae15127
--- /dev/null
+++ b/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);
+      });
+    });
+  });
+
+});