eagle-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hdenduk...@apache.org
Subject [2/9] incubator-eagle git commit: EAGLE-139 EAGLE-163 Eagle UI Modularization and fix bugs in policy extensions
Date Fri, 26 Feb 2016 21:26:20 GMT
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/applicationSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/applicationSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/applicationSrv.js
new file mode 100644
index 0000000..67d714f
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/applicationSrv.js
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+	serviceModule.service('Application', function($q, $location, $wrapState, Entities) {
+		var Application = {};
+		var _current;
+		var _featureCache = {};// After loading feature will be in cache. Which will not load twice.
+		var _deferred;
+
+		Application.list = [];
+		Application.list.set = {};
+		Application.featureList = [];
+		Application.featureList.set = {};
+
+		// Set current application
+		Application.current = function(app) {
+			if(arguments.length && _current !== app) {
+				var _prev = _current;
+				_current = app;
+
+				if(sessionStorage && _current) {
+					sessionStorage.setItem("application", _current.tags.application);
+				}
+
+				if(_prev) {
+					console.log("[Application] Switch. Redirect to landing page.");
+					$wrapState.go('landing', true);
+				}
+			}
+			return _current;
+		};
+		Application.find = function(appName) {
+			return common.array.find(appName, Application.list, "tags.application");
+		};
+
+		Application.reload = function() {
+			_deferred = $q.defer();
+
+			if(Application.list && Application.list._promise) Application.list._promise.abort();
+			if(Application.featureList && Application.featureList._promise) Application.featureList._promise.abort();
+
+			Application.list = Entities.queryEntities("ApplicationDescService", '');
+			Application.list.set = {};
+			Application.featureList = Entities.queryEntities("FeatureDescService", '');
+			Application.featureList.set = {};
+
+			Application.featureList._promise.then(function() {
+				var _promiseList;
+				// Load feature script
+				_promiseList = $.map(Application.featureList, function(feature) {
+					var _ajax_deferred, _script;
+					if(_featureCache[feature.tags.feature]) return;
+
+					_featureCache[feature.tags.feature] = true;
+					_ajax_deferred = $q.defer();
+					_script = document.createElement('script');
+					_script.type = 'text/javascript';
+					_script.src = "public/feature/" + feature.tags.feature + "/controller.js?_=" + Math.random();
+					document.head.appendChild(_script);
+					_script.onload = function() {
+						feature._loaded = true;
+						_ajax_deferred.resolve();
+					};
+					_script.onerror = function() {
+						feature._loaded = false;
+						_featureCache[feature.tags.feature] = false;
+						_ajax_deferred.reject();
+					};
+					return _ajax_deferred.promise;
+				});
+
+				// Merge application & feature
+				Application.list._promise.then(function() {
+					// Fill feature set
+					$.each(Application.featureList, function(i, feature) {
+						Application.featureList.set[feature.tags.feature] = feature;
+					});
+
+					// Fill application set
+					$.each(Application.list, function(i, application) {
+						Application.list.set[application.tags.application] = application;
+						application.features = application.features || [];
+						var _configObj = common.parseJSON(application.config, {});
+						var _appFeatureList = $.map(application.features, function(featureName) {
+							var _feature = Application.featureList.set[featureName];
+							if(!_feature) {
+								console.warn("[Application] Feature not mapping:", application.tags.application,
"-", featureName);
+							} else {
+								return _feature;
+							}
+						});
+
+						// Find feature
+						_appFeatureList.find = function(featureName) {
+							return common.array.find(featureName, _appFeatureList, "tags.feature");
+						};
+
+						Object.defineProperties(application, {
+							featureList: {
+								get: function () {
+									return _appFeatureList;
+								}
+							},
+							// Get format group name. Will mark as 'Others' if no group defined
+							groupName: {
+								get: function () {
+									return this.group || "Others";
+								}
+							},
+							configObj: {
+								get: function() {
+									return _configObj;
+								}
+							},
+							displayName: {
+								get: function() {
+									return this.alias || this.tags.application;
+								}
+							}
+						});
+					});
+
+					// Set current application
+					if(!Application.current() && sessionStorage && Application.find(sessionStorage.getItem("application")))
{
+						Application.current(Application.find(sessionStorage.getItem("application")));
+					}
+				});
+
+				// Process all promise
+				$q.all(_promiseList.concat(Application.list._promise)).finally(function() {
+					_deferred.resolve(Application);
+				});
+			}, function() {
+				_deferred.reject(Application);
+			});
+
+			return _deferred.promise;
+		};
+
+		Application._promise = function() {
+			if(!_deferred) {
+				Application.reload();
+			}
+			return _deferred.promise;
+		};
+
+		return Application;
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/authorizationSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/authorizationSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/authorizationSrv.js
new file mode 100644
index 0000000..337b567
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/authorizationSrv.js
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+	serviceModule.service('Authorization', function ($rootScope, $http, $wrapState, $q) {
+		$http.defaults.withCredentials = true;
+
+		var _promise;
+		var _path = "";
+
+		var content = {
+			isLogin: true,	// Status mark. Work for UI status check, changed when eagle api return
403 authorization failure.
+			needLogin: function () {
+				console.log("[Authorization] Need Login!");
+				if(content.isLogin) {
+					_path = _path || $wrapState.path();
+					content.isLogin = false;
+					console.log("[Authorization] Call need login. Redirect...");
+					$wrapState.go("login", 99);
+				} else {
+					console.log("[Authorization] Already login state...");
+				}
+			},
+			login: function (username, password) {
+				var _hash = btoa(username + ':' + password);
+				return $http({
+					url: app.getURL('userProfile'),
+					method: "GET",
+					headers: {
+						'Authorization': "Basic " + _hash
+					}
+				}).then(function () {
+					content.isLogin = true;
+					return true;
+				}, function () {
+					return false;
+				});
+			},
+			logout: function () {
+				$http({
+					url: app.getURL('logout'),
+					method: "GET"
+				});
+			},
+			path: function (path) {
+				if (typeof path === "string") {
+					_path = path;
+				} else if (path === true) {
+					$wrapState.path(_path || "");
+					_path = "";
+				}
+			}
+		};
+
+		content.userProfile = {};
+		content.isRole = function (role) {
+			if (!content.userProfile.roles) return null;
+
+			return content.userProfile.roles[role] === true;
+		};
+
+		content.reload = function () {
+			_promise = $http({
+				url: app.getURL('userProfile'),
+				method: "GET"
+			}).then(function (data) {
+				content.userProfile = data.data;
+
+				// Role
+				content.userProfile.roles = {};
+				$.each(content.userProfile.authorities, function (i, role) {
+					content.userProfile.roles[role.authority] = true;
+				});
+
+				return content;
+			}, function(data) {
+				if(data.status === 403) {
+					content.needLogin();
+				}
+			});
+			return _promise;
+		};
+
+		content._promise = function () {
+			if (!_promise) {
+				content.reload();
+			}
+			return _promise;
+		};
+
+		content.rolePromise = function(role, rejectState) {
+			var _deferred = $q.defer();
+			var _oriPromise = content._promise();
+			_oriPromise.then(function() {
+				if(content.isRole(role)) {
+					_deferred.resolve(content);
+				} else if(content.isLogin) {
+					_deferred.resolve(content);
+					console.log("[Authorization] go landing...");
+					$wrapState.go(rejectState || "landing");
+				} else {
+					_deferred.reject(content);
+				}
+
+				return content;
+			});
+
+			return _deferred.promise;
+		};
+
+		return content;
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/entitiesSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/entitiesSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/entitiesSrv.js
new file mode 100644
index 0000000..b580f92
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/entitiesSrv.js
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+	serviceModule.service('Entities', function($http, $q, $rootScope, $location, Authorization)
{
+		var pkg;
+
+		// Query
+		function _query(name, kvs) {
+			kvs = kvs || {};
+			var _list = [];
+			var _condition = kvs._condition || {};
+			var _addtionalCondition = _condition.additionalCondition || {};
+			var _startTime, _endTime;
+			var _startTimeStr, _endTimeStr;
+
+			// Initial
+			// > Condition
+			delete kvs._condition;
+			if(_condition) {
+				kvs.condition = _condition.condition;
+			}
+
+			// > Values
+			if(!kvs.values) {
+				kvs.values = "*";
+			} else if($.isArray(kvs.values)) {
+				kvs.values = $.map(kvs.values, function(field) {
+					return (field[0] === "@" ? '' : '@') + field;
+				}).join(",");
+			}
+
+			var _url = app.getURL(name, kvs);
+
+			// Fill special parameters
+			// > Query by time duration
+			if(_addtionalCondition._duration) {
+				_endTime = app.time.now();
+				_startTime = _endTime.clone().subtract(_addtionalCondition._duration, "ms");
+
+				// Debug usage. Extend more time duration for end time
+				if(_addtionalCondition.__ETD) {
+					_endTime.add(_addtionalCondition.__ETD, "ms");
+				}
+
+				_addtionalCondition._startTime = _startTime;
+				_addtionalCondition._endTime = _endTime;
+
+				_startTimeStr = _startTime.format("YYYY-MM-DD HH:mm:ss");
+				_endTimeStr = _endTime.clone().add(1, "s").format("YYYY-MM-DD HH:mm:ss");
+
+				_url += "&startTime=" + _startTimeStr + "&endTime=" + _endTimeStr;
+			} else if(_addtionalCondition._startTime && _addtionalCondition._endTime) {
+				_startTimeStr = _addtionalCondition._startTime.format("YYYY-MM-DD HH:mm:ss");
+				_endTimeStr = _addtionalCondition._endTime.clone().add(1, "s").format("YYYY-MM-DD HH:mm:ss");
+
+				_url += "&startTime=" + _startTimeStr + "&endTime=" + _endTimeStr;
+			}
+
+			// > Query contains metric name
+			if(_addtionalCondition._metricName) {
+				_url += "&metricName=" + _addtionalCondition._metricName;
+			}
+
+			// > Customize page size
+			if(_addtionalCondition._pageSize) {
+				_url = _url.replace(/pageSize=\d+/, "pageSize=" + _addtionalCondition._pageSize);
+			}
+
+			// AJAX
+			var canceler = $q.defer();
+			_list._promise = $http.get(_url, {timeout: canceler.promise}).then(function(status) {
+				_list.push.apply(_list, status.data.obj);
+				return _list;
+			});
+			_list._promise.abort = function() {
+				canceler.resolve();
+			};
+
+			_list._promise.then(function() {}, function(data) {
+				if(data.status === 403) {
+					Authorization.needLogin();
+				}
+			});
+
+			return _list;
+		}
+		function _post(url, entities) {
+			var _list = [];
+			_list._promise = $http({
+				method: 'POST',
+				url: url,
+				headers: {
+					"Content-Type": "application/json"
+				},
+				data: entities
+			}).success(function(data) {
+				_list.push.apply(_list, data.obj);
+			});
+			return _list;
+		}
+		function _delete(url) {
+			var _list = [];
+			_list._promise = $http({
+				method: 'DELETE',
+				url: url,
+				headers: {
+					"Content-Type": "application/json"
+				}
+			}).success(function(data) {
+				_list.push.apply(_list, data.obj);
+			});
+			return _list;
+		}
+		function ParseCondition(condition) {
+			var _this = this;
+			_this.condition = "";
+			_this.additionalCondition = {};
+
+			if(typeof condition === "string") {
+				_this.condition = condition;
+			} else {
+				_this.condition = $.map(condition, function(value, key) {
+					if(!key.match(/^_/)) {
+						if(value === undefined || value === null) {
+							return '@' + key + '=~".*"';
+						} else {
+							return '@' + key + '="' + value + '"';
+						}
+					} else {
+						_this.additionalCondition[key] = value;
+						return null;
+					}
+				}).join(" AND ");
+			}
+			return _this;
+		}
+
+		pkg = {
+			_query: _query,
+			_post: _post,
+
+			updateEntity: function(serviceName, entities, config) {
+				var _url;
+				config = config || {};
+				if(!$.isArray(entities)) entities = [entities];
+
+				// Post clone entities
+				var _entities = $.map(entities, function(entity) {
+					var _entity = {};
+
+					// Clone variables
+					$.each(entity, function(key) {
+						// Skip inner variables
+						if(!key.match(/^__/)) {
+							_entity[key] = entity[key];
+						}
+					});
+
+					// Add timestamp
+					if(config.timestamp !== false) {
+						if(config.createTime !== false && !_entity.createdTime) {
+							_entity.createdTime = new moment().valueOf();
+						}
+						if(config.lastModifiedDate !== false) {
+							_entity.lastModifiedDate = new moment().valueOf();
+						}
+					}
+
+					return _entity;
+				});
+
+				// Check for url hook
+				if(config.hook) {
+					_url = app.getUpdateURL(serviceName);
+				} else {
+					_url = app.getURL("updateEntity", {serviceName: serviceName});
+				}
+
+				return _post(_url, _entities);
+			},
+
+			deleteEntity: function(serviceName, entities) {
+				if (!$.isArray(entities)) entities = [entities];
+
+				var _entities = $.map(entities, function (entity) {
+					return typeof entity === "object" ? entity.encodedRowkey : entity;
+				});
+				return _post(app.getURL("deleteEntity", {serviceName: serviceName}), _entities);
+			},
+			deleteEntities: function(serviceName, condition) {
+				return _delete(app.getURL("deleteEntities", {serviceName: serviceName, condition: new
ParseCondition(condition).condition}));
+			},
+			delete: function(serviceName, kvs) {
+				var _deleteURL = app.getDeleteURL(serviceName);
+				return _delete(common.template(_deleteURL, kvs));
+			},
+
+			queryEntity: function(serviceName, encodedRowkey) {
+				return _query("queryEntity", {serviceName: serviceName, encodedRowkey: encodedRowkey});
+			},
+			queryEntities: function(serviceName, condition, fields) {
+				return _query("queryEntities", {serviceName: serviceName, _condition: new ParseCondition(condition),
values: fields});
+			},
+			queryGroup: function(serviceName, condition, groupBy, fields) {
+				return _query("queryGroup", {serviceName: serviceName, _condition: new ParseCondition(condition),
groupBy: groupBy, values: fields});
+			},
+			querySeries: function(serviceName, condition, groupBy, fields, intervalmin) {
+				var _cond = new ParseCondition(condition);
+				var _list = _query("querySeries", {serviceName: serviceName, _condition: _cond, groupBy:
groupBy, values: fields, intervalmin: intervalmin});
+				_list._promise.then(function() {
+					if(_list.length === 0) {
+						_list._empty = true;
+						_list._convert = true;
+
+						for(var i = 0; i <= (_cond.additionalCondition._endTime.valueOf() - _cond.additionalCondition._startTime.valueOf())
/ (1000 * 60 * intervalmin); i += 1) {
+							_list.push(0);
+						}
+					} else if(_list.length === 1) {
+						_list._convert = true;
+						var _unit = _list.pop();
+						_list.push.apply(_list, _unit.value[0]);
+					}
+
+					if(_list._convert) {
+						var _current = _cond.additionalCondition._startTime.clone();
+						$.each(_list, function(i, value) {
+							_list[i] = {
+								x: _current.valueOf(),
+								y: value
+							};
+							_current.add(intervalmin, "m");
+						});
+					}
+				});
+				return _list;
+			},
+
+			query: function(path, params) {
+				var _list = [];
+				_list._promise = $http({
+					method: 'GET',
+					url: app.getURL("query") + path,
+					params: params
+				}).success(function(data) {
+					_list.push.apply(_list, data.obj);
+				});
+				return _list;
+			},
+
+			dialog: function(data, callback) {
+				if(data.success === false || (data.exception || "").trim()) {
+					return $.dialog({
+						title: "OPS",
+						content: $("<pre>").html(data.exception)
+					}, callback);
+				}
+				return false;
+			}
+		};
+		return pkg;
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/main.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/main.js b/eagle-webservice/src/main/webapp/app/public/js/srv/main.js
new file mode 100644
index 0000000..82765b8
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/main.js
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+angular.module('eagle.service', []);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/pageSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/pageSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/pageSrv.js
new file mode 100644
index 0000000..e59d8a3
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/pageSrv.js
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+
+	// ===========================================================
+	// =                         Service                         =
+	// ===========================================================
+	// Feature page
+	serviceModule.service('PageConfig', function() {
+		var _tmplConfig = {
+			pageTitle: "",
+			pageSubTitle: "",
+
+			hideSite: false,
+			lockSite: false,
+			hideApplication: false,
+			hideSidebar: false,
+			hideUser: false,
+
+			// Current page navigation path
+			navPath: [],
+
+			navConfig: {}
+		};
+
+		var PageConfig = {};
+
+		// Reset
+		PageConfig.reset = function() {
+			$.extend(PageConfig, _tmplConfig);
+			PageConfig.navPath = [];
+		};
+		PageConfig.reset();
+
+		// Create navigation path
+		PageConfig.addNavPath = function(title, path) {
+			PageConfig.navPath.push({
+				title: title,
+				path: path
+			});
+			return PageConfig;
+		};
+
+		return PageConfig;
+	});
+
+	// Feature page
+	serviceModule.service('FeaturePageConfig', function(Application) {
+		var config = {
+			// Feature mapping pages
+			_navItemMapping: {}
+		};
+
+		// Register feature controller
+		config.addNavItem = function(feature, item) {
+			var _navItemList = config._navItemMapping[feature] = config._navItemMapping[feature] ||
[];
+			_navItemList.push(item);
+		};
+
+		// Page list
+		Object.defineProperty(config, "pageList", {
+			get: function() {
+				var _app = Application.current();
+				var _list = [];
+
+				if(_app && _app.features) {
+					$.each(_app.features, function(i, featureName) {
+						_list = _list.concat(config._navItemMapping[featureName] || []);
+					});
+				}
+
+				return _list;
+			}
+		});
+
+		return config;
+	});
+
+	// Configuration page
+	serviceModule.service('ConfigPageConfig', function(Application) {
+		var _originPageList = [
+			{icon: "server", title: "Sites", url: "#/config/site"},
+			{icon: "cubes", title: "Applications", url: "#/config/application"},
+			{icon: "leaf", title: "Features", url: "#/config/feature"}
+		];
+
+		var config = {
+			_navItemMapping: {}
+		};
+
+		// Register feature controller
+		config.addNavItem = function(feature, item) {
+			var _navItemList = config._navItemMapping[feature] = config._navItemMapping[feature] ||
[];
+			_navItemList.push(item);
+		};
+
+		// Page list
+		Object.defineProperty(config, "pageList", {
+			get: function() {
+				var _list = _originPageList;
+
+				$.each(Application.featureList, function(i, feature) {
+					_list = _list.concat(config._navItemMapping[feature.tags.feature] || []);
+				});
+
+				return _list;
+			}
+		});
+
+		return config;
+	});
+})();

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/siteSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/siteSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/siteSrv.js
new file mode 100644
index 0000000..907915d
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/siteSrv.js
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+	serviceModule.service('Site', function($rootScope, $wrapState, $location, $q, Entities,
Application) {
+		var _currentSite;
+		var Site = {};
+		var _promise;
+
+		Site.list = [];
+		Site.list.set = {};
+
+		Site.current = function(site) {
+			if(site) {
+				var _prev = _currentSite;
+				_currentSite = site;
+
+				// Keep current site and reload page
+				if(!_prev || _prev.tags.site !== _currentSite.tags.site) {
+					if(sessionStorage) {
+						sessionStorage.setItem("site", _currentSite.tags.site);
+					}
+
+					if(!$wrapState.current.abstract && $wrapState.current.name !== "login") {
+						console.log("[Site]", "Switch. Reload.");
+						$wrapState.reload();
+					}
+				}
+			}
+			return _currentSite;
+		};
+		Site.find = function(siteName) {
+			return common.array.find(siteName, Site.list, "tags.site");
+		};
+		Site.url = function(site, url) {
+			console.warn("[Site] Site.url is a deprecated function.");
+			if(arguments.length == 1) {
+				url = site;
+			} else {
+				Site.current(site);
+			}
+			$wrapState.url(url);
+
+			if ($rootScope.$$phase != '$apply' && $rootScope.$$phase != '$digest') {
+				$rootScope.$apply();
+			}
+		};
+
+		Site.currentSiteApplication = function() {
+			var _app = Application.current();
+			if(!_app) return null;
+
+			return _currentSite.applicationList.set[_app.tags.application];
+		};
+
+		Site.reload = function() {
+			var _applicationList;
+
+			if(Site.list && Site.list._promise) Site.list._promise.abort();
+
+			Site.list = Entities.queryEntities("SiteDescService", '');
+			Site.list.set = {};
+			_applicationList = Entities.queryEntities("SiteApplicationService", '');
+
+			_promise = $q.all([Site.list._promise, _applicationList._promise, Application._promise()]).then(function()
{
+				// Fill site set
+				$.each(Site.list, function(i, site) {
+					var _list = [];
+					var _appGrp = {};
+					var _appGrpList = [];
+					_list.set = {};
+					Site.list.set[site.tags.site] = site;
+
+					// Find application
+					_list.find = function(applicationName) {
+						return common.array.find(applicationName, _list, "tags.application");
+					};
+
+					// Define properties
+					Object.defineProperties(site, {
+						applicationList: {
+							get: function() {
+								return _list;
+							}
+						},
+						applicationGroup: {
+							get: function() {
+								return _appGrp;
+							}
+						},
+						applicationGroupList: {
+							get: function() {
+								return _appGrpList;
+							}
+						}
+					});
+				});
+
+				// Fill site application mapping
+				$.each(_applicationList, function(i, siteApplication) {
+					var _site = Site.list.set[siteApplication.tags.site];
+					var _application = Application.find(siteApplication.tags.application);
+					var _appGroup, _configObj;
+
+					if(!_site) {
+						console.warn("[Site] Application not match site:", siteApplication.tags.site, "-",
siteApplication.tags.application);
+					} else if(!_application) {
+						console.warn("[Site] Application not found:", siteApplication.tags.site, "-", siteApplication.tags.application);
+					} else {
+						_configObj = common.parseJSON(siteApplication.config, {});
+						Object.defineProperties(siteApplication, {
+							application: {
+								get: function () {
+									return _application;
+								}
+							},
+							configObj: {
+								get: function () {
+									return _configObj;
+								}
+							}
+						});
+
+						_site.applicationList.push(siteApplication);
+						_site.applicationList.set[siteApplication.tags.application] = siteApplication;
+
+						_appGroup = _site.applicationGroup[_application.groupName] = _site.applicationGroup[_application.groupName]
|| [];
+						_appGroup.push(_application);
+					}
+				});
+
+				// Fill site application group attributes
+				$.each(Site.list, function(i, site) {
+					$.each(site.applicationGroup, function(grpName, grpList) {
+						var grp = {
+							name: grpName,
+							list: grpList,
+							enabledList: $.grep(grpList, function(application) {return site.applicationList.set[application.tags.application].enabled;}),
+							disabledList: $.grep(grpList, function(application) {return !site.applicationList.set[application.tags.application].enabled;})
+						};
+
+						site.applicationGroupList.push(grp);
+					});
+
+					site.applicationGroupList.sort(function(a, b) {
+						if(a.name === b.name) return 0;
+						if(a.name === "Others") return 1;
+						if(b.name === "Others") return -1;
+						return a.name < b.name ? -1 : 1;
+					});
+				});
+
+				// Set current site
+				if(sessionStorage && Site.find(sessionStorage.getItem("site"))) {
+					Site.current(Site.find(sessionStorage.getItem("site")));
+				} else {
+					Site.current(Site.list[0]);
+				}
+
+				return Site;
+			});
+
+			return _promise;
+		};
+
+		Site._promise = function() {
+			if(!_promise) {
+				Site.reload();
+			}
+			return _promise;
+		};
+
+		return Site;
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/uiSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/uiSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/uiSrv.js
new file mode 100644
index 0000000..f82c838
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/uiSrv.js
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+
+	// ===========================================================
+	// =                         Service                         =
+	// ===========================================================
+	// Feature page
+	serviceModule.service('UI', function($rootScope, $q, $compile) {
+		var UI = {};
+
+		function _fieldDialog(create, name, entity, fieldList, checkFunc) {
+			var _deferred, $mdl, $scope;
+
+			_deferred = $q.defer();
+			$scope = $rootScope.$new(true);
+			$scope.name = name;
+			$scope.entity = entity;
+			$scope.fieldList = fieldList;
+			$scope.checkFunc = checkFunc;
+			$scope.lock = false;
+			$scope.create = create;
+
+			$scope.config = typeof name === "object" ? name : {};
+
+			// Modal
+			$mdl = $(TMPL_FIELDS).appendTo('body');
+			$compile($mdl)($scope);
+			$mdl.modal();
+
+			$mdl.on("hide.bs.modal", function() {
+				_deferred.reject();
+			});
+			$mdl.on("hidden.bs.modal", function() {
+				_deferred.resolve({
+					entity: entity
+				});
+				$mdl.remove();
+			});
+
+			// Function
+			$scope.emptyFieldList = function() {
+				return $.map(fieldList, function(field) {
+					if(!field.optional && !entity[field.field]) {
+						return field.field;
+					}
+				});
+			};
+
+			$scope.confirm = function() {
+				$scope.lock = true;
+				_deferred.notify({
+					entity: entity,
+					closeFunc: function() {
+						$mdl.modal('hide');
+					},
+					unlock: function() {
+						$scope.lock = false;
+					}
+				});
+			};
+
+			return _deferred.promise;
+		}
+
+		/***
+		 * Create a creation confirm modal.
+		 * @param name			Name title
+		 * @param entity		bind entity
+		 * @param fieldList	Array. Format: {name, field, type(optional: blob), rows(optional: number),
description(optional), optional(optional), readonly(optional)}
+		 * @param checkFunc	Check logic function. Return string will prevent access
+		 */
+		UI.createConfirm = function(name, entity, fieldList, checkFunc) {
+			return _fieldDialog(true, name, entity, fieldList, checkFunc);
+		};
+
+		/***
+		 * Create a update confirm modal.
+		 * @param name			Name title
+		 * @param entity		bind entity
+		 * @param fieldList	Array. Format: {name, field, type(optional: blob), rows(optional: number),
description(optional), optional(optional), readonly(optional)}
+		 * @param checkFunc	Check logic function. Return string will prevent access
+		 */
+		UI.updateConfirm = function(name, entity, fieldList, checkFunc) {
+			return _fieldDialog(false, name, entity, fieldList, checkFunc);
+		};
+
+		/***
+		 * Create a customize field confirm modal.
+		 * @param config		Configuration object
+		 * 			@param config.title			Title of dialog box
+		 * 			@param config.size				"large". Set dialog size
+		 * 			@param config.confirm			Boolean. Display or not confirm button
+		 * 			@param config.confirmDesc		Confirm button display description
+		 * @param entity		bind entity
+		 * @param fieldList	Array. Format: {name, field, type(optional: blob), rows(optional: number),
description(optional), optional(optional), readonly(optional)}
+		 * @param checkFunc	Check logic function. Return string will prevent access
+		 */
+		UI.fieldConfirm = function(config, entity, fieldList, checkFunc) {
+			return _fieldDialog("field", config, entity, fieldList, checkFunc);
+		};
+
+		UI.deleteConfirm = function(name) {
+			var _deferred, $mdl, $scope;
+
+			_deferred = $q.defer();
+			$scope = $rootScope.$new(true);
+			$scope.name = name;
+			$scope.lock = false;
+
+			// Modal
+			$mdl = $(TMPL_DELETE).appendTo('body');
+			$compile($mdl)($scope);
+			$mdl.modal();
+
+			$mdl.on("hide.bs.modal", function() {
+				_deferred.reject();
+			});
+			$mdl.on("hidden.bs.modal", function() {
+				_deferred.resolve({
+					name: name
+				});
+				$mdl.remove();
+			});
+
+			// Function
+			$scope.delete = function() {
+				$scope.lock = true;
+				_deferred.notify({
+					name: name,
+					closeFunc: function() {
+						$mdl.modal('hide');
+					},
+					unlock: function() {
+						$scope.lock = false;
+					}
+				});
+			};
+
+			return _deferred.promise;
+		};
+
+		return UI;
+	});
+
+	// ===========================================================
+	// =                         Template                        =
+	// ===========================================================
+	var TMPL_FIELDS =
+		'<div class="modal fade" tabindex="-1" role="dialog">' +
+			'<div class="modal-dialog" ng-class="{\'modal-lg\': config.size === \'large\'}" role="document">'
+
+				'<div class="modal-content">' +
+					'<div class="modal-header">' +
+						'<button type="button" class="close" data-dismiss="modal" aria-label="Close">'
+
+							'<span aria-hidden="true">&times;</span>' +
+						'</button>' +
+						'<h4 class="modal-title">{{config.title || (create ? "New" : "Update") + " "
+ name}}</h4>' +
+					'</div>' +
+					'<div class="modal-body">' +
+						'<div class="form-group" ng-repeat="field in fieldList" ng-switch="field.type">'
+
+							'<label for="featureName">' +
+								'<span ng-if="!field.optional">*</span> ' +
+								'{{field.name || field.field}}' +
+							'</label>' +
+							'<textarea class="form-control" placeholder="{{field.description || field.name
|| field.field + \'...\'}}" ng-model="entity[field.field]" rows="{{ field.rows || 10 }}" ng-readonly="field.readonly"
ng-disabled="lock" ng-switch-when="blob"></textarea>' +
+							'<input type="text" class="form-control" placeholder="{{field.description || field.name
|| field.field + \'...\'}}" ng-model="entity[field.field]" ng-readonly="field.readonly" ng-disabled="lock"
ng-switch-default>' +
+						'</div>' +
+					'</div>' +
+					'<div class="modal-footer">' +
+						'<p class="pull-left text-danger">{{checkFunc(entity)}}</p>' +
+						'<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="lock">Close</button>'
+
+						'<button type="button" class="btn btn-primary" ng-click="confirm()" ng-disabled="checkFunc(entity)
|| emptyFieldList().length || lock" ng-if="config.confirm !== false">' +
+							'{{config.confirmDesc || (create ? "Create" : "Update")}}' +
+						'</button>' +
+					'</div>' +
+				'</div>' +
+			'</div>' +
+		'</div>';
+
+	var TMPL_DELETE =
+		'<div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">' +
+			'<div class="modal-dialog">' +
+				'<div class="modal-content">' +
+					'<div class="modal-header">' +
+						'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>'
+
+						'<h4 class="modal-title">Delete Confirm</h4></div>' +
+						'<div class="modal-body">' +
+							'<span class="text-red fa fa-exclamation-triangle pull-left" style="font-size:
50px;"></span>' +
+							'<p>You are <strong class="text-red">DELETING</strong> \'{{name}}\'!</p>'
+
+							'<p>Proceed to delete?</p>' +
+						'</div>' +
+						'<div class="modal-footer">' +
+							'<button type="button" class="btn btn-danger" ng-click="delete()" ng-disabled="lock">Delete</button>'
+
+							'<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="lock">Cancel</button>'
+
+						'</div>' +
+				'</div>' +
+			'</div>' +
+		'</div>';
+})();

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/js/srv/wrapStateSrv.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/srv/wrapStateSrv.js b/eagle-webservice/src/main/webapp/app/public/js/srv/wrapStateSrv.js
new file mode 100644
index 0000000..57872b2
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/js/srv/wrapStateSrv.js
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var serviceModule = angular.module('eagle.service');
+	serviceModule.service('$wrapState', function($state, $location, $stateParams) {
+		var $wrapState = {};
+		var _targetState = null;
+		var _targetPriority = 0;
+
+		// Go
+		$wrapState.go = function(state, param, priority) {
+			setTimeout(function() {
+				_targetState = null;
+				_targetPriority = 0;
+			});
+
+			if(typeof param !== "object") {
+				param = {};
+				priority = param;
+			}
+
+			priority = priority === true ? 1 : (priority || 0);
+			if(_targetPriority > priority) {
+				console.log("[Wrap State] Go - low priority:", state, "(Skip)");
+				return false;
+			}
+
+			if(_targetState !== state || priority) {
+				if($state.current && $state.current.name === state && angular.equals($state.params,
param)) {
+					console.log($state);
+					console.log("[Wrap State] Go reload.");
+					$state.reload();
+				} else {
+					console.log("[Wrap State] Go:", state, param, priority);
+					$state.go(state, param);
+				}
+				_targetState = state;
+				_targetPriority = priority;
+				return true;
+			} else {
+				console.log("[Wrap State] Go:", state, "(Ignored)");
+			}
+			return false;
+		};
+
+		// Reload
+		$wrapState.reload = function() {
+			console.log("[Wrap State] Do reload.");
+			$state.reload();
+		};
+
+		// Path
+		$wrapState.path = function(path) {
+			if(path !== undefined) {
+				console.log("[Wrap State][Deprecated] Switch path:", path);
+			}
+			return $location.path(path);
+		};
+
+		// URL
+		$wrapState.url = function(url) {
+			if(url !== undefined) console.log("[Wrap State] Switch url:", url);
+			return $location.url(url);
+		};
+
+		Object.defineProperties($wrapState, {
+			// Origin $state
+			origin: {
+				get: function() {
+					return $state;
+				}
+			},
+
+			// Current
+			current: {
+				get: function() {
+					return $state.current;
+				}
+			},
+
+			// Parameter
+			param: {
+				get: function() {
+					return $.extend({}, $location.search(), $stateParams);
+				}
+			}
+		});
+
+		return $wrapState;
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/grunt.json
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/grunt.json b/eagle-webservice/src/main/webapp/grunt.json
index 0b8ad5a..c921862 100644
--- a/eagle-webservice/src/main/webapp/grunt.json
+++ b/eagle-webservice/src/main/webapp/grunt.json
@@ -15,8 +15,9 @@
 				"node_modules/angular/angular.min.js",
 				"node_modules/angular-resource/angular-resource.min.js",
 				"node_modules/angular-route/angular-route.min.js",
-				"node_modules/angular-cookies/angular-cookies.min.js",
+				"node_modules/angular-animate/angular-animate.min.js",
 				"node_modules/angular-ui-bootstrap/ui-bootstrap-tpls.min.js",
+				"node_modules/angular-ui-router/release/angular-ui-router.min.js",
 				"node_modules/d3/d3.min.js",
 				"node_modules/zombiej-nvd3/build/nv.d3.min.js",
 
@@ -28,8 +29,8 @@
 			"src": [
 				"node_modules/bootstrap/dist/css/bootstrap.min.css",
 				"node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.min.css",
-				"node_modules/font-awesome/css/font-awesome.min.css",
 				"node_modules/zombiej-nvd3/build/nv.d3.min.css",
+				"node_modules/font-awesome/css/font-awesome.min.css",
 				"node_modules/admin-lte/dist/css/AdminLTE.min.css",
 				"node_modules/admin-lte/dist/css/skins/skin-blue.min.css",
 

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/package.json
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/package.json b/eagle-webservice/src/main/webapp/package.json
index 713b93c..1e6f8d4 100644
--- a/eagle-webservice/src/main/webapp/package.json
+++ b/eagle-webservice/src/main/webapp/package.json
@@ -20,10 +20,11 @@
 		"angular-cookies"		: "1.4.7",
 		"angular-animate"		: "1.4.7",
 		"angular-ui-bootstrap"	: "0.14.3",
-		"d3"					: "~3.5.10",
-		"zombiej-nvd3"			: "~1.8.1-1",
-		"jquery-slimscroll": "1.3.6",
-		"zombiej-bootstrap-components"		: "~1.1.1"
+		"angular-ui-router"		: "~0.2.17",
+		"d3"					: "3.5.14",
+		"zombiej-nvd3"			: "1.8.1-1",
+		"jquery-slimscroll"		:"1.3.6",
+		"zombiej-bootstrap-components"		: "1.1.1"
 	},
 
 	"devDependencies": {



Mime
View raw message