You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by ji...@apache.org on 2016/09/28 05:38:42 UTC
[01/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Repository: incubator-eagle
Updated Branches:
refs/heads/master 1fa490e0c -> afb897940
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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
deleted file mode 100644
index dad9f6d..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/authorizationSrv.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(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;
- };
-
- // Call web service to keep session
- setInterval(function() {
- if(!content.isLogin) return;
-
- $http.get(app.getURL('userProfile')).then(null, function (response) {
- if(response.status === 403) {
- console.log("[Session] Out of date...", response);
- content.needLogin();
- }
- });
- }, 1000 * 60 * 5);
-
- return content;
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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
deleted file mode 100644
index b725054..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/entitiesSrv.js
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(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 _additionalCondition = _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(_additionalCondition._duration) {
- _endTime = app.time.now();
- _startTime = _endTime.clone().subtract(_additionalCondition._duration, "ms");
-
- // Debug usage. Extend more time duration for end time
- if(_additionalCondition.__ETD) {
- _endTime.add(_additionalCondition.__ETD, "ms");
- }
-
- _additionalCondition._startTime = _startTime;
- _additionalCondition._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(_additionalCondition._startTime && _additionalCondition._endTime) {
- _startTimeStr = _additionalCondition._startTime.format("YYYY-MM-DD HH:mm:ss");
- _endTimeStr = _additionalCondition._endTime.clone().add(1, "s").format("YYYY-MM-DD HH:mm:ss");
-
- _url += "&startTime=" + _startTimeStr + "&endTime=" + _endTimeStr;
- }
-
- // > Query contains metric name
- if(_additionalCondition._metricName) {
- _url += "&metricName=" + _additionalCondition._metricName;
- }
-
- // > Customize page size
- if(_additionalCondition._pageSize) {
- _url = _url.replace(/pageSize=\d+/, "pageSize=" + _additionalCondition._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 _get(url) {
- var _list = [];
- _list._promise = $http({
- method: 'GET',
- url: url,
- headers: {
- "Content-Type": "text/plain"
- }
- }).success(function(data) {
- // console.log(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,
-
- maprfsNameToID: function(serviceName, value, site) {
- //var _url = "../rest/maprIDResolver/fNameResolver?fName="+name;
- var _url = app.getMapRNameResolverURL(serviceName, value, site);
- return _get(_url);
- },
-
- 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) || app.packageURL(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/afb89794/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
deleted file mode 100644
index 4f5a72a..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/main.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var eagleSrv = angular.module('eagle.service', []);
-
- eagleSrv.provider('ServiceError', function() {
- var errorContainer = {
- list: [],
- newError: function(err) {
- err._read = false;
- errorContainer.list.unshift(err);
- },
- showError: function(err) {
- err._read = true;
- $.dialog({
- size: "large",
- title: err.title,
- content: $("<pre>").html(err.description)
- });
- },
- clearAll: function() {
- errorContainer.list = [];
- }
- };
-
- Object.defineProperty(errorContainer, 'hasUnread', {
- get: function() {
- return !!common.array.find(false, errorContainer.list, "_read");
- }
- });
-
- this.$get = function() {
- return errorContainer;
- };
- });
-
- eagleSrv.config(function ($httpProvider, ServiceErrorProvider) {
- $httpProvider.interceptors.push(function ($q, $timeout) {
- return {
- response: function (response) {
- var data = response.data;
- if(data.exception) {
- console.log(response);
- ServiceErrorProvider.$get().newError({
- title: "Http Request Error",
- description: "URL:\n" + response.config.url + "\n\nParams:\n" + JSON.stringify(response.config.params, null, "\t") + "\n\nException:\n" + data.exception
- });
- }
- return response;
- }
- };
- });
- });
-})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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
deleted file mode 100644
index e59d8a3..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/pageSrv.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(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/afb89794/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
deleted file mode 100644
index fce64c0..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/siteSrv.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(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.properties.parse(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.group] = _site.applicationGroup[_application.group] || [];
- _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/afb89794/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
deleted file mode 100644
index 882e179..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/uiSrv.js
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var serviceModule = angular.module('eagle.service');
-
- // ===========================================================
- // = Service =
- // ===========================================================
- // Feature page
- serviceModule.service('UI', function($rootScope, $q, $compile) {
- var UI = {};
-
- function _bindShortcut($dialog) {
- $dialog.on("keydown", function (event) {
- if(event.which === 13) {
- if(!$(":focus").is("textarea")) {
- $dialog.find(".confirmBtn:enabled").click();
- }
- }
- });
- }
-
- 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.getFieldDescription = function (field) {
- if(typeof field.description === "function") {
- return field.description($scope.entity);
- }
- return field.description || ((field.name || field.field) + '...');
- };
-
- $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;
- }
- });
- };
-
- _bindShortcut($mdl);
-
- return _deferred.promise;
- }
-
- /***
- * Create a creation confirm modal.
- * @param name Name title
- * @param entity bind entity
- * @param fieldList Array. Format: {name, field, type(optional: select, blob), rows(optional: number), description(optional), optional(optional), readonly(optional), valueList(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: select, blob), rows(optional: number), description(optional), optional(optional), readonly(optional), valueList(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: select, blob), rows(optional: number), description(optional), optional(optional), readonly(optional), valueList(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">×</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="{{getFieldDescription(field)}}" ng-model="entity[field.field]" rows="{{ field.rows || 10 }}" ng-readonly="field.readonly" ng-disabled="lock" ng-switch-when="blob"></textarea>' +
- '<select class="form-control" ng-model="entity[field.field]" ng-init="entity[field.field] = entity[field.field] || field.valueList[0]" ng-switch-when="select">' +
- '<option ng-repeat="value in field.valueList">{{value}}</option>' +
- '</select>' +
- '<input type="text" class="form-control" placeholder="{{getFieldDescription(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 confirmBtn" 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">×</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/afb89794/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
deleted file mode 100644
index 57872b2..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/wrapStateSrv.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(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/afb89794/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
deleted file mode 100644
index 15f58d9..0000000
--- a/eagle-webservice/src/main/webapp/grunt.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "concat": {
- "js": {
- "options": {
- "separator": "\n"
- },
- "src": [
- "node_modules/jquery/dist/jquery.min.js",
- "node_modules/jquery-slimscroll/jquery.slimscroll.min.js",
- "node_modules/bootstrap/dist/js/bootstrap.min.js",
- "node_modules/zombiej-bootstrap-components/bootstrap-components/js/bootstrap-components.min.js",
- "node_modules/moment/min/moment-with-locales.min.js",
- "node_modules/moment-timezone/builds/moment-timezone-with-data.min.js",
- "node_modules/admin-lte/dist/js/app.min.js",
- "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-animate/angular-animate.min.js",
- "node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.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",
-
- "tmp/public/js/scripts.min.js"
- ],
- "dest": "tmp/public/js/doc.js"
- },
- "css": {
- "src": [
- "node_modules/bootstrap/dist/css/bootstrap.min.css",
- "node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.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",
-
- "tmp/public/css/styles.css"
- ],
- "dest": "tmp/public/css/styles.min.css"
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/index.html b/eagle-webservice/src/main/webapp/index.html
deleted file mode 100755
index 831f3f0..0000000
--- a/eagle-webservice/src/main/webapp/index.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
- -->
-
-<html>
- <head>
- <script>
- window.location.href = "ui";
- </script>
- </head>
- <body>
- </body>
-</html>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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
deleted file mode 100644
index 1db0bbb..0000000
--- a/eagle-webservice/src/main/webapp/package.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "name": "ApacheEagleWebApp",
- "description": "Apache Eagle Web Application",
- "author": "ApacheEagle",
- "repository": {
- "type:": "git",
- "url": "https://github.com/apache/incubator-eagle.git"
- },
- "license": "Apache-2.0",
- "dependencies": {
- "jquery" : "1.12.0",
- "bootstrap" : "3.3.6",
- "moment" : "2.11.2",
- "moment-timezone" : "0.5.0",
- "font-awesome" : "4.5.0",
- "admin-lte" : "2.3.2",
- "angular" : "1.5.0",
- "angular-resource" : "1.5.0",
- "angular-route" : "1.5.0",
- "angular-cookies" : "1.5.0",
- "angular-animate" : "1.5.0",
- "angular-ui-bootstrap" : "1.1.2",
- "angular-ui-router" : "~0.2.18",
- "d3" : "3.5.16",
- "zombiej-nvd3" : "1.8.2-3",
- "jquery-slimscroll" :"1.3.6",
- "zombiej-bootstrap-components" : "1.1.1"
- },
-
- "devDependencies": {
- "grunt": "~0.4.5",
- "grunt-cli": "~0.1.13",
-
- "grunt-contrib-jshint": "~0.11.3",
-
- "grunt-regex-replace": "~0.2.6",
- "grunt-contrib-clean": "~0.7.0",
- "grunt-contrib-uglify": "~0.5.0",
- "grunt-contrib-concat": "~0.5.1",
- "grunt-contrib-cssmin": "~0.14.0",
- "grunt-contrib-copy": "~0.8.2",
- "grunt-htmlrefs": "~0.5.0"
- },
-
- "scripts": {
- "grunt": "grunt"
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/ui-build.sh
----------------------------------------------------------------------
diff --git a/eagle-webservice/ui-build.sh b/eagle-webservice/ui-build.sh
index 9e4f75c..4ca13eb 100644
--- a/eagle-webservice/ui-build.sh
+++ b/eagle-webservice/ui-build.sh
@@ -16,25 +16,4 @@
# limitations under the License.
echo "=============== Web APP Building Start ==============="
-echo "Environment Check..."
-# Pre-build check
-if [ -z "$(command -v git)" ]
-then
- echo "git not installed!"
- exit 1
-fi
-if [ -z "$(command -v npm)" ]
-then
- echo "npm not installed!"
- exit 1
-fi
-echo "Environment Check...Pass"
-
-# npm install
-cd src/main/webapp
-echo "npm install..."
-npm install
-
-# grunt build
-echo "grunt building..."
-npm run grunt
+echo "Environment Check..."
\ No newline at end of file
[06/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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..dad9f6d
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/srv/authorizationSrv.js
@@ -0,0 +1,143 @@
+/*
+ * 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;
+ };
+
+ // Call web service to keep session
+ setInterval(function() {
+ if(!content.isLogin) return;
+
+ $http.get(app.getURL('userProfile')).then(null, function (response) {
+ if(response.status === 403) {
+ console.log("[Session] Out of date...", response);
+ content.needLogin();
+ }
+ });
+ }, 1000 * 60 * 5);
+
+ return content;
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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..b725054
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/srv/entitiesSrv.js
@@ -0,0 +1,301 @@
+/*
+ * 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 _additionalCondition = _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(_additionalCondition._duration) {
+ _endTime = app.time.now();
+ _startTime = _endTime.clone().subtract(_additionalCondition._duration, "ms");
+
+ // Debug usage. Extend more time duration for end time
+ if(_additionalCondition.__ETD) {
+ _endTime.add(_additionalCondition.__ETD, "ms");
+ }
+
+ _additionalCondition._startTime = _startTime;
+ _additionalCondition._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(_additionalCondition._startTime && _additionalCondition._endTime) {
+ _startTimeStr = _additionalCondition._startTime.format("YYYY-MM-DD HH:mm:ss");
+ _endTimeStr = _additionalCondition._endTime.clone().add(1, "s").format("YYYY-MM-DD HH:mm:ss");
+
+ _url += "&startTime=" + _startTimeStr + "&endTime=" + _endTimeStr;
+ }
+
+ // > Query contains metric name
+ if(_additionalCondition._metricName) {
+ _url += "&metricName=" + _additionalCondition._metricName;
+ }
+
+ // > Customize page size
+ if(_additionalCondition._pageSize) {
+ _url = _url.replace(/pageSize=\d+/, "pageSize=" + _additionalCondition._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 _get(url) {
+ var _list = [];
+ _list._promise = $http({
+ method: 'GET',
+ url: url,
+ headers: {
+ "Content-Type": "text/plain"
+ }
+ }).success(function(data) {
+ // console.log(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,
+
+ maprfsNameToID: function(serviceName, value, site) {
+ //var _url = "../rest/maprIDResolver/fNameResolver?fName="+name;
+ var _url = app.getMapRNameResolverURL(serviceName, value, site);
+ return _get(_url);
+ },
+
+ 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) || app.packageURL(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/afb89794/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..4f5a72a
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/srv/main.js
@@ -0,0 +1,72 @@
+/*
+ * 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 eagleSrv = angular.module('eagle.service', []);
+
+ eagleSrv.provider('ServiceError', function() {
+ var errorContainer = {
+ list: [],
+ newError: function(err) {
+ err._read = false;
+ errorContainer.list.unshift(err);
+ },
+ showError: function(err) {
+ err._read = true;
+ $.dialog({
+ size: "large",
+ title: err.title,
+ content: $("<pre>").html(err.description)
+ });
+ },
+ clearAll: function() {
+ errorContainer.list = [];
+ }
+ };
+
+ Object.defineProperty(errorContainer, 'hasUnread', {
+ get: function() {
+ return !!common.array.find(false, errorContainer.list, "_read");
+ }
+ });
+
+ this.$get = function() {
+ return errorContainer;
+ };
+ });
+
+ eagleSrv.config(function ($httpProvider, ServiceErrorProvider) {
+ $httpProvider.interceptors.push(function ($q, $timeout) {
+ return {
+ response: function (response) {
+ var data = response.data;
+ if(data.exception) {
+ console.log(response);
+ ServiceErrorProvider.$get().newError({
+ title: "Http Request Error",
+ description: "URL:\n" + response.config.url + "\n\nParams:\n" + JSON.stringify(response.config.params, null, "\t") + "\n\nException:\n" + data.exception
+ });
+ }
+ return response;
+ }
+ };
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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/afb89794/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..fce64c0
--- /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.properties.parse(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.group] = _site.applicationGroup[_application.group] || [];
+ _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/afb89794/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..9955fac
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/srv/uiSrv.js
@@ -0,0 +1,247 @@
+/*
+ * 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';
+
+ /**
+ * Check function to check fields pass or not
+ * @callback checkFieldFunction
+ * @param {{}} entity
+ * @return {string}
+ */
+
+ var serviceModule = angular.module('eagle.service');
+
+ // ===========================================================
+ // = Service =
+ // ===========================================================
+ // Feature page
+ serviceModule.service('UI', function($rootScope, $q, $compile) {
+ var UI = {};
+
+ function _bindShortcut($dialog) {
+ $dialog.on("keydown", function (event) {
+ if(event.which === 13) {
+ if(!$(":focus").is("textarea")) {
+ $dialog.find(".confirmBtn:enabled").click();
+ }
+ }
+ });
+ }
+
+ 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.getFieldDescription = function (field) {
+ if(typeof field.description === "function") {
+ return field.description($scope.entity);
+ }
+ return field.description || ((field.name || field.field) + '...');
+ };
+
+ $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;
+ }
+ });
+ };
+
+ _bindShortcut($mdl);
+
+ return _deferred.promise;
+ }
+
+ /***
+ * Create a creation confirm modal.
+ * @param name Name title
+ * @param entity bind entity
+ * @param fieldList Array. Format: {name, field, type(optional: select, blob), rows(optional: number), description(optional), optional(optional), readonly(optional), valueList(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: select, blob), rows(optional: number), description(optional), optional(optional), readonly(optional), valueList(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 {object} config - Configuration object
+ * @param {string=} config.title - Title of dialog box
+ * @param {string=} config.size - "large". Set dialog size
+ * @param {boolean=} config.confirm - Display or not confirm button
+ * @param {string=} config.confirmDesc - Confirm button display description
+ * @param {object} entity - bind entity
+ * @param {{name:string, field:string,type:('select'|'blob'),rows:number,description:string,optional:boolean,readonly:boolean,valueList:Array}[]} fieldList - Display fields
+ * @param {checkFieldFunction=} 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">×</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="{{getFieldDescription(field)}}" ng-model="entity[field.field]" rows="{{ field.rows || 10 }}" ng-readonly="field.readonly" ng-disabled="lock" ng-switch-when="blob"></textarea>' +
+ '<select class="form-control" ng-model="entity[field.field]" ng-init="entity[field.field] = entity[field.field] || field.valueList[0]" ng-switch-when="select">' +
+ '<option ng-repeat="value in field.valueList">{{value}}</option>' +
+ '</select>' +
+ '<input type="text" class="form-control" placeholder="{{getFieldDescription(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 confirmBtn" 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">×</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/afb89794/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/afb89794/eagle-webservice/src/main/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/index.html b/eagle-webservice/src/main/webapp/app/index.html
deleted file mode 100644
index 7cd3e25..0000000
--- a/eagle-webservice/src/main/webapp/app/index.html
+++ /dev/null
@@ -1,281 +0,0 @@
-<!DOCTYPE html>
-<!--
- 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.
- -->
-
-<html ng-app="eagleApp" ng-controller="MainCtrl">
- <head>
- <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <meta charset="UTF-8">
- <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
- <link rel="shortcut icon" href="public/images/favicon.png">
-
- <title>Eagle</title>
- <link rel="shortcut icon" type="image/png" href="public/images/favicon.png">
-
- <!-- ref:css public/css/styles.min.css -->
- <link href="public/css/main.css" rel="stylesheet" type="text/css" media="screen">
- <link href="public/css/animation.css" rel="stylesheet" type="text/css" media="screen">
- <link href="../node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" media="screen">
- <link href="../node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.css" rel="stylesheet" type="text/css" media="screen">
- <link href="../node_modules/zombiej-nvd3/build/nv.d3.css" rel="stylesheet" type="text/css" />
- <link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
- <link href="../node_modules/admin-lte/dist/css/AdminLTE.css" rel="stylesheet" type="text/css" />
- <link href="../node_modules/admin-lte/dist/css/skins/skin-blue.css" rel="stylesheet" type="text/css" />
- <!-- endref -->
- </head>
- <body class="skin-blue sidebar-mini" ng-class="{'no-sidebar' : PageConfig.hideSidebar}">
- <!-- Site wrapper -->
- <div class="wrapper">
- <header class="main-header">
- <a href="#/" class="logo">
- <span class="logo-mini"><img src="public/images/favicon_white.png" /></span>
- <span class="logo-lg">Apache Eagle</span>
- </a>
- <!-- Header Navbar: style can be found in header.less -->
- <nav class="navbar navbar-static-top" role="navigation">
- <!-- Sidebar toggle button-->
- <a ng-hide="PageConfig.hideSidebar" class="sidebar-toggle" data-toggle="offcanvas" role="button">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </a>
-
- <div class="navbar-custom-menu">
- <ul class="nav navbar-nav">
- <!-- Admin error list -->
- <li class="dropdown" ng-show="ServiceError.list.length">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- <i class="fa fa-exclamation-triangle" ng-class="{blink: ServiceError.hasUnread}"></i>
- </a>
- <ul class="dropdown-menu">
- <li ng-repeat="error in ServiceError.list">
- <a ng-click="ServiceError.showError(error);">
- <span class="fa" ng-class="{'fa-envelope': !error._read, 'fa-check': error._read}"></span>
- {{error.title}}
- </a>
- </li>
- <li role="separator" class="divider"></li>
- <li>
- <a ng-click="ServiceError.clearAll();">
- <span class="fa fa-trash"></span>
- Clear All
- </a>
- </li>
- </ul>
- </li>
-
- <!-- Site -->
- <li class="dropdown" ng-show="!PageConfig.hideSite && !PageConfig.lockSite">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- <i class="fa fa-server"></i>
- {{Site.current().tags.site}}
- <i class="fa fa-caret-down"></i>
- </a>
- <ul class="dropdown-menu">
- <li ng-repeat="_site in Site.list" ng-if="_site.enabled">
- <a ng-click="Site.current(_site);">
- <span class="fa fa-database"></span> {{_site.tags.site}}
- </a>
- </li>
- </ul>
- </li>
- <li class="dropdown" ng-show="PageConfig.lockSite">
- <a>
- <i class="fa fa-server"></i>
- {{Site.current().tags.site}}
- </a>
- </li>
-
- <!-- User -->
- <li class="dropdown user user-menu" ng-hide="PageConfig.hideUser">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- <i class="fa fa-user"></i>
- {{Auth.userProfile.username}}
- </a>
- <ul class="dropdown-menu">
- <!-- User image -->
- <li class="user-header">
- <span class="img-circle">
- <span class="fa fa-user" alt="User Image"></span>
- </span>
- <p>
- {{Auth.userProfile.username}}
- <small>
- <span ng-repeat="role in Auth.userProfile.authorities">{{role.authority}} </span>
- </small>
- </p>
- </li>
- <!-- Menu Footer-->
- <li class="user-footer">
- <div class="pull-left" ng-if="Auth.isRole('ROLE_ADMIN')">
- <a href="#/config/site" class="btn btn-default btn-flat">Management</a>
- </div>
- <div class="pull-right">
- <a ng-click="logout();" class="btn btn-default btn-flat">Sign out</a>
- </div>
- </li>
- </ul>
- </li>
- </ul>
- </div>
-
- <!-- Applications -->
- <div ng-hide="PageConfig.hideApplication">
- <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#moduleMenu">
- <span class="sr-only">Toggle navigation</span>
- <span class="fa fa-map"></span>
- </button>
- <div class="collapse navbar-collapse" id="moduleMenu">
- <ul class="nav navbar-nav">
- <li ng-repeat="_grp in Site.current().applicationGroupList" ng-if="_grp.enabledList.length"
- class="dropdown" ng-class="{active: Application.current().group === _grp.name}">
- <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
- {{_grp.name}}
- </a>
- <ul class="dropdown-menu">
- <li ng-repeat="_app in _grp.enabledList">
- <a ng-click="Application.current(_app);">
- <span class="fa fa-cubes"></span> {{_app.displayName}}
- </a>
- </li>
- </ul>
- </li>
- </ul>
- </div>
- </div>
- </nav>
- </header>
-
- <!-- =============================================== -->
- <!-- Left side column. contains the side bar -->
- <aside class="main-sidebar" ng-hide="PageConfig.hideSidebar">
- <!-- side bar: style can be found in sidebar.less -->
- <section class="sidebar">
- <ul class="sidebar-menu">
- <li class="header">
- {{Application.current().group || 'Application'}} >
- {{Application.current().displayName || 'Features'}}
- </li>
- <li ng-repeat="page in PageConfig.navConfig.pageList track by $index" ng-class="getNavClass(page)" ng-show="getNavVisible(page)">
- <a href="{{page.url}}">
- <i class="fa fa-{{page.icon}}"></i> <span>{{page.title}}</span>
- </a>
- </li>
- </ul>
- </section>
- <!-- /.sidebar -->
- </aside>
-
- <!-- =============================================== -->
- <!-- Right side column. Contains the navbar and content of the page -->
- <div class="content-wrapper">
- <!-- Content Header (Page header) -->
- <section class="content-header" ng-hide="PageConfig.hideSidebar">
- <h1>
- <span class="pageTitle">{{PageConfig.pageTitle}}</span>
- <small class="pageSubTitle">{{PageConfig.pageSubTitle}}</small>
- </h1>
-
-
- <ol class="breadcrumb">
- <li ng-repeat="navPath in PageConfig.navPath">
- <a ng-href="#{{navPath.path}}">
- <span class="fa fa-home" ng-if="$first"></span>
- {{navPath.title || navPath.path}}
- </a>
- </li>
- </ol>
- </section>
-
- <!-- Main content -->
- <section class="content">
- <div id="content">
- <div ui-view></div>
- </div>
- </section><!-- /.content -->
- </div><!-- /.content-wrapper -->
-
- <footer class="main-footer">
- <div class="pull-right hidden-xs">
- <b>License</b>
- <a href="http://www.apache.org/licenses/LICENSE-2.0" class="text-muted">Apache-2.0</a>
- </div>
- <strong>
- Apache Eagle
- <a target="_blank" href="https://eagle.incubator.apache.org/">Home</a> /
- <a target="_blank" href="https://eagle.incubator.apache.org/docs/community.html">Community</a> /
- <a target="_blank" href="https://cwiki.apache.org/confluence/display/EAG/FAQ">FAQ</a>
- </strong>
- </footer>
- </div><!-- ./wrapper -->
-
- <!-- ref:js public/js/doc.js -->
- <script src="../node_modules/jquery/dist/jquery.js"></script>
- <script src="../node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>
- <script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
- <script src="../node_modules/zombiej-bootstrap-components/bootstrap-components/js/bootstrap-components.min.js"></script>
- <script src="../node_modules/moment/min/moment-with-locales.min.js"></script>
- <script src="../node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"></script>
- <script src="../node_modules/admin-lte/dist/js/app.min.js"></script>
- <script src="../node_modules/angular/angular.js"></script>
- <script src="../node_modules/angular-resource/angular-resource.js"></script>
- <script src="../node_modules/angular-route/angular-route.js"></script>
- <script src="../node_modules/angular-animate/angular-animate.js"></script>
- <script src="../node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
- <script src="../node_modules/angular-ui-router/release/angular-ui-router.js"></script>
- <script src="../node_modules/d3/d3.js"></script>
- <script src="../node_modules/zombiej-nvd3/build/nv.d3.js"></script>
-
- <!-- Application -->
- <script src="public/js/app.js" type="text/javascript" charset="utf-8"></script>
-
- <!-- Service -->
- <script src="public/js/srv/main.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/applicationSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/authorizationSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/entitiesSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/siteSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/pageSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/wrapStateSrv.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/srv/uiSrv.js" type="text/javascript" charset="utf-8"></script>
-
- <!-- Misc -->
- <script src="public/js/app.ui.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/app.time.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/app.config.js" type="text/javascript" charset="utf-8"></script>
-
- <script src="public/js/common.js" type="text/javascript" charset="utf-8"></script>
-
- <!-- Components -->
- <script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/sortTable.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/tabs.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/file.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/charts/line3d.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/nvd3.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/components/sortable.js" type="text/javascript" charset="utf-8"></script>
-
- <!-- Controllers -->
- <script src="public/js/ctrl/main.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/authController.js" type="text/javascript" charset="utf-8"></script>
- <script src="public/js/ctrl/configurationController.js" type="text/javascript" charset="utf-8"></script>
- <!-- endref -->
- </body>
-</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/partials/config/application.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/partials/config/application.html b/eagle-webservice/src/main/webapp/app/partials/config/application.html
deleted file mode 100644
index 0bf194c..0000000
--- a/eagle-webservice/src/main/webapp/app/partials/config/application.html
+++ /dev/null
@@ -1,124 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title">
- <span class="fa fa-cogs"></span>
- Configuration
- <small class="text-danger" ng-show="changed">
- <span class="label label-warning label-sm">Unsaved</span>
- </small>
- </h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <div class="row">
- <div class="col-md-3">
- <ul class="nav nav-pills nav-stacked">
- <li class="disabled"><a>Application</a></li>
- <li role="presentation" ng-repeat="_application in Application.list track by $index" ng-class="{active: application === _application}">
- <a ng-click="setApplication(_application)">
- <span class="fa fa-server"></span>
- {{_application.tags.application}}
- <span ng-if="_application.alias">({{_application.alias}})</span>
- </a>
- </li>
-
- <li>
- <a class="text-light-blue" ng-click="newApplication()" ng-disabled="_pageLock">
- <span class="fa fa-plus-square"></span>
- New Application
- </a>
- </li>
- </ul>
- </div>
-
- <div class="col-md-9">
- <a class="pull-right btn btn-danger btn-xs" ng-click="deleteApplication(application)" ng-disabled="_pageLock">
- <span class="fa fa-trash-o"></span>
- Delete Application
- </a>
-
- <!-- Title -->
- <h3 class="guideline">
- Application
- <small>{{application.tags.application}}</small>
- </h3>
- <hr/>
-
- <!-- Config -->
- <div class="form-group">
- <label for="displayName">Display Name</label>
- <input type="text" class="form-control" id="displayName" placeholder="(Optional) Display name." ng-model="applications[application.tags.application].alias">
- </div>
- <div class="form-group">
- <label for="applicationGroup">Group</label>
- <input type="text" class="form-control" id="applicationGroup" placeholder="(Optional) Group name" ng-model="applications[application.tags.application].groupName">
- </div>
- <div class="form-group">
- <label for="applicationDescription">Description</label>
- <textarea id="applicationDescription" class="form-control" placeholder="(Optional) Application description" rows="2" ng-model="applications[application.tags.application].description"></textarea>
- </div>
- <div class="form-group">
- <label for="applicationConfiguration">Configuration</label>
- <span class="text-danger">{{configCheck(applications[application.tags.application].config)}}</span>
- <textarea id="applicationConfiguration" class="form-control" placeholder="Application configuration. Feature can read this " rows="5" ng-model="applications[application.tags.application].config"></textarea>
- </div>
-
- <!-- Feature -->
- <label>* Feature</label>
- <div class="row">
- <div class="col-sm-6">
- <h1 class="text-muted text-center" ng-show="applications[application.tags.application].features.length === 0">No feature in using</h1>
- <ul class="products-list product-list-in-box fixed-height" ng-show="applications[application.tags.application].features.length !== 0">
- <li class="item" ng-repeat="feature in applications[application.tags.application].features track by $index" ng-class="{active: _feature === feature}">
- <div class="product-operation">
- <a class="fa fa-chevron-up" ng-click="moveFeature(feature, applications[application.tags.application].features, -1)"></a>
- <a class="fa fa-chevron-down" ng-click="moveFeature(feature, applications[application.tags.application].features, 1)"></a>
- </div>
- <div class="product-info">
- <a class="fa fa-times pull-right" ng-click="removeFeature(feature, applications[application.tags.application])"></a>
- <span class="product-title">{{feature}}</span>
- <span class="product-description">{{Application.featureList.set[feature].description}}</span>
- </div>
- </li>
- </ul>
- </div>
- <div class="col-sm-6">
- <ul class="products-list product-list-in-box fixed-height">
- <li class="item" ng-repeat="feature in applications[application.tags.application].optionalFeatures track by $index">
- <button class="btn btn-lg btn-primary pull-left" ng-click="addFeature(feature, applications[application.tags.application])" ng-disabled="_pageLock">
- <span class="fa fa-star-o"></span>
- </button>
- <div class="product-info">
- <span class="product-title">{{feature}}</span>
- <span class="product-description">{{Application.featureList.set[feature].description}}</span>
- </div>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- </div><!-- /.box-body -->
-
- <div class="box-footer clearfix">
- <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/partials/config/feature.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/partials/config/feature.html b/eagle-webservice/src/main/webapp/app/partials/config/feature.html
deleted file mode 100644
index 945d90b..0000000
--- a/eagle-webservice/src/main/webapp/app/partials/config/feature.html
+++ /dev/null
@@ -1,85 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title">
- <span class="fa fa-cogs"></span>
- Configuration
- <small class="text-danger" ng-show="changed">
- <span class="label label-warning label-sm">Unsaved</span>
- </small>
- </h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <div class="row">
- <div class="col-md-3">
- <ul class="nav nav-pills nav-stacked">
- <li class="disabled">
- <a>Feature</a>
- </li>
- <li role="presentation" ng-repeat="_feature in Application.featureList" ng-class="{active: feature === _feature}">
- <a ng-click="setFeature(_feature)">
- <span class="fa fa-leaf" ng-class="{'text-danger': _feature._loaded === false}" uib-tooltip="Module load failed!" tooltip-enable="_feature._loaded === false"></span>
- {{_feature.tags.feature}}
- </a>
- </li>
- <li>
- <a class="text-light-blue" ng-click="newFeature()" ng-disabled="_pageLock">
- <span class="fa fa-plus-square"></span>
- New Feature
- </a>
- </li>
- </ul>
- </div>
-
- <div class="col-md-9">
- <a class="pull-right btn btn-danger btn-xs" ng-click="deleteFeature(feature)" ng-disabled="_pageLock">
- <span class="fa fa-trash-o"></span>
- Delete Feature
- </a>
-
- <h3 class="guideline">
- <span class="fa fa-exclamation-triangle text-danger" uib-tooltip="Module load failed!" ng-show="feature._loaded === false"></span>
- {{feature.tags.feature}}
- </h3>
- <hr/>
-
- <p class="text text-muted">
- Will load the start up file <code>controller.js</code> from <code>public/feature/{{feature.tags.feature}}</code>.
- If you are developing customized feature, please reference provided feature.
- </p>
-
- <!-- Config -->
- <div class="form-group">
- <label for="featureVersion">Version</label>
- <input id="featureVersion" type="text" class="form-control" placeholder="(Optional) Feature version." ng-model="features[feature.tags.feature].version">
- </div>
- <div class="form-group">
- <label for="featureDescription">Description</label>
- <textarea id="featureDescription" class="form-control" placeholder="(Optional) Feature description." rows="10" ng-model="features[feature.tags.feature].description"></textarea>
- </div>
- </div>
- </div>
- </div><!-- /.box-body -->
-
- <div class="box-footer clearfix">
- <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/partials/config/site.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/partials/config/site.html b/eagle-webservice/src/main/webapp/app/partials/config/site.html
deleted file mode 100644
index f7d43eb..0000000
--- a/eagle-webservice/src/main/webapp/app/partials/config/site.html
+++ /dev/null
@@ -1,115 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title">
- <span class="fa fa-cogs"></span>
- Configuration
- <small class="text-danger" ng-show="changed">
- <span class="label label-warning label-sm">Unsaved</span>
- </small>
- </h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <div class="row">
- <div class="col-md-3">
- <ul class="nav nav-pills nav-stacked">
- <li class="disabled"><a>Site</a></li>
- <li role="presentation" ng-repeat="_site in Site.list track by $index" ng-class="{active: site === _site}">
- <a ng-click="setSite(_site)">
- <span class="fa fa-server"></span>
- {{_site.tags.site}}
- </a>
- </li>
-
- <li>
- <a class="text-light-blue" ng-click="newSite()" ng-disabled="_pageLock">
- <span class="fa fa-plus-square"></span>
- New Site
- </a>
- </li>
- </ul>
- </div>
-
- <div class="col-md-9">
- <a class="pull-right btn btn-danger btn-xs" ng-click="deleteSite(site)" ng-disabled="_pageLock">
- <span class="fa fa-trash-o"></span>
- Delete Site
- </a>
-
- <!-- Title -->
- <h3 class="guideline">
- Site
- <small>{{site.tags.site}}</small>
- </h3>
- <hr/>
-
- <!-- Config -->
- <div class="checkbox">
- <label>
- <input type="checkbox" ng-checked="sites[site.tags.site].enabled" ng-click="sites[site.tags.site].enabled = !sites[site.tags.site].enabled">
- <strong>Enabled</strong>
- </label>
- </div>
- <hr/>
-
- <!-- Application -->
- <label>* Application</label>
- <div class="row">
- <div class="col-sm-6">
- <h1 class="text-muted text-center" ng-show="sites[site.tags.site].applications.length === 0">No application in using</h1>
- <ul class="products-list product-list-in-box fixed-height" ng-show="sites[site.tags.site].applications.length !== 0">
- <li class="item" ng-repeat="application in sites[site.tags.site].applications track by $index" ng-class="{active: _application === application}">
- <div class="product-operation single">
- <span class="fa fa-cubes"></span>
- </div>
- <div class="product-info">
- <a class="fa fa-times pull-right" ng-click="removeApplication(application, sites[site.tags.site])"></a>
- <span class="product-title">
- <a class="fa fa-cog" ng-click="setApplication(application)"></a>
- {{application.tags.application}}
- </span>
- <span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
- </div>
- </li>
- </ul>
- </div>
- <div class="col-sm-6">
- <ul class="products-list product-list-in-box fixed-height">
- <li class="item" ng-repeat="application in sites[site.tags.site].optionalApplications track by $index">
- <button class="btn btn-lg btn-primary pull-left" ng-click="addApplication(application, sites[site.tags.site])" ng-disabled="_pageLock">
- <span class="fa fa-star-o"></span>
- </button>
- <div class="product-info">
- <span class="product-title">{{application.tags.application}}</span>
- <span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
- </div>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </div>
- </div><!-- /.box-body -->
-
- <div class="box-footer clearfix">
- <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/partials/landing.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/partials/landing.html b/eagle-webservice/src/main/webapp/app/partials/landing.html
deleted file mode 100644
index a2e0f47..0000000
--- a/eagle-webservice/src/main/webapp/app/partials/landing.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<p class="lead">
- <span ng-if="!Application.current()">Current site do not use any application.</span>
- <span ng-if="Application.current()">Current application do not install any feature.</span>
-
- <span ng-if="Auth.isRole('ROLE_ADMIN')">
- Click
- <a href="#/config/site" ng-if="!Application.current()">here</a>
- <a href="#/config/application" ng-if="Application.current()">here</a>
- to configure.
- </span>
- <span ng-if="!Auth.isRole('ROLE_ADMIN')">Please contact your admin.</span>
-</p>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/partials/login.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/partials/login.html b/eagle-webservice/src/main/webapp/app/partials/login.html
deleted file mode 100644
index 7faef42..0000000
--- a/eagle-webservice/src/main/webapp/app/partials/login.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="login-box">
- <div class="login-logo">
- <a href="#/">Apache Eagle</a>
- </div>
-
- <div class="login-box-body" ng-show="!loginSuccess">
- <p class="login-box-msg">Sign in to start your session</p>
- <div class="form-group has-feedback">
- <input type="text" class="form-control" placeholder="User Name" ng-model="username" ng-keypress="login($event)" autocomplete="off" id="username">
- <span class="glyphicon glyphicon-user form-control-feedback"></span>
- </div>
- <div class="form-group has-feedback">
- <input type="password" class="form-control" placeholder="Password" ng-model="password" ng-keypress="login($event)">
- <span class="glyphicon glyphicon-lock form-control-feedback"></span>
- </div>
- <div class="row">
- <div class="col-xs-8">
- <div class="checkbox">
- <label> <input type="checkbox" ng-checked="rememberUser" ng-click="rememberUser = !rememberUser;" /> Remember Me
- </label>
- </div>
- </div>
- <div class="col-xs-4">
- <button class="btn btn-primary btn-block btn-flat" ng-click="login($event, true)" ng-disabled="lock">Sign In</button>
- </div>
- </div>
- </div>
-
- <div class="login-box-body text-center" ng-show="loginSuccess">
- <p class="login-box-msg">Login success</p>
- <p>
- <span class="fa fa-refresh fa-spin"></span>
- Loading environment...
- </p>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/css/animation.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/css/animation.css b/eagle-webservice/src/main/webapp/app/public/css/animation.css
deleted file mode 100644
index 954bd29..0000000
--- a/eagle-webservice/src/main/webapp/app/public/css/animation.css
+++ /dev/null
@@ -1,46 +0,0 @@
-@CHARSET "UTF-8";
-/*
- * 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.
- */
-
-[ui-view].ng-enter, [ui-view].ng-leave {
- position: absolute;
- left: 0;
- right: 0;
- -webkit-transition: all .5s ease-in-out;
- -moz-transition: all .5s ease-in-out;
- -o-transition: all .5s ease-in-out;
- transition: all .3s ease-in-out;
-}
-
-[ui-view].ng-enter {
- opacity: 0;
-}
-
-[ui-view].ng-enter-active {
- opacity: 1;
-}
-
-[ui-view].ng-leave {
- opacity: 1;
- transform:translate3d(0, 0, 0);
-}
-
-[ui-view].ng-leave-active {
- opacity: 0;
- transform:translate3d(20%, 0, 0);
-}
\ No newline at end of file
[12/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/index.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/index.html b/eagle-server/src/main/webapp/app/dev/index.html
new file mode 100644
index 0000000..56850d7
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/index.html
@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<!--
+ 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.
+ -->
+
+<html ng-controller="MainCtrl">
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta charset="UTF-8">
+ <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
+ <link rel="shortcut icon" href="public/images/favicon.png">
+
+ <title>Eagle</title>
+ <link rel="shortcut icon" type="image/png" href="public/images/favicon.png">
+
+ <!-- ref:css public/css/doc.css -->
+ <link href="../node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="../node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.css" rel="stylesheet" type="text/css" media="screen">
+
+ <link href="../node_modules/zombiej-nvd3/build/nv.d3.css" rel="stylesheet" type="text/css" />
+
+ <link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
+
+ <link href="../node_modules/admin-lte/dist/css/AdminLTE.css" rel="stylesheet" type="text/css" />
+ <link href="../node_modules/admin-lte/dist/css/skins/skin-blue.css" rel="stylesheet" type="text/css" />
+
+ <link href="public/css/animation.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="public/css/sortTable.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="public/css/main.css" rel="stylesheet" type="text/css" media="screen">
+ <!-- endref -->
+ </head>
+ <body class="skin-blue sidebar-mini" ng-class="{'no-sidebar' : PageConfig.hideSidebar}">
+ <!-- Site wrapper -->
+ <div class="wrapper">
+ <header class="main-header">
+ <a href="#/" class="logo">
+ <span class="logo-mini"><img src="public/images/favicon_white.png" /></span>
+ <span class="logo-lg">Apache Eagle</span>
+ </a>
+ <!-- Header Navbar: style can be found in header.less -->
+ <nav class="navbar navbar-static-top" role="navigation">
+ <!-- Sidebar toggle button-->
+ <a ng-hide="PageConfig.hideSidebar" class="sidebar-toggle" data-toggle="offcanvas" role="button">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+
+ <div class="navbar-custom-menu">
+ <ul class="nav navbar-nav">
+ <li class="dropdown time-picker" ng-if="Time.pickerType === Time.TIME_RANGE_PICKER">
+ <a data-toggle="dropdown" aria-expanded="false">
+ <i class="fa fa-calendar"></i>
+ {{Time.format("startTime", Time.SHORT_FORMAT)}} ~ {{Time.format("endTime", Time.SHORT_FORMAT)}}
+ </a>
+ <ul class="dropdown-menu">
+ <li><a ng-click="setLastDuration(2)"><i class="fa fa-clock-o"></i>Last 2 Hours</a></li>
+ <li><a ng-click="setLastDuration(6)"><i class="fa fa-clock-o"></i>Last 6 Hours</a></li>
+ <li><a ng-click="setLastDuration(12)"><i class="fa fa-clock-o"></i>Last 12 Hours</a></li>
+ <li><a ng-click="setLastDuration(24)"><i class="fa fa-clock-o"></i>Last 24 Hours</a></li>
+ <li><a ng-click="customizeTimeRange()"><i class="fa fa-clock-o"></i>Customize</a></li>
+ </ul>
+ </li>
+ <li>
+ <a data-toggle="dropdown" aria-expanded="false">
+ <i class="glyphicon glyphicon-question-sign"></i>
+ </a>
+
+ <ul class="dropdown-menu">
+ <li><a>How to start using eagle</a></li>
+ <li><a>How to register new site</a></li>
+ <li><a>How to install application</a></li>
+ <li><a>How to manage application</a></li>
+ <li><a>How to develop application</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ </nav>
+ </header>
+
+ <!-- =============================================== -->
+ <!-- Left side column. contains the side bar -->
+ <aside class="main-sidebar" ng-hide="PageConfig.hideSidebar">
+ <!-- side bar: style can be found in sidebar.less -->
+ <section class="sidebar">
+ <ul class="sidebar-menu">
+ <li ng-repeat="portal in Portal.list track by $index" ng-class="{treeview: portal.list}">
+ <a ng-href="{{portal.path}}">
+ <i class="fa fa-{{portal.icon || 'circle-o'}}"></i>
+ <span>{{portal.name}}</span>
+ <i class="fa fa-angle-left pull-right" ng-if="portal.list"></i>
+ </a>
+ <ul class="treeview-menu" ng-if="portal.list">
+ <li ng-repeat="subPortal in portal.list track by $index" ng-class="{active: getNavClass(subPortal)}">
+ <a ng-href="{{subPortal.path}}">
+ <i class="fa fa-{{subPortal.icon || 'circle-o'}}"></i>
+ <span>{{subPortal.name}}</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </section>
+ <!-- /.sidebar -->
+ </aside>
+
+ <!-- =============================================== -->
+ <!-- Right side column. Contains the navbar and content of the page -->
+ <div class="content-wrapper">
+ <!-- Content Header (Page header) -->
+ <section class="content-header" ng-hide="PageConfig.hideTitle">
+ <h1>
+ <span class="pageTitle">{{PageConfig.title}}</span>
+ <small class="pageSubTitle">{{PageConfig.subTitle}}</small>
+ </h1>
+
+
+ <ol class="breadcrumb">
+ <li ng-repeat="navPath in PageConfig.navPath">
+ <a ng-href="#{{navPath.path}}">
+ <span class="fa fa-home" ng-if="$first"></span>
+ {{navPath.title || navPath.path}}
+ </a>
+ </li>
+ </ol>
+ </section>
+
+ <!-- Main content -->
+ <section class="content">
+ <div id="content">
+ <div ui-view></div>
+ </div>
+ </section><!-- /.content -->
+ </div><!-- /.content-wrapper -->
+
+ <footer class="main-footer">
+ <div class="pull-right hidden-xs">
+ <b>License</b>
+ <a href="http://www.apache.org/licenses/LICENSE-2.0" class="text-muted">Apache-2.0</a>
+ </div>
+ <strong>
+ Apache Eagle
+ <a target="_blank" href="https://eagle.incubator.apache.org/">Home</a> /
+ <a target="_blank" href="https://eagle.incubator.apache.org/docs/community.html">Community</a> /
+ <a target="_blank" href="https://cwiki.apache.org/confluence/display/EAG/FAQ">FAQ</a>
+ </strong>
+ </footer>
+ </div><!-- ./wrapper -->
+
+ <!-- Modal: Time Range Picker -->
+ <div class="modal fade" tabindex="-1" role="dialog" id="eagleTimeRangeMDL">
+ <div class="modal-dialog" 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">×</span></button>
+ <h4 class="modal-title">Customize Time Range</h4>
+ </div>
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-sm-6">
+ <div class="form-group">
+ <label for="eagleStartTime">Start Time</label>
+ <input type="text" class="form-control" data-container="body" data-toggle="datepicker" id="eagleStartTime">
+ </div>
+ </div>
+ <div class="col-sm-6">
+ <div class="form-group">
+ <label for="eagleEndTime">End Time</label>
+ <input type="text" class="form-control" data-container="body" data-toggle="datepicker" id="eagleEndTime" data-position="right">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ <button type="button" class="btn btn-primary" ng-click="updateTimeRange()">Change</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- ref:js public/js/modules.js -->
+ <script src="../node_modules/jquery/dist/jquery.min.js"></script>
+ <script src="../node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>
+ <script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
+ <script src="../node_modules/zombiej-bootstrap-components/bootstrap-components/js/bootstrap-components.min.js"></script>
+ <script src="../node_modules/moment/min/moment-with-locales.min.js"></script>
+ <script src="../node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"></script>
+ <script src="../node_modules/echarts/dist/echarts.min.js"></script>
+ <script src="../node_modules/admin-lte/dist/js/app.min.js"></script>
+ <script src="../node_modules/angular/angular.min.js"></script>
+ <script src="../node_modules/angular-resource/angular-resource.min.js"></script>
+ <script src="../node_modules/angular-route/angular-route.min.js"></script>
+ <script src="../node_modules/angular-animate/angular-animate.min.js"></script>
+ <script src="../node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
+ <script src="../node_modules/angular-ui-router/release/angular-ui-router.min.js"></script>
+ <!-- endref -->
+
+ <!-- ref:js public/js/doc.min.js -->
+ <!-- Worker -->
+ <script src="public/js/worker/sortTableFunc.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Application -->
+ <script src="public/js/common.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/index.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/app.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Service -->
+ <script src="public/js/services/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/timeSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/pageSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/widgetSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/wrapStateSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/entitySrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/siteSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/applicationSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/services/uiSrv.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Components -->
+ <script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/sortTable.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/chart.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/widget.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Controllers -->
+ <script src="public/js/ctrls/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrls/mainCtrl.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrls/alertCtrl.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrls/integrationCtrl.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrls/siteCtrl.js" type="text/javascript" charset="utf-8"></script>
+ <!-- endref -->
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/alert/list.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/alert/list.html b/eagle-server/src/main/webapp/app/dev/partials/alert/list.html
new file mode 100644
index 0000000..d493976
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/alert/list.html
@@ -0,0 +1,21 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ Good!
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/alert/main.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/alert/main.html b/eagle-server/src/main/webapp/app/dev/partials/alert/main.html
new file mode 100644
index 0000000..2e062a8
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/alert/main.html
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li ng-class="{active: getState() === 'alert.list'}"><a href="#/alert/">Explore Triggered Alerts</a></li>
+ <li ng-class="{active: getState() === 'alert.policyList'}"><a href="#/alert/policyList">Manage Policies</a></li>
+ <li ng-class="{active: ['alert.policyCreate', 'alert.policyEdit'].indexOf(getState()) >= 0}"><a href="#/alert/policyCreate">Define Alert Policy</a></li>
+ </ul>
+ <div class="tab-content no-padding">
+ <div ui-view></div>
+ </div>
+</div>
+
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.back.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.back.html b/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.back.html
new file mode 100644
index 0000000..3c335f7
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.back.html
@@ -0,0 +1,108 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ <ul class="timeline">
+ <!-- Base Info -->
+ <li class="time-label">
+ <span class="bg-blue">#1. Basic Information</span>
+ </li>
+ <li>
+ <span class="fa fa-file-text bg-aqua"></span>
+ <div class="timeline-item">
+ <div class="timeline-body">
+ <div class="form-group">
+ <label>Policy Name</label>
+ <input type="text" class="form-control" ng-model="policy.name" />
+ </div>
+ <div class="form-group">
+ <label>Severity</label>
+ <select class="form-control" ng-model="policy.severity">
+ <option>WARNING</option>
+ <option>CRITICAL</option>
+ <option>DANGER</option>
+ </select>
+ </div>
+ <div class="form-group">
+ <label>Description</label>
+ <textarea class="form-control" ng-model="policy.description" rows="3"></textarea>
+ </div>
+ </div>
+ </div>
+ </li>
+
+ <!-- Alert Stream -->
+ <li class="time-label">
+ <span class="bg-blue">#2. Alert Stream</span>
+ </li>
+ <li>
+ <span class="fa fa-rocket bg-aqua"></span>
+ <div class="timeline-item">
+ <div class="timeline-body">
+ <div class="form-group">
+ <label>App Integration</label>
+ <select class="form-control"></select>
+ </div>
+ <div class="form-group">
+ <label>Alert Stream</label>
+ <select class="form-control"></select>
+ </div>
+ </div>
+ </div>
+ </li>
+
+ <!-- Streaming Logic -->
+ <li class="time-label">
+ <span class="bg-blue">#3. Streaming Logic</span>
+ </li>
+ <li>
+ <span class="fa fa-trophy bg-aqua"></span>
+ <div class="timeline-item">
+ <div class="timeline-body">
+ <div class="form-group">
+ <label>Policy Type</label>
+ <select class="form-control"></select>
+ </div>
+ <div class="form-group">
+ <label>Policy Logic</label>
+ <textarea class="form-control" rows="5"></textarea>
+ </div>
+ </div>
+ </div>
+ </li>
+
+ <!-- Publication Configuration -->
+ <li class="time-label">
+ <span class="bg-blue">#4. Publication Configuration</span>
+ </li>
+ <li>
+ <span class="fa fa-envelope bg-aqua"></span>
+ <div class="timeline-item">
+ <div class="timeline-body">
+ <div class="form-group">
+ <label>Publication Type</label>
+ <select class="form-control">
+ <option>NOTIFICATION</option>
+ </select>
+ </div>
+ <a>+ New Publication</a>
+ </div>
+ </div>
+ </li>
+ </ul>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.html b/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.html
new file mode 100644
index 0000000..9a1cbe4
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/alert/policyEdit.html
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ <ul class="stepGuide">
+ <li>
+ <span class="icon bg-green">1</span>
+ <span class="title">This is the title!!!</span>
+ </li>
+ <li>
+ <span class="icon">2</span>
+ </li>
+ </ul>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/alert/policyList.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/alert/policyList.html b/eagle-server/src/main/webapp/app/dev/partials/alert/policyList.html
new file mode 100644
index 0000000..2d4703f
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/alert/policyList.html
@@ -0,0 +1,63 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-solid">
+ <div class="box-body">
+ <div sort-table="policyList" ng-show="policyList.length">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th sortpath="name" width="20%">Name</th>
+ <th sortpath="definition.type" width="70">Type</th>
+ <th>Description</th>
+ <th width="85">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <a ng-href="#/alert/policyEdit/{{item.name}}">{{item.name}}</a>
+ </td>
+ <td class="text-center"><span class="label label-primary">{{item.definition.type}}</span></td>
+ <td>{{item.description}}</td>
+ <td class="text-center">
+ <div class="btn-group btn-group-xs">
+ <button class="btn btn-default"><span class="fa fa-play"></span></button>
+ <button class="btn btn-default"><span class="fa fa-pencil"></span></button>
+ <button class="btn btn-danger" ng-click="deletePolicy(item)"><span class="fa fa-trash"></span></button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="callout callout-warning no-margin" ng-show="policyList._done && policyList.length === 0">
+ <h4>No Policy yet</h4>
+ <p>You have not create policy yet. Click <a href="#/alert/policyCreate">here</a> to create a new policy.</p>
+ </div>
+ </div>
+
+ <div class="overlay" ng-if="!policyList._done">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+
+ <div class="box-footer text-right">
+ <a href="#/alert/policyCreate" class="btn btn-primary">New Policy</a>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/home.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/home.html b/eagle-server/src/main/webapp/app/dev/partials/home.html
new file mode 100644
index 0000000..ab75b18
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/home.html
@@ -0,0 +1,60 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!--div class="row" ng-repeat="banner in bannerList track by $index">
+ <div class="col-sm-12" ng-if="banner.title">
+ <h3>{{banner.title}}</h3>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3" ng-repeat="widget in banner.list track by $index">
+ <div class="small-box {{widget.color || 'bg-aqua'}}">
+ <div class="inner">
+ <h3>{{widget.title || "Untitled"}}</h3>
+ <p>{{widget.description || "-"}}</p>
+ <p>{{widget.additionalTips || " "}}</p>
+ </div>
+ <div class="icon">
+ <i class="fa fa-question-circle"></i>
+ </div>
+ <a class="small-box-footer">More info <i class="fa fa-arrow-circle-right"></i></a>
+ </div>
+ </div>
+</div-->
+<div class="row flex">
+ <div class="col-sm-6 col-md-4 col-lg-3" ng-repeat="widget in Widget.list track by $index">
+ <div widget="widget"></div>
+ </div>
+</div>
+
+<!--div class="row flex">
+ <div class="col-md-4">
+ <div style="background: red; height: 200px;">111</div>
+ </div>
+ <div class="col-md-4">
+ <div style="background: green;">222</div>
+ <div style="background: blue;">222</div>
+ </div>
+ <div class="col-md-4">
+ <div style="background: red;">333</div>
+ </div>
+ <div class="col-md-4">
+ <div style="background: green;">444</div>
+ </div>
+ <div class="col-md-4">
+ <div style="background: red;">555</div>
+ </div>
+</div-->
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/integration/applicationList.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/integration/applicationList.html b/eagle-server/src/main/webapp/app/dev/partials/integration/applicationList.html
new file mode 100644
index 0000000..bc603a7
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/integration/applicationList.html
@@ -0,0 +1,80 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>Application</th>
+ <th>Type</th>
+ <th>Version</th>
+ <th>Path</th>
+ <th>Streams</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="app in Application.providerList track by $index">
+ <td class="text-no-break"><a ng-click="showAppDetail(app)">{{app.name}}</a></td>
+ <td>{{app.type}}</td>
+ <td class="text-no-break">{{app.version}}</td>
+ <td>{{app.viewPath}}</td>
+ <td>
+ <ul>
+ <li ng-repeat="stream in app.streams track by $index">
+ {{stream.streamId}}
+ </li>
+ </ul>
+ </td>
+ <td>{{app.description}}</td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+
+<!-- Modal: Application information -->
+<div class="modal fade" role="dialog" id="appMDL">
+ <div class="modal-dialog modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">�</span>
+ </button>
+ <h4 class="modal-title" id="myModalLabel">{{application.name}}</h4>
+ </div>
+ <div class="modal-body">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="[data-id='install']" data-toggle="tab">Install</a></li>
+ <li><a href="[data-id='uninstall']" data-toggle="tab">Uninstall</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" data-id="install">
+ <pre ng-bind-html="installHTML"></pre>
+ </div>
+ <div class="tab-pane" data-id="uninstall">
+ <pre ng-bind-html="uninstallHTML"></pre>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/integration/main.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/integration/main.html b/eagle-server/src/main/webapp/app/dev/partials/integration/main.html
new file mode 100644
index 0000000..daea22e
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/integration/main.html
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li ng-class="{active: getState().indexOf('integration.site') !== -1}"><a href="#/integration/siteList">Sites</a></li>
+ <li ng-class="{active: getState() === 'integration.applicationList'}"><a href="#/integration/applicationList">Applications</a></li>
+ <li ng-class="{active: getState() === 'integration.streamList'}"><a href="#/integration/streamList">Streams</a></li>
+ </ul>
+ <div class="tab-content no-padding">
+ <div ui-view></div>
+ </div>
+</div>
+
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/integration/site.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/integration/site.html b/eagle-server/src/main/webapp/app/dev/partials/integration/site.html
new file mode 100644
index 0000000..b391b49
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/integration/site.html
@@ -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.
+ -->
+
+<div class="box-body">
+ <p class="text-danger" ng-if="site.applicationList.length === 0">
+ <span class="fa fa-exclamation-triangle"></span> Site must install at least one application to start up.
+ </p>
+ <p class="text-warning" ng-if="site.applicationList.length !== 0 && getStartedAppCount() === 0">
+ <span class="fa fa-exclamation-triangle"></span> No application started.
+ </p>
+
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th>App</th>
+ <th width="10">Status</th>
+ <th>Version</th>
+ <th>Description</th>
+ <!--th>Quick Links</th-->
+ <th width="150">Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="app in applicationList track by $index">
+ <td><a ng-click="showAppDetail(app)">{{app.origin.name}}</a></td>
+ <td class="text-center">
+ <span class="label label-{{getAppStatusClass(app)}}" ng-if="app.installed">{{app.status}}</span>
+ <span class="label label-default" ng-if="!app.installed">UNINSTALLED</span>
+ </td>
+ <td>{{app.origin.version}}</td>
+ <td>{{app.description}}</td>
+ <!--td>TODO: ui link</td-->
+ <td class="text-center">
+ <div class="btn-group btn-group-xs" ng-if="app.installed">
+ <!--button class="btn btn-default btn-sm">Monitor</button-->
+ <button class="btn btn-default btn-sm" ng-click="startApp(app)">Start</button>
+ <button class="btn btn-default btn-sm" ng-click="stopApp(app)">Stop</button>
+ <button class="btn btn-default btn-sm" ng-click="uninstallApp(app)">Uninstall</button>
+ </div>
+ <div class="btn-group btn-group-xs" ng-if="!app.installed">
+ <button class="btn btn-primary btn-sm" ng-click="installApp(app)">Install Application</button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+
+
+
+<!-- Modal: Application information -->
+<div class="modal fade" role="dialog" id="appMDL">
+ <div class="modal-dialog modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">�</span>
+ </button>
+ <h4 class="modal-title" id="myModalLabel">{{application.name}}</h4>
+ </div>
+ <div class="modal-body">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="[data-id='install']" data-toggle="tab">Install</a></li>
+ <li><a href="[data-id='uninstall']" data-toggle="tab">Uninstall</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" data-id="install">
+ <pre ng-bind-html="installHTML"></pre>
+ </div>
+ <div class="tab-pane" data-id="uninstall">
+ <pre ng-bind-html="uninstallHTML"></pre>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/integration/siteList.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/integration/siteList.html b/eagle-server/src/main/webapp/app/dev/partials/integration/siteList.html
new file mode 100644
index 0000000..491d902
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/integration/siteList.html
@@ -0,0 +1,60 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th>Site</th>
+ <th>Description</th>
+ <th>Enabled Apps</th>
+ <th width="95">Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="site in Site.list track by $index">
+ <td>
+ <a ui-sref="integration.site({id: site.siteId})">
+ {{site.siteId}}
+ <span ng-if="site.siteName">({{site.siteName}})</span>
+ </a>
+ </td>
+ <td>{{site.description}}</td>
+ <td>
+ <span class="text-muted" ng-if="site.applicationList.length === 0">(Nothing installed...)</span>
+ <ul class="list-inline no-margin">
+ <li ng-repeat="app in site.applicationList track by $index">
+ <span class="label label-primary">
+ {{app.descriptor.name}}
+ </span>
+ </li>
+ </ul>
+ </td>
+ <td class="text-center">
+ <div class="btn-group btn-group-xs">
+ <a class="btn btn-default btn-sm" ui-sref="integration.site({id: site.siteId})">Edit</a>
+ <button class="btn btn-default btn-sm" ng-click="deleteSite(site)">Delete</button>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
+<div class="box-footer text-right">
+ <button class="btn btn-primary" ng-click="newSite()">New Site</button>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/integration/streamList.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/integration/streamList.html b/eagle-server/src/main/webapp/app/dev/partials/integration/streamList.html
new file mode 100644
index 0000000..beaf743
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/integration/streamList.html
@@ -0,0 +1,52 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box-body">
+ <div sort-table="streamList">
+ <table class="table table-bordered table-hover">
+ <thead>
+ <tr>
+ <th>Stream</th>
+ <th>Provider (App)</th>
+ <th>Site</th>
+ <th>Schema</th>
+ <th width="10">Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="label label-primary">{{item.streamId}}</span></td>
+ <td>{{item.appType}}</td>
+ <td>{{item.siteId}}</td>
+ <td>
+ <ul class="no-margin">
+ <li ng-repeat="column in item.schema.columns track by $index">
+ <strong>{{column.name}}</strong>:
+ {{column.type}}
+ </li>
+ </ul>
+ </td>
+ <td>
+ <!-- TODO:link with alert -->
+ <button class="btn btn-primary btn-sm">New Alert</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/partials/setup.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/partials/setup.html b/eagle-server/src/main/webapp/app/dev/partials/setup.html
new file mode 100644
index 0000000..64944fb
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/partials/setup.html
@@ -0,0 +1,46 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<h2>Welcome for using Apache Eagle!</h2>
+<p class="lead">
+ As the first installation, please create a <code>SITE</code>.
+ (A site is a cluster/service which you want to monitor, as a quick start, you could use <code>sandbox</code> by default)
+</p>
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">Create new site</h3>
+ </div>
+ <div class="box-body">
+ <div class="form-group">
+ <label>* Site Id</label>
+ <input class="form-control" placeholder="Site id should be unique" ng-model="siteId">
+ </div>
+ <div class="form-group">
+ <label>Display Name</label>
+ <input class="form-control" placeholder="Site display name in UI" ng-model="siteName">
+ </div>
+ <div class="form-group">
+ <label>Description</label>
+ <textarea class="form-control" placeholder="Description about current site" ng-model="description" rows="5"></textarea>
+ </div>
+ </div>
+ <div class="box-footer text-right">
+ <button class="btn btn-primary" ng-click="createSite()" ng-disabled="site === '' || lock">Next</button>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/css/animation.css
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/css/animation.css b/eagle-server/src/main/webapp/app/dev/public/css/animation.css
new file mode 100644
index 0000000..cbf4973
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/css/animation.css
@@ -0,0 +1,47 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+#content > [ui-view].ng-enter,
+#content > [ui-view].ng-leave {
+ position: absolute;
+ left: 0;
+ right: 0;
+ -webkit-transition: all .5s ease-in-out;
+ -moz-transition: all .5s ease-in-out;
+ -o-transition: all .5s ease-in-out;
+ transition: all .3s ease-in-out;
+}
+
+#content > [ui-view].ng-enter {
+ opacity: 0;
+}
+
+#content > [ui-view].ng-enter-active {
+ opacity: 1;
+}
+
+#content > [ui-view].ng-leave {
+ opacity: 1;
+ transform:translate3d(0, 0, 0);
+}
+
+#content > [ui-view].ng-leave-active {
+ opacity: 0;
+ transform:translate3d(20%, 0, 0);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/css/main.css
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/css/main.css b/eagle-server/src/main/webapp/app/dev/public/css/main.css
new file mode 100644
index 0000000..83f9b14
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/css/main.css
@@ -0,0 +1,317 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+a {
+ cursor: pointer;
+}
+
+/* ========================================================================
+ * = Side Bar =
+ * ======================================================================== */
+.sidebar-mini.sidebar-collapse .main-header .logo > .logo-mini > img {
+ max-height: 30px;
+}
+
+.main-sidebar .customize-panel {
+ padding: 10px;
+}
+
+.sidebar-collapse .main-sidebar .customize-panel {
+ display: none;
+}
+
+.main-sidebar .customize-panel .btn-group,
+.main-sidebar .customize-panel .btn-group button,
+.main-sidebar .customize-panel .btn-group .dropdown-menu {
+ width: 100%;
+}
+
+.main-sidebar .customize-panel .btn-group button {
+ padding: 5px;
+ background: #374850;
+ border: none;
+ overflow-x: hidden;
+}
+.main-sidebar .customize-panel .btn-group.open button{
+ background: #455b63;
+}
+
+.main-sidebar .customize-panel .btn-group button .caret {
+ position: absolute;
+ right: 10px;
+ top: 13px;
+}
+
+/* ========================================================================
+ * = Main =
+ * ======================================================================== */
+#content {
+ position: relative;
+}
+
+/* ========================================================================
+ * = Grid =
+ * ======================================================================== */
+
+@media (min-width: 1200px) {
+ .row.flex {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .row.flex > [class*='col-'] {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ flex-direction: column;
+ flex-wrap: nowrap;
+ }
+
+ .row.flex > [class*='col-'] > * {
+ flex: auto;
+ }
+}
+
+.row.border-split > div {
+ border: 1px solid #f4f4f4;
+}
+
+.no-padding > .row.border-split {
+ margin-left: 0;
+ margin-right: 0;
+}
+
+/* ========================================================================
+ * = Table =
+ * ======================================================================== */
+table .info-wrapper .info-detail {
+ display: none;
+}
+
+table .info-wrapper:hover .info-detail {
+ display: table-row;
+}
+
+table ul {
+ padding: 0 0 0 20px;
+}
+
+table.table pre {
+ white-space: pre-wrap;
+ margin: 0;
+}
+
+table.table pre.inline {
+ padding: 0;
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+}
+
+table.table.table-sm th,
+table.table.table-sm td {
+ padding: 3px 5px;
+ line-height: 120%;
+}
+
+/* ========================================================================
+ * = Step Guide =
+ * ======================================================================== */
+ul.stepGuide {
+ padding: 0;
+ position: relative;
+ display: inline-block;
+}
+ul.stepGuide:before {
+ display: block;
+ height: 6px;
+ background: #f4f4f4;
+ content: "";
+ position: absolute;
+ top: 12px;
+ left: 5px;
+ right: 5px;
+}
+
+ul.stepGuide li {
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+ text-align: center;
+}
+ul.stepGuide li:not(:first-child) {
+ margin-left: 15px;
+}
+
+ul.stepGuide li > .icon {
+ display: inline-block;
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ background: #f4f4f4;
+ border-radius: 100%;
+}
+
+ul.stepGuide li > .title {
+ display: block;
+}
+
+/* ========================================================================
+ * = Box =
+ * ======================================================================== */
+.box .box-title .label {
+ font-size: 12px;
+ padding: 1px 7px;
+}
+
+.small-box {
+ position: relative;
+ padding-bottom: 30px;
+}
+
+.small-box > .inner a {
+ color: #FFFFFF;
+}
+
+.small-box > .inner a:hover {
+ text-decoration: underline;
+}
+
+.small-box > .small-box-footer {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+ /* ========================================================================
+ * = Tab =
+ * ======================================================================== */
+.tab-content.keepContent > .tab-pane {
+ display: block;
+}
+
+.tab-content.keepContent > .tab-pane:not(.active) {
+ max-height: 0;
+ overflow: hidden;
+}
+
+/* ========================================================================
+ * = Modal =
+ * ======================================================================== */
+.modal .nav-tabs li {
+ border-top: 3px solid transparent;
+}
+
+.modal .nav-tabs li.active {
+ border-top-color: #3c8dbc;
+}
+
+.modal .nav-tabs li a {
+ padding: 8px 15px;
+ border-radius: 0;
+ margin: 0;
+}
+
+.modal .nav-tabs li a,
+.modal .nav-tabs li.active a,
+.modal .nav-tabs li:hover a {
+ border-top: 0;
+}
+
+.modal .tab-content {
+ padding-top: 10px;
+}
+
+/* ========================================================================
+ * = Call out =
+ * ======================================================================== */
+.callout p,
+.callout span {
+ color: #FFF;
+}
+
+/* ========================================================================
+ * = list =
+ * ======================================================================== */
+.list-inline > li > .label {
+ margin-bottom: 2px;
+ display: inline-block;
+}
+
+/* ========================================================================
+ * = label =
+ * ======================================================================== */
+.label.label-sm {
+ padding: .1em .4em .2em;
+}
+
+/* ========================================================================
+ * = Timeline =
+ * ======================================================================== */
+.nav-tabs-custom .timeline li .timeline-item {
+ background: #f4f4f4;
+}
+
+/* ========================================================================
+ * = Widget =
+ * ======================================================================== */
+@media (min-width: 1200px) {
+ .row.flex div[widget] {
+ height: 100%;
+ }
+}
+
+/* ========================================================================
+ * = Misc =
+ * ======================================================================== */
+.ng-hide.ng-hide-animate.no-animate {
+ -webkit-transition: none !important;
+ transition: none !important;
+}
+
+.ng-hide.ng-hide-animate.no-animate {
+ display: none;
+}
+
+.text-break {
+ word-break:break-all;
+}
+
+.text-no-break {
+ white-space: nowrap;
+}
+
+.no-select {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.bsc-datepicker {
+ z-index: 2000;
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/css/sortTable.css
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/css/sortTable.css b/eagle-server/src/main/webapp/app/dev/public/css/sortTable.css
new file mode 100644
index 0000000..529eb1a
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/css/sortTable.css
@@ -0,0 +1,61 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+[sort-table] .tool-container .search-box {
+ margin: 0 0 10px 0;
+ position: relative;
+ max-width: 250px;
+ float: left;
+}
+
+[sort-table] .tool-container .search-box input {
+ padding-left: 28px;
+}
+
+[sort-table] .tool-container .search-box .fa-search {
+ pointer-events:none;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ opacity: 0.5;
+}
+
+[sort-table] .tool-container .page-size {
+ float: right;
+}
+
+[sort-table] .tool-container .page-size select {
+ width: initial;
+ display: inline-block;
+ margin: 0 7px;
+ height: 26px;
+ padding: 0 5px;
+}
+
+[sort-table] .fa.sort-mark {
+ float: right;
+ margin-top: 3px;
+ pointer-events: none;
+ color: #AAA;
+}
+
+[sort-table] .navigation-bar .pagination {
+ float: right;
+ margin-top: 0;
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/images/favicon.png
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/images/favicon.png b/eagle-server/src/main/webapp/app/dev/public/images/favicon.png
new file mode 100644
index 0000000..3bede2a
Binary files /dev/null and b/eagle-server/src/main/webapp/app/dev/public/images/favicon.png differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/images/favicon_white.png
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/images/favicon_white.png b/eagle-server/src/main/webapp/app/dev/public/images/favicon_white.png
new file mode 100644
index 0000000..9879e92
Binary files /dev/null and b/eagle-server/src/main/webapp/app/dev/public/images/favicon_white.png differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/app.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/app.js b/eagle-server/src/main/webapp/app/dev/public/js/app.js
new file mode 100644
index 0000000..4ec77d9
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/app.js
@@ -0,0 +1,311 @@
+/*
+ * 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 = {};
+
+(function() {
+ 'use strict';
+
+ $(document).on("APPLICATION_READY", function (event, register) {
+ console.info("[Eagle] Angular bootstrap...");
+
+ var STATE_NAME_MATCH = /^[^.]*/;
+ var state_next;
+ var state_current;
+ var param_next;
+ var param_current;
+
+ // ======================================================================================
+ // = Initialization =
+ // ======================================================================================
+ var eagleApp = angular.module('eagleApp', ['ngRoute', 'ngAnimate', 'ui.router', 'eagleControllers', 'eagle.service'].concat(register.appList));
+
+ // ======================================================================================
+ // = Router config =
+ // ======================================================================================
+ function routeResolve(config) {
+ var resolve = {};
+ if(config === false) return resolve;
+
+ config = $.extend({
+ auth: true,
+ site: true,
+ application: true
+ }, config);
+
+ if(config.auth) {
+ // TODO: need auth module
+ }
+
+ resolve.Site = function (Site) {
+ return Site.getPromise(config);
+ };
+
+ resolve.Application = function (Application) {
+ return Application.getPromise();
+ };
+
+ resolve.Time = function (Time) {
+ return Time.getPromise(config, state_next, param_next);
+ };
+
+ return resolve;
+ }
+
+ eagleApp.config(function ($stateProvider, $urlRouterProvider, $httpProvider, $animateProvider) {
+ $urlRouterProvider.otherwise("/");
+ $stateProvider
+ // ================================== Home ==================================
+ .state('home', {
+ url: "/",
+ templateUrl: "partials/home.html?_=" + window._TRS(),
+ controller: "homeCtrl",
+ resolve: routeResolve()
+ })
+ .state('setup', {
+ url: "/setup",
+ templateUrl: "partials/setup.html?_=" + window._TRS(),
+ controller: "setupCtrl",
+ resolve: routeResolve({ site: false, application: false })
+ })
+ // ================================= Alerts =================================
+ .state('alert', {
+ abstract: true,
+ url: "/alert/",
+ templateUrl: "partials/alert/main.html?_=" + window._TRS(),
+ controller: "alertCtrl",
+ resolve: routeResolve(false)
+ })
+ .state('alert.list', {
+ url: "",
+ templateUrl: "partials/alert/list.html?_=" + window._TRS(),
+ controller: "alertListCtrl",
+ resolve: routeResolve()
+ })
+ .state('alert.policyList', {
+ url: "policyList",
+ templateUrl: "partials/alert/policyList.html?_=" + window._TRS(),
+ controller: "policyListCtrl",
+ resolve: routeResolve()
+ })
+ .state('alert.policyCreate', {
+ url: "policyCreate",
+ templateUrl: "partials/alert/policyEdit.html?_=" + window._TRS(),
+ controller: "policyCreateCtrl",
+ resolve: routeResolve()
+ })
+ .state('alert.policyEdit', {
+ url: "policyEdit/{name}",
+ templateUrl: "partials/alert/policyEdit.html?_=" + window._TRS(),
+ controller: "policyEditCtrl",
+ resolve: routeResolve()
+ })
+ // =============================== Integration ==============================
+ .state('integration', {
+ abstract: true,
+ url: "/integration/",
+ templateUrl: "partials/integration/main.html?_=" + window._TRS(),
+ controller: "integrationCtrl",
+ resolve: routeResolve(false)
+ })
+ .state('integration.siteList', {
+ url: "siteList",
+ templateUrl: "partials/integration/siteList.html?_=" + window._TRS(),
+ controller: "integrationSiteListCtrl",
+ resolve: routeResolve({ application: false })
+ })
+ .state('integration.site', {
+ url: "site/:id",
+ templateUrl: "partials/integration/site.html?_=" + window._TRS(),
+ controller: "integrationSiteCtrl",
+ resolve: routeResolve({ application: false })
+ })
+ .state('integration.applicationList', {
+ url: "applicationList",
+ templateUrl: "partials/integration/applicationList.html?_=" + window._TRS(),
+ controller: "integrationApplicationListCtrl",
+ resolve: routeResolve({ application: false })
+ })
+ .state('integration.streamList', {
+ url: "streamList",
+ templateUrl: "partials/integration/streamList.html?_=" + window._TRS(),
+ controller: "integrationStreamListCtrl",
+ resolve: routeResolve()
+ })
+ // ================================== Site ==================================
+ .state('site', {
+ url: "/site/:siteId",
+ templateUrl: "partials/home.html?_=" + window._TRS(),
+ controller: "siteCtrl",
+ resolve: routeResolve()
+ })
+ ;
+
+ // =========================== Application States ===========================
+ $.each(register.routeList, function (i, route) {
+ var config = $.extend({}, route.config);
+
+ var resolve = {};
+ var resolveConfig = {};
+ if(route.config.resolve) {
+ $.each(route.config.resolve, function (key, value) {
+ if (typeof value === "function") {
+ resolve[key] = value;
+ } else {
+ resolveConfig[key] = value;
+ }
+ });
+ }
+ config.resolve = $.extend(routeResolve(resolveConfig), resolve);
+
+ $stateProvider.state(route.state, config);
+ });
+
+ $httpProvider.interceptors.push(function($q) {
+ function eagleRequestHandle(res) {
+ var data = res.data || {
+ exception: "",
+ message: ""
+ };
+ if(res.status === -1) {
+ $.dialog({
+ title: "AJAX Failed",
+ content: $("<pre>")
+ .text("url:\n" + common.getValueByPath(res, ["config", "url"]))
+ });
+ } else if(data.success === false || res.status === 404) {
+ $.dialog({
+ title: "AJAX Error",
+ content: $("<pre>")
+ .text(
+ "url:\n" + common.getValueByPath(res, ["config", "url"]) + "\n\n" +
+ "status:\n" + res.status + "\n\n" +
+ "exception:\n" + data.exception + "\n\n" +
+ "message:\n" + data.message
+ )
+ });
+ }
+ return res;
+ }
+
+ return {
+ response: eagleRequestHandle,
+ responseError: function(res) {
+ return $q.reject(eagleRequestHandle(res));
+ }
+ };
+ });
+ });
+
+ // ======================================================================================
+ // = Main Controller =
+ // ======================================================================================
+ eagleApp.controller('MainCtrl', function ($scope, $wrapState, $urlRouter, PageConfig, Portal, Widget, Entity, Site, Application, UI, Time) {
+ window._WrapState = $scope.$wrapState = $wrapState;
+ window._PageConfig = $scope.PageConfig = PageConfig;
+ window._Portal = $scope.Portal = Portal;
+ window._Widget = $scope.Widget = Widget;
+ window._Entity = $scope.Entity = Entity;
+ window._Site = $scope.Site = Site;
+ window._Application = $scope.Application = Application;
+ window._UI = $scope.UI = UI;
+ window._Time = $scope.Time = Time;
+ $scope.common = common;
+
+ Object.defineProperty(window, "scope", {
+ get: function () {
+ return angular.element("#content .ng-scope").scope();
+ }
+ });
+
+ // ============================== Route Update ==============================
+ $scope.$on('$stateChangeStart', function (event, next, nextParam, current, currentParam) {
+ console.log("[Switch] current ->", current, currentParam);
+ console.log("[Switch] next ->", next, nextParam);
+
+ state_next = next || {};
+ state_current = current || {};
+ param_next = nextParam;
+ param_current = currentParam;
+
+ var currentName = (current || {}).name || "";
+ var nextName = (next || {}).name || "";
+
+ // Page initialization
+ if(currentName.match(STATE_NAME_MATCH)[0] !== nextName.match(STATE_NAME_MATCH)[0]) {
+ PageConfig.reset();
+ }
+ });
+
+ // ================================ Function ================================
+ // Get side bar navigation item class
+ $scope.getNavClass = function (portal) {
+ var path = (portal.path || "").replace(/^#/, '');
+
+ if ($wrapState.path() === path) {
+ return "active";
+ } else {
+ return "";
+ }
+ };
+
+ // Customize time range
+ $scope.customizeTimeRange = function () {
+ $("#eagleStartTime").val(Time.format("startTime"));
+ $("#eagleEndTime").val(Time.format("endTime"));
+ $("#eagleTimeRangeMDL").modal();
+ };
+
+ $scope.setLastDuration = function (hours) {
+ var endTime = new Time();
+ var startTime = endTime.clone().subtract(hours, "hours");
+ Time.timeRange(startTime, endTime);
+ };
+
+ $scope.updateTimeRange = function () {
+ var startTime = Time.verifyTime($("#eagleStartTime").val());
+ var endTime = Time.verifyTime($("#eagleEndTime").val());
+ if(startTime && endTime) {
+ Time.timeRange(startTime, endTime);
+ $("#eagleTimeRangeMDL").modal("hide");
+ } else {
+ alert("Time range not validate");
+ }
+ };
+
+ // ================================== Init ==================================
+ $.each(register.portalList, function (i, config) {
+ Portal.register(config.portal, config.isSite);
+ });
+
+ $.each(register.widgetList, function (i, config) {
+ Widget.register(config.widget, config.isSite);
+ });
+ });
+
+ // ======================================================================================
+ // = Bootstrap =
+ // ======================================================================================
+ //noinspection JSCheckFunctionSignatures
+ angular.element(document).ready(function() {
+ console.info("[Eagle] UI start...");
+ //noinspection JSCheckFunctionSignatures
+ angular.bootstrap(document, ['eagleApp']);
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/common.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/common.js b/eagle-server/src/main/webapp/app/dev/public/js/common.js
new file mode 100644
index 0000000..9f5c4b1
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/common.js
@@ -0,0 +1,387 @@
+/*
+ * 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 scope = {};
+ if(typeof window != 'undefined') {
+ scope = window;
+ } else if(typeof self != 'undefined') {
+ scope = self;
+ }
+ var common = scope.common = {};
+
+ // ============================ Common ============================
+ common.template = function (str, list) {
+ $.each(list, function(key, value) {
+ var _regex = new RegExp("\\$\\{" + key + "\\}", "g");
+ str = str.replace(_regex, value);
+ });
+ return str;
+ };
+
+ common.getValueByPath = function (unit, path, defaultValue) {
+ if(unit === null || unit === undefined) throw "Unit can't be empty!";
+ if(path === "" || path === null || path === undefined) return unit;
+
+ if(typeof path === "string") {
+ path = path.replace(/\[(\d+)\]/g, ".$1").replace(/^\./, "").split(/\./);
+ }
+ for(var i = 0 ; i < path.length ; i += 1) {
+ unit = unit[path[i]];
+ if(unit === null || unit === undefined) {
+ unit = null;
+ break;
+ }
+ }
+ if(unit === null && defaultValue !== undefined) {
+ unit = defaultValue;
+ }
+ return unit;
+ };
+
+ common.setValueByPath = function(unit, path, value) {
+ if(!unit || typeof path !== "string" || path === "") throw "Unit or path can't be empty!";
+
+ var _inArray = false;
+ var _end = 0;
+ var _start = 0;
+ var _unit = unit;
+
+ function _nextPath(array) {
+ var _key = path.slice(_start, _end);
+ if(_inArray) {
+ _key = _key.slice(0, -1);
+ }
+ if(!_unit[_key]) {
+ if(array) {
+ _unit[_key] = [];
+ } else {
+ _unit[_key] = {};
+ }
+ }
+ _unit = _unit[_key];
+ }
+
+ for(; _end < path.length ; _end += 1) {
+ if(path[_end] === ".") {
+ _nextPath(false);
+ _start = _end + 1;
+ _inArray = false;
+ } else if(path[_end] === "[") {
+ _nextPath(true);
+ _start = _end + 1;
+ _inArray = true;
+ }
+ }
+
+ _unit[path.slice(_start, _inArray ? -1 : _end)] = value;
+
+ return unit;
+ };
+
+ common.parseJSON = function (str, defaultVal) {
+ try {
+ str = (str + "").trim();
+ if(Number(str).toString() === str) throw "Number format";
+ return JSON.parse(str);
+ } catch(err) {
+ if(defaultVal === undefined) {
+ console.warn("Can't parse JSON: " + str);
+ }
+ }
+ return defaultVal === undefined ? null : defaultVal;
+ };
+
+ common.stringify = function(json) {
+ return JSON.stringify(json, function(key, value) {
+ if(/^(_|\$)/.test(key)) return undefined;
+ return value;
+ });
+ };
+
+ common.isEmpty = function(val) {
+ if($.isArray(val)) {
+ return val.length === 0;
+ } else {
+ return val === null || val === undefined;
+ }
+ };
+
+ common.extend = function(target, origin) {
+ $.each(origin, function(key, value) {
+ if(/^(_|\$)/.test(key)) return;
+
+ target[key] = value;
+ });
+ return target;
+ };
+
+ function merge(obj1, obj2) {
+ $.each(obj2, function (key, value) {
+ var oriValue = obj1[key];
+
+ if(typeof oriValue === "object" && typeof value === "object" && !common.isEmpty(value)) {
+ merge(oriValue, value);
+ } else {
+ obj1[key] = value;
+ }
+ });
+ }
+
+ common.merge = function (mergedObj) {
+ for(var i = 1 ; i < arguments.length ; i += 1) {
+ var obj = arguments[i];
+ merge(mergedObj, obj);
+ }
+
+ return mergedObj;
+ };
+
+ // ============================ String ============================
+ common.string = {};
+ common.string.safeText = function (str) {
+ return str
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>');
+ };
+
+ common.string.capitalize = function (str) {
+ return (str + "").replace(/\b\w/g, function(match) {
+ return match.toUpperCase();
+ });
+ };
+
+ common.string.preFill = function (str, key, len) {
+ str = str + "";
+ len = len || 2;
+ while(str.length < len) {
+ str = key + str;
+ }
+ return str;
+ };
+
+ // ============================ Array =============================
+ common.array = {};
+
+ common.array.findIndex = function(val, list, path, findAll, caseSensitive) {
+ var _list = [];
+ val = caseSensitive === false ? (val + "").toUpperCase() : val;
+
+ for(var i = 0 ; i < list.length ; i += 1) {
+ var unit = list[i];
+ var _val = common.getValueByPath(unit, path);
+ _val = caseSensitive === false ? (_val + "").toUpperCase() : _val;
+
+ if(_val === val) {
+ if(!findAll) return i;
+ _list.push(i);
+ }
+ }
+
+ return findAll ? _list: -1;
+ };
+
+ common.array.find = function(val, list, path, findAll, caseSensitive) {
+ var index = common.array.findIndex(val, list, path, findAll, caseSensitive);
+
+ if(findAll) {
+ return $.map(index, function (index) {
+ return list[index];
+ });
+ } else {
+ return index === -1 ? null : list[index];
+ }
+ };
+
+ common.array.minus = function (list1, list2, path1, path2) {
+ if(arguments.length === 3) path2 = path1;
+ var list = [];
+ $.each(list1, function (i, item) {
+ var val1 = common.getValueByPath(item, path1);
+ if(!common.array.find(val1, list2, path2)) {
+ list.push(item);
+ }
+ });
+ return list;
+ };
+
+ common.array.doSort = function (list, path, asc, sortList) {
+ var sortFunc;
+ sortList = sortList || [];
+
+ if(asc !== false) {
+ sortFunc = function (obj1, obj2) {
+ var val1 = common.getValueByPath(obj1, path);
+ var val2 = common.getValueByPath(obj2, path);
+
+ var index1 = common.array.findIndex(val1, sortList);
+ var index2 = common.array.findIndex(val2, sortList);
+
+ if(index1 !== -1 && index2 === -1) {
+ return -1;
+ } else if(index1 == -1 && index2 !== -1) {
+ return 1;
+ } else if(index1 !== -1 && index2 !== -1) {
+ return index1 - index2;
+ }
+
+ if (val1 === val2) {
+ return 0;
+ } else if (val1 === null || val1 === undefined || val1 < val2) {
+ return -1;
+ }
+ return 1;
+ };
+ } else {
+ sortFunc = function (obj1, obj2) {
+ var val1 = common.getValueByPath(obj1, path);
+ var val2 = common.getValueByPath(obj2, path);
+
+ var index1 = common.array.findIndex(val1, sortList);
+ var index2 = common.array.findIndex(val2, sortList);
+
+ if(index1 !== -1 && index2 === -1) {
+ return -1;
+ } else if(index1 == -1 && index2 !== -1) {
+ return 1;
+ } else if(index1 !== -1 && index2 !== -1) {
+ return index1 - index2;
+ }
+
+ if (val1 === val2) {
+ return 0;
+ } else if (val1 === null || val1 === undefined || val1 < val2) {
+ return 1;
+ }
+ return -1;
+ };
+ }
+
+ return list.sort(sortFunc);
+ };
+
+ // =========================== Deferred ===========================
+ common.deferred = {};
+
+
+ common.deferred.all = function (deferredList) {
+ var deferred = $.Deferred();
+ var successList = [];
+ var failureList = [];
+ var hasFailure = false;
+ var rest = deferredList.length;
+ function doCheck() {
+ rest -= 1;
+ if(rest === 0) {
+ if(hasFailure) {
+ deferred.reject(failureList);
+ } else {
+ deferred.resolve(successList);
+ }
+ }
+ }
+
+ $.each(deferredList, function (i, deferred) {
+ if(deferred && deferred.then) {
+ deferred.then(function (data) {
+ successList[i] = data;
+ }, function (data) {
+ failureList[i] = data;
+ hasFailure = true;
+ }).always(doCheck);
+ } else {
+ successList[i] = deferred;
+ doCheck();
+ }
+ });
+
+ return deferred;
+ };
+
+ // ============================ Number ============================
+ common.number = {};
+
+ common.number.isNumber = function (num) {
+ return typeof num === "number" && !isNaN(num);
+ };
+
+ common.number.toFixed = function (num, fixed) {
+ if(!common.number.isNumber(num)) return "-";
+ num = Number(num);
+ return num.toFixed(fixed || 0);
+ };
+
+ common.number.format = function (num, fixed) {
+ if(!common.number.isNumber(num)) return "-";
+ return common.number.toFixed(num, fixed).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+ };
+
+ common.number.abbr = function (number, isByte, digits) {
+ digits = digits || 2;
+ var decPlaces = Math.pow(10, digits);
+ var abbrev = isByte ? ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['K', 'M', 'B', 'T', 'Q'];
+ var base = isByte ? 1024 : 1000;
+ var sign = number < 0 ? -1 : 1;
+ var unit = '';
+ number = Math.abs(number);
+
+ for(var i = abbrev.length - 1; i >= 0; i--) {
+ var size = Math.pow(base, i + 1);
+ if(size <= number) {
+ number = Math.round(number * decPlaces / size) / decPlaces;
+ if((number === base) && (i < abbrev.length - 1)) {
+ number = 1;
+ i++;
+ }
+ unit = abbrev[i];
+ break;
+ }
+ }
+ unit = unit ? unit : "";
+ return (number * sign).toFixed(digits) + unit;
+ };
+
+ common.number.compare = function (num1, num2) {
+ if(!common.number.isNumber(num1) || !common.number.isNumber(num2)) return "-";
+ if(num1 === 0) return 'N/A';
+ return (num2 - num1) / num1;
+ };
+
+ common.number.inRange = function (rangList, num) {
+ for(var i = 0 ; i < rangList.length - 1 ; i += 1) {
+ var start = rangList[i];
+ var end = rangList[i + 1];
+ if(start <= num && num < end) return i;
+ }
+ return rangList.length - 1;
+ };
+
+ common.number.sum = function (list, path) {
+ var total = 0;
+ $.each(list, function (i, obj) {
+ var value = common.getValueByPath(obj, path);
+ if(typeof value === "number" && !isNaN(value)) {
+ total += value;
+ }
+ });
+ return total;
+ };
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/components/chart.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/components/chart.js b/eagle-server/src/main/webapp/app/dev/public/js/components/chart.js
new file mode 100644
index 0000000..99f74d5
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/components/chart.js
@@ -0,0 +1,188 @@
+/*
+ * 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 eagleComponents = angular.module('eagle.components');
+
+ eagleComponents.service('Chart', function () {
+ return {
+ color: [ "#0073b7", "#dd4b39", "#00a65a", "#f39c12", "#605ca8", "#001F3F", "#39CCCC", "#D81B60", "#3c8dbc", "#f56954", "#00c0ef", "#3D9970", "#FF851B" , "#01FF70", "#F012BE"],
+ //color: ['#4285f4', '#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
+ charts: {}
+ };
+ });
+
+ eagleComponents.directive('chart', function(Chart) {
+ var charts = Chart.charts;
+
+ function chartResize() {
+ setTimeout(function () {
+ $.each(charts, function (id, chart) {
+ chart.resize();
+ });
+ }, 310);
+ }
+
+ $(window).resize(chartResize);
+ $("body").on("expanded.pushMenu collapsed.pushMenu", chartResize);
+
+ return {
+ restrict: 'AE',
+ scope: {
+ title: "@?title",
+ series: "=",
+ category: "=?category",
+ categoryFunc: "=?categoryFunc",
+ xTitle: "@?xTitle",
+ yTitle: "@?yTitle",
+
+ option: "=?option",
+
+ click: "=?ngClick",
+
+ chart: "@?chart"
+ },
+ controller: function ($scope, $element, $attrs, Time) {
+ var i;
+ var lastTooltipEvent;
+ var chart = echarts.init($element[0]);
+ charts[chart.id] = chart;
+
+ function refreshChart() {
+ var maxYAxis = 0;
+ var legendList = [];
+ var categoryList = $scope.category ? $scope.category : [];
+
+ var seriesList = $.map($scope.series || [], function (series, id) {
+ if(id === 0 && !$scope.category) {
+ //var preDate = -1;
+ categoryList = $.map(series.data, function (point) {
+ /*ivar time = new Time(point.x);
+ f(preDate !== time.date()) {
+ preDate = time.date();
+ return Time.format(point.x, "MMM.D HH:mm");
+ }*/
+ if($scope.categoryFunc) {
+ return $scope.categoryFunc(point.x);
+ }
+ return Time.format(point.x, "HH:mm");
+ });
+ }
+
+ legendList.push(series.name);
+ if(series.yAxisIndex) maxYAxis = Math.max(series.yAxisIndex, maxYAxis);
+
+ return $.extend({}, series, {
+ data: $scope.category ? series.data : $.map(series.data, function (point) {
+ return point.y;
+ })
+ });
+ });
+
+ var yAxis = [];
+ for(i = 0 ; i <= maxYAxis ; i += 1) {
+ yAxis.push({
+ name: $scope.yTitle,
+ type: "value"
+ });
+ }
+
+ var option = {
+ color: Chart.color.concat(),
+ title: [{text: $scope.title}],
+ tooltip: {trigger: 'axis'},
+ legend: [{
+ data: legendList
+ }],
+ grid: {
+ top: '30',
+ left: '0',
+ right: '0',
+ bottom: '0',
+ containLabel: true
+ },
+ xAxis: {
+ name: $scope.xTitle,
+ type: 'category',
+ data: categoryList,
+ axisTick: { show: false }
+ },
+ yAxis: yAxis,
+ series: seriesList
+ };
+
+ if($scope.option) {
+ option = common.merge(option, $scope.option);
+ }
+
+ chart.setOption(option);
+ }
+
+ // Event handle
+ var chartClick = false;
+ chart.on("click", function (e) {
+ if($scope.click) {
+ if($scope.click(e)) {
+ refreshChart();
+ }
+ }
+ chartClick = true;
+ });
+
+ chart.getZr().on('click', function () {
+ if(!chartClick && $scope.click) {
+ if($scope.click($.extend({
+ componentType: "tooltip"
+ }, lastTooltipEvent))) {
+ refreshChart();
+ }
+ }
+ chartClick = false;
+ });
+
+ chart.on('showtip', function (e) {
+ lastTooltipEvent = e;
+ });
+
+ // Insert chart object to parent scope
+ if($attrs.chart) {
+ $scope.$parent.$parent[$attrs.chart] = chart;
+ }
+
+ chart.refresh = function () {
+ refreshChart();
+ };
+
+ // Render
+ refreshChart();
+ $scope.$watch("series", refreshChart);
+
+ $scope.$on('$destroy', function() {
+ delete charts[chart.id];
+ chart.dispose();
+
+ delete $scope.$parent.$parent[$attrs.chart];
+ });
+ },
+ template: '<div>Loading...</div>',
+ replace: true
+ };
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/components/main.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/components/main.js b/eagle-server/src/main/webapp/app/dev/public/js/components/main.js
new file mode 100644
index 0000000..0c8a54c
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/components/main.js
@@ -0,0 +1,24 @@
+/*
+ * 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';
+
+ angular.module('eagle.components', []);
+})();
[08/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/metrics/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/metrics/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/metrics/controller.js
new file mode 100644
index 0000000..d717ad1
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/metrics/controller.js
@@ -0,0 +1,571 @@
+/*
+ * 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 featureControllers = angular.module('featureControllers');
+ var feature = featureControllers.register("metrics");
+
+ // ==============================================================
+ // = Initialization =
+ // ==============================================================
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+ // Format dashboard unit. Will adjust format with old version and add miss attributes.
+ feature.service("DashboardFormatter", function() {
+ return {
+ parse: function(unit) {
+ unit = unit || {};
+ unit.groups = unit.groups || [];
+
+ $.each(unit.groups, function (i, group) {
+ group.charts = group.charts || [];
+ $.each(group.charts, function (i, chart) {
+ if (!chart.metrics && chart.metric) {
+ chart.metrics = [{
+ aggregations: chart.aggregations,
+ dataSource: chart.dataSource,
+ metric: chart.metric
+ }];
+
+ delete chart.aggregations;
+ delete chart.dataSource;
+ delete chart.metric;
+ } else if (!chart.metrics) {
+ chart.metrics = [];
+ }
+ });
+ });
+
+ return unit;
+ }
+ };
+ });
+
+ // ==============================================================
+ // = Controller =
+ // ==============================================================
+
+ // ========================= Dashboard ==========================
+ feature.navItem("dashboard", "Metrics", "line-chart");
+
+ feature.controller('dashboard', function(PageConfig, $scope, $http, $q, UI, Site, Authorization, Application, Entities, DashboardFormatter) {
+ var _siteApp = Site.currentSiteApplication();
+ var _druidConfig = _siteApp.configObj.getValueByPath("web.druid");
+ var _refreshInterval;
+
+ var _menu_newChart;
+
+ $scope.lock = false;
+
+ $scope.dataSourceListReady = false;
+ $scope.dataSourceList = [];
+ $scope.dashboard = {
+ groups: []
+ };
+ $scope.dashboardEntity = null;
+ $scope.dashboardReady = false;
+
+ $scope._newMetricFilter = "";
+ $scope._newMetricDataSrc = null;
+ $scope._newMetricDataMetric = null;
+
+ $scope.tabHolder = {};
+
+ $scope.endTime = app.time.now();
+ $scope.startTime = $scope.endTime.clone();
+
+ // =================== Initialization ===================
+ if(!_druidConfig || !_druidConfig.coordinator || !_druidConfig.broker) {
+ $.dialog({
+ title: "OPS",
+ content: "Druid configuration can't be empty!"
+ });
+ return;
+ }
+
+ $scope.autoRefreshList = [
+ {title: "Last 1 Month", timeDes: "day", getStartTime: function(endTime) {return endTime.clone().subtract(1, "month");}},
+ {title: "Last 1 Day", timeDes: "thirty_minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "day");}},
+ {title: "Last 6 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(6, "hour");}},
+ {title: "Last 2 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(2, "hour");}},
+ {title: "Last 1 Hour", timeDes: "minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "hour");}}
+ ];
+ $scope.autoRefreshSelect = $scope.autoRefreshList[2];
+
+ // ====================== Function ======================
+ $scope.setAuthRefresh = function(item) {
+ $scope.autoRefreshSelect = item;
+ $scope.refreshAllChart(true);
+ };
+
+ $scope.refreshTimeDisplay = function() {
+ PageConfig.pageSubTitle = common.format.date($scope.startTime) + " ~ " + common.format.date($scope.endTime) + " [refresh interval: 30s]";
+ };
+ $scope.refreshTimeDisplay();
+
+ // ======================= Metric =======================
+ // Fetch metric data
+ $http.get(_druidConfig.coordinator + "/druid/coordinator/v1/metadata/datasources", {withCredentials: false}).then(function(data) {
+ var _endTime = new moment();
+ var _startTime = _endTime.clone().subtract(1, "day");
+ var _intervals = _startTime.toISOString() + "/" + _endTime.toISOString();
+
+ $scope.dataSourceList = $.map(data.data, function(dataSrc) {
+ return {
+ dataSource: dataSrc,
+ metricList: []
+ };
+ });
+
+ // List dataSource metrics
+ var _metrixList_promiseList = $.map($scope.dataSourceList, function(dataSrc) {
+ var _data = JSON.stringify({
+ "queryType": "groupBy",
+ "dataSource": dataSrc.dataSource,
+ "granularity": "all",
+ "dimensions": ["metric"],
+ "aggregations": [
+ {
+ "type":"count",
+ "name":"count"
+ }
+ ],
+ "intervals": [_intervals]
+ });
+
+ return $http.post(_druidConfig.broker + "/druid/v2", _data, {withCredentials: false}).then(function(response) {
+ dataSrc.metricList = $.map(response.data, function(entity) {
+ return entity.event.metric;
+ });
+ });
+ });
+
+ $q.all(_metrixList_promiseList).finally(function() {
+ $scope.dataSourceListReady = true;
+
+ $scope._newMetricDataSrc = $scope.dataSourceList[0];
+ $scope._newMetricDataMetric = common.getValueByPath($scope._newMetricDataSrc, "metricList.0");
+ });
+ }, function() {
+ $.dialog({
+ title: "OPS",
+ content: "Fetch data source failed. Please check Site Application Metrics configuration."
+ });
+ });
+
+ // Filter data source
+ $scope.dataSourceMetricList = function(dataSrc, filter) {
+ filter = (filter || "").toLowerCase().trim().split(/\s+/);
+ return $.grep((dataSrc && dataSrc.metricList) || [], function(metric) {
+ for(var i = 0 ; i < filter.length ; i += 1) {
+ if(metric.toLowerCase().indexOf(filter[i]) === -1) return false;
+ }
+ return true;
+ });
+ };
+
+ // New metric select
+ $scope.newMetricSelectDataSource = function(dataSrc) {
+ if(dataSrc !== $scope._newMetricDataMetric) $scope._newMetricDataMetric = dataSrc.metricList[0];
+ $scope._newMetricDataSrc = dataSrc;
+ };
+ $scope.newMetricSelectMetric = function(metric) {
+ $scope._newMetricDataMetric = metric;
+ };
+
+ // Confirm new metric
+ $scope.confirmSelectMetric = function() {
+ var group = $scope.tabHolder.selectedPane.data;
+ var metric = {
+ dataSource: $scope._newMetricDataSrc.dataSource,
+ metric: $scope._newMetricDataMetric,
+ aggregations: ["max"]
+ };
+ $("#metricMDL").modal('hide');
+
+ if($scope.metricForConfigChart) {
+ $scope.configPreviewChart.metrics.push(metric);
+ $scope.refreshChart($scope.configPreviewChart, true, true);
+ } else {
+ group.charts.push({
+ chart: "line",
+ metrics: [metric]
+ });
+ $scope.refreshAllChart();
+ }
+ };
+
+ // ======================== Menu ========================
+ function _checkGroupName(entity) {
+ if(common.array.find(entity.name, $scope.dashboard.groups, "name")) {
+ return "Group name conflict";
+ }
+ }
+
+ $scope.newGroup = function() {
+ if($scope.lock) return;
+
+ UI.createConfirm("Group", {}, [{field: "name"}], _checkGroupName).then(null, null, function(holder) {
+ $scope.dashboard.groups.push({
+ name: holder.entity.name,
+ charts: []
+ });
+ holder.closeFunc();
+
+ setTimeout(function() {
+ $scope.tabHolder.setSelect(holder.entity.name);
+ }, 0);
+ });
+ };
+
+ function renameGroup() {
+ var group = $scope.tabHolder.selectedPane.data;
+ UI.updateConfirm("Group", {}, [{field: "name", name: "New Name"}], _checkGroupName).then(null, null, function(holder) {
+ group.name = holder.entity.name;
+ holder.closeFunc();
+ });
+ }
+
+ function deleteGroup() {
+ var group = $scope.tabHolder.selectedPane.data;
+ UI.deleteConfirm(group.name).then(null, null, function(holder) {
+ common.array.remove(group, $scope.dashboard.groups);
+ holder.closeFunc();
+ });
+ }
+
+ _menu_newChart = {title: "Add Metric", func: function() {$scope.newChart();}};
+ Object.defineProperties(_menu_newChart, {
+ icon: {
+ get: function() {return $scope.dataSourceListReady ? 'plus' : 'refresh fa-spin';}
+ },
+ disabled: {
+ get: function() {return !$scope.dataSourceListReady;}
+ }
+ });
+
+ $scope.menu = Authorization.isRole('ROLE_ADMIN') ? [
+ {icon: "cog", title: "Configuration", list: [
+ _menu_newChart,
+ {icon: "pencil", title: "Rename Group", func: renameGroup},
+ {icon: "trash", title: "Delete Group", danger: true, func: deleteGroup}
+ ]},
+ {icon: "plus", title: "New Group", func: $scope.newGroup}
+ ] : [];
+
+ // ===================== Dashboard ======================
+ $scope.dashboardList = Entities.queryEntities("GenericResourceService", {
+ site: Site.current().tags.site,
+ application: Application.current().tags.application
+ });
+ $scope.dashboardList._promise.then(function(list) {
+ $scope.dashboardEntity = list[0];
+ $scope.dashboard = DashboardFormatter.parse(common.parseJSON($scope.dashboardEntity.value));
+ $scope.refreshAllChart();
+ }).finally(function() {
+ $scope.dashboardReady = true;
+ });
+
+ $scope.saveDashboard = function() {
+ $scope.lock = true;
+
+ if(!$scope.dashboardEntity) {
+ $scope.dashboardEntity = {
+ tags: {
+ site: Site.current().tags.site,
+ application: Application.current().tags.application,
+ name: "/metric_dashboard/dashboard/default"
+ }
+ };
+ }
+ $scope.dashboardEntity.value = common.stringify($scope.dashboard);
+
+ Entities.updateEntity("GenericResourceService", $scope.dashboardEntity)._promise.then(function() {
+ $.dialog({
+ title: "Done",
+ content: "Save success!"
+ });
+ }, function() {
+ $.dialog({
+ title: "POS",
+ content: "Save failed. Please retry."
+ });
+ }).finally(function() {
+ $scope.lock = false;
+ });
+ };
+
+ // ======================= Chart ========================
+ $scope.configTargetChart = null;
+ $scope.configPreviewChart = null;
+ $scope.metricForConfigChart = false;
+ $scope.viewChart = null;
+
+ $scope.chartConfig = {
+ xType: "time"
+ };
+
+ $scope.chartTypeList = [
+ {icon: "line-chart", chart: "line"},
+ {icon: "area-chart", chart: "area"},
+ {icon: "bar-chart", chart: "column"},
+ {icon: "pie-chart", chart: "pie"}
+ ];
+
+ $scope.chartSeriesList = [
+ {name: "Min", series: "min"},
+ {name: "Max", series: "max"},
+ {name: "Avg", series: "avg"},
+ {name: "Count", series: "count"},
+ {name: "Sum", series: "sum"}
+ ];
+
+ $scope.newChart = function() {
+ $scope.metricForConfigChart = false;
+ $("#metricMDL").modal();
+ };
+
+ $scope.configPreviewChartMinimumCheck = function() {
+ $scope.configPreviewChart.min = $scope.configPreviewChart.min === 0 ? undefined : 0;
+ };
+
+ $scope.seriesChecked = function(metric, series) {
+ if(!metric) return false;
+ return $.inArray(series, metric.aggregations || []) !== -1;
+ };
+ $scope.seriesCheckClick = function(metric, series, chart) {
+ if(!metric || !chart) return;
+ if($scope.seriesChecked(metric, series)) {
+ common.array.remove(series, metric.aggregations);
+ } else {
+ metric.aggregations.push(series);
+ }
+ $scope.chartSeriesUpdate(chart);
+ };
+
+ $scope.chartSeriesUpdate = function(chart) {
+ chart._data = $.map(chart._oriData, function(groupData, i) {
+ var metric = chart.metrics[i];
+ return $.map(groupData, function(series) {
+ if($.inArray(series._key, metric.aggregations) !== -1) return series;
+ });
+ });
+ };
+
+ $scope.configAddMetric = function() {
+ $scope.metricForConfigChart = true;
+ $("#metricMDL").modal();
+ };
+
+ $scope.configRemoveMetric = function(metric) {
+ common.array.remove(metric, $scope.configPreviewChart.metrics);
+ };
+
+ $scope.getChartConfig = function(chart) {
+ if(!chart) return null;
+
+ var _config = chart._config = chart._config || $.extend({}, $scope.chartConfig);
+ _config.yMin = chart.min;
+
+ return _config;
+ };
+
+ $scope.configChart = function(chart) {
+ $scope.configTargetChart = chart;
+ $scope.configPreviewChart = $.extend({}, chart);
+ $scope.configPreviewChart.metrics = $.map(chart.metrics, function(metric) {
+ return $.extend({}, metric, {aggregations: (metric.aggregations || []).slice()});
+ });
+ delete $scope.configPreviewChart._config;
+ $("#chartMDL").modal();
+ setTimeout(function() {
+ $(window).resize();
+ }, 200);
+ };
+
+ $scope.confirmUpdateChart = function() {
+ $("#chartMDL").modal('hide');
+ $.extend($scope.configTargetChart, $scope.configPreviewChart);
+ $scope.chartSeriesUpdate($scope.configTargetChart);
+ if($scope.configTargetChart._holder) $scope.configTargetChart._holder.refreshAll();
+ $scope.configPreviewChart = null;
+ };
+
+ $scope.deleteChart = function(group, chart) {
+ UI.deleteConfirm(chart.metric).then(null, null, function(holder) {
+ common.array.remove(chart, group.charts);
+ holder.closeFunc();
+ $scope.refreshAllChart(false, true);
+ });
+ };
+
+ $scope.showChart = function(chart) {
+ $scope.viewChart = chart;
+ $("#chartViewMDL").modal();
+ setTimeout(function() {
+ $(window).resize();
+ }, 200);
+ };
+
+ $scope.refreshChart = function(chart, forceRefresh, refreshAll) {
+ var _intervals = $scope.startTime.toISOString() + "/" + $scope.endTime.toISOString();
+
+ function _refreshChart() {
+ if (chart._holder) {
+ if (refreshAll) {
+ chart._holder.refreshAll();
+ } else {
+ chart._holder.refresh();
+ }
+ }
+ }
+
+ var _tmpData, _metricPromiseList;
+
+ if (chart._data && !forceRefresh) {
+ // Refresh chart without reload
+ _refreshChart();
+ } else {
+ // Refresh chart with reload
+ _tmpData = [];
+ _metricPromiseList = $.map(chart.metrics, function (metric, k) {
+ // Each Metric
+ var _query = JSON.stringify({
+ "queryType": "groupBy",
+ "dataSource": metric.dataSource,
+ "granularity": $scope.autoRefreshSelect.timeDes,
+ "dimensions": ["metric"],
+ "filter": {"type": "selector", "dimension": "metric", "value": metric.metric},
+ "aggregations": [
+ {
+ "type": "max",
+ "name": "max",
+ "fieldName": "maxValue"
+ },
+ {
+ "type": "min",
+ "name": "min",
+ "fieldName": "maxValue"
+ },
+ {
+ "type": "count",
+ "name": "count",
+ "fieldName": "maxValue"
+ },
+ {
+ "type": "longSum",
+ "name": "sum",
+ "fieldName": "maxValue"
+ }
+ ],
+ "postAggregations" : [
+ {
+ "type": "javascript",
+ "name": "avg",
+ "fieldNames": ["sum", "count"],
+ "function": "function(sum, cnt) { return sum / cnt;}"
+ }
+ ],
+ "intervals": [_intervals]
+ });
+
+ return $http.post(_druidConfig.broker + "/druid/v2", _query, {withCredentials: false}).then(function (response) {
+ var _data = nvd3.convert.druid([response.data]);
+ _tmpData[k] = _data;
+
+ // Process series name
+ $.each(_data, function(i, series) {
+ series._key = series.key;
+ if(chart.metrics.length > 1) {
+ series.key = metric.metric.replace(/^.*\./, "") + "-" +series._key;
+ }
+ });
+ });
+ });
+
+ $q.all(_metricPromiseList).then(function() {
+ chart._oriData = _tmpData;
+ $scope.chartSeriesUpdate(chart);
+ _refreshChart();
+ });
+ }
+ };
+
+ $scope.refreshAllChart = function(forceRefresh, refreshAll) {
+ setTimeout(function() {
+ $scope.endTime = app.time.now();
+ $scope.startTime = $scope.autoRefreshSelect.getStartTime($scope.endTime);
+
+ $scope.refreshTimeDisplay();
+
+ $.each($scope.dashboard.groups, function (i, group) {
+ $.each(group.charts, function (j, chart) {
+ $scope.refreshChart(chart, forceRefresh, refreshAll);
+ });
+ });
+
+ $(window).resize();
+ }, 0);
+ };
+
+ $scope.chartSwitchRefresh = function(source, target) {
+ var _oriSize = source.size;
+ source.size = target.size;
+ target.size = _oriSize;
+
+ if(source._holder) source._holder.refreshAll();
+ if(target._holder) target._holder.refreshAll();
+
+ };
+
+ _refreshInterval = setInterval(function() {
+ if(!$scope.dashboardReady) return;
+ $scope.refreshAllChart(true);
+ }, 1000 * 30);
+
+ // > Chart UI
+ $scope.configChartSize = function(chart, sizeOffset) {
+ chart.size = (chart.size || 6) + sizeOffset;
+ if(chart.size <= 0) chart.size = 1;
+ if(chart.size > 12) chart.size = 12;
+ setTimeout(function() {
+ $(window).resize();
+ }, 1);
+ };
+
+ // ========================= UI =========================
+ $("#metricMDL").on('hidden.bs.modal', function () {
+ if($(".modal-backdrop").length) {
+ $("body").addClass("modal-open");
+ }
+ });
+
+ $("#chartViewMDL").on('hidden.bs.modal', function () {
+ $scope.viewChart = null;
+ });
+
+ // ====================== Clean Up ======================
+ $scope.$on('$destroy', function() {
+ clearInterval(_refreshInterval);
+ });
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/metrics/page/dashboard.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/metrics/page/dashboard.html b/eagle-webservice/src/main/webapp/_app/public/feature/metrics/page/dashboard.html
new file mode 100644
index 0000000..2acb954
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/metrics/page/dashboard.html
@@ -0,0 +1,250 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="page-fixed">
+ <div class="dropdown inline">
+ <button data-toggle="dropdown" class="btn btn-primary">
+ <span class="fa fa-clock-o"></span> {{autoRefreshSelect.title}}
+ </button>
+ <ul class="dropdown-menu left">
+ <li ng-repeat="item in autoRefreshList track by $index">
+ <a ng-click="setAuthRefresh(item)">
+ <span class="fa fa-clock-o"></span>
+ <span ng-class="{'text-bold': item === autoRefreshSelect}">{{item.title}}</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <button class="btn btn-primary" ng-if="Auth.isRole('ROLE_ADMIN')"
+ ng-click="saveDashboard()" ng-disabled="lock">
+ <span class="fa fa-floppy-o"></span> Save
+ </button>
+</div>
+
+<div class="box box-default" ng-if="!dashboard.groups.length">
+ <div class="box-header with-border">
+ <h3 class="box-title">Empty Dashboard</h3>
+ </div>
+ <div class="box-body">
+ <div ng-show="!dashboardReady">
+ Loading...
+ </div>
+ <div ng-show="dashboardReady">
+ Current dashboard is empty.
+ <span ng-if="Auth.isRole('ROLE_ADMIN')">Click <a ng-click="newGroup()">here</a> to create a new group.</span>
+ </div>
+ </div>
+ <div class="overlay" ng-show="!dashboardReady">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+</div>
+
+<div tabs menu="menu" holder="tabHolder" ng-show="dashboard.groups.length" data-sortable-model="Auth.isRole('ROLE_ADMIN') ? dashboard.groups : null">
+ <pane ng-repeat="group in dashboard.groups" data-data="group" data-title="{{group.name}}">
+ <div uie-sortable ng-model="group.charts" class="row narrow" sortable-update-func="chartSwitchRefresh" ng-show="group.charts.length">
+ <div ng-repeat="chart in group.charts track by $index" class="col-md-{{chart.size || 6}}">
+ <div class="nvd3-chart-wrapper">
+ <div nvd3="chart._data" data-holder="chart._holder" data-title="{{chart.title || chart.metrics[0].metric}}" data-watching="false"
+ data-chart="{{chart.chart || 'line'}}" data-config="getChartConfig(chart)" class="nvd3-chart-cntr"></div>
+ <div class="nvd3-chart-config">
+ <a class="fa fa-expand" ng-click="showChart(chart, -1)"></a>
+ <span ng-if="Auth.isRole('ROLE_ADMIN')">
+ <a class="fa fa-minus" ng-click="configChartSize(chart, -1)"></a>
+ <a class="fa fa-plus" ng-click="configChartSize(chart, 1)"></a>
+ <a class="fa fa-cog" ng-click="configChart(chart)"></a>
+ <a class="fa fa-trash" ng-click="deleteChart(group, chart)"></a>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <p ng-if="!group.charts.length">
+ Empty group.
+ <span ng-if="Auth.isRole('ROLE_ADMIN')">
+ Click
+ <span ng-hide="dataSourceListReady" class="fa fa-refresh fa-spin"></span>
+ <a ng-show="dataSourceListReady" ng-click="newChart()">here</a>
+ to add metric.
+ </span>
+ </p>
+ </pane>
+</div>
+
+
+
+<!-- Modal: Chart configuration -->
+<div class="modal fade" id="chartMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" 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">×</span>
+ </button>
+ <h4 class="modal-title">Chart Configuration</h4>
+ </div>
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-md-6">
+ <div class="nvd3-chart-wrapper">
+ <div nvd3="configPreviewChart._data" data-title="{{configPreviewChart.title || configPreviewChart.metrics[0].metric}}"
+ data-watching="true" data-chart="{{configPreviewChart.chart || 'line'}}" data-config="getChartConfig(configPreviewChart)" class="nvd3-chart-cntr"></div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <!-- Chart Configuration -->
+ <table class="table">
+ <tbody>
+ <tr>
+ <th width="100">Name</th>
+ <td><input type="text" class="form-control input-xs" ng-model="configPreviewChart.title" placeholder="Default: {{configPreviewChart.metrics[0].metric}}" /></td>
+ </tr>
+ <tr>
+ <th>Chart Type</th>
+ <td>
+ <div class="btn-group" data-toggle="buttons">
+ <label class="btn btn-default btn-xs" ng-class="{active: (configPreviewChart.chart || 'line') === type.chart}"
+ ng-repeat="type in chartTypeList track by $index" ng-click="configPreviewChart.chart = type.chart;">
+ <input type="radio" name="chartType" autocomplete="off"
+ ng-checked="(configPreviewChart.chart || 'line') === type.chart">
+ <span class="fa fa-{{type.icon}}"></span>
+ </label>
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <th>Minimum</th>
+ <td><input type="checkbox" ng-checked="configPreviewChart.min === 0" ng-disabled="configPreviewChart.chart === 'area' || configPreviewChart.chart === 'pie'"
+ ng-click="configPreviewChartMinimumCheck()" /></td>
+ </tr>
+ <tr>
+ <th>Metrics</th>
+ <td>
+ <div ng-repeat="metric in configPreviewChart.metrics" class="box inner-box">
+ <div class="box-tools">
+ <button class="btn btn-box-tool" ng-click="configRemoveMetric(metric)">
+ <span class="fa fa-times"></span>
+ </button>
+ </div>
+
+ <h3 class="box-title">{{metric.metric}}</h3>
+ <div class="checkbox noMargin" ng-repeat="series in chartSeriesList track by $index">
+ <label>
+ <input type="checkbox" ng-checked="seriesChecked(metric, series.series)"
+ ng-click="seriesCheckClick(metric, series.series, configPreviewChart)" />
+ {{series.name}}
+ </label>
+ </div>
+ </div>
+ <a ng-click="configAddMetric()">+ Add Metric</a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ <button type="button" class="btn btn-primary" ng-click="confirmUpdateChart()">
+ Confirm
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+
+
+<!-- Modal: Metric selector -->
+<div class="modal fade" id="metricMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" 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">×</span>
+ </button>
+ <h4 class="modal-title">Select Metric</h4>
+ </div>
+ <div class="modal-body">
+ <div class="input-group" style="margin-bottom: 10px;">
+ <input type="text" class="form-control" placeholder="Filter..." ng-model="_newMetricFilter" />
+ <span class="input-group-btn">
+ <button class="btn btn-default btn-flat" ng-click="_newMetricFilter = '';"><span class="fa fa-times"></span></button>
+ </span>
+ </div>
+
+ <div class="row">
+ <div class="col-md-4">
+ <ul class="nav nav-pills nav-stacked fixed-height">
+ <li class="disabled"><a>Data Source</a></li>
+ <li ng-repeat="dataSrc in dataSourceList track by $index" ng-class="{active: _newMetricDataSrc === dataSrc}">
+ <a ng-click="newMetricSelectDataSource(dataSrc)">{{dataSrc.dataSource}}</a>
+ </li>
+ </ul>
+ </div>
+ <div class="col-md-8">
+ <ul class="nav nav-pills nav-stacked fixed-height">
+ <li class="disabled"><a>Metric</a></li>
+ <li ng-repeat="metric in dataSourceMetricList(_newMetricDataSrc, _newMetricFilter) track by $index" ng-class="{active: _newMetricDataMetric === metric}">
+ <a ng-click="newMetricSelectMetric(metric)">{{metric}}</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <span class="text-primary pull-left">{{_newMetricDataSrc.dataSource}} <span class="fa fa-arrow-right"></span> {{_newMetricDataMetric}}</span>
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ <button type="button" class="btn btn-primary" ng-click="confirmSelectMetric()">
+ Select
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
+
+
+
+<!-- Modal: Chart View -->
+<div class="modal fade" id="chartViewMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" 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">×</span>
+ </button>
+ <h4 class="modal-title">{{viewChart.title || viewChart.metrics[0].metric}}</h4>
+ </div>
+ <div class="modal-body">
+ <div nvd3="viewChart._data" data-title="{{viewChart.title || viewChart.metrics[0].metric}}"
+ data-watching="true" data-chart="{{viewChart.chart || 'line'}}" data-config="getChartConfig(viewChart)" class="nvd3-chart-cntr lg"></div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/topology/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/topology/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/topology/controller.js
new file mode 100644
index 0000000..94886c9
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/topology/controller.js
@@ -0,0 +1,257 @@
+/*
+ * 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 featureControllers = angular.module('featureControllers');
+ var feature = featureControllers.register("topology", {
+ global: true // Global Feature needn't add to applications
+ });
+
+ // ==============================================================
+ // = Initialization =
+ // ==============================================================
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+ //feature.service("DashboardFormatter", function() {
+ //});
+
+ // ==============================================================
+ // = Controller =
+ // ==============================================================
+ feature.configNavItem("monitoring", "Topology", "usb");
+
+ // ========================= Monitoring =========================
+ feature.configController('monitoring', function(PageConfig, $scope, $interval, Entities, UI, Site, Application) {
+ var topologyRefreshInterval;
+
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ PageConfig.pageTitle = "Topology Execution";
+
+ $scope.topologyExecutionList = null;
+
+ $scope.currentTopologyExecution = null;
+ $scope.currentTopologyExecutionOptList = [];
+
+ // ======================= Function =======================
+ function refreshExecutionList() {
+ var _list = Entities.queryEntities("TopologyExecutionService");
+ _list._promise.then(function () {
+ $scope.topologyExecutionList = _list;
+ });
+ }
+
+ $scope.showTopologyDetail = function (topologyExecution) {
+ $scope.currentTopologyExecution = topologyExecution;
+ $("#topologyMDL").modal();
+
+ $scope.currentTopologyExecutionOptList = Entities.queryEntities("TopologyOperationService", {
+ site: topologyExecution.tags.site,
+ application: topologyExecution.tags.application,
+ topology: topologyExecution.tags.topology,
+ _pageSize: 10,
+ _duration: 1000 * 60 * 60 * 24 * 30
+ });
+ };
+
+ $scope.getStatusClass = function (status) {
+ switch (status) {
+ case "NEW":
+ return "info";
+ case "STARTING":
+ case "STOPPING":
+ return "warning";
+ case "STARTED":
+ return "success";
+ case "STOPPED":
+ return "danger";
+ default:
+ return "default";
+ }
+ };
+
+ // ==================== Initialization ====================
+ refreshExecutionList();
+ topologyRefreshInterval = $interval(refreshExecutionList, 10 * 1000);
+
+ $scope.topologyList = Entities.queryEntities("TopologyDescriptionService");
+ $scope.topologyList._promise.then(function () {
+ $scope.topologyList = $.map($scope.topologyList, function (topology) {
+ return topology.tags.topology;
+ });
+ });
+
+ $scope.applicationList = $.map(Application.list, function (application) {
+ return application.tags.application;
+ });
+
+ $scope.siteList = $.map(Site.list, function (site) {
+ return site.tags.site;
+ });
+
+ // ================== Topology Execution ==================
+ $scope.newTopologyExecution = function () {
+ UI.createConfirm("Topology", {}, [
+ {field: "site", type: "select", valueList: $scope.siteList},
+ {field: "application", type: "select", valueList: $scope.applicationList},
+ {field: "topology", type: "select", valueList: $scope.topologyList}
+ ], function (entity) {
+ for(var i = 0 ; i < $scope.topologyExecutionList.length; i += 1) {
+ var _entity = $scope.topologyExecutionList[i].tags;
+ if(_entity.site === entity.site && _entity.application === entity.application && _entity.topology === entity.topology) {
+ return "Topology already exist!";
+ }
+ }
+ }).then(null, null, function(holder) {
+ var _entity = {
+ tags: {
+ site: holder.entity.site,
+ application: holder.entity.application,
+ topology: holder.entity.topology
+ },
+ status: "NEW"
+ };
+ Entities.updateEntity("TopologyExecutionService", _entity)._promise.then(function() {
+ holder.closeFunc();
+ $scope.topologyExecutionList.push(_entity);
+ refreshExecutionList();
+ });
+ });
+ };
+
+ $scope.deleteTopologyExecution = function (topologyExecution) {
+ UI.deleteConfirm(topologyExecution.tags.topology).then(null, null, function(holder) {
+ Entities.deleteEntities("TopologyExecutionService", topologyExecution.tags)._promise.then(function() {
+ holder.closeFunc();
+ common.array.remove(topologyExecution, $scope.topologyExecutionList);
+ });
+ });
+ };
+
+ // ================== Topology Operation ==================
+ $scope.doTopologyOperation = function (topologyExecution, operation) {
+ $.dialog({
+ title: operation + " Confirm",
+ content: "Do you want to " + operation + " '" + topologyExecution.tags.topology + "'?",
+ confirm: true
+ }, function (ret) {
+ if(!ret) return;
+
+ var list = Entities.queryEntities("TopologyOperationService", {
+ site: topologyExecution.tags.site,
+ application: topologyExecution.tags.application,
+ topology: topologyExecution.tags.topology,
+ _pageSize: 20
+ });
+
+ list._promise.then(function () {
+ var lastOperation = common.array.find(operation, list, "tags.operation");
+ if(lastOperation && (lastOperation.status === "INITIALIZED" || lastOperation.status === "PENDING")) {
+ refreshExecutionList();
+ return;
+ }
+
+ Entities.updateEntity("rest/app/operation", {
+ tags: {
+ site: topologyExecution.tags.site,
+ application: topologyExecution.tags.application,
+ topology: topologyExecution.tags.topology,
+ operation: operation
+ },
+ status: "INITIALIZED"
+ }, {timestamp: false, hook: true});
+ });
+ });
+ };
+
+ $scope.startTopologyOperation = function (topologyExecution) {
+ $scope.doTopologyOperation(topologyExecution, "START");
+ };
+ $scope.stopTopologyOperation = function (topologyExecution) {
+ $scope.doTopologyOperation(topologyExecution, "STOP");
+ };
+
+ // ======================= Clean Up =======================
+ $scope.$on('$destroy', function() {
+ $interval.cancel(topologyRefreshInterval);
+ });
+ });
+
+ // ========================= Management =========================
+ feature.configController('management', function(PageConfig, $scope, Entities, UI) {
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ PageConfig.pageTitle = "Topology";
+
+ var typeList = ["CLASS", "DYNAMIC"];
+ var topologyDefineAttrs = [
+ {field: "topology", name: "name"},
+ {field: "type", type: "select", valueList: typeList},
+ {field: "exeClass", name: "execution entry", description: function (entity) {
+ if(entity.type === "CLASS") return "Class implements interface TopologyExecutable";
+ if(entity.type === "DYNAMIC") return "DSL based topology definition";
+ }, type: "blob", rows: 5},
+ {field: "version", optional: true},
+ {field: "description", optional: true, type: "blob"}
+ ];
+ var topologyUpdateAttrs = $.extend(topologyDefineAttrs.concat(), [{field: "topology", name: "name", readonly: true}]);
+
+ $scope.topologyList = Entities.queryEntities("TopologyDescriptionService");
+
+ $scope.newTopology = function () {
+ UI.createConfirm("Topology", {}, topologyDefineAttrs, function (entity) {
+ if(common.array.find(entity.topology, $scope.topologyList, "tags.topology", false, false)) {
+ return "Topology name conflict!";
+ }
+ }).then(null, null, function(holder) {
+ holder.entity.tags = {
+ topology: holder.entity.topology
+ };
+ Entities.updateEntity("TopologyDescriptionService", holder.entity, {timestamp: false})._promise.then(function() {
+ holder.closeFunc();
+ $scope.topologyList.push(holder.entity);
+ });
+ });
+ };
+
+ $scope.updateTopology = function (topology) {
+ UI.updateConfirm("Topology", $.extend({}, topology, {topology: topology.tags.topology}), topologyUpdateAttrs).then(null, null, function(holder) {
+ holder.entity.tags = {
+ topology: holder.entity.topology
+ };
+ Entities.updateEntity("TopologyDescriptionService", holder.entity, {timestamp: false})._promise.then(function() {
+ holder.closeFunc();
+ $.extend(topology, holder.entity);
+ });
+ });
+ };
+
+ $scope.deleteTopology = function (topology) {
+ UI.deleteConfirm(topology.tags.topology).then(null, null, function(holder) {
+ Entities.delete("TopologyDescriptionService", {topology: topology.tags.topology})._promise.then(function() {
+ holder.closeFunc();
+ common.array.remove(topology, $scope.topologyList);
+ });
+ });
+ };
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/management.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/management.html b/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/management.html
new file mode 100644
index 0000000..9e22c84
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/management.html
@@ -0,0 +1,52 @@
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-cog"></i>
+ <a class="pull-right" href="#/config/topology/monitoring"><span class="fa fa-angle-right"></span> Monitoring</a>
+ <h3 class="box-title">
+ Management
+ </h3>
+ </div>
+ <div class="box-body">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th width="20%">Execution Class</th>
+ <th>Type</th>
+ <th width="50%">Description</th>
+ <th>Version</th>
+ <th width="70"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in topologyList">
+ <td>{{item.tags.topology}}</td>
+ <td><pre class="noWrap">{{item.exeClass}}</pre></td>
+ <td>{{item.type}}</td>
+ <td>{{item.description}}</td>
+ <td>{{item.version}}</td>
+ <td class="text-center">
+ <button class="rm fa fa-pencil btn btn-default btn-xs" uib-tooltip="Edit" tooltip-animation="false" ng-click="updateTopology(item)"> </button>
+ <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="deleteTopology(item)"> </button>
+ </td>
+ </tr>
+ <tr ng-if="topologyList.length === 0">
+ <td colspan="6">
+ <span class="text-muted">Empty list</span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div class="box-footer">
+ <button class="btn btn-primary pull-right" ng-click="newTopology()">
+ New Topology
+ <i class="fa fa-plus-circle"> </i>
+ </button>
+ </div>
+
+ <div class="overlay" ng-hide="topologyList._promise.$$state.status === 1;">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/monitoring.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/monitoring.html b/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/monitoring.html
new file mode 100644
index 0000000..0acb2c1
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/topology/page/monitoring.html
@@ -0,0 +1,151 @@
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-eye"></i>
+ <a class="pull-right" href="#/config/topology/management"><span class="fa fa-angle-right"></span> Management</a>
+ <h3 class="box-title">
+ Monitoring
+ </h3>
+ </div>
+ <div class="box-body">
+ <div sorttable source="topologyExecutionList">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th width="70" sortpath="status">Status</th>
+ <th width="90" sortpath="tags.topology">Topology</th>
+ <th width="60" sortpath="tags.site">Site</th>
+ <th width="100" sortpath="tags.application">Application</th>
+ <th width="60" sortpath="mode">Mode</th>
+ <th sortpath="description">Description</th>
+ <th width="70" style="min-width: 70px;"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="text-center">
+ <span class="label label-{{_parent.getStatusClass(item.status)}}">{{item.status}}</span>
+ </td>
+ <td><a ng-click="_parent.showTopologyDetail(item)">{{item.tags.topology}}</a></td>
+ <td>{{item.tags.site}}</td>
+ <td>{{item.tags.application}}</td>
+ <td>{{item.mode}}</td>
+ <td>{{item.description}}</td>
+ <td class="text-center">
+ <button ng-if="item.status === 'NEW' || item.status === 'STOPPED'" class="fa fa-play sm btn btn-default btn-xs" uib-tooltip="Start" tooltip-animation="false" ng-click="_parent.startTopologyOperation(item)"> </button>
+ <button ng-if="item.status === 'STARTED'" class="fa fa-stop sm btn btn-default btn-xs" uib-tooltip="Stop" tooltip-animation="false" ng-click="_parent.stopTopologyOperation(item)"> </button>
+ <button ng-if="item.status !== 'NEW' && item.status !== 'STARTED' && item.status !== 'STOPPED'" class="fa fa-ban sm btn btn-default btn-xs" disabled="disabled"> </button>
+ <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="_parent.deleteTopologyExecution(item)"> </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div class="box-footer">
+ <button class="btn btn-primary pull-right" ng-click="newTopologyExecution()">
+ New Topology Execution
+ <i class="fa fa-plus-circle"> </i>
+ </button>
+ </div>
+
+ <div class="overlay" ng-hide="topologyExecutionList._promise.$$state.status === 1;">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+</div>
+
+
+
+
+<!-- Modal: Topology Detail -->
+<div class="modal fade" id="topologyMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" 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">×</span>
+ </button>
+ <h4 class="modal-title">Topology Detail</h4>
+ </div>
+ <div class="modal-body">
+ <h3>Detail</h3>
+ <table class="table">
+ <tbody>
+ <tr>
+ <th>Site</th>
+ <td>{{currentTopologyExecution.tags.site}}</td>
+ </tr>
+ <tr>
+ <th>Application</th>
+ <td>{{currentTopologyExecution.tags.application}}</td>
+ </tr>
+ <tr>
+ <th>Topology</th>
+ <td>{{currentTopologyExecution.tags.topology}}</td>
+ </tr>
+ <tr>
+ <th>Full Name</th>
+ <td>{{currentTopologyExecution.fullName || "-"}}</td>
+ </tr>
+ <tr>
+ <th>Status</th>
+ <td>
+ <span class="label label-{{getStatusClass(currentTopologyExecution.status)}}">{{currentTopologyExecution.status}}</span>
+ </td>
+ </tr>
+ <tr>
+ <th>Mode</th>
+ <td>{{currentTopologyExecution.mode || "-"}}</td>
+ </tr>
+ <tr>
+ <th>Environment</th>
+ <td>{{currentTopologyExecution.environment || "-"}}</td>
+ </tr>
+ <tr>
+ <th>Url</th>
+ <td>
+ <a ng-if="currentTopologyExecution.url" href="{{currentTopologyExecution.url}}" target="_blank">{{currentTopologyExecution.url}}</a>
+ <span ng-if="!currentTopologyExecution.url">-</span>
+ </td>
+ </tr>
+ <tr>
+ <th>Description</th>
+ <td>{{currentTopologyExecution.description || "-"}}</td>
+ </tr>
+ <tr>
+ <th>Last Modified Date</th>
+ <td>{{common.format.date(currentTopologyExecution.lastModifiedDate) || "-"}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <h3>Latest Operations</h3>
+ <div class="table-responsive">
+ <table class="table table-bordered table-sm margin-bottom-none">
+ <thead>
+ <tr>
+ <th>Date Time</th>
+ <th>Operation</th>
+ <th>Status</th>
+ <th>Message</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="action in currentTopologyExecutionOptList track by $index">
+ <td>{{common.format.date(action.lastModifiedDate) || "-"}}</td>
+ <td>{{action.tags.operation}}</td>
+ <td>{{action.status}}</td>
+ <td><pre class="noWrap">{{action.message}}</pre></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/controller.js
new file mode 100644
index 0000000..ed619d3
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/controller.js
@@ -0,0 +1,268 @@
+/*
+ * 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 featureControllers = angular.module('featureControllers');
+ var feature = featureControllers.register("userProfile");
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+
+ // ==============================================================
+ // = User Profile =
+ // ==============================================================
+
+ // ======================== Profile List ========================
+ //feature.navItem("list", "User Profiles", "graduation-cap");
+ feature.controller('list', function(PageConfig, Site, $scope, $interval, Entities) {
+ PageConfig.pageSubTitle = Site.current().tags.site;
+
+ $scope.common = common;
+ $scope.algorithms = [];
+
+ // ======================================== Algorithms ========================================
+ $scope.algorithmEntity = {};
+ Entities.queryEntities("AlertDefinitionService", {site: Site.current().tags.site, application: "userProfile"})._promise.then(function(data) {
+ $scope.algorithmEntity = common.getValueByPath(data, "obj[0]");
+ $scope.algorithmEntity.policy = common.parseJSON($scope.algorithmEntity.policyDef);
+ });
+
+ // ======================================= User profile =======================================
+ $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().tags.site}, ["user", "algorithm", "content", "version"]);
+ $scope.profileList._promise.then(function() {
+ var _algorithms = {};
+ var _users = {};
+
+ // Map user
+ $.each($scope.profileList, function(i, unit) {
+ _algorithms[unit.tags.algorithm] = unit.tags.algorithm;
+ var _user = _users[unit.tags.user] = _users[unit.tags.user] || {user: unit.tags.user};
+ _user[unit.tags.algorithm] = {
+ version: unit.version
+ };
+
+ // DE
+ if(unit.tags.algorithm === "DE") {
+ var _statistics = common.parseJSON(unit.content);
+ _statistics = common.getValueByPath(_statistics, "statistics", []);
+ _user[unit.tags.algorithm].topCommands = $.map(common.array.top(_statistics, "mean"), function(command) {
+ return command.commandName;
+ });
+ }
+ });
+
+ // Map algorithms
+ $scope.algorithms = $.map(_algorithms, function(algorithm) {
+ return algorithm;
+ }).sort();
+
+ $scope.profileList.splice(0);
+ $scope.profileList.push.apply($scope.profileList, common.map.toArray(_users));
+ });
+
+ // =========================================== Task ===========================================
+ $scope.tasks = [];
+ function _loadTasks() {
+ var _tasks = Entities.queryEntities("ScheduleTaskService", {
+ site: Site.current().tags.site,
+ _pageSize: 100,
+ _duration: 1000 * 60 * 60 * 24 * 14,
+ __ETD: 1000 * 60 * 60 * 24
+ });
+ _tasks._promise.then(function() {
+ $scope.tasks.splice(0);
+ $scope.tasks.push.apply($scope.tasks, _tasks);
+
+ // Duration
+ $.each($scope.tasks, function(i, data) {
+ if(data.timestamp && data.updateTime) {
+ var _ms = (new moment(data.updateTime)).diff(new moment(data.timestamp));
+ var _d = moment.duration(_ms);
+ data._duration = Math.floor(_d.asHours()) + moment.utc(_ms).format(":mm:ss");
+ data.duration = _ms;
+ } else {
+ data._duration = "--";
+ }
+ });
+ });
+ }
+
+ $scope.runningTaskCount = function () {
+ return common.array.count($scope.tasks, "INITIALIZED", "status") +
+ common.array.count($scope.tasks, "PENDING", "status") +
+ common.array.count($scope.tasks, "EXECUTING", "status");
+ };
+
+ // Create task
+ $scope.updateTask = function() {
+ $.dialog({
+ title: "Confirm",
+ content: "Do you want to update now?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ var _entity = {
+ status: "INITIALIZED",
+ detail: "Newly created command",
+ tags: {
+ site: Site.current().tags.site,
+ type: "USER_PROFILE_TRAINING"
+ },
+ timestamp: +new Date()
+ };
+ Entities.updateEntity("ScheduleTaskService", _entity, {timestamp: false})._promise.success(function(data) {
+ if(!Entities.dialog(data)) {
+ _loadTasks();
+ }
+ });
+ });
+ };
+
+ // Show detail
+ $scope.showTaskDetail = function(task) {
+ var _content = $("<pre>").text(task.detail);
+
+ var $mdl = $.dialog({
+ title: "Detail",
+ content: _content
+ });
+
+ _content.click(function(e) {
+ if(!e.ctrlKey) return;
+
+ $.dialog({
+ title: "Confirm",
+ content: "Remove this task?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ $mdl.modal('hide');
+ Entities.deleteEntity("ScheduleTaskService", task)._promise.then(function() {
+ _loadTasks();
+ });
+ });
+ });
+ };
+
+ _loadTasks();
+ var _loadInterval = $interval(_loadTasks, app.time.refreshInterval);
+ $scope.$on('$destroy',function(){
+ $interval.cancel(_loadInterval);
+ });
+ });
+
+ // ======================= Profile Detail =======================
+ feature.controller('detail', function(PageConfig, Site, $scope, $wrapState, Entities) {
+ PageConfig.pageTitle = "User Profile";
+ PageConfig.pageSubTitle = Site.current().tags.site;
+ PageConfig
+ .addNavPath("User Profile", "/userProfile/list")
+ .addNavPath("Detail");
+
+ $scope.user = $wrapState.param.filter;
+
+ // User profile
+ $scope.profiles = {};
+ $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().tags.site, user: $scope.user});
+ $scope.profileList._promise.then(function() {
+ $.each($scope.profileList, function(i, unit) {
+ unit._content = common.parseJSON(unit.content);
+ $scope.profiles[unit.tags.algorithm] = unit;
+ });
+
+ // DE
+ if($scope.profiles.DE) {
+ console.log($scope.profiles.DE);
+
+ $scope.profiles.DE._chart = {};
+
+ $scope.profiles.DE.estimates = {};
+ $.each($scope.profiles.DE._content, function(key, value) {
+ if(key !== "statistics") {
+ $scope.profiles.DE.estimates[key] = value;
+ }
+ });
+
+ var _meanList = [];
+ var _stddevList = [];
+
+ $.each($scope.profiles.DE._content.statistics, function(i, unit) {
+ _meanList[i] = {
+ x: unit.commandName,
+ y: unit.mean
+ };
+ _stddevList[i] = {
+ x: unit.commandName,
+ y: unit.stddev
+ };
+ });
+ $scope.profiles.DE._chart.series = [
+ {
+ key: "mean",
+ values: _meanList
+ },
+ {
+ key: "stddev",
+ values: _stddevList
+ }
+ ];
+
+ // Percentage table list
+ $scope.profiles.DE.meanList = [];
+ var _total = common.array.sum($scope.profiles.DE._content.statistics, "mean");
+ $.each($scope.profiles.DE._content.statistics, function(i, unit) {
+ $scope.profiles.DE.meanList.push({
+ command: unit.commandName,
+ percentage: unit.mean / _total
+ });
+ });
+ }
+
+ // EigenDecomposition
+ if($scope.profiles.EigenDecomposition && $scope.profiles.EigenDecomposition._content.principalComponents) {
+ $scope.profiles.EigenDecomposition._chart = {
+ series: [],
+ };
+
+ $.each($scope.profiles.EigenDecomposition._content.principalComponents, function(z, grp) {
+ var _line = [];
+ $.each(grp, function(x, y) {
+ _line.push([x,y,z]);
+ });
+
+ $scope.profiles.EigenDecomposition._chart.series.push({
+ data: _line
+ });
+ });
+ }
+ });
+
+ // UI
+ $scope.showRawData = function(content) {
+ $.dialog({
+ title: "Raw Data",
+ content: $("<pre>").text(content)
+ });
+ };
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/detail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/detail.html b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/detail.html
new file mode 100644
index 0000000..0f94e03
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/detail.html
@@ -0,0 +1,87 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-user"> </i>
+ <h3 class="box-title">
+ {{user}}
+ </h3>
+ </div>
+ <div class="box-body">
+ <div>
+ <div class="inline-group">
+ <dl><dt>User</dt><dd>{{user}}</dd></dl>
+ <dl><dt>Site</dt><dd>{{Site.current().tags.site}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Other Info</dt><dd class="text-muted">N/A</dd></dl>
+ </div>
+ </div>
+
+ <div class="overlay" ng-hide="profileList._promise.$$state.status === 1;">
+ <span class="fa fa-refresh fa-spin"></span>
+ </div>
+ </div>
+</div>
+
+<!-- Analysis -->
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li class="active">
+ <a href="[data-id='DE']" data-toggle="tab" ng-click=" currentTab='DE'">DE</a>
+ </li>
+ <li>
+ <a href="[data-id='EigenDecomposition']" data-toggle="tab" ng-click=" currentTab='EigenDecomposition'">EigenDecomposition</a>
+ </li>
+ <li class="pull-right">
+ <button class="btn btn-primary" ng-click="showRawData(currentTab === 'EigenDecomposition' ? profiles.EigenDecomposition.content : profiles.DE.content)">Raw Data</button>
+ </li>
+ </ul>
+ <div class="tab-content">
+ <div class="tab-pane active" data-id="DE">
+ <div class="row">
+ <div class="col-md-9">
+ <div nvd3="profiles.DE._chart.series" data-config="{chart: 'column', xType: 'text', height: 400}" class="nvd3-chart-cntr" height="400"></div>
+ <div class="inline-group text-center">
+ <dl ng-repeat="(key, value) in profiles.DE.estimates"><dt>{{key}}</dt><dd>{{value}}</dd></dl>
+ </div>
+ </div>
+
+ <div class="col-md-3">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th>Command</th>
+ <th>Percentage</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="unit in profiles.DE.meanList">
+ <td>{{unit.command}}</td>
+ <td class="text-right">{{(unit.percentage*100).toFixed(2)}}%</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div><!-- /.tab-pane -->
+ <div class="tab-pane" data-id="EigenDecomposition">
+ <div line3d-chart height="400" data="profiles.EigenDecomposition._chart.series"> </div>
+ </div><!-- /.tab-pane -->
+ </div><!-- /.tab-content -->
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/list.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/list.html b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/list.html
new file mode 100644
index 0000000..2f14479
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/userProfile/page/list.html
@@ -0,0 +1,138 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-list-alt"> </i>
+ <h3 class="box-title">
+ User Profiles
+ <small><a data-toggle="collapse" href="[data-id='algorithms']">Detail</a></small>
+ </h3>
+ <div class="pull-right">
+ <a class="label label-primary" ng-class="runningTaskCount() ? 'label-primary' : 'label-default'" data-toggle="modal" data-target="#taskMDL">Update</a>
+ </div>
+ </div>
+ <div class="box-body">
+ <!-- Algorithms -->
+ <div data-id="algorithms" class="collapse">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <td>Feature</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="algorithm in algorithmEntity.policy.algorithms">
+ <td>{{algorithm.name}}</td>
+ <td>{{algorithm.features}}</td>
+ </tr>
+ </tbody>
+ </table>
+ <hr/>
+ </div>
+
+ <!-- User Profile List -->
+ <p ng-show="profileList._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+
+ <div sorttable source="profileList" ng-show="profileList._promise.$$state.status === 1">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th width="10%" sortpath="user">User</th>
+ <th>Most Predominat Feature</th>
+ <th width="10"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <a href="#/userProfile/detail/{{item.user}}">{{item.user}}</a>
+ </td>
+ <td>
+ {{item.DE.topCommands.slice(0,3).join(", ")}}
+ </td>
+ <td>
+ <a href="#/userProfile/detail/{{item.user}}">Detail</a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<!-- Modal: User profile Schedule Task -->
+<div class="modal fade" id="taskMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" 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">×</span>
+ </button>
+ <h4 class="modal-title">Training History</h4>
+ </div>
+ <div class="modal-body">
+ <div sorttable source="tasks">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th sortpath="tags.type">Command</th>
+ <th sortpath="timestamp">Start Time</th>
+ <th sortpath="updateTime">Update Time</th>
+ <th sortpath="duration">Duration</th>
+ <th sortpath="status">Status</th>
+ <th width="10"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{item.tags.type}}</td>
+ <td>{{common.format.date(item.timestamp) || "--"}}</td>
+ <td>{{common.format.date(item.updateTime) || "--"}}</td>
+ <td>{{item._duration}}</td>
+ <td class="text-nowrap">
+ <span class="fa fa-hourglass-start text-muted" ng-show="item.status === 'INITIALIZED'"></span>
+ <span class="fa fa-hourglass-half text-info" ng-show="item.status === 'PENDING'"></span>
+ <span class="fa fa-circle-o-notch text-primary" ng-show="item.status === 'EXECUTING'"></span>
+ <span class="fa fa-check-circle text-success" ng-show="item.status === 'SUCCEEDED'"></span>
+ <span class="fa fa-exclamation-circle text-danger" ng-show="item.status === 'FAILED'"></span>
+ <span class="fa fa-ban text-muted" ng-show="item.status === 'CANCELED'"></span>
+ {{item.status}}
+ </td>
+ <td>
+ <a ng-click="_parent.showTaskDetail(item)">Detail</a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-primary pull-left" ng-click="updateTask()" ng-show="Auth.isRole('ROLE_ADMIN')">
+ Update Now
+ </button>
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/images/favicon.png
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/images/favicon.png b/eagle-webservice/src/main/webapp/_app/public/images/favicon.png
new file mode 100644
index 0000000..3bede2a
Binary files /dev/null and b/eagle-webservice/src/main/webapp/_app/public/images/favicon.png differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/images/favicon_white.png
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/images/favicon_white.png b/eagle-webservice/src/main/webapp/_app/public/images/favicon_white.png
new file mode 100644
index 0000000..9879e92
Binary files /dev/null and b/eagle-webservice/src/main/webapp/_app/public/images/favicon_white.png differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/app.config.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/app.config.js b/eagle-webservice/src/main/webapp/_app/public/js/app.config.js
new file mode 100644
index 0000000..d7c4be9
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/app.config.js
@@ -0,0 +1,126 @@
+/*
+ * 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';
+
+ app.config = {
+ // ============================================================================
+ // = URLs =
+ // ============================================================================
+ urls: {
+ HOST: '..',
+
+ updateEntity: 'rest/entities?serviceName=${serviceName}',
+ queryEntity: 'rest/entities/rowkey?serviceName=${serviceName}&value=${encodedRowkey}',
+ queryEntities: 'rest/entities?query=${serviceName}[${condition}]{${values}}&pageSize=100000',
+ deleteEntity: 'rest/entities/delete?serviceName=${serviceName}&byId=true',
+ deleteEntities: 'rest/entities?query=${serviceName}[${condition}]{*}&pageSize=100000',
+
+ queryGroup: 'rest/entities?query=${serviceName}[${condition}]<${groupBy}>{${values}}&pageSize=100000',
+ querySeries: 'rest/entities?query=${serviceName}[${condition}]<${groupBy}>{${values}}&pageSize=100000&timeSeries=true&intervalmin=${intervalmin}',
+
+ query: 'rest/',
+
+ userProfile: 'rest/authentication',
+ logout: 'logout',
+
+ maprNameResolver: '../rest/maprNameResolver',
+
+ DELETE_HOOK: {
+ FeatureDescService: 'rest/module/feature?feature=${feature}',
+ ApplicationDescService: 'rest/module/application?application=${application}',
+ SiteDescService: 'rest/module/site?site=${site}',
+ TopologyDescriptionService: 'rest/app/topology?topology=${topology}'
+ },
+ UPDATE_HOOK: {
+ SiteDescService: 'rest/module/siteApplication'
+ }
+ },
+ };
+
+ // ============================================================================
+ // = URLs =
+ // ============================================================================
+ app.getURL = function(name, kvs) {
+ var _path = app.config.urls[name];
+ if(!_path) throw "URL:'" + name + "' not exist!";
+ var _url = app.packageURL(_path);
+ if(kvs !== undefined) {
+ _url = common.template(_url, kvs);
+ }
+ return _url;
+ };
+
+ app.getMapRNameResolverURL = function(name,value, site) {
+ var key = "maprNameResolver";
+ var _path = app.config.urls[key];
+ if(!_path) throw "URL:'" + name + "' not exist!";
+ var _url = _path;
+ if(name == "fNameResolver") {
+ _url += "/" + name + "?fName=" + value + "&site=" + site;
+ } else if(name == "sNameResolver") {
+ _url += "/" + name + "?sName=" + value + "&site=" + site;
+ } else if (name == "vNameResolver") {
+ _url += "/" + name + "?vName=" + value + "&site=" + site;
+ } else{
+ throw "resolver:'" + name + "' not exist!";
+ }
+ return _url;
+ };
+
+ function getHookURL(hookType, serviceName) {
+ var _path = app.config.urls[hookType][serviceName];
+ if(!_path) return null;
+
+ return app.packageURL(_path);
+ }
+
+ /***
+ * Eagle support delete function to process special entity delete. Which will delete all the relative entity.
+ * @param serviceName
+ */
+ app.getDeleteURL = function(serviceName) {
+ return getHookURL('DELETE_HOOK', serviceName);
+ };
+
+ /***
+ * Eagle support update function to process special entity update. Which will update all the relative entity.
+ * @param serviceName
+ */
+ app.getUpdateURL = function(serviceName) {
+ return getHookURL('UPDATE_HOOK', serviceName);
+ };
+
+ app.packageURL = function (path) {
+ var _host = localStorage.getItem("HOST") || app.config.urls.HOST;
+ return (_host ? _host + "/" : '') + path;
+ };
+
+ app._Host = function(host) {
+ if(host) {
+ localStorage.setItem("HOST", host);
+ return app;
+ }
+ return localStorage.getItem("HOST");
+ };
+ app._Host.clear = function() {
+ localStorage.removeItem("HOST");
+ };
+ app._Host.sample = "http://localhost:9099/eagle-service";
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/app.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/app.js b/eagle-webservice/src/main/webapp/_app/public/js/app.js
new file mode 100644
index 0000000..70b4afe
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/app.js
@@ -0,0 +1,499 @@
+/*
+ * 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 = {};
+
+(function() {
+ 'use strict';
+
+ /* App Module */
+ var eagleApp = angular.module('eagleApp', ['ngRoute', 'ngAnimate', 'ui.router', 'eagleControllers', 'featureControllers', 'eagle.service']);
+
+ // GRUNT REPLACEMENT: eagleApp.buildTimestamp = TIMESTAMP
+ eagleApp._TRS = function() {
+ return eagleApp.buildTimestamp || Math.random();
+ };
+
+ // ======================================================================================
+ // = Feature Module =
+ // ======================================================================================
+ var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
+ var FN_ARG_SPLIT = /,/;
+ var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
+ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
+
+ var featureControllers = angular.module('featureControllers', ['ui.bootstrap', 'eagle.components']);
+ var featureControllerCustomizeHtmlTemplate = {};
+ var featureControllerProvider;
+ var featureProvider;
+
+ featureControllers.config(function ($controllerProvider, $provide) {
+ featureControllerProvider = $controllerProvider;
+ featureProvider = $provide;
+ });
+
+ featureControllers.service("Feature", function($wrapState, PageConfig, ConfigPageConfig, FeaturePageConfig) {
+ var _features = {};
+ var _services = {};
+
+ var Feature = function(name, config) {
+ this.name = name;
+ this.config = config || {};
+ this.features = {};
+ };
+
+ /***
+ * Inner function. Replace the dependency of constructor.
+ * @param constructor
+ * @private
+ */
+ Feature.prototype._replaceDependencies = function(constructor) {
+ var i, srvName;
+ var _constructor, _$inject;
+ var fnText, argDecl;
+
+ if($.isArray(constructor)) {
+ _constructor = constructor[constructor.length - 1];
+ _$inject = constructor.slice(0, -1);
+ } else if(constructor.$inject) {
+ _constructor = constructor;
+ _$inject = constructor.$inject;
+ } else {
+ _$inject = [];
+ _constructor = constructor;
+ fnText = constructor.toString().replace(STRIP_COMMENTS, '');
+ argDecl = fnText.match(FN_ARGS);
+ $.each(argDecl[1].split(FN_ARG_SPLIT), function(i, arg) {
+ arg.replace(FN_ARG, function(all, underscore, name) {
+ _$inject.push(name);
+ });
+ });
+ }
+ _constructor.$inject = _$inject;
+
+ for(i = 0 ; i < _$inject.length ; i += 1) {
+ srvName = _$inject[i];
+ _$inject[i] = this.features[srvName] || _$inject[i];
+ }
+
+ return _constructor;
+ };
+
+ /***
+ * Register a common service for feature usage. Common service will share between the feature. If you are coding customize feature, use 'Feature.service' is the better way.
+ * @param name
+ * @param constructor
+ */
+ Feature.prototype.commonService = function(name, constructor) {
+ if(!_services[name]) {
+ featureProvider.service(name, constructor);
+ _services[name] = this.name;
+ } else {
+ throw "Service '" + name + "' has already be registered by feature '" + _services[name] + "'";
+ }
+ };
+
+ /***
+ * Register a service for feature usage.
+ * @param name
+ * @param constructor
+ */
+ Feature.prototype.service = function(name, constructor) {
+ var _serviceName;
+ if(!this.features[name]) {
+ _serviceName = "__FEATURE_" + this.name + "_" + name;
+ featureProvider.service(_serviceName, this._replaceDependencies(constructor));
+ this.features[name] = _serviceName;
+ } else {
+ console.warn("Service '" + name + "' has already be registered.");
+ }
+ };
+
+ /***
+ * Create an navigation item in left navigation bar
+ * @param path
+ * @param title
+ * @param icon use Font Awesome. Needn't with 'fa fa-'.
+ */
+ Feature.prototype.navItem = function(path, title, icon) {
+ title = title || path;
+ icon = icon || "question";
+
+ FeaturePageConfig.addNavItem(this.name, {
+ icon: icon,
+ title: title,
+ url: "#/" + this.name + "/" + path
+ });
+ };
+
+ /***
+ * Register a controller.
+ * @param name
+ * @param constructor
+ */
+ Feature.prototype.controller = function(name, constructor, htmlTemplatePath) {
+ var _name = this.name + "_" + name;
+
+ // Replace feature registered service
+ constructor = this._replaceDependencies(constructor);
+
+ // Register controller
+ featureControllerProvider.register(_name, constructor);
+ if(htmlTemplatePath) {
+ featureControllerCustomizeHtmlTemplate[_name] = htmlTemplatePath;
+ }
+
+ return _name;
+ };
+
+ /***
+ * Register a configuration controller for admin usage.
+ * @param name
+ * @param constructor
+ */
+ Feature.prototype.configController = function(name, constructor, htmlTemplatePath) {
+ var _name = "config_" + this.name + "_" + name;
+
+ // Replace feature registered service
+ constructor = this._replaceDependencies(constructor);
+
+ // Register controller
+ featureControllerProvider.register(_name, constructor);
+ if(htmlTemplatePath) {
+ featureControllerCustomizeHtmlTemplate[_name] = htmlTemplatePath;
+ }
+
+ return _name;
+ };
+
+ /***
+ * Create an navigation item in left navigation bar for admin configuraion page
+ * @param path
+ * @param title
+ * @param icon use Font Awesome. Needn't with 'fa fa-'.
+ */
+ Feature.prototype.configNavItem = function(path, title, icon) {
+ title = title || path;
+ icon = icon || "question";
+
+ ConfigPageConfig.addNavItem(this.name, {
+ icon: icon,
+ title: title,
+ url: "#/config/" + this.name + "/" + path
+ });
+ };
+
+ // Register
+ featureControllers.register = Feature.register = function(featureName, config) {
+ _features[featureName] = _features[featureName] || new Feature(featureName, config);
+ return _features[featureName];
+ };
+
+ // Page go
+ Feature.go = function(feature, page, filter) {
+ if(!filter) {
+ $wrapState.go("page", {
+ feature: feature,
+ page: page
+ }, 2);
+ } else {
+ $wrapState.go("pageFilter", {
+ feature: feature,
+ page: page,
+ filter: filter
+ }, 2);
+ }
+ };
+
+ // Get feature by name
+ Feature.get = function (featureName) {
+ return _features[featureName];
+ };
+
+ return Feature;
+ });
+
+ // ======================================================================================
+ // = Router config =
+ // ======================================================================================
+ eagleApp.config(function ($stateProvider, $urlRouterProvider, $animateProvider) {
+ // Resolve
+ function _resolve(config) {
+ config = config || {};
+
+ var resolve = {
+ Site: function (Site) {
+ return Site._promise();
+ },
+ Authorization: function (Authorization) {
+ if(!config.roleType) {
+ return Authorization._promise();
+ } else {
+ return Authorization.rolePromise(config.roleType);
+ }
+ },
+ Application: function (Application) {
+ return Application._promise();
+ }
+ };
+
+ if(config.featureCheck) {
+ resolve._navigationCheck = function($q, $wrapState, Site, Application) {
+ var _deferred = $q.defer();
+
+ $q.all(Site._promise(), Application._promise()).then(function() {
+ var _match, i, tmpApp;
+ var _site = Site.current();
+ var _app = Application.current();
+
+ // Check application
+ if(_site && (
+ !_app ||
+ !_site.applicationList.set[_app.tags.application] ||
+ !_site.applicationList.set[_app.tags.application].enabled
+ )
+ ) {
+ _match = false;
+
+ for(i = 0 ; i < _site.applicationGroupList.length ; i += 1) {
+ tmpApp = _site.applicationGroupList[i].enabledList[0];
+ if(tmpApp) {
+ _app = Application.current(tmpApp);
+ _match = true;
+ break;
+ }
+ }
+
+ if(!_match) {
+ _app = null;
+ Application.current(null);
+ }
+ }
+ }).finally(function() {
+ _deferred.resolve();
+ });
+
+ return _deferred.promise;
+ };
+ }
+
+ return resolve;
+ }
+
+ // Router
+ var _featureBase = {
+ templateUrl: function ($stateParams) {
+ var _htmlTemplate = featureControllerCustomizeHtmlTemplate[$stateParams.feature + "_" + $stateParams.page];
+ return "public/feature/" + $stateParams.feature + "/page/" + (_htmlTemplate || $stateParams.page) + ".html?_=" + eagleApp._TRS();
+ },
+ controllerProvider: function ($stateParams) {
+ return $stateParams.feature + "_" + $stateParams.page;
+ },
+ resolve: _resolve({featureCheck: true}),
+ pageConfig: "FeaturePageConfig"
+ };
+
+ $urlRouterProvider.otherwise("/landing");
+ $stateProvider
+ // =================== Landing ===================
+ .state('landing', {
+ url: "/landing",
+ templateUrl: "partials/landing.html?_=" + eagleApp._TRS(),
+ controller: "landingCtrl",
+ resolve: _resolve({featureCheck: true})
+ })
+
+ // ================ Authorization ================
+ .state('login', {
+ url: "/login",
+ templateUrl: "partials/login.html?_=" + eagleApp._TRS(),
+ controller: "authLoginCtrl",
+ access: {skipCheck: true}
+ })
+
+ // ================ Configuration ================
+ // Site
+ .state('configSite', {
+ url: "/config/site",
+ templateUrl: "partials/config/site.html?_=" + eagleApp._TRS(),
+ controller: "configSiteCtrl",
+ pageConfig: "ConfigPageConfig",
+ resolve: _resolve({roleType: 'ROLE_ADMIN'})
+ })
+
+ // Application
+ .state('configApplication', {
+ url: "/config/application",
+ templateUrl: "partials/config/application.html?_=" + eagleApp._TRS(),
+ controller: "configApplicationCtrl",
+ pageConfig: "ConfigPageConfig",
+ resolve: _resolve({roleType: 'ROLE_ADMIN'})
+ })
+
+ // Feature
+ .state('configFeature', {
+ url: "/config/feature",
+ templateUrl: "partials/config/feature.html?_=" + eagleApp._TRS(),
+ controller: "configFeatureCtrl",
+ pageConfig: "ConfigPageConfig",
+ resolve: _resolve({roleType: 'ROLE_ADMIN'})
+ })
+
+ // Feature configuration page
+ .state('configFeatureDetail', $.extend({url: "/config/:feature/:page"}, {
+ templateUrl: function ($stateParams) {
+ var _htmlTemplate = featureControllerCustomizeHtmlTemplate[$stateParams.feature + "_" + $stateParams.page];
+ return "public/feature/" + $stateParams.feature + "/page/" + (_htmlTemplate || $stateParams.page) + ".html?_=" + eagleApp._TRS();
+ },
+ controllerProvider: function ($stateParams) {
+ return "config_" + $stateParams.feature + "_" + $stateParams.page;
+ },
+ pageConfig: "ConfigPageConfig",
+ resolve: _resolve({roleType: 'ROLE_ADMIN'})
+ }))
+
+ // =================== Feature ===================
+ // Dynamic feature page
+ .state('page', $.extend({url: "/:feature/:page"}, _featureBase))
+ .state('pageFilter', $.extend({url: "/:feature/:page/:filter"}, _featureBase))
+ ;
+
+ // Animation
+ $animateProvider.classNameFilter(/^((?!(fa-spin)).)*$/);
+ $animateProvider.classNameFilter(/^((?!(tab-pane)).)*$/);
+ });
+
+ eagleApp.filter('parseJSON', function () {
+ return function (input, defaultVal) {
+ return common.parseJSON(input, defaultVal);
+ };
+ });
+
+ eagleApp.filter('split', function () {
+ return function (input, regex) {
+ return input.split(regex);
+ };
+ });
+
+ eagleApp.filter('reverse', function () {
+ return function (items) {
+ return items.slice().reverse();
+ };
+ });
+
+ // ======================================================================================
+ // = Main Controller =
+ // ======================================================================================
+ eagleApp.controller('MainCtrl', function ($scope, $wrapState, $http, $injector, ServiceError, PageConfig, FeaturePageConfig, Site, Authorization, Entities, nvd3, Application, Feature, UI) {
+ window.serviceError = $scope.ServiceError = ServiceError;
+ window.site = $scope.Site = Site;
+ window.auth = $scope.Auth = Authorization;
+ window.entities = $scope.Entities = Entities;
+ window.application = $scope.Application = Application;
+ window.pageConfig = $scope.PageConfig = PageConfig;
+ window.featurePageConfig = $scope.FeaturePageConfig = FeaturePageConfig;
+ window.feature = $scope.Feature = Feature;
+ window.ui = $scope.UI = UI;
+ window.nvd3 = nvd3;
+ $scope.app = app;
+ $scope.common = common;
+
+ Object.defineProperty(window, "scope",{
+ get: function() {
+ return angular.element("[ui-view]").scope();
+ }
+ });
+
+ // Clean up
+ $scope.$on('$stateChangeStart', function (event, next, nextParam, current, currentParam) {
+ console.log("[Switch] current ->", current, currentParam);
+ console.log("[Switch] next ->", next, nextParam);
+ // Page initialization
+ PageConfig.reset();
+
+ // Dynamic navigation list
+ if(next.pageConfig) {
+ $scope.PageConfig.navConfig = $injector.get(next.pageConfig);
+ } else {
+ $scope.PageConfig.navConfig = {};
+ }
+
+ // Authorization
+ // > Login check
+ if (!common.getValueByPath(next, "access.skipCheck", false)) {
+ if (!Authorization.isLogin) {
+ console.log("[Authorization] Need access. Redirect...");
+ $wrapState.go("login");
+ }
+ }
+
+ // > Role control
+ /*var _roles = common.getValueByPath(next, "access.roles", []);
+ if (_roles.length && Authorization.userProfile.roles) {
+ var _roleMatch = false;
+ $.each(_roles, function (i, roleName) {
+ if (Authorization.isRole(roleName)) {
+ _roleMatch = true;
+ return false;
+ }
+ });
+
+ if (!_roleMatch) {
+ $wrapState.path("/dam");
+ }
+ }*/
+ });
+
+ $scope.$on('$stateChangeError', function (event, next, nextParam, current, currentParam, error) {
+ console.error("[Switch] Error", arguments);
+ });
+
+ // Get side bar navigation item class
+ $scope.getNavClass = function (page) {
+ var path = page.url.replace(/^#/, '');
+
+ if ($wrapState.path() === path) {
+ PageConfig.pageTitle = PageConfig.pageTitle || page.title;
+ return "active";
+ } else {
+ return "";
+ }
+ };
+
+ // Get side bar navigation item class visible
+ $scope.getNavVisible = function (page) {
+ if (!page.roles) return true;
+
+ for (var i = 0; i < page.roles.length; i += 1) {
+ var roleName = page.roles[i];
+ if (Authorization.isRole(roleName)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ // Authorization
+ $scope.logout = function () {
+ console.log("[Authorization] Logout. Redirect...");
+ Authorization.logout();
+ $wrapState.go("login");
+ };
+ });
+})();
\ No newline at end of file
[11/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/components/sortTable.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/components/sortTable.js b/eagle-server/src/main/webapp/app/dev/public/js/components/sortTable.js
new file mode 100644
index 0000000..4143491
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/components/sortTable.js
@@ -0,0 +1,231 @@
+/*
+ * 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 eagleComponents = angular.module('eagle.components');
+
+ eagleComponents.directive('sortTable', function($compile) {
+ return {
+ restrict: 'AE',
+ scope: true,
+ //terminal: true,
+ priority: 1001,
+
+ /**
+ * @param $scope
+ * @param $element
+ * @param {{}} $attrs
+ * @param {string} $attrs.sortTable Data source
+ * @param {string?} $attrs.isSorting Will bind parent variable of sort state
+ * @param {string?} $attrs.scope Will bind parent variable of current scope
+ * @param {string?} $attrs.sortpath Default sort path
+ * @param {[]?} $attrs.searchPathList Filter search path list
+ */
+ controller: function($scope, $element, $attrs) {
+ var sortmatch;
+ var worker;
+ var worker_id = 0;
+ if(typeof(Worker) !== "undefined") {
+ worker = new Worker("public/js/worker/sortTableWorker.js?_=" + window._TRS());
+ }
+
+ // Initialization
+ $scope.pageNumber = 1;
+ $scope.pageSize = 10;
+ $scope.maxSize = 10;
+ $scope.search = "";
+ $scope.orderKey = "";
+ $scope.orderAsc = true;
+
+ if($attrs.sortpath) {
+ sortmatch = $attrs.sortpath.match(/^(-)?(.*)$/);
+ if(sortmatch[1]) {
+ $scope.orderAsc = false;
+ }
+ $scope.orderKey = sortmatch[2];
+ }
+
+ // UI - Column sort
+ $scope.doSort = function(path) {
+ if($scope.orderKey === path) {
+ $scope.orderAsc = !$scope.orderAsc;
+ } else {
+ $scope.orderKey = path;
+ $scope.orderAsc = true;
+ }
+ };
+ $scope.checkSortClass = function(key) {
+ if($scope.orderKey === key) {
+ return "fa sort-mark " + ($scope.orderAsc ? "fa-sort-asc" : "fa-sort-desc");
+ }
+ return "fa fa-sort sort-mark";
+ };
+
+ // List filter & sort
+ function setState(bool) {
+ if(!$attrs.isSorting) return;
+
+ $scope.$parent[$attrs.isSorting] = bool;
+ }
+
+
+ var cacheSearch = "";
+ var cacheOrder = "";
+ var cacheOrderAsc = null;
+ var cacheFilteredList = null;
+ $scope.getFilteredList = function () {
+ if(
+ cacheSearch !== $scope.search ||
+ cacheOrder !== $scope.orderKey ||
+ cacheOrderAsc !== $scope.orderAsc ||
+ !cacheFilteredList
+ ) {
+ cacheSearch = $scope.search;
+ cacheOrder = $scope.orderKey;
+ cacheOrderAsc = $scope.orderAsc;
+
+ var fullList = $scope.$parent[$attrs.sortTable] || [];
+ if(!cacheFilteredList) cacheFilteredList = fullList;
+
+ if(!worker) {
+ cacheFilteredList = __sortTable_generateFilteredList(fullList, cacheSearch, cacheOrder, cacheOrderAsc, $scope.$parent[$attrs.searchPathList]);
+ setState(false);
+ } else {
+ worker_id += 1;
+ setState(true);
+ var list = JSON.stringify(fullList);
+ worker.postMessage({
+ search: cacheSearch,
+ order: cacheOrder,
+ orderAsc: cacheOrderAsc,
+ searchPathList: $scope.$parent[$attrs.searchPathList],
+ list: list,
+ id: worker_id
+ });
+ }
+ }
+
+ return cacheFilteredList;
+ };
+
+ // Week watch. Will not track each element
+ $scope.$watch($attrs.sortTable + ".length", function () {
+ cacheFilteredList = null;
+ });
+
+ function workMessage(event) {
+ var data = event.data;
+ if(worker_id !== data.id) return;
+
+ setState(false);
+ cacheFilteredList = data.list;
+ $scope.$apply();
+ }
+ worker.addEventListener("message", workMessage);
+
+ $scope.$on('$destroy', function() {
+ worker.removeEventListener("message", workMessage);
+ });
+
+ // Scope bind
+ if($attrs.scope) {
+ $scope.$parent[$attrs.scope] = $scope;
+ }
+ },
+ compile: function ($element) {
+ var contents = $element.contents().remove();
+
+ return {
+ post: function preLink($scope, $element) {
+ $scope.defaultPageSizeList = [10, 25, 50, 100];
+
+ $element.append(contents);
+
+ // Tool Container
+ var $toolContainer = $(
+ '<div class="tool-container clearfix">' +
+ '</div>'
+ ).insertBefore($element.find("table"));
+
+ // Search Box
+ var $search = $(
+ '<div class="search-box">' +
+ '<input type="search" class="form-control input-sm" placeholder="Search" ng-model="search" />' +
+ '<span class="fa fa-search" />' +
+ '</div>'
+ ).appendTo($toolContainer);
+ $compile($search)($scope);
+
+ // Page Size
+ var $pageSize = $(
+ '<div class="page-size">' +
+ 'Show' +
+ '<select class="form-control" ng-model="pageSize" convert-to-number>' +
+ '<option ng-repeat="size in pageSizeList || defaultPageSizeList track by $index">{{size}}</option>' +
+ '</select>' +
+ 'Entities' +
+ '</div>'
+ ).appendTo($toolContainer);
+ $compile($pageSize)($scope);
+
+ // Sort Column
+ $element.find("table [sortpath]").each(function () {
+ var $this = $(this);
+ var _sortpath = $this.attr("sortpath");
+ $this.attr("ng-click", "doSort('" + _sortpath + "')");
+ $this.prepend('<span ng-class="checkSortClass(\'' + _sortpath + '\')"></span>');
+ $compile($this)($scope);
+ });
+
+ // Repeat Items
+ var $tr = $element.find("table [ts-repeat], table > tbody > tr").filter(":first");
+ $tr.attr("ng-repeat", 'item in getFilteredList().slice((pageNumber - 1) * pageSize, pageNumber * pageSize) track by $index');
+ $compile($tr)($scope);
+
+ // Page Navigation
+ var $navigation = $(
+ '<div class="navigation-bar clearfix">' +
+ '<span>' +
+ 'show {{(pageNumber - 1) * pageSize + 1}} to {{pageNumber * pageSize}} of {{getFilteredList().length}} items' +
+ '</span>' +
+ '<uib-pagination total-items="getFilteredList().length" ng-model="pageNumber" boundary-links="true" items-per-page="pageSize" max-size="maxSize"></uib-pagination>' +
+ '</div>'
+ ).appendTo($element);
+ $compile($navigation)($scope);
+ }
+ };
+ }
+ };
+ });
+
+ eagleComponents.directive('convertToNumber', function() {
+ return {
+ require: 'ngModel',
+ link: function(scope, element, attrs, ngModel) {
+ ngModel.$parsers.push(function(val) {
+ return parseInt(val, 10);
+ });
+ ngModel.$formatters.push(function(val) {
+ return '' + val;
+ });
+ }
+ };
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/components/widget.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/components/widget.js b/eagle-server/src/main/webapp/app/dev/public/js/components/widget.js
new file mode 100644
index 0000000..bc59b3c
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/components/widget.js
@@ -0,0 +1,52 @@
+/*
+ * 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 eagleComponents = angular.module('eagle.components');
+
+ eagleComponents.directive('widget', function($compile, Site) {
+ return {
+ restrict: 'AE',
+ priority: 1001,
+
+ controller: function($scope, $element, $attrs) {
+ },
+ compile: function ($element) {
+ $element.contents().remove();
+
+ return {
+ post: function preLink($scope, $element) {
+ var widget = $scope.widget;
+ $scope.site = Site.current();
+
+ if(widget.renderFunc) {
+ // Prevent auto compile if return false
+ if(widget.renderFunc($element, $scope, $compile) !== false) {
+ $compile($element.contents())($scope);
+ }
+ } else {
+ $element.append("Widget don't provide render function:" + widget.application + " - " + widget.name);
+ }
+ }
+ };
+ }
+ };
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/ctrls/alertCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/ctrls/alertCtrl.js b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/alertCtrl.js
new file mode 100644
index 0000000..17ce775
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/alertCtrl.js
@@ -0,0 +1,119 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+
+ // TODO: Mock data
+ var publishmentTypes = [
+ {
+ "type": "email",
+ "className": "org.apache.eagle.alert.engine.publisher.impl.AlertEmailPublisher",
+ "description": "send alert to email",
+ "enabled":true,
+ "fields": [{"name":"sender"},{"name":"recipients"},{"name":"subject"},{"name":"smtp.server", "value":"host1"},{"name":"connection", "value":"plaintext"},{"name":"smtp.port", "value": "25"}]
+ },
+ {
+ "type": "kafka",
+ "className": "org.apache.eagle.alert.engine.publisher.impl.AlertKafkaPublisher",
+ "description": "send alert to kafka bus",
+ "enabled":true,
+ "fields": [{"name":"kafka_broker","value":"sandbox.hortonworks.com:6667"},{"name":"topic"}]
+ }
+ ];
+
+
+ // ======================================================================================
+ // = Main =
+ // ======================================================================================
+ eagleControllers.controller('alertCtrl', function ($scope, $wrapState, PageConfig) {
+ PageConfig.title = "Alert";
+ $scope.getState = function() {
+ return $wrapState.current.name;
+ };
+ });
+
+ // ======================================================================================
+ // = List =
+ // ======================================================================================
+ eagleControllers.controller('alertListCtrl', function ($scope, $wrapState, PageConfig) {
+ PageConfig.subTitle = "Explore Alerts";
+ });
+
+ // ======================================================================================
+ // = Policy List =
+ // ======================================================================================
+ eagleControllers.controller('policyListCtrl', function ($scope, $wrapState, PageConfig, Entity, UI) {
+ PageConfig.subTitle = "Manage Policies";
+
+ $scope.policyList = Entity.queryMetadata("policies");
+
+ $scope.deletePolicy = function (item) {
+ UI.deleteConfirm(item.name)(function (entity, closeFunc) {
+ Entity.deleteMetadata("policies/" + item.name)._promise.finally(function () {
+ closeFunc();
+ $scope.policyList._refresh();
+ });
+ });
+ };
+ });
+
+ // ======================================================================================
+ // = Policy Create =
+ // ======================================================================================
+ function connectPolicyEditController(entity, args) {
+ var newArgs = [entity];
+ Array.prototype.push.apply(newArgs, args);
+ /* jshint validthis: true */
+ policyEditController.apply(this, newArgs);
+ }
+ function policyEditController(policy, $scope, $wrapState, PageConfig, Entity) {
+ $scope.policy = policy;
+ }
+
+ eagleControllers.controller('policyCreateCtrl', function ($scope, $wrapState, PageConfig, Entity) {
+ PageConfig.subTitle = "Define Alert Policy";
+ connectPolicyEditController({}, arguments);
+ });
+ eagleControllers.controller('policyEditCtrl', function ($scope, $wrapState, PageConfig, Entity) {
+ PageConfig.subTitle = "Edit Alert Policy";
+ var args = arguments;
+
+ // TODO: Wait for backend data update
+ $scope.policyList = Entity.queryMetadata("policies");
+ $scope.policyList._promise.then(function () {
+ var policy = $scope.policyList.find(function (entity) {
+ return entity.name === $wrapState.param.name;
+ });
+
+ if(policy) {
+ connectPolicyEditController(policy, args);
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: "Policy '" + $wrapState.param.name + "' not found!"
+ }, function () {
+ $wrapState.go("alert.policyList");
+ });
+ }
+ });
+
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/ctrls/integrationCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/ctrls/integrationCtrl.js b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/integrationCtrl.js
new file mode 100644
index 0000000..a807520
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/integrationCtrl.js
@@ -0,0 +1,226 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+
+ // ======================================================================================
+ // = Main =
+ // ======================================================================================
+ eagleControllers.controller('integrationCtrl', function ($scope, $wrapState, PageConfig) {
+ PageConfig.title = "Integration";
+ $scope.getState = function() {
+ return $wrapState.current.name;
+ };
+ });
+
+ // ======================================================================================
+ // = Site =
+ // ======================================================================================
+ eagleControllers.controller('integrationSiteListCtrl', function ($scope, $wrapState, PageConfig, UI, Entity, Site) {
+ PageConfig.title = "Integration";
+ PageConfig.subTitle = "Site";
+
+ $scope.deleteSite = function (site) {
+ UI.deleteConfirm(site.siteId)
+ (function (entity, closeFunc, unlock) {
+ Entity.delete("sites", site.uuid)._then(function () {
+ Site.reload();
+ closeFunc();
+ }, unlock);
+ });
+ };
+
+ $scope.newSite = function () {
+ UI.createConfirm("Site", {}, [
+ {field: "siteId", name: "Site Id"},
+ {field: "siteName", name: "Display Name", optional: true},
+ {field: "description", name: "Description", optional: true, type: "blob", rows: 5}
+ ])(function (entity, closeFunc, unlock) {
+ Entity.create("sites", entity)._then(function () {
+ Site.reload();
+ closeFunc();
+ }, unlock);
+ });
+ };
+ });
+
+ eagleControllers.controller('integrationSiteCtrl', function ($sce, $scope, $wrapState, PageConfig, Entity, UI, Site, Application) {
+ PageConfig.title = "Site";
+ PageConfig.subTitle = $wrapState.param.id;
+
+ // Check site
+ $scope.site = Site.find($wrapState.param.id);
+ if(!$scope.site) {
+ $.dialog({
+ title: "OPS",
+ content: "Site not found!"
+ }, function () {
+ $wrapState.go("integration.siteList");
+ });
+ return;
+ }
+
+ // Map applications
+ function mapApplications() {
+ Site.getPromise().then(function () {
+ $scope.site = Site.find($wrapState.param.id);
+ var uninstalledApplicationList = common.array.minus(Application.providerList, $scope.site.applicationList, "type", "descriptor.type");
+ $scope.applicationList = $.map($scope.site.applicationList, function (app) {
+ app.installed = true;
+ return app;
+ }).concat($.map(uninstalledApplicationList, function (oriApp) {
+ return { origin: oriApp };
+ }));
+ });
+ }
+ mapApplications();
+
+ // Application refresh
+ function refreshApplications() {
+ Application.reload().getPromise().then(mapApplications);
+ }
+
+ // Application status class
+ $scope.getAppStatusClass = function (application) {
+ switch((application.status || "").toUpperCase()) {
+ case "INITIALIZED":
+ return "primary";
+ case "STARTING":
+ return "warning";
+ case "RUNNING":
+ return "success";
+ case "STOPPING":
+ return "warning";
+ case "STOPPED":
+ return "danger";
+ }
+ return "default";
+ };
+
+ // Get started application count
+ $scope.getStartedAppCount = function () {
+ return $.grep($scope.site.applicationList, function (app) {
+ return $.inArray((app.status || "").toUpperCase(), ["STARTING", "RUNNING"]) >= 0;
+ }).length;
+ };
+
+ // Application detail
+ $scope.showAppDetail = function (application) {
+ application = application.origin;
+ var docs = application.docs || {install: "", uninstall: ""};
+ $scope.application = application;
+ $scope.installHTML = $sce.trustAsHtml(docs.install);
+ $scope.uninstallHTML = $sce.trustAsHtml(docs.uninstall);
+ $("#appMDL").modal();
+ };
+
+ // Install application
+ $scope.installApp = function (application) {
+ application = application.origin;
+ var fields = common.getValueByPath(application, "configuration.properties", []);
+ fields = $.map(fields, function (prop) {
+ return {
+ field: prop.name,
+ name: prop.displayName,
+ description: prop.description,
+ defaultValue: prop.value,
+ optional: prop.required === false
+ };
+ });
+
+ UI.fieldConfirm({
+ title: "Install '" + application.type + "'"
+ }, null, fields)(function (entity, closeFunc, unlock) {
+ Entity.create("apps/install", {
+ siteId: $scope.site.siteId,
+ appType: application.type,
+ configuration: entity
+ })._then(function (res) {
+ refreshApplications();
+ closeFunc();
+ }, function (res) {
+ $.dialog({
+ title: "OPS",
+ content: res.data.message
+ });
+ unlock();
+ });
+ });
+ };
+
+ // Uninstall application
+ $scope.uninstallApp = function (application) {
+ UI.deleteConfirm(application.descriptor.name + " - " + application.site.siteId)
+ (function (entity, closeFunc, unlock) {
+ Entity.delete("apps/uninstall", application.uuid)._then(function () {
+ refreshApplications();
+ closeFunc();
+ }, unlock);
+ });
+ };
+
+ // Start application
+ $scope.startApp = function (application) {
+ Entity.post("apps/start", { uuid: application.uuid })._then(function () {
+ refreshApplications();
+ });
+ };
+
+ // Stop application
+ $scope.stopApp = function (application) {
+ Entity.post("apps/stop", { uuid: application.uuid })._then(function () {
+ refreshApplications();
+ });
+ };
+ });
+
+ // ======================================================================================
+ // = Application =
+ // ======================================================================================
+ eagleControllers.controller('integrationApplicationListCtrl', function ($sce, $scope, $wrapState, PageConfig, Application) {
+ $scope.showAppDetail = function(application) {
+ var docs = application.docs || {install: "", uninstall: ""};
+ $scope.application = application;
+ $scope.installHTML = $sce.trustAsHtml(docs.install);
+ $scope.uninstallHTML = $sce.trustAsHtml(docs.uninstall);
+ $("#appMDL").modal();
+ };
+ });
+
+ // ======================================================================================
+ // = Stream =
+ // ======================================================================================
+ eagleControllers.controller('integrationStreamListCtrl', function ($scope, $wrapState, PageConfig, Application) {
+ PageConfig.title = "Integration";
+ PageConfig.subTitle = "Streams";
+
+ $scope.streamList = $.map(Application.list, function (app) {
+ return (app.streams || []).map(function (stream) {
+ return {
+ streamId: stream.streamId,
+ appType: app.descriptor.type,
+ siteId: app.site.siteId,
+ schema: stream.schema
+ };
+ });
+ });
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/ctrls/main.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/ctrls/main.js b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/main.js
new file mode 100644
index 0000000..e4a0075
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/main.js
@@ -0,0 +1,27 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers', ['ui.bootstrap', 'eagle.components', 'eagle.service']);
+
+ // ===========================================================
+ // = Controller =
+ // ===========================================================
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/ctrls/mainCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/ctrls/mainCtrl.js b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/mainCtrl.js
new file mode 100644
index 0000000..ddd3314
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/mainCtrl.js
@@ -0,0 +1,62 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+
+ // ======================================================================================
+ // = Home =
+ // ======================================================================================
+ eagleControllers.controller('homeCtrl', function ($scope, $wrapState, PageConfig) {
+ PageConfig.title = "Home";
+ });
+
+ // ======================================================================================
+ // = Set Up =
+ // ======================================================================================
+ eagleControllers.controller('setupCtrl', function ($wrapState, $scope, PageConfig, Entity, Site) {
+ PageConfig.hideTitle = true;
+
+ $scope.lock = false;
+ $scope.siteId = "sandbox";
+ $scope.siteName = "Sandbox";
+ $scope.description = "";
+
+ $scope.createSite = function () {
+ $scope.lock = true;
+
+ Entity.create("sites", {
+ siteId: $scope.siteId,
+ siteName: $scope.siteName,
+ description: $scope.description
+ })._then(function () {
+ Site.reload();
+ $wrapState.go('home');
+ }, function (res) {
+ $.dialog({
+ title: "OPS!",
+ content: res.message
+ });
+ }).finally(function () {
+ $scope.lock = false;
+ });
+ };
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/ctrls/siteCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/ctrls/siteCtrl.js b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/siteCtrl.js
new file mode 100644
index 0000000..94c2ed7
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/ctrls/siteCtrl.js
@@ -0,0 +1,33 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+
+
+ // ======================================================================================
+ // = Main =
+ // ======================================================================================
+ eagleControllers.controller('siteCtrl', function ($scope, PageConfig, Site) {
+ var site = Site.current();
+ PageConfig.title = site.siteName || site.siteId;
+ PageConfig.subTitle = "home";
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/index.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/index.js b/eagle-server/src/main/webapp/app/dev/public/js/index.js
new file mode 100644
index 0000000..906479f
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/index.js
@@ -0,0 +1,326 @@
+/*
+ * 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';
+
+ // ======================================================================================
+ // = Host =
+ // ======================================================================================
+ var _host = "";
+ var _app = {};
+ if(localStorage) {
+ _host = localStorage.getItem("host") || "";
+ _app = common.parseJSON(localStorage.getItem("app") || "") || {};
+ }
+
+ window._host = function (host) {
+ if(host) {
+ _host = host.replace(/[\\\/]+$/, "");
+ if(localStorage) {
+ localStorage.setItem("host", _host);
+ }
+ }
+ return _host;
+ };
+
+ window._app = function (appName, viewPath) {
+ if(arguments.length) {
+ _app[appName] = {
+ viewPath: viewPath
+ };
+ if(localStorage) {
+ localStorage.setItem("app", JSON.stringify(_app));
+ }
+ }
+ return _app;
+ };
+
+ // ======================================================================================
+ // = Register =
+ // ======================================================================================
+ var _moduleStateId = 0;
+ var _registerAppList = [];
+ var _lastRegisterApp = null;
+ var _hookRequireFunc = null;
+
+ function Module(dependencies) {
+ this.dependencies = dependencies;
+ this.queueList = [];
+ this.routeList = [];
+ this.portalList = [];
+ this.widgetList = [];
+
+ this.requireRest = 0;
+ this.requireDeferred = null;
+
+ return this;
+ }
+
+ // GRUNT REPLACEMENT: Module.buildTimestamp = TIMESTAMP
+ window._TRS = function() {
+ return Module.buildTimestamp || Math.random();
+ };
+
+ Module.prototype.service = function () {
+ this.queueList.push({type: "service", args: arguments});
+ return this;
+ };
+ Module.prototype.directive = function () {
+ this.queueList.push({type: "directive", args: arguments});
+ return this;
+ };
+ Module.prototype.controller = function () {
+ this.queueList.push({type: "controller", args: arguments});
+ return this;
+ };
+
+ /**
+ * Add portal into side navigation bar.
+ * @param {{}} portal Config portal content
+ * @param {string} portal.name Display name
+ * @param {string} portal.icon Display icon. Use 'FontAwesome'
+ * @param {string=} portal.path Route path
+ * @param {[]=} portal.list Sub portal
+ * @param {boolean} isSite true will show in site page or will shown in main page
+ */
+ Module.prototype.portal = function (portal, isSite) {
+ this.portalList.push({portal: portal, isSite: isSite});
+ return this;
+ };
+
+ /**
+ * Set application route
+ * @param {{}|string=} state Config state. More info please check angular ui router
+ * @param {{}} config Route config
+ * @param {string} config.url Root url. start with '/'
+ * @param {string} config.templateUrl Template url. Relative path of application `viewPath`
+ * @param {string} config.controller Set page controller
+ */
+ Module.prototype.route = function (state, config) {
+ if(arguments.length === 1) {
+ config = state;
+ state = "_APPLICATION_STATE_" + _moduleStateId++;
+ }
+
+ if(!config.url) throw "Url not defined!";
+
+ this.routeList.push({
+ state: state,
+ config: config
+ });
+ return this;
+ };
+
+ /**
+ * Register home page widget
+ * @param {string} name Widget name
+ * @param {Function} renderFunc Register function
+ * @param {boolean} isSite true will show in site page or will shown in main page
+ */
+ Module.prototype.widget = function (name, renderFunc, isSite) {
+ this.widgetList.push({
+ widget: {
+ name: name,
+ renderFunc: renderFunc
+ },
+ isSite: isSite
+ });
+ return this;
+ };
+
+ Module.prototype.require = function (scriptURL) {
+ var _this = this;
+
+ _this.requireRest += 1;
+ if(!_this.requireDeferred) {
+ _this.requireDeferred = $.Deferred();
+ }
+
+ setTimeout(function () {
+ $.getScript(_this.baseURL + "/" + scriptURL).then(function () {
+ if(_hookRequireFunc) {
+ _hookRequireFunc(_this);
+ } else {
+ console.error("Hook function not set!", _this);
+ }
+ }).always(function () {
+ _hookRequireFunc = null;
+ _this.requireRest -= 1;
+ _this.requireCheck();
+ });
+ }, 0);
+ };
+
+ Module.prototype.requireCSS = function (styleURL) {
+ var _this = this;
+ setTimeout(function () {
+ $("<link/>", {
+ rel: "stylesheet",
+ type: "text/css",
+ href: _this.baseURL + "/" + styleURL + "?_=" + _TRS()
+ }).appendTo("head");
+ }, 0);
+ };
+
+ Module.prototype.requireCheck = function () {
+ if(this.requireRest === 0) {
+ this.requireDeferred.resolve();
+ }
+ };
+
+ /**
+ * Get module instance. Will init angular module.
+ * @param {string} moduleName angular module name
+ */
+ Module.prototype.getInstance = function (moduleName) {
+ var _this = this;
+ var deferred = $.Deferred();
+ var module = angular.module(moduleName, this.dependencies);
+
+ // Required list
+ $.when(this.requireDeferred).always(function () {
+ // Fill module props
+ $.each(_this.queueList, function (i, item) {
+ var type = item.type;
+ var args = Array.prototype.slice.apply(item.args);
+ if (type === "controller") {
+ args[0] = moduleName + "_" + args[0];
+ }
+ module[type].apply(module, args);
+ });
+
+ // Render routes
+ var routeList = $.map(_this.routeList, function (route) {
+ var config = route.config = $.extend({}, route.config);
+
+ // Parse url
+ if(config.site) {
+ config.url = "/site/:siteId/" + config.url.replace(/^[\\\/]/, "");
+ }
+
+ // Parse template url
+ var parser = document.createElement('a');
+ parser.href = _this.baseURL + "/" + config.templateUrl;
+ parser.search = parser.search ? parser.search + "&_=" + window._TRS() : "?_=" + window._TRS();
+ config.templateUrl = parser.href;
+
+ if (typeof config.controller === "string") {
+ config.controller = moduleName + "_" + config.controller;
+ }
+
+ return route;
+ });
+
+ // Portal update
+ $.each(_this.portalList, function (i, config) {
+ config.portal.application = moduleName;
+ });
+
+ // Widget update
+ $.each(_this.widgetList, function (i, config) {
+ config.widget.application = moduleName;
+ });
+
+ deferred.resolve({
+ application: moduleName,
+ portalList: _this.portalList,
+ routeList: routeList,
+ widgetList: _this.widgetList
+ });
+ });
+
+ return deferred;
+ };
+
+ window.register = function (dependencies) {
+ if($.isArray(dependencies)) {
+ _lastRegisterApp = new Module(dependencies);
+ } else if(typeof dependencies === "function") {
+ _hookRequireFunc = function (module) {
+ dependencies(module);
+ };
+ }
+ return _lastRegisterApp;
+ };
+
+ // ======================================================================================
+ // = Main =
+ // ======================================================================================
+ $(function () {
+ console.info("[Eagle] Application initialization...");
+
+ // Load providers
+ $.get(_host + "/rest/apps/providers").then(function (res) {
+ /**
+ * @param {{}} oriApp application provider
+ * @param {string} oriApp.viewPath path of application interface
+ */
+ var promiseList = $.map(res.data || [], function (oriApp) {
+ var deferred = $.Deferred();
+ var viewPath = common.getValueByPath(_app, [oriApp.type, "viewPath"], oriApp.viewPath);
+
+ if(viewPath) {
+ var url = viewPath;
+ url = url.replace(/^[\\\/]/, "").replace(/[\\\/]$/, "");
+
+ $.getScript(url + "/index.js").then(function () {
+ if(_lastRegisterApp) {
+ _registerAppList.push(oriApp.type);
+ _lastRegisterApp.baseURL = url;
+ _lastRegisterApp.getInstance(oriApp.type).then(function (module) {
+ deferred.resolve(module);
+ });
+ } else {
+ console.error("Application not register:", oriApp.type);
+ deferred.resolve();
+ }
+ }, function () {
+ console.error("Load application failed:", oriApp.type, viewPath);
+ deferred.resolve();
+ }).always(function () {
+ _lastRegisterApp = null;
+ });
+ } else {
+ deferred.resolve();
+ }
+
+ return deferred;
+ });
+
+ common.deferred.all(promiseList).then(function (moduleList) {
+ var routeList = $.map(moduleList, function (module) {
+ return module && module.routeList;
+ });
+ var portalList = $.map(moduleList, function (module) {
+ return module && module.portalList;
+ });
+ var widgetList = $.map(moduleList, function (module) {
+ return module && module.widgetList;
+ });
+
+ $(document).trigger("APPLICATION_READY", {
+ appList: _registerAppList,
+ routeList: routeList,
+ portalList: portalList,
+ widgetList: widgetList
+ });
+ });
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/applicationSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/applicationSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/applicationSrv.js
new file mode 100644
index 0000000..fac44f9
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/applicationSrv.js
@@ -0,0 +1,71 @@
+/*
+ * 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, $wrapState, Entity) {
+ var Application = {};
+ var reloadListenerList = [];
+
+ Application.list = [];
+
+ Application.find = function (type, site) {
+ return $.grep(Application.list, function (app) {
+ return app.descriptor.type === type && (site ? app.site.siteId === site : true);
+ });
+ };
+
+ // Load applications
+ Application.reload = function () {
+ Application.list = Entity.query('apps');
+ Application.list._then(function () {
+ $.each(reloadListenerList, function (i, listener) {
+ listener(Application);
+ });
+ });
+ return Application;
+ };
+
+ Application.onReload = function (func) {
+ reloadListenerList.push(func);
+ };
+
+ // Load providers
+ Application.providers = {};
+ Application.providerList = Entity.query('apps/providers');
+ Application.providerList._promise.then(function () {
+ $.each(Application.providerList, function (i, oriApp) {
+ Application.providers[oriApp.type] = oriApp;
+ });
+ });
+
+ Application.getPromise = function () {
+ return Application.list._promise.then(function() {
+ return Application;
+ });
+ };
+
+ // Initialization
+ Application.reload();
+
+ return Application;
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/entitySrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/entitySrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/entitySrv.js
new file mode 100644
index 0000000..61c244d
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/entitySrv.js
@@ -0,0 +1,135 @@
+/*
+ * 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');
+
+ var _host = "";
+ if(localStorage) {
+ _host = localStorage.getItem("host") || "";
+ }
+
+ serviceModule.service('Entity', function($http, $q) {
+ function Entity() {}
+
+ function wrapList(list, promise) {
+ list._done = false;
+ list._promise = promise.then(function (res) {
+ var data = res.data;
+ list.splice(0);
+ Array.prototype.push.apply(list, data.data);
+ list._done = true;
+
+ return res;
+ });
+ return withThen(list);
+ }
+
+ function withThen(list) {
+ list._then = list._promise.then.bind(list._promise);
+ return list;
+ }
+
+ // Dev usage. Set rest api source
+ Entity._host = function (host) {
+ console.warn("This function only used for development usage.");
+ if(host) {
+ _host = host.replace(/[\\\/]+$/, "");
+ if(localStorage) {
+ localStorage.setItem("host", _host);
+ }
+ }
+ return _host;
+ };
+
+ Entity.query = function (url) {
+ var list = [];
+ list._refresh = function () {
+ return wrapList(list, $http.get(_host + "/rest/" + url));
+ };
+
+ return list._refresh();
+ };
+
+ Entity.create = Entity.post = function (url, entity) {
+ var list = [];
+ return wrapList(list, $http({
+ method: 'POST',
+ url: _host + "/rest/" + url,
+ headers: {
+ "Content-Type": "application/json"
+ },
+ data: entity
+ }));
+ };
+
+ Entity.delete = function (url, uuid) {
+ var list = [];
+ return wrapList(list, $http({
+ method: 'DELETE',
+ url: _host + "/rest/" + url,
+ headers: {
+ "Content-Type": "application/json"
+ },
+ data: {uuid: uuid}
+ }));
+ };
+
+ /**
+ * Merge 2 array into one. Will return origin list before target list is ready. Then fill with target list.
+ * @param oriList
+ * @param tgtList
+ * @return {[]}
+ */
+ Entity.merge = function (oriList, tgtList) {
+ oriList = oriList || [];
+
+ var list = [].concat(oriList);
+ list._done = tgtList._done;
+ list._refresh = tgtList._refresh;
+ list._promise = tgtList._promise;
+
+ list._promise.then(function () {
+ list.splice(0);
+ Array.prototype.push.apply(list, tgtList);
+ list._done = true;
+ });
+
+ list = withThen(list);
+
+ return list;
+ };
+
+ // TODO: metadata will be removed
+ Entity.queryMetadata = function (url) {
+ return Entity.query('metadata/' + url);
+ };
+
+ Entity.deleteMetadata = function (url) {
+ return {
+ _promise: $http.delete(_host + "/rest/metadata/" + url).then(function (res) {
+ console.log(res);
+ })
+ };
+ };
+
+ return Entity;
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/main.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/main.js b/eagle-server/src/main/webapp/app/dev/public/js/services/main.js
new file mode 100644
index 0000000..c060de8
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/main.js
@@ -0,0 +1,23 @@
+/*
+ * 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 eagleSrv = angular.module('eagle.service', []);
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js
new file mode 100644
index 0000000..cd0e8b4
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/pageSrv.js
@@ -0,0 +1,137 @@
+/*
+ * 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');
+
+ // ============================================================
+ // = Page =
+ // ============================================================
+ serviceModule.service('PageConfig', function() {
+ function PageConfig() {
+ }
+
+ PageConfig.reset = function () {
+ PageConfig.title = "";
+ PageConfig.subTitle = "";
+ PageConfig.navPath = [];
+ PageConfig.hideTitle = false;
+ };
+
+ return PageConfig;
+ });
+
+ // ============================================================
+ // = Portal =
+ // ============================================================
+ var defaultPortalList = [
+ {name: "Home", icon: "home", path: "#/"},
+ {name: "Insight", icon: "heartbeat", list: [
+ {name: "Dashboards"},
+ {name: "Metrics"}
+ ]},
+ {name: "Alert", icon: "bell", list: [
+ {name: "Explore Alerts", path: "#/alert/"},
+ {name: "Manage Policies", path: "#/alert/policyList"},
+ {name: "Define Policy", path: "#/alert/policyCreate"}
+ ]}
+ ];
+ var adminPortalList = [
+ {name: "Integration", icon: "puzzle-piece", list: [
+ {name: "Sites", path: "#/integration/siteList"},
+ {name: "Applications", path: "#/integration/applicationList"},
+ {name: "Streams", path: "#/integration/streamList"}
+ ]}
+ ];
+
+ serviceModule.service('Portal', function($wrapState, Site) {
+ var Portal = {};
+
+ var mainPortalList = [];
+ var sitePortalList = [];
+ var connectedMainPortalList = [];
+ var sitePortals = {};
+
+ var backHome = {name: "Back home", icon: "arrow-left", path: "#/"};
+
+ Portal.register = function (portal, isSite) {
+ (isSite ? sitePortalList : mainPortalList).push(portal);
+ };
+
+ function convertSitePortal(site, portal) {
+ portal = $.extend({}, portal, {
+ path: portal.path ? "#/site/" + site.siteId + "/" + portal.path.replace(/^[\\\/]/, "") : null
+ });
+
+ if(portal.list) {
+ portal.list = $.map(portal.list, function (portal) {
+ return convertSitePortal(site, portal);
+ });
+ }
+
+ return portal;
+ }
+
+ Portal.refresh = function () {
+ // TODO: check admin
+
+ // Main level
+ connectedMainPortalList = defaultPortalList.concat(adminPortalList);
+ var siteList = $.map(Site.list, function (site) {
+ return {
+ name: site.siteName || site.siteId,
+ path: "#/site/" + site.siteId
+ };
+ });
+ connectedMainPortalList.push({name: "Sites", icon: "server", list: siteList});
+
+ // Site level
+ sitePortals = {};
+ $.each(Site.list, function (i, site) {
+ var siteHome = {name: "Home", icon: "home", path: "#/site/" + site.siteId};
+ sitePortals[site.siteId] = [backHome, siteHome].concat($.map(sitePortalList, function (portal) {
+ var hasApp = !!common.array.find(portal.application, site.applicationList, "descriptor.type");
+ if(hasApp) {
+ return convertSitePortal(site, portal);
+ }
+ }));
+ });
+ };
+
+ Object.defineProperty(Portal, 'list', {
+ get: function () {
+ var match = $wrapState.path().match(/^\/site\/([^\/]*)/);
+ if(match && match[1]) {
+ return sitePortals[match[1]];
+ } else {
+ return connectedMainPortalList;
+ }
+ }
+ });
+
+
+ // Initialization
+ Site.onReload(Portal.refresh);
+
+ Portal.refresh();
+
+ return Portal;
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/siteSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/siteSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/siteSrv.js
new file mode 100644
index 0000000..399456d
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/siteSrv.js
@@ -0,0 +1,111 @@
+/*
+ * 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($q, $wrapState, Entity, Application) {
+ var Site = {};
+ var reloadListenerList = [];
+
+ Site.list = [];
+
+ // Link with application
+ function linkApplications(siteList, ApplicationList) {
+ $.each(siteList, function (i, site) {
+ var applications = common.array.find(site.siteId, ApplicationList, 'site.siteId', true);
+
+ $.each(applications, function (i, app) {
+ app.descriptor = app.descriptor || {};
+ var oriApp = Application.providers[app.descriptor.type];
+ Object.defineProperty(app, 'origin', {
+ configurable: true,
+ get: function () {
+ return oriApp;
+ }
+ });
+ });
+
+ Object.defineProperties(site, {
+ applicationList: {
+ configurable: true,
+ get: function () {
+ return applications;
+ }
+ }
+ });
+ });
+ }
+
+ // Load sites
+ Site.reload = function () {
+ var list = Site.list = Entity.query('sites');
+ list._promise.then(function () {
+ linkApplications(list, Application.list);
+ $.each(reloadListenerList, function (i, listener) {
+ listener(Site);
+ });
+ });
+ return Site;
+ };
+
+ Site.onReload = function (func) {
+ reloadListenerList.push(func);
+ };
+
+ // Find Site
+ Site.find = function (siteId) {
+ return common.array.find(siteId, Site.list, 'siteId');
+ };
+
+ Site.current = function () {
+ return Site.find($wrapState.param.siteId);
+ };
+
+ Site.getPromise = function (config) {
+ var siteList = Site.list;
+
+ return $q.all([siteList._promise, Application.getPromise()]).then(function() {
+ // Site check
+ if(config && config.site !== false && siteList.length === 0) {
+ $wrapState.go('setup', 1);
+ return $q.reject(Site);
+ }
+
+ // Application check
+ if(config && config.application !== false && Application.list.length === 0) {
+ $wrapState.go('integration.site', {id: siteList[0].siteId}, 1);
+ return $q.reject(Site);
+ }
+
+ return Site;
+ });
+ };
+
+ // Initialization
+ Application.onReload(function () {
+ Site.reload();
+ });
+
+ Site.reload();
+
+ return Site;
+ });
+}());
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/timeSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/timeSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/timeSrv.js
new file mode 100644
index 0000000..9d1f85c
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/timeSrv.js
@@ -0,0 +1,277 @@
+/*
+ * 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 UNITS = [
+ ["days", "day", "day"],
+ ["hours", "hr", "hr"],
+ ["minutes", "min", "min"],
+ ["seconds", "s", "s"]
+ ];
+
+ var keepTime = false;
+ var serviceModule = angular.module('eagle.service');
+
+ serviceModule.service('Time', function($q, $wrapState) {
+ var startTime, endTime;
+ var reloadListenerList = [];
+
+ var $Time = function (time) {
+ var _mom;
+
+ if(arguments.length === 1 && time === undefined) {
+ return null;
+ }
+
+ switch (time) {
+ case "startTime":
+ return startTime;
+ case "endTime":
+ return endTime;
+ case "month":
+ _mom = new moment();
+ _mom.utcOffset($Time.UTC_OFFSET);
+ _mom.date(1).hours(0).minutes(0).seconds(0).millisecond(0);
+ break;
+ case "monthEnd":
+ _mom = $Time("month").add(1, "month").subtract(1, "s");
+ break;
+ case "week":
+ _mom = new moment();
+ _mom.utcOffset($Time.UTC_OFFSET);
+ _mom.weekday(0).hours(0).minutes(0).seconds(0).millisecond(0);
+ break;
+ case "weekEnd":
+ _mom = $Time("week").add(7, "d").subtract(1, "s");
+ break;
+ case "day":
+ _mom = new moment();
+ _mom.utcOffset($Time.UTC_OFFSET);
+ _mom.hours(0).minutes(0).seconds(0).millisecond(0);
+ break;
+ case "dayEnd":
+ _mom = $Time("day").add(1, "d").subtract(1, "s");
+ break;
+ case "hour":
+ _mom = new moment();
+ _mom.utcOffset($Time.UTC_OFFSET);
+ _mom.minutes(0).seconds(0).millisecond(0);
+ break;
+ case "hourEnd":
+ _mom = $Time("hour").add(1, "h").subtract(1, "s");
+ break;
+ default:
+ // Parse string number
+ if(typeof time === "string") {
+ if(!isNaN(+time)) {
+ time = +time;
+ } else {
+ time = new moment(time);
+ time.add(time.utcOffset(), "minutes");
+ }
+ }
+
+ _mom = new moment(time);
+ _mom.utcOffset($Time.UTC_OFFSET);
+ }
+ return _mom;
+ };
+
+ $Time.TIME_RANGE_PICKER = "timeRange";
+ $Time.pickerType = null;
+ $Time._reloadListenerList = reloadListenerList;
+
+ // TODO: time zone
+ $Time.UTC_OFFSET = 0;
+
+ $Time.FORMAT = "YYYY-MM-DD HH:mm:ss";
+ $Time.SHORT_FORMAT = "MM-DD HH:mm";
+
+ $Time.format = function (time, format) {
+ time = $Time(time);
+ return time ? time.format(format || $Time.FORMAT) : "-";
+ };
+
+ $Time.startTime = function () {
+ return startTime;
+ };
+
+ $Time.endTime = function () {
+ return endTime;
+ };
+
+ $Time.timeRange = function (startTimeValue, endTimeValue) {
+ startTime = $Time(startTimeValue);
+ endTime = $Time(endTimeValue);
+
+ keepTime = true;
+ $wrapState.go(".", $.extend({}, $wrapState.param, {
+ startTime: $Time.format(startTime),
+ endTime: $Time.format(endTime)
+ }), {notify: false});
+
+ $.each(reloadListenerList, function (i, listener) {
+ listener($Time);
+ });
+ };
+
+ $Time.onReload = function (func, $scope) {
+ reloadListenerList.push(func);
+
+ // Clean up
+ if($scope) {
+ $scope.$on('$destroy', function() {
+ $Time.offReload(func);
+ });
+ }
+ };
+
+ $Time.offReload = function (func) {
+ reloadListenerList = $.grep(reloadListenerList, function(_func) {
+ return _func !== func;
+ });
+ };
+
+ $Time.verifyTime = function(str, format) {
+ format = format || $Time.FORMAT;
+ var date = $Time(str);
+ if(str === $Time.format(date, format)) {
+ return date;
+ }
+ return null;
+ };
+
+ $Time.diff = function (from, to) {
+ from = $Time(from);
+ to = $Time(to);
+ if (!from || !to) return null;
+ return to.diff(from);
+ };
+
+ $Time.diffStr = function (from, to) {
+ var diff = from;
+ if(arguments.length === 2) {
+ diff = $Time.diff(from, to);
+ }
+ if(diff === null) return "-";
+ if(diff === 0) return "0s";
+
+ var match = false;
+ var rows = [];
+ var duration = moment.duration(diff);
+ var rest = 3;
+
+ $.each(UNITS, function (i, unit) {
+ var interval = Math.floor(duration[unit[0]]());
+ if(interval > 0) match = true;
+
+ if(match) {
+ if(interval !== 0) {
+ rows.push(interval + (interval > 1 ? unit[1] : unit[2]));
+ }
+
+ rest -=1;
+ if(rest === 0) return false;
+ }
+ });
+
+ return rows.join(", ");
+ };
+
+ $Time.diffInterval = function (from, to) {
+ var timeDiff = $Time.diff(from, to);
+ if(timeDiff <= 1000 * 60 * 60 * 6) {
+ return 1000 * 60 * 5;
+ } else if(timeDiff <= 1000 * 60 * 60 * 24) {
+ return 1000 * 60 * 15;
+ } else if(timeDiff <= 1000 * 60 * 60 * 24 * 7) {
+ return 1000 * 60 * 30;
+ } else if(timeDiff <= 1000 * 60 * 60 * 24 * 14) {
+ return 1000 * 60 * 60;
+ } else {
+ return 1000 * 60 * 60 * 24;
+ }
+ };
+
+ $Time.align = function (time, interval, ceil) {
+ time = $Time(time);
+ if(!time) return null;
+
+ var func = ceil ? Math.ceil : Math.floor;
+
+ var timestamp = time.valueOf();
+ return $Time(func(timestamp / interval) * interval);
+ };
+
+ $Time.millionFormat = function (num) {
+ if(!num) return "-";
+ num = Math.floor(num / 1000);
+ var s = num % 60;
+ num = Math.floor(num / 60);
+ var m = num % 60;
+ num = Math.floor(num / 60);
+ var h = num % 60;
+ return common.string.preFill(h, "0") + ":" +
+ common.string.preFill(m, "0") + ":" +
+ common.string.preFill(s, "0");
+ };
+
+ var promiseLock = false;
+ $Time.getPromise = function (config, state, param) {
+ if(keepTime) {
+ keepTime = false;
+ return $q.when($Time);
+ }
+
+ if(config.time === true) {
+ $Time.pickerType = $Time.TIME_RANGE_PICKER;
+
+ if(!promiseLock) {
+ startTime = $Time.verifyTime(param.startTime);
+ endTime = $Time.verifyTime(param.endTime);
+
+ if (!startTime || !endTime) {
+ endTime = $Time();
+ startTime = endTime.clone().subtract(2, "hour");
+
+ setTimeout(function () {
+ promiseLock = true;
+ keepTime = true;
+ $wrapState.go(state.name, $.extend({}, param, {
+ startTime: $Time.format(startTime),
+ endTime: $Time.format(endTime)
+ }), {location: "replace", notify: false});
+
+ setTimeout(function () {
+ promiseLock = false;
+ }, 150);
+ }, 100);
+ }
+ }
+ } else {
+ $Time.pickerType = null;
+ }
+
+ return $q.when($Time);
+ };
+
+ return $Time;
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/uiSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/uiSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/uiSrv.js
new file mode 100644
index 0000000..b4a1a42
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/uiSrv.js
@@ -0,0 +1,276 @@
+/*
+ * 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');
+
+ function wrapPromise(promise) {
+ var retFunc = function (notifyFunc, resolveFunc, rejectFunc) {
+ promise.then(resolveFunc, rejectFunc, function (holder) {
+ notifyFunc(holder.entity, holder.closeFunc, holder.unlock);
+ });
+ };
+
+ retFunc.then = promise.then;
+
+ return retFunc;
+ }
+
+ /**
+ * Check function to check fields pass or not
+ * @callback checkFieldFunction
+ * @param {{}} entity
+ * @return {string}
+ */
+
+ serviceModule.service('UI', function($rootScope, $q, $compile) {
+ function UI() {}
+
+ function _bindShortcut($dialog) {
+ $dialog.on("keydown", function (event) {
+ if(event.which === 13) {
+ if(!$(":focus").is("textarea")) {
+ $dialog.find(".confirmBtn:enabled").click();
+ }
+ }
+ });
+ }
+
+ function _fieldDialog(create, name, entity, fieldList, checkFunc) {
+ var _deferred, $mdl, $scope;
+
+ var _entity = entity || {};
+
+ _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 : {};
+
+ // Init
+ if(!entity) {
+ $.each(fieldList, function (i, field) {
+ if(field.defaultValue) {
+ _entity[field.field] = field.defaultValue;
+ }
+ });
+ }
+
+ // Modal
+ $mdl = $(TMPL_FIELDS).appendTo('body');
+ $compile($mdl)($scope);
+ $mdl.modal();
+ setTimeout(function () {
+ $mdl.find("input, select").filter(':visible:first:enabled').focus();
+ }, 500);
+
+ $mdl.on("hide.bs.modal", function() {
+ _deferred.reject();
+ });
+ $mdl.on("hidden.bs.modal", function() {
+ _deferred.resolve({
+ entity: _entity
+ });
+ $mdl.remove();
+ });
+
+ // Function
+ $scope.getFieldDescription = function (field) {
+ if(typeof field.description === "function") {
+ return field.description($scope.entity);
+ }
+ return field.description || ((field.name || field.field) + '...');
+ };
+
+ $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;
+ }
+ });
+ };
+
+ _bindShortcut($mdl);
+
+ return _deferred.promise;
+ }
+
+ /***
+ * Create a customize field confirm modal.
+ * @param {string} name - Create entity name
+ * @param {object} entity - Bind entity
+ * @param {Object[]} fieldList - Display fields
+ * @param {string} fieldList[].field - Mapping entity field
+ * @param {string=} fieldList[].name - Field display name
+ * @param {*=} fieldList[].defaultValue - Field default value. Only will be set if entity object is undefined
+ * @param {string=} fieldList[].type - Field types: 'select', 'blob'
+ * @param {number=} fieldList[].rows - Display as textarea if rows is set
+ * @param {string=} fieldList[].description - Display as placeholder
+ * @param {boolean=} fieldList[].optional - Optional field will not block the confirm
+ * @param {boolean=} fieldList[].readonly - Read Only can not be updated
+ * @param {string[]=} fieldList[].valueList - For select type usage
+ * @param {checkFieldFunction=} checkFunc - Check logic function. Return string will prevent access
+ */
+ UI.createConfirm = function(name, entity, fieldList, checkFunc) {
+ return wrapPromise(_fieldDialog(true, name, entity, fieldList, checkFunc));
+ };
+
+ /***
+ * Create a customize field confirm modal.
+ * @param {object} config - Configuration object
+ * @param {string} config.title - Title of dialog box
+ * @param {string=} config.size - "large". Set dialog size
+ * @param {boolean=} config.confirm - Display or not confirm button
+ * @param {string=} config.confirmDesc - Confirm button display description
+ * @param {object} entity - bind entity
+ * @param {Object[]} fieldList - Display fields
+ * @param {string} fieldList[].field - Mapping entity field
+ * @param {string=} fieldList[].name - Field display name
+ * @param {*=} fieldList[].defaultValue - Field default value. Only will be set if entity object is undefined
+ * @param {string=} fieldList[].type - Field types: 'select', 'blob'
+ * @param {number=} fieldList[].rows - Display as textarea if rows is set
+ * @param {string=} fieldList[].description - Display as placeholder
+ * @param {boolean=} fieldList[].optional - Optional field will not block the confirm
+ * @param {boolean=} fieldList[].readonly - Read Only can not be updated
+ * @param {string[]=} fieldList[].valueList - For select type usage
+ * @param {checkFieldFunction=} checkFunc - Check logic function. Return string will prevent access
+ */
+ UI.fieldConfirm = function(config, entity, fieldList, checkFunc) {
+ return wrapPromise(_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 wrapPromise(_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">×</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="{{getFieldDescription(field)}}" ng-model="entity[field.field]" rows="{{ field.rows || 10 }}" ng-readonly="field.readonly" ng-disabled="lock" ng-switch-when="blob"></textarea>' +
+ '<select class="form-control" ng-model="entity[field.field]" ng-init="entity[field.field] = entity[field.field] || field.valueList[0]" ng-switch-when="select">' +
+ '<option ng-repeat="value in field.valueList">{{value}}</option>' +
+ '</select>' +
+ '<input type="text" class="form-control" placeholder="{{getFieldDescription(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 confirmBtn" 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">×</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/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/widgetSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/widgetSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/widgetSrv.js
new file mode 100644
index 0000000..2d6093a
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/widgetSrv.js
@@ -0,0 +1,79 @@
+/*
+ * 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('Widget', function($wrapState, Site, Application) {
+ var Widget = {};
+
+ var mainWidgetList = [];
+ var siteWidgetList = [];
+
+ var displayWidgetList = [];
+ var siteWidgets = {};
+
+ Widget.register = function (widget, isSite) {
+ (isSite ? siteWidgetList : mainWidgetList).push(widget);
+ };
+
+ Widget.refresh = function () {
+ // Common widget
+ displayWidgetList = $.map(mainWidgetList, function (widget) {
+ var hasApp = !!common.array.find(widget.application, Application.list, "descriptor.type");
+ if(hasApp) {
+ return widget;
+ }
+ });
+
+ // Site widget
+ siteWidgets = {};
+ $.each(Site.list, function (i, site) {
+ siteWidgets[site.siteId] = $.map(siteWidgetList, function (widget) {
+ var hasApp = !!common.array.find(widget.application, site.applicationList, "descriptor.type");
+ if(hasApp) {
+ return widget;
+ }
+ });
+ });
+ };
+
+ Object.defineProperty(Widget, 'list', {
+ get: function () {
+ var site = Site.current();
+ if(!site) {
+ return displayWidgetList;
+ } else if(site.siteId) {
+ return siteWidgets[site.siteId];
+ } else {
+ console.warn("Can't find current site id.");
+ return [];
+ }
+ }
+ });
+
+ // Initialization
+ Site.onReload(Widget.refresh);
+
+ Widget.refresh();
+
+ return Widget;
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/services/wrapStateSrv.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/services/wrapStateSrv.js b/eagle-server/src/main/webapp/app/dev/public/js/services/wrapStateSrv.js
new file mode 100644
index 0000000..43e8cc2
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/services/wrapStateSrv.js
@@ -0,0 +1,119 @@
+/*
+ * 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, option, priority) {
+ setTimeout(function() {
+ _targetState = null;
+ _targetPriority = 0;
+ });
+
+ if(typeof param !== "object") {
+ priority = param;
+ param = {};
+ option = {};
+ } else if(typeof option !== "object") {
+ priority = option;
+ option = {};
+ }
+
+ 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("[Wrap State] Go reload.", $state);
+ $state.reload();
+ } else {
+ console.log("[Wrap State] Go:", state, param, priority);
+ $state.go(state, param, option);
+ }
+ _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);
+ }
+ },
+
+ // Parameter
+ state: {
+ get: function() {
+ return $state;
+ }
+ }
+ });
+
+ return $wrapState;
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableFunc.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableFunc.js b/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableFunc.js
new file mode 100644
index 0000000..eb0629b
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableFunc.js
@@ -0,0 +1,93 @@
+/*
+ * 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 __sortTable_generateFilteredList;
+
+(function () {
+ 'use strict';
+
+ var isArray;
+ if(typeof $ !== "undefined") {
+ isArray = $.isArray;
+ } else {
+ isArray = Array.isArray;
+ }
+
+ function hasContentByPathList(object, content, pathList) {
+ for(var i = 0 ; i < pathList.length ; i += 1) {
+ var path = pathList[i];
+ var value = common.getValueByPath(object, path);
+ if((value + "").toUpperCase().indexOf(content) >= 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function hasContent(object, content, depth) {
+ var i, keys;
+
+ depth = depth || 1;
+ if(!content) return true;
+ if(depth > 10) return false;
+
+ if(object === undefined || object === null) {
+ return false;
+ } else if(isArray(object)) {
+ for(i = 0 ; i < object.length ; i += 1) {
+ if(hasContent(object[i], content, depth + 1)) return true;
+ }
+ } else if(typeof object === "object") {
+ keys = Object.keys(object);
+ for(i = 0 ; i < keys.length ; i += 1) {
+ var value = object[keys[i]];
+ if(hasContent(value, content, depth + 1)) return true;
+ }
+ } else {
+ return (object + "").toUpperCase().indexOf(content) >= 0;
+ }
+
+ return false;
+ }
+
+ __sortTable_generateFilteredList = function(list, search, order, orderAsc, searchPathList) {
+ var i, _list;
+ var _search = (search + "").toUpperCase();
+
+ if (search) {
+ _list = [];
+ if(searchPathList) {
+ for(i = 0 ; i < list.length ; i += 1) {
+ if(hasContentByPathList(list[i], _search, searchPathList)) _list.push(list[i]);
+ }
+ } else {
+ for(i = 0 ; i < list.length ; i += 1) {
+ if(hasContent(list[i], _search)) _list.push(list[i]);
+ }
+ }
+ } else {
+ _list = list;
+ }
+
+ if (order) {
+ common.array.doSort(_list, order, orderAsc);
+ }
+
+ return _list;
+ };
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableWorker.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableWorker.js b/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableWorker.js
new file mode 100644
index 0000000..669ab51
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/dev/public/js/worker/sortTableWorker.js
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+self.importScripts("../common.js");
+self.importScripts("sortTableFunc.js");
+
+self.addEventListener("message", function (event) {
+ var data = event.data;
+ var list = JSON.parse(data.list);
+
+ list = __sortTable_generateFilteredList(list, data.search, data.order, data.orderAsc, data.searchPathList);
+
+ self.postMessage({
+ list: list,
+ id: data.id
+ });
+});
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/index.html
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/index.html b/eagle-server/src/main/webapp/app/index.html
index f3d3c80..831f3f0 100644
--- a/eagle-server/src/main/webapp/app/index.html
+++ b/eagle-server/src/main/webapp/app/index.html
@@ -1,3 +1,4 @@
+<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -16,4 +17,12 @@
limitations under the License.
-->
-Eagle Server has started!
\ No newline at end of file
+<html>
+ <head>
+ <script>
+ window.location.href = "ui";
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/package.json
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/package.json b/eagle-server/src/main/webapp/app/package.json
new file mode 100644
index 0000000..f5b43bb
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "ApacheEagleWebApp",
+ "description": "Apache Eagle Web Application",
+ "author": "ApacheEagle",
+ "repository": {
+ "type:": "git",
+ "url": "https://github.com/apache/incubator-eagle.git"
+ },
+ "scripts": {
+ "build": "node build/index.js",
+ "grunt": "grunt"
+ },
+ "license": "Apache-2.0",
+ "dependencies": {
+ "admin-lte": "2.3.2",
+ "angular": "1.5.0",
+ "angular-animate": "1.5.0",
+ "angular-cookies": "1.5.0",
+ "angular-resource": "1.5.0",
+ "angular-route": "1.5.0",
+ "angular-ui-bootstrap": "1.1.2",
+ "angular-ui-router": "~0.2.18",
+ "bootstrap": "3.3.6",
+ "d3": "3.5.16",
+ "echarts": "^3.2.3",
+ "font-awesome": "4.5.0",
+ "jquery": "2.2.4",
+ "jquery-slimscroll": "1.3.6",
+ "jsdom": "^9.5.0",
+ "moment": "2.11.2",
+ "moment-timezone": "0.5.0",
+ "zombiej-bootstrap-components": "1.1.6",
+ "zombiej-nvd3": "1.8.2-3"
+ },
+ "devDependencies": {
+ "grunt": "~0.4.5",
+ "grunt-cli": "~0.1.13",
+ "grunt-contrib-jshint": "~0.11.3",
+ "grunt-regex-replace": "~0.2.6",
+ "grunt-contrib-clean": "~0.7.0",
+ "grunt-contrib-uglify": "~0.5.0",
+ "grunt-contrib-concat": "~0.5.1",
+ "grunt-contrib-cssmin": "~0.14.0",
+ "grunt-contrib-copy": "~0.8.2",
+ "grunt-htmlrefs": "~0.5.0"
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/package.json
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/package.json b/eagle-server/src/main/webapp/package.json
deleted file mode 100644
index e69de29..0000000
[10/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/ui-build.sh
----------------------------------------------------------------------
diff --git a/eagle-server/ui-build.sh b/eagle-server/ui-build.sh
new file mode 100644
index 0000000..9f17ba6
--- /dev/null
+++ b/eagle-server/ui-build.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+echo "=============== Web APP Building Start ==============="
+echo "Environment Check..."
+# Pre-build check
+if [ -z "$(command -v git)" ]
+then
+ echo "git not installed!"
+ exit 1
+fi
+if [ -z "$(command -v npm)" ]
+then
+ echo "npm not installed!"
+ exit 1
+fi
+echo "Environment Check...Pass"
+
+# npm install
+cd src/main/webapp/app
+echo "npm install..."
+npm install
+
+# grunt build
+echo "building..."
+npm run build
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/Gruntfile.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/Gruntfile.js b/eagle-webservice/src/main/webapp/Gruntfile.js
deleted file mode 100644
index d2a3a42..0000000
--- a/eagle-webservice/src/main/webapp/Gruntfile.js
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-'use strict';
-
-module.exports = function (grunt) {
- // Project configuration.
- grunt.initConfig({
- pkg: grunt.file.readJSON('package.json'),
- config: grunt.file.readJSON('grunt.json'),
-
- jshint: {
- options: {
- browser: true,
- globals: {
- $: true,
- jQuery: true,
- moment: true
- }
- },
- all: [
- 'app/**/*.js'
- ]
- },
-
- clean: {
- build: ['ui/', 'tmp/'],
- tmp: ['tmp/'],
- ui: ['ui/']
- },
- concat: {
- app: {
- src: [
- 'app/public/js/app.js',
-
- 'app/public/js/srv/main.js',
- 'app/public/js/srv/**.js',
-
- 'app/public/js/app.*.js',
-
- 'app/public/js/common.js',
-
- 'app/public/js/components/main.js',
- 'app/public/js/components/**.js',
- 'app/public/js/components/**/**.js',
-
- 'app/public/js/ctrl/main.js',
- 'app/public/js/ctrl/*.js'
- ],
- dest: 'tmp/public/js/scripts.js'
- },
- js: '<%= config.concat.js %>',
- css: {
- options: {
- process: function(src, filepath) {
- return "@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);" +
- src.replace('@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);', '');
- }
- },
- src: '<%= config.concat.css.src %>',
- dest: '<%= config.concat.css.dest %>'
- }
- },
- 'regex-replace': {
- strict: {
- src: ['tmp/public/js/scripts.js'],
- actions: [
- {
- name: 'use strict',
- search: '\\\'use strict\\\';?',
- replace: '',
- flags: 'gmi'
- },
- {
- name: 'build timestamp',
- search: '\\/\\/ GRUNT REPLACEMENT\\: eagleApp\\.buildTimestamp \\= TIMESTAMP',
- replace: 'eagleApp.buildTimestamp = ' + (+new Date()) + ';',
- flags: 'gmi'
- }
- ]
- }
- },
- uglify: {
- ui: {
- options: {
- mangle: false
- },
- files: [
- {
- src: 'tmp/public/js/scripts.js',
- dest: 'tmp/public/js/scripts.min.js'
- },
- {
- expand: true,
- src: '**/*.js',
- dest: 'tmp/feature',
- cwd: 'app/public/feature'
- }
- ]
- }
- },
- cssmin: {
- ui: {
- files: {
- 'tmp/public/css/styles.css': ['app/public/css/main.css', 'app/public/css/animation.css']
- }
- }
- },
- htmlrefs: {
- ui: {
- src: 'app/index.html',
- dest: "tmp/index.html"
- }
- },
- copy: {
- feature: {
- files: [
- {expand: true, cwd: 'app/', src: ['public/feature/**'], dest: 'tmp'}
- ]
- },
- ui: {
- files: [
- {expand: true, cwd: 'tmp/', src: ['**'], dest: 'ui'},
- {expand: true, cwd: 'app/', src: ['public/images/**', 'partials/**'], dest: 'ui'},
- {expand: true, cwd: 'node_modules/font-awesome/', src: ['fonts/**'], dest: 'ui/public'},
- {expand: true, cwd: 'node_modules/bootstrap/', src: ['fonts/**'], dest: 'ui/public'}
- ]
- }
- }
- });
-
- grunt.loadNpmTasks('grunt-contrib-jshint');
- grunt.loadNpmTasks('grunt-contrib-clean');
- grunt.loadNpmTasks('grunt-contrib-concat');
- grunt.loadNpmTasks('grunt-contrib-uglify');
- grunt.loadNpmTasks('grunt-contrib-cssmin');
- grunt.loadNpmTasks('grunt-htmlrefs');
- grunt.loadNpmTasks('grunt-regex-replace');
- grunt.loadNpmTasks('grunt-contrib-copy');
-
- grunt.registerTask('default', [
- // jshint
- 'jshint:all',
- // Clean Env
- 'clean:build',
- // Compress JS
- 'copy:feature',
- 'concat:app',
- 'regex-replace:strict',
- 'uglify',
- 'concat:js',
- // Compress CSS
- 'cssmin',
- 'concat:css',
- // Pass HTML Resources
- 'htmlrefs',
- 'copy:ui',
- // Clean Env
- 'clean:tmp'
- ]);
-};
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/README.md
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/README.md b/eagle-webservice/src/main/webapp/README.md
deleted file mode 100644
index b4168d5..0000000
--- a/eagle-webservice/src/main/webapp/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Apache Eagle Web APP
-==
-
-Web client for Apache Eagle
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/index.html b/eagle-webservice/src/main/webapp/_app/index.html
new file mode 100644
index 0000000..7cd3e25
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/index.html
@@ -0,0 +1,281 @@
+<!DOCTYPE html>
+<!--
+ 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.
+ -->
+
+<html ng-app="eagleApp" ng-controller="MainCtrl">
+ <head>
+ <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta charset="UTF-8">
+ <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
+ <link rel="shortcut icon" href="public/images/favicon.png">
+
+ <title>Eagle</title>
+ <link rel="shortcut icon" type="image/png" href="public/images/favicon.png">
+
+ <!-- ref:css public/css/styles.min.css -->
+ <link href="public/css/main.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="public/css/animation.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="../node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="../node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.css" rel="stylesheet" type="text/css" media="screen">
+ <link href="../node_modules/zombiej-nvd3/build/nv.d3.css" rel="stylesheet" type="text/css" />
+ <link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
+ <link href="../node_modules/admin-lte/dist/css/AdminLTE.css" rel="stylesheet" type="text/css" />
+ <link href="../node_modules/admin-lte/dist/css/skins/skin-blue.css" rel="stylesheet" type="text/css" />
+ <!-- endref -->
+ </head>
+ <body class="skin-blue sidebar-mini" ng-class="{'no-sidebar' : PageConfig.hideSidebar}">
+ <!-- Site wrapper -->
+ <div class="wrapper">
+ <header class="main-header">
+ <a href="#/" class="logo">
+ <span class="logo-mini"><img src="public/images/favicon_white.png" /></span>
+ <span class="logo-lg">Apache Eagle</span>
+ </a>
+ <!-- Header Navbar: style can be found in header.less -->
+ <nav class="navbar navbar-static-top" role="navigation">
+ <!-- Sidebar toggle button-->
+ <a ng-hide="PageConfig.hideSidebar" class="sidebar-toggle" data-toggle="offcanvas" role="button">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </a>
+
+ <div class="navbar-custom-menu">
+ <ul class="nav navbar-nav">
+ <!-- Admin error list -->
+ <li class="dropdown" ng-show="ServiceError.list.length">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <i class="fa fa-exclamation-triangle" ng-class="{blink: ServiceError.hasUnread}"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li ng-repeat="error in ServiceError.list">
+ <a ng-click="ServiceError.showError(error);">
+ <span class="fa" ng-class="{'fa-envelope': !error._read, 'fa-check': error._read}"></span>
+ {{error.title}}
+ </a>
+ </li>
+ <li role="separator" class="divider"></li>
+ <li>
+ <a ng-click="ServiceError.clearAll();">
+ <span class="fa fa-trash"></span>
+ Clear All
+ </a>
+ </li>
+ </ul>
+ </li>
+
+ <!-- Site -->
+ <li class="dropdown" ng-show="!PageConfig.hideSite && !PageConfig.lockSite">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <i class="fa fa-server"></i>
+ {{Site.current().tags.site}}
+ <i class="fa fa-caret-down"></i>
+ </a>
+ <ul class="dropdown-menu">
+ <li ng-repeat="_site in Site.list" ng-if="_site.enabled">
+ <a ng-click="Site.current(_site);">
+ <span class="fa fa-database"></span> {{_site.tags.site}}
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li class="dropdown" ng-show="PageConfig.lockSite">
+ <a>
+ <i class="fa fa-server"></i>
+ {{Site.current().tags.site}}
+ </a>
+ </li>
+
+ <!-- User -->
+ <li class="dropdown user user-menu" ng-hide="PageConfig.hideUser">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <i class="fa fa-user"></i>
+ {{Auth.userProfile.username}}
+ </a>
+ <ul class="dropdown-menu">
+ <!-- User image -->
+ <li class="user-header">
+ <span class="img-circle">
+ <span class="fa fa-user" alt="User Image"></span>
+ </span>
+ <p>
+ {{Auth.userProfile.username}}
+ <small>
+ <span ng-repeat="role in Auth.userProfile.authorities">{{role.authority}} </span>
+ </small>
+ </p>
+ </li>
+ <!-- Menu Footer-->
+ <li class="user-footer">
+ <div class="pull-left" ng-if="Auth.isRole('ROLE_ADMIN')">
+ <a href="#/config/site" class="btn btn-default btn-flat">Management</a>
+ </div>
+ <div class="pull-right">
+ <a ng-click="logout();" class="btn btn-default btn-flat">Sign out</a>
+ </div>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+
+ <!-- Applications -->
+ <div ng-hide="PageConfig.hideApplication">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#moduleMenu">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="fa fa-map"></span>
+ </button>
+ <div class="collapse navbar-collapse" id="moduleMenu">
+ <ul class="nav navbar-nav">
+ <li ng-repeat="_grp in Site.current().applicationGroupList" ng-if="_grp.enabledList.length"
+ class="dropdown" ng-class="{active: Application.current().group === _grp.name}">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ {{_grp.name}}
+ </a>
+ <ul class="dropdown-menu">
+ <li ng-repeat="_app in _grp.enabledList">
+ <a ng-click="Application.current(_app);">
+ <span class="fa fa-cubes"></span> {{_app.displayName}}
+ </a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </nav>
+ </header>
+
+ <!-- =============================================== -->
+ <!-- Left side column. contains the side bar -->
+ <aside class="main-sidebar" ng-hide="PageConfig.hideSidebar">
+ <!-- side bar: style can be found in sidebar.less -->
+ <section class="sidebar">
+ <ul class="sidebar-menu">
+ <li class="header">
+ {{Application.current().group || 'Application'}} >
+ {{Application.current().displayName || 'Features'}}
+ </li>
+ <li ng-repeat="page in PageConfig.navConfig.pageList track by $index" ng-class="getNavClass(page)" ng-show="getNavVisible(page)">
+ <a href="{{page.url}}">
+ <i class="fa fa-{{page.icon}}"></i> <span>{{page.title}}</span>
+ </a>
+ </li>
+ </ul>
+ </section>
+ <!-- /.sidebar -->
+ </aside>
+
+ <!-- =============================================== -->
+ <!-- Right side column. Contains the navbar and content of the page -->
+ <div class="content-wrapper">
+ <!-- Content Header (Page header) -->
+ <section class="content-header" ng-hide="PageConfig.hideSidebar">
+ <h1>
+ <span class="pageTitle">{{PageConfig.pageTitle}}</span>
+ <small class="pageSubTitle">{{PageConfig.pageSubTitle}}</small>
+ </h1>
+
+
+ <ol class="breadcrumb">
+ <li ng-repeat="navPath in PageConfig.navPath">
+ <a ng-href="#{{navPath.path}}">
+ <span class="fa fa-home" ng-if="$first"></span>
+ {{navPath.title || navPath.path}}
+ </a>
+ </li>
+ </ol>
+ </section>
+
+ <!-- Main content -->
+ <section class="content">
+ <div id="content">
+ <div ui-view></div>
+ </div>
+ </section><!-- /.content -->
+ </div><!-- /.content-wrapper -->
+
+ <footer class="main-footer">
+ <div class="pull-right hidden-xs">
+ <b>License</b>
+ <a href="http://www.apache.org/licenses/LICENSE-2.0" class="text-muted">Apache-2.0</a>
+ </div>
+ <strong>
+ Apache Eagle
+ <a target="_blank" href="https://eagle.incubator.apache.org/">Home</a> /
+ <a target="_blank" href="https://eagle.incubator.apache.org/docs/community.html">Community</a> /
+ <a target="_blank" href="https://cwiki.apache.org/confluence/display/EAG/FAQ">FAQ</a>
+ </strong>
+ </footer>
+ </div><!-- ./wrapper -->
+
+ <!-- ref:js public/js/doc.js -->
+ <script src="../node_modules/jquery/dist/jquery.js"></script>
+ <script src="../node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>
+ <script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
+ <script src="../node_modules/zombiej-bootstrap-components/bootstrap-components/js/bootstrap-components.min.js"></script>
+ <script src="../node_modules/moment/min/moment-with-locales.min.js"></script>
+ <script src="../node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"></script>
+ <script src="../node_modules/admin-lte/dist/js/app.min.js"></script>
+ <script src="../node_modules/angular/angular.js"></script>
+ <script src="../node_modules/angular-resource/angular-resource.js"></script>
+ <script src="../node_modules/angular-route/angular-route.js"></script>
+ <script src="../node_modules/angular-animate/angular-animate.js"></script>
+ <script src="../node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
+ <script src="../node_modules/angular-ui-router/release/angular-ui-router.js"></script>
+ <script src="../node_modules/d3/d3.js"></script>
+ <script src="../node_modules/zombiej-nvd3/build/nv.d3.js"></script>
+
+ <!-- Application -->
+ <script src="public/js/app.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Service -->
+ <script src="public/js/srv/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/applicationSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/authorizationSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/entitiesSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/siteSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/pageSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/wrapStateSrv.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/srv/uiSrv.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Misc -->
+ <script src="public/js/app.ui.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/app.time.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/app.config.js" type="text/javascript" charset="utf-8"></script>
+
+ <script src="public/js/common.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Components -->
+ <script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/sortTable.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/tabs.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/file.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/charts/line3d.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/nvd3.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/components/sortable.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- Controllers -->
+ <script src="public/js/ctrl/main.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrl/authController.js" type="text/javascript" charset="utf-8"></script>
+ <script src="public/js/ctrl/configurationController.js" type="text/javascript" charset="utf-8"></script>
+ <!-- endref -->
+ </body>
+</html>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/application.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/application.html b/eagle-webservice/src/main/webapp/_app/partials/config/application.html
new file mode 100644
index 0000000..0bf194c
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/application.html
@@ -0,0 +1,124 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ <span class="fa fa-cogs"></span>
+ Configuration
+ <small class="text-danger" ng-show="changed">
+ <span class="label label-warning label-sm">Unsaved</span>
+ </small>
+ </h3>
+ </div><!-- /.box-header -->
+
+ <div class="box-body">
+ <div class="row">
+ <div class="col-md-3">
+ <ul class="nav nav-pills nav-stacked">
+ <li class="disabled"><a>Application</a></li>
+ <li role="presentation" ng-repeat="_application in Application.list track by $index" ng-class="{active: application === _application}">
+ <a ng-click="setApplication(_application)">
+ <span class="fa fa-server"></span>
+ {{_application.tags.application}}
+ <span ng-if="_application.alias">({{_application.alias}})</span>
+ </a>
+ </li>
+
+ <li>
+ <a class="text-light-blue" ng-click="newApplication()" ng-disabled="_pageLock">
+ <span class="fa fa-plus-square"></span>
+ New Application
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <div class="col-md-9">
+ <a class="pull-right btn btn-danger btn-xs" ng-click="deleteApplication(application)" ng-disabled="_pageLock">
+ <span class="fa fa-trash-o"></span>
+ Delete Application
+ </a>
+
+ <!-- Title -->
+ <h3 class="guideline">
+ Application
+ <small>{{application.tags.application}}</small>
+ </h3>
+ <hr/>
+
+ <!-- Config -->
+ <div class="form-group">
+ <label for="displayName">Display Name</label>
+ <input type="text" class="form-control" id="displayName" placeholder="(Optional) Display name." ng-model="applications[application.tags.application].alias">
+ </div>
+ <div class="form-group">
+ <label for="applicationGroup">Group</label>
+ <input type="text" class="form-control" id="applicationGroup" placeholder="(Optional) Group name" ng-model="applications[application.tags.application].groupName">
+ </div>
+ <div class="form-group">
+ <label for="applicationDescription">Description</label>
+ <textarea id="applicationDescription" class="form-control" placeholder="(Optional) Application description" rows="2" ng-model="applications[application.tags.application].description"></textarea>
+ </div>
+ <div class="form-group">
+ <label for="applicationConfiguration">Configuration</label>
+ <span class="text-danger">{{configCheck(applications[application.tags.application].config)}}</span>
+ <textarea id="applicationConfiguration" class="form-control" placeholder="Application configuration. Feature can read this " rows="5" ng-model="applications[application.tags.application].config"></textarea>
+ </div>
+
+ <!-- Feature -->
+ <label>* Feature</label>
+ <div class="row">
+ <div class="col-sm-6">
+ <h1 class="text-muted text-center" ng-show="applications[application.tags.application].features.length === 0">No feature in using</h1>
+ <ul class="products-list product-list-in-box fixed-height" ng-show="applications[application.tags.application].features.length !== 0">
+ <li class="item" ng-repeat="feature in applications[application.tags.application].features track by $index" ng-class="{active: _feature === feature}">
+ <div class="product-operation">
+ <a class="fa fa-chevron-up" ng-click="moveFeature(feature, applications[application.tags.application].features, -1)"></a>
+ <a class="fa fa-chevron-down" ng-click="moveFeature(feature, applications[application.tags.application].features, 1)"></a>
+ </div>
+ <div class="product-info">
+ <a class="fa fa-times pull-right" ng-click="removeFeature(feature, applications[application.tags.application])"></a>
+ <span class="product-title">{{feature}}</span>
+ <span class="product-description">{{Application.featureList.set[feature].description}}</span>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <div class="col-sm-6">
+ <ul class="products-list product-list-in-box fixed-height">
+ <li class="item" ng-repeat="feature in applications[application.tags.application].optionalFeatures track by $index">
+ <button class="btn btn-lg btn-primary pull-left" ng-click="addFeature(feature, applications[application.tags.application])" ng-disabled="_pageLock">
+ <span class="fa fa-star-o"></span>
+ </button>
+ <div class="product-info">
+ <span class="product-title">{{feature}}</span>
+ <span class="product-description">{{Application.featureList.set[feature].description}}</span>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- /.box-body -->
+
+ <div class="box-footer clearfix">
+ <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/feature.html b/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
new file mode 100644
index 0000000..945d90b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
@@ -0,0 +1,85 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ <span class="fa fa-cogs"></span>
+ Configuration
+ <small class="text-danger" ng-show="changed">
+ <span class="label label-warning label-sm">Unsaved</span>
+ </small>
+ </h3>
+ </div><!-- /.box-header -->
+
+ <div class="box-body">
+ <div class="row">
+ <div class="col-md-3">
+ <ul class="nav nav-pills nav-stacked">
+ <li class="disabled">
+ <a>Feature</a>
+ </li>
+ <li role="presentation" ng-repeat="_feature in Application.featureList" ng-class="{active: feature === _feature}">
+ <a ng-click="setFeature(_feature)">
+ <span class="fa fa-leaf" ng-class="{'text-danger': _feature._loaded === false}" uib-tooltip="Module load failed!" tooltip-enable="_feature._loaded === false"></span>
+ {{_feature.tags.feature}}
+ </a>
+ </li>
+ <li>
+ <a class="text-light-blue" ng-click="newFeature()" ng-disabled="_pageLock">
+ <span class="fa fa-plus-square"></span>
+ New Feature
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <div class="col-md-9">
+ <a class="pull-right btn btn-danger btn-xs" ng-click="deleteFeature(feature)" ng-disabled="_pageLock">
+ <span class="fa fa-trash-o"></span>
+ Delete Feature
+ </a>
+
+ <h3 class="guideline">
+ <span class="fa fa-exclamation-triangle text-danger" uib-tooltip="Module load failed!" ng-show="feature._loaded === false"></span>
+ {{feature.tags.feature}}
+ </h3>
+ <hr/>
+
+ <p class="text text-muted">
+ Will load the start up file <code>controller.js</code> from <code>public/feature/{{feature.tags.feature}}</code>.
+ If you are developing customized feature, please reference provided feature.
+ </p>
+
+ <!-- Config -->
+ <div class="form-group">
+ <label for="featureVersion">Version</label>
+ <input id="featureVersion" type="text" class="form-control" placeholder="(Optional) Feature version." ng-model="features[feature.tags.feature].version">
+ </div>
+ <div class="form-group">
+ <label for="featureDescription">Description</label>
+ <textarea id="featureDescription" class="form-control" placeholder="(Optional) Feature description." rows="10" ng-model="features[feature.tags.feature].description"></textarea>
+ </div>
+ </div>
+ </div>
+ </div><!-- /.box-body -->
+
+ <div class="box-footer clearfix">
+ <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/site.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/site.html b/eagle-webservice/src/main/webapp/_app/partials/config/site.html
new file mode 100644
index 0000000..f7d43eb
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/site.html
@@ -0,0 +1,115 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ <span class="fa fa-cogs"></span>
+ Configuration
+ <small class="text-danger" ng-show="changed">
+ <span class="label label-warning label-sm">Unsaved</span>
+ </small>
+ </h3>
+ </div><!-- /.box-header -->
+
+ <div class="box-body">
+ <div class="row">
+ <div class="col-md-3">
+ <ul class="nav nav-pills nav-stacked">
+ <li class="disabled"><a>Site</a></li>
+ <li role="presentation" ng-repeat="_site in Site.list track by $index" ng-class="{active: site === _site}">
+ <a ng-click="setSite(_site)">
+ <span class="fa fa-server"></span>
+ {{_site.tags.site}}
+ </a>
+ </li>
+
+ <li>
+ <a class="text-light-blue" ng-click="newSite()" ng-disabled="_pageLock">
+ <span class="fa fa-plus-square"></span>
+ New Site
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <div class="col-md-9">
+ <a class="pull-right btn btn-danger btn-xs" ng-click="deleteSite(site)" ng-disabled="_pageLock">
+ <span class="fa fa-trash-o"></span>
+ Delete Site
+ </a>
+
+ <!-- Title -->
+ <h3 class="guideline">
+ Site
+ <small>{{site.tags.site}}</small>
+ </h3>
+ <hr/>
+
+ <!-- Config -->
+ <div class="checkbox">
+ <label>
+ <input type="checkbox" ng-checked="sites[site.tags.site].enabled" ng-click="sites[site.tags.site].enabled = !sites[site.tags.site].enabled">
+ <strong>Enabled</strong>
+ </label>
+ </div>
+ <hr/>
+
+ <!-- Application -->
+ <label>* Application</label>
+ <div class="row">
+ <div class="col-sm-6">
+ <h1 class="text-muted text-center" ng-show="sites[site.tags.site].applications.length === 0">No application in using</h1>
+ <ul class="products-list product-list-in-box fixed-height" ng-show="sites[site.tags.site].applications.length !== 0">
+ <li class="item" ng-repeat="application in sites[site.tags.site].applications track by $index" ng-class="{active: _application === application}">
+ <div class="product-operation single">
+ <span class="fa fa-cubes"></span>
+ </div>
+ <div class="product-info">
+ <a class="fa fa-times pull-right" ng-click="removeApplication(application, sites[site.tags.site])"></a>
+ <span class="product-title">
+ <a class="fa fa-cog" ng-click="setApplication(application)"></a>
+ {{application.tags.application}}
+ </span>
+ <span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <div class="col-sm-6">
+ <ul class="products-list product-list-in-box fixed-height">
+ <li class="item" ng-repeat="application in sites[site.tags.site].optionalApplications track by $index">
+ <button class="btn btn-lg btn-primary pull-left" ng-click="addApplication(application, sites[site.tags.site])" ng-disabled="_pageLock">
+ <span class="fa fa-star-o"></span>
+ </button>
+ <div class="product-info">
+ <span class="product-title">{{application.tags.application}}</span>
+ <span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div><!-- /.box-body -->
+
+ <div class="box-footer clearfix">
+ <button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/landing.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/landing.html b/eagle-webservice/src/main/webapp/_app/partials/landing.html
new file mode 100644
index 0000000..a2e0f47
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/landing.html
@@ -0,0 +1,30 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<p class="lead">
+ <span ng-if="!Application.current()">Current site do not use any application.</span>
+ <span ng-if="Application.current()">Current application do not install any feature.</span>
+
+ <span ng-if="Auth.isRole('ROLE_ADMIN')">
+ Click
+ <a href="#/config/site" ng-if="!Application.current()">here</a>
+ <a href="#/config/application" ng-if="Application.current()">here</a>
+ to configure.
+ </span>
+ <span ng-if="!Auth.isRole('ROLE_ADMIN')">Please contact your admin.</span>
+</p>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/login.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/login.html b/eagle-webservice/src/main/webapp/_app/partials/login.html
new file mode 100644
index 0000000..7faef42
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/login.html
@@ -0,0 +1,54 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="login-box">
+ <div class="login-logo">
+ <a href="#/">Apache Eagle</a>
+ </div>
+
+ <div class="login-box-body" ng-show="!loginSuccess">
+ <p class="login-box-msg">Sign in to start your session</p>
+ <div class="form-group has-feedback">
+ <input type="text" class="form-control" placeholder="User Name" ng-model="username" ng-keypress="login($event)" autocomplete="off" id="username">
+ <span class="glyphicon glyphicon-user form-control-feedback"></span>
+ </div>
+ <div class="form-group has-feedback">
+ <input type="password" class="form-control" placeholder="Password" ng-model="password" ng-keypress="login($event)">
+ <span class="glyphicon glyphicon-lock form-control-feedback"></span>
+ </div>
+ <div class="row">
+ <div class="col-xs-8">
+ <div class="checkbox">
+ <label> <input type="checkbox" ng-checked="rememberUser" ng-click="rememberUser = !rememberUser;" /> Remember Me
+ </label>
+ </div>
+ </div>
+ <div class="col-xs-4">
+ <button class="btn btn-primary btn-block btn-flat" ng-click="login($event, true)" ng-disabled="lock">Sign In</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="login-box-body text-center" ng-show="loginSuccess">
+ <p class="login-box-msg">Login success</p>
+ <p>
+ <span class="fa fa-refresh fa-spin"></span>
+ Loading environment...
+ </p>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/css/animation.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/css/animation.css b/eagle-webservice/src/main/webapp/_app/public/css/animation.css
new file mode 100644
index 0000000..954bd29
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/css/animation.css
@@ -0,0 +1,46 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+[ui-view].ng-enter, [ui-view].ng-leave {
+ position: absolute;
+ left: 0;
+ right: 0;
+ -webkit-transition: all .5s ease-in-out;
+ -moz-transition: all .5s ease-in-out;
+ -o-transition: all .5s ease-in-out;
+ transition: all .3s ease-in-out;
+}
+
+[ui-view].ng-enter {
+ opacity: 0;
+}
+
+[ui-view].ng-enter-active {
+ opacity: 1;
+}
+
+[ui-view].ng-leave {
+ opacity: 1;
+ transform:translate3d(0, 0, 0);
+}
+
+[ui-view].ng-leave-active {
+ opacity: 0;
+ transform:translate3d(20%, 0, 0);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/css/main.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/css/main.css b/eagle-webservice/src/main/webapp/_app/public/css/main.css
new file mode 100644
index 0000000..a7eba4b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/css/main.css
@@ -0,0 +1,805 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+/* Frame */
+body.no-sidebar .content-wrapper {
+ margin-left: 0;
+
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+body.no-sidebar .main-footer {
+ margin-left: 0;
+}
+
+/* Navigation */
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header .img-circle {
+ display: inline-block;
+ border: 3px solid;
+ border-color: rgba(255,255,255,0.2);
+ width: 90px;
+ height: 90px;
+ margin-top: 10px;
+}
+
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header .fa {
+ font-size: 60px;
+ color: rgba(255,255,255,0.8);
+ margin-top: 10px;
+}
+
+ /* Common */
+a {
+ cursor: pointer;
+}
+
+/* Table */
+.table.table-sm>tbody>tr>td,
+.table.table-sm>tbody>tr>th,
+.table.table-sm>tfoot>tr>td,
+.table.table-sm>tfoot>tr>th,
+.table.table-sm>thead>tr>td,
+.table.table-sm>thead>tr>th{
+ padding: 3px 8px;
+}
+
+.table thead th .fa.fa-sort,
+.table thead th .fa.fa-sort-asc,
+.table thead th .fa.fa-sort-desc {
+ margin-top: 5px;
+ opacity: 0.3;
+ float: right;
+}
+.table thead th:hover .fa.fa-sort,
+.table thead th:hover .fa.fa-sort-asc,
+.table thead th:hover .fa.fa-sort-desc {
+ opacity: 0.8;
+}
+
+.table tr th,
+.table tr td {
+ -webkit-transition: background .5s linear;
+ -o-transition: background .5s linear;
+ transition: background .5s linear;
+}
+
+.sortTable-cntr .pagination {
+ margin-top: 0;
+}
+
+.table th.input-field,
+.table td.input-field {
+ padding: 0;
+ vertical-align: middle;
+}
+
+.table th.input-field > input,
+.table td.input-field > input,
+.table th.input-field > select,
+.table td.input-field > select {
+ border: none;
+ transition: border-color 0s;
+}
+
+.table th.input-field > input:focus,
+.table td.input-field > input:focus,
+.table th.input-field > select:focus,
+.table td.input-field > select:focus {
+ box-shadow: inset 1px 1px 0px #3c8dbc, inset -1px -1px 0px #3c8dbc;
+}
+
+.table th.input-field > input.has-warning,
+.table td.input-field > input.has-warning {
+ box-shadow: inset 1px 1px 0px #f39c12, inset -1px -1px 0px #f39c12;
+}
+
+/* Box */
+.small-box > a.inner {
+ color: #FFF;
+ display: block;
+}
+
+.small-box > a.inner h3 {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ font-size: 32px;
+}
+
+.info-box.bg-gray,
+.info-box a {
+ color: #FFFFFF;
+}
+.info-box a:hover {
+ color: #FFFFFF;
+ text-decoration: underline;
+}
+
+.info-box-content a.config {
+ color: rgba(255,255,255,0.8);
+}
+.info-box-content a.config:hover {
+ color: #FFFFFF;
+}
+
+.info-box-content.box-clickable {
+ box-shadow: 0 0 3px;
+}
+.box-clickable {
+ cursor: pointer;
+}
+
+.info-box-content .info-box-text.text-large {
+ font-size: 26px;
+ margin: 5px 0 10px 0;
+}
+
+/* inline group */
+.inline-group dl,
+.inline-group dl dt,
+.inline-group dl dd {
+ display: inline-block;
+}
+
+.inline-group dl {
+ margin-right: 35px;
+}
+.inline-group dl dt {
+ margin-right: 20px;
+}
+
+.inline-group.form-inline {
+ margin-top: 5px;
+}
+.inline-group dl {
+ margin-right: 25px;
+}
+.inline-group dl dt {
+ margin-right: 5px;
+}
+
+/* Search box */
+.search-box {
+ position: relative;
+ margin-bottom: 15px;
+}
+.search-box input[type="search"] {
+ padding-left: 26px;
+}
+.search-box .fa.fa-search {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ z-index: 2;
+ pointer-events: none;
+ color: #999;
+}
+
+/* Navigation Tab */
+ul.nav.nav-tabs li .btn {
+ margin-top: 1px;
+}
+
+.modal-body ul.nav.nav-tabs {
+ border-bottom-color: #F4F4F4;
+ margin-bottom: 15px;
+}
+
+.modal-body ul.nav.nav-tabs li {
+ border-top: 3px solid #FFFFFF;
+ margin-right: 3px;
+}
+.modal-body ul.nav.nav-tabs li.active {
+ border-top-color: #3c8dbc;
+}
+
+.modal-body ul.nav.nav-tabs li > a,
+.modal-body ul.nav.nav-tabs li > a:active,
+.modal-body ul.nav.nav-tabs li > a:hover {
+ border: none;
+ border-radius: 0;
+ margin: 0;
+ padding: 6px 15px 8px 15px;
+ color: #444;
+}
+.modal-body ul.nav.nav-tabs li:not(.active) > a:hover {
+ background: rgba(0,0,0,0);
+ color: #999;
+}
+.modal-body ul.nav.nav-tabs li.active > a {
+ border-left: 1px solid #F4F4F4;
+ border-right: 1px solid #F4F4F4;
+}
+
+/* Step Navigation */
+.step-cntr .step {
+ background: #3c8dbc;
+ margin: 0 0 20px 0;
+ color: #FFF;
+ height: 60px;
+ border-radius: 3px;
+ box-shadow: 0 1px 1px rgba(0,0,0,0.1);
+ display: block;
+
+ -webkit-transition: background .15s linear;
+ -o-transition: background .15s linear;
+ transition: background .15s linear;
+}
+.step-cntr .step.active {
+ background: #f39c12;
+}
+
+.step-cntr .step h1,
+.step-cntr .step h2,
+.step-cntr .step p {
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.step-cntr .step h1 {
+ display: inline-block;
+ font-size: 30px;
+ float: left;
+ border-right: 2px solid rgba(255,255,255,0.2);
+ width: 60px;
+ height: 60px;
+ text-align: center;
+ padding-top: 12px;
+ margin-right: 10px;
+}
+.step-cntr .step h2 {
+ font-size: 18px;
+ padding: 8px 0 5px 0;
+}
+
+/* Panel */
+.panel-group.panel-group-sm .panel .panel-heading {
+ padding: 5px 6px 5px 10px;
+}
+.panel-group.panel-group-sm .panel .panel-heading h4 {
+ font-size: 14px;
+}
+.panel-group.panel-group-sm .panel .panel-heading h4 a {
+ display: block;
+}
+.panel-group.panel-group-sm .panel .panel-heading .pull-right {
+ padding-left: 5px;
+ padding-right: 5px;
+ border-radius: 3px;
+}
+
+/* Drop Down */
+.dropdown-menu > li.danger > a {
+ color: #dd4b39;
+}
+.dropdown-menu > li.danger > a:hover {
+ color: #FFFFFF;
+ background: #dd4b39;
+}
+
+/* Drop Down */
+.dropdown-menu.left {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-submenu{position:relative;}
+.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
+.dropdown-submenu:hover>.dropdown-menu{display:block;}
+.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
+.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
+.dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
+
+/* Input Group */
+.input-group .input-group-btn select {
+ width: auto;
+}
+
+/* Form group */
+.form-group .checkbox {
+ display: inline;
+ margin-right: 10px;
+}
+
+.form-group select.has-warning,
+.form-group input.has-warning {
+ border-color: #f39c12;
+ box-shadow: none;
+}
+
+.checkbox.noMargin {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+
+/* UL */
+ul.path {
+ margin-left: 0;
+}
+
+ul.path li {
+ padding: 0;
+ margin-right: 5px;
+}
+ul.path li a {
+ color: #FFFFFF;
+}
+
+ul.tree {
+ padding: 0 0 0 5px;
+}
+
+ul.tree > li,
+ul.tree > li > ul > li {
+ list-style-type: none;
+}
+
+ul.tree .tree-item .hover {
+ display: none;
+}
+ul.tree .tree-item:hover .hover {
+ display: inline-block;
+}
+
+ul.tree > li > ul {
+ padding: 0 0 0 25px;
+}
+
+ul.tree > li > ul > li.active {
+ background: #F4F4F4;
+}
+
+ul.tree.tree-bordered {
+ border: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered,
+ul.tree.tree-bordered > li > ul {
+ padding: 0;
+}
+ul.tree.tree-bordered > li:not(:last-child) {
+ border-bottom: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered > li > ul > li {
+ border-top: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered > li > span,
+ul.tree.tree-bordered > li > a {
+ display: block;
+ padding: 8px;
+}
+ul.tree.tree-bordered > li > ul > li > span,
+ul.tree.tree-bordered > li > ul > li > a {
+ display: block;
+ padding: 8px 8px 8px 30px;
+}
+
+.product-list-in-box > .item {
+ -webkit-transition: background .5s linear;
+ -o-transition: background .5s linear;
+ transition: background .5s linear;
+}
+.product-list-in-box > .item.ng-animate {
+ transition: 0s;
+}
+.product-list-in-box > .item.active {
+ background: #F5FAFC;
+ -webkit-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.nav.fixed-height,
+.products-list.fixed-height {
+ height: 402px;
+ overflow-y: auto;
+}
+
+.products-list .product-operation {
+ float: left;
+ border: 1px solid #9EC8E0;
+ border-radius: 5px;
+ overflow: hidden;
+}
+
+.products-list .product-operation .fa {
+ display: block;
+ padding: 4px 16px;
+ color: #3c8dbc;
+}
+.products-list .product-operation a.fa:hover {
+ color: #FFFFFF;
+ background: #337ab7;
+}
+
+.products-list .product-operation.single .fa {
+ padding: 12px 12px;
+ font-size: 20px;
+}
+
+.products-list .item .product-info a.fa.fa-times {
+ display: none;
+}
+.products-list .item:hover .product-info a.fa.fa-times {
+ display: block;
+}
+
+/* Label */
+.label.label-default {
+ color: #FFFFFF;
+}
+
+.label.label-sm {
+ padding: .0em .4em .1em;
+}
+
+/* Row */
+.row.narrow {
+ margin-left: -5px;
+ margin-right: -5px;
+ margin-bottom: -10px;
+}
+
+.row.narrow>.col-xs-1, .row.narrow>.col-sm-1, .row.narrow>.col-md-1, .row.narrow>.col-lg-1, .row.narrow>.col-xs-2, .row.narrow>.col-sm-2, .row.narrow>.col-md-2, .row.narrow>.col-lg-2, .row.narrow>.col-xs-3, .row.narrow>.col-sm-3, .row.narrow>.col-md-3, .row.narrow>.col-lg-3, .row.narrow>.col-xs-4, .row.narrow>.col-sm-4, .row.narrow>.col-md-4, .row.narrow>.col-lg-4, .row.narrow>.col-xs-5, .row.narrow>.col-sm-5, .row.narrow>.col-md-5, .row.narrow>.col-lg-5, .row.narrow>.col-xs-6, .row.narrow>.col-sm-6, .row.narrow>.col-md-6, .row.narrow>.col-lg-6, .row.narrow>.col-xs-7, .row.narrow>.col-sm-7, .row.narrow>.col-md-7, .row.narrow>.col-lg-7, .row.narrow>.col-xs-8, .row.narrow>.col-sm-8, .row.narrow>.col-md-8, .row.narrow>.col-lg-8, .row.narrow>.col-xs-9, .row.narrow>.col-sm-9, .row.narrow>.col-md-9, .row.narrow>.col-lg-9, .row.narrow>.col-xs-10, .row.narrow>.col-sm-10, .row.narrow>.col-md-10, .row.narrow>.col-lg-10, .row.narrow>.col-xs-11, .row.narrow>.col-sm-11, .row.narrow>.col-md-11, .
row.narrow>.col-lg-11, .row.narrow>.col-xs-12, .row.narrow>.col-sm-12, .row.narrow>.col-md-12, .row.narrow>.col-lg-12 {
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.row.narrow > [class^="col-"],
+.row.narrow > [class*=" col-"] {
+ margin-bottom: 10px;
+}
+
+/* Chart */
+.sortable-mock-element .nvd3-chart-wrapper {
+ background: #FFFFFF;
+ opacity: 0.8;
+}
+
+.sortable-enter .nvd3-chart-wrapper {
+ border-color: #3c8dbc;
+ pointer-events: none;
+}
+.sortable-enter .nvd3-chart-wrapper .nvtooltip {
+ display: none;
+}
+
+.nvd3-chart-wrapper {
+ position: relative;
+ border: 1px solid rgba(0,0,0,0.1);
+}
+.nvd3-chart-wrapper:hover {
+ //border-color: #F4F4F4;
+}
+
+.nvd3-chart-wrapper .nvd3-chart-config {
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ display: none;
+ border-radius: 0;
+ padding: 0 5px;
+ background: rgba(0,0,0,0.7);
+}
+.nvd3-chart-wrapper:hover .nvd3-chart-config {
+ display: block;
+}
+
+.nvd3-chart-wrapper .nvd3-chart-config a {
+ color: rgba(255,255,255, 0.9);
+ padding: 5px 2px 4px 2px;
+ font-size: 16px;
+}
+.nvd3-chart-wrapper .nvd3-chart-config a:hover {
+ color: #FFFFFF;
+}
+
+.nvd3-chart-cntr {
+ padding: 5px;
+}
+
+.nvd3-chart-cntr > h3 {
+ text-align: center;
+ font-size: 16px;
+ font-weight: bolder;
+ margin: 0;
+ padding: 5px 0;
+
+ overflow:hidden;
+ text-overflow:ellipsis;
+
+}
+
+.nvd3-chart-cntr > svg.nvd3-svg {
+ height: 200px;
+}
+
+.nvd3-chart-cntr.lg > svg.nvd3-svg {
+ height: 400px;
+}
+
+/* Tab */
+body .tab-content>.tab-pane {
+ display: block;
+ height: 0px;
+ overflow: hidden;
+ position: relative;
+}
+body .tab-content>.tab-pane.active {
+ height: auto;
+ overflow-x: visible;
+ overflow-y: visible;
+}
+
+body .modal-body .nav-pills > li > a,
+body .box-body .nav-pills > li > a {
+ padding: 5px 15px;
+ border: none;
+}
+
+body .modal-body .nav-stacked > li {
+ border-bottom: 1px solid #f4f4f4;
+ margin: 0;
+}
+body .modal-body .nav-stacked > li:last-child {
+ border-bottom: none;
+}
+
+body .box-body .nav-tabs-custom {
+ box-shadow: none;
+ margin-bottom: 0;
+}
+body .box-body .nav-tabs-custom > .nav-tabs > li:first-of-type.active > a {
+ border-left-color: #f4f4f4;
+}
+body .box-body .nav-tabs-custom > .nav-tabs > li > a {
+ padding: 8px 15px;
+}
+body .box-body .nav-tabs-custom > .tab-content {
+ padding: 10px 0;
+}
+
+/* Box */
+.box .guideline {
+ margin-top: 0;
+}
+
+.box.inner-box {
+ border: none;
+ box-shadow: none;
+ padding: 5px 10px;
+ margin: 0;
+ border-bottom: 1px solid #f4f4f4;
+ position: relative;
+ border-radius: 0;
+}
+
+.box.inner-box .box-title {
+ margin: 0 5px 5px 0;
+ padding: 0;
+ font-size: 16px;
+ font-weight: bolder;
+ display: inline-block;
+ word-break: break-all;
+}
+
+.box.inner-box .box-tools {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.box.inner-box:last-child {
+ border-bottom: none;
+}
+
+/* Navigation Tab */
+.nav-tabs-custom {
+ position: relative;
+}
+
+.nav-tabs-custom .box-tools {
+ position: absolute;
+ right: 15px;
+ top: 8px;
+}
+
+.nav-tabs-custom .box-tools .strong {
+ font-weight: bolder;
+}
+
+/* Customize */
+#content {
+ position: relative;
+}
+
+.page-fixed {
+ position: absolute;
+ top: -45px;
+ right: 0;
+}
+
+@media (max-width:991px) {
+ .page-fixed {
+ top: -70px;
+ }
+}
+
+.fixed-right {
+ position: absolute;
+ right: 0;
+ z-index: 3;
+}
+
+.main-header .logo img {
+ height: 34px;
+}
+
+.main-header .navbar-toggle {
+ float: none;
+ border-radius: 0;
+}
+.main-header .navbar-toggle:hover {
+ background: rgba(0, 0, 0, 0.1);
+}
+
+#moduleMenu > ul > li.active > a {
+ border-top: 3px solid rgba(255,255,255,0.8);
+ padding-top: 12px;
+}
+
+@media (max-width: 767px) {
+ #moduleMenu > ul > li.active > a {
+ padding: 10px 15px;
+ border-top: none;
+ border-left: 3px solid rgba(255,255,255,0.8);
+ }
+
+ .main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a {
+ color: #333;
+ }
+ .main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a:hover {
+ color: #FFF;
+ }
+}
+
+#timeRangePickerCntr .navbar-form {
+ display: inline-block;
+ padding-right: 0;
+}
+
+#timeRangePickerCntr #timeRangePicker {
+ min-width: 300px;
+}
+
+body .login-box, body .register-box {
+ margin: 3% auto;
+}
+
+.content-header > .breadcrumb > li {
+ font-size: 14px;
+}
+
+.daterangepicker .ranges {
+ width: 110px!important;
+}
+.daterangepicker .daterangepicker_start_input,
+.daterangepicker .daterangepicker_end_input {
+ display: block!important;
+ padding: 0!important;
+ float: none!important;
+}
+.daterangepicker .daterangepicker_start_input .input-mini,
+.daterangepicker .daterangepicker_end_input .input-mini {
+ width: 110px!important;
+}
+
+.form-group.inner-icon {
+ position: relative;
+}
+.form-group.inner-icon .fa {
+ position: absolute;
+ left: 10px;
+ top: 10px;
+}
+.form-group.inner-icon input {
+ padding-left: 35px;
+}
+
+#autoRefreshCntr > a {
+ border: none;
+ opacity: 0.3;
+}
+#autoRefreshCntr.autoRefresh > a {
+ opacity: 1;
+}
+
+.table-responsive .row {
+ margin: 0;
+}
+
+
+/* Misc */
+body .tooltip-inner {
+ max-width: 500px;
+}
+
+.text-nowrap {
+ white-space: nowrap;
+}
+
+.text-ellipsis,
+.label.text-ellipsis {
+ overflow:hidden;
+ text-overflow:ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ max-width: 100%;
+}
+td.text-ellipsis {
+ display: table-cell;
+}
+
+.text-breakall {
+ max-width: 100%;
+ display: inline-block;
+ word-wrap: break-word;
+}
+
+.btn.btn-xs.sm {
+ font-size: 12px;
+ padding: 2px 6px;
+}
+
+.form-control.input-xs {
+ height: 24px;
+ padding: 2px 8px;
+ font-size: 12px;
+ line-height: 100%;
+}
+
+pre.noWrap {
+ border: none;
+ border-radius: 0;
+ background: transparent;
+ margin: 0;
+ padding: 0;
+}
+
+.noSelect {
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+}
+
+.blink {
+ animation: blinker 1s linear infinite;
+}
+
+@keyframes blinker {
+ 50% {opacity: 0.0;}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
new file mode 100644
index 0000000..462b41b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
@@ -0,0 +1,358 @@
+/*
+ * 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 featureControllers = angular.module('featureControllers');
+ var feature = featureControllers.register("classification");
+ var eagleApp = angular.module('eagleApp');
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+
+ // =============================================================
+ // = Sensitivity =
+ // =============================================================
+ feature.navItem("sensitivity", "Classification", "user-secret");
+ feature.controller('sensitivity', function(PageConfig, Site, $scope, Application, Entities, UI) {
+ PageConfig.pageTitle = "Data Classification";
+ PageConfig.pageSubTitle = Site.current().tags.site;
+ $scope.ajaxId = eagleApp._TRS();
+ $scope.viewConfig = Application.current().configObj.view;
+
+ if(!$scope.viewConfig) {
+ $.dialog({
+ title: "OPS",
+ content: "View configuration not defined in Application."
+ });
+ return;
+ }
+
+ // ===================== Function =====================
+ $scope.export = function() {
+ var _data = {};
+ UI.fieldConfirm({title: "Export Classification", confirm: false, size: "large"}, _data, [
+ {name: "Data", field: "data", type: "blob", rows: 20, optional: true, readonly: true}]
+ );
+
+ Entities.queryEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function(data) {
+ _data.data = JSON.stringify(data, null, "\t");
+ });
+ };
+
+ $scope.import = function() {
+ UI.fieldConfirm({title: "Import Classification", size: "large"}, {}, [
+ {name: "Data", field: "data", type: "blob", rows: 20, optional: true}
+ ], function(entity) {
+ var _list = common.parseJSON(entity.data, false);
+ if(!_list) {
+ return "Invalid JSON format";
+ }
+ if(!$.isArray(_list)) {
+ return "Not an array";
+ }
+ }).then(null, null, function(holder) {
+ Entities.updateEntity($scope.viewConfig.service, common.parseJSON(holder.entity.data, []), {timestamp: false})._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ $scope.deleteAll = function() {
+ UI.deleteConfirm("All the Classification Data").then(null, null, function(holder) {
+ Entities.deleteEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+ });
+ // =============================================================
+ // = Sensitivity - Job =
+ // =============================================================
+ feature.controller('sensitivityViewJob', function(Site, $scope, $wrapState, Entities) {
+ $scope.items = [];
+
+ // Mark sensitivity
+ $scope._oriItem = {};
+ $scope._markItem = {};
+
+ // ======================= View =======================
+ // Item
+ $scope.updateItems = function() {
+ $scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site});
+ };
+
+
+ $scope.updateItems();
+
+ // =================== Sensitivity ===================
+ $scope.markSensitivity = function(item) {
+ $scope._oriItem = item;
+ $scope._markItem = {
+ prefix: $scope.viewConfig.prefix,
+ tags: {
+ site: Site.current().tags.site
+ },
+ sensitivityType: ""
+ };
+
+ $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.jobId;
+ $("#sensitivityMDL").modal();
+ };
+ $scope.confirmUpateSensitivity = function() {
+ $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+ Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+ Entities.dialog(data);
+ });
+ $("#sensitivityMDL").modal('hide');
+ };
+ $scope.unmarkSensitivity = function(item) {
+ $.dialog({
+ title: "Unmark Confirm",
+ content: "Do you want to remove the sensitivity mark on '" + item.jobId + "'?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ var _cond = {site: Site.current().tags.site};
+ _cond[$scope.viewConfig.keys[0]] = item.jobId;
+ Entities.deleteEntities($scope.viewConfig.service, _cond);
+
+ item.sensitiveType = null;
+ $scope.$apply();
+ });
+ };
+ });
+ // =============================================================
+ // = Sensitivity - Folder =
+ // =============================================================
+ feature.controller('sensitivityViewFolder', function(Site, $scope, $wrapState, Entities) {
+ $scope.path = $wrapState.param.path || "/";
+ $scope.pathUnitList = [];
+ $scope.items = [];
+
+ // Mark sensitivity
+ $scope._oriItem = {};
+ $scope._markItem = {};
+
+ // ======================= View =======================
+ // Path
+ function _refreshPathUnitList(_path) {
+ var _start,_current, _unitList = [];
+ _path = _path + (_path.match(/\/$/) ? "" : "/");
+ for(_current = _start = 0 ; _current < _path.length ; _current += 1) {
+ if(_path[_current] === "/") {
+ _unitList.push({
+ name: _path.substring(_start, _current + (_current === 0 ? 1 : 0)),
+ path: _path.substring(0, _current === 0 ? 1 : _current)
+ });
+ _start = _current + 1;
+ }
+ }
+ $scope.pathUnitList = _unitList;
+ }
+
+ // Item
+ $scope.updateItems = function(path) {
+ if(path) $scope.path = path;
+
+ $scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site, path: $scope.path});
+ $scope.items._promise.success(function(data) {
+ Entities.dialog(data, function() {
+ if($scope.path !== "/") $scope.updateItems("/");
+ });
+ });
+ _refreshPathUnitList($scope.path);
+ };
+
+ $scope.getFileName = function(item) {
+ return (item.resource + "").replace(/^.*\//, "");
+ };
+
+ $scope.updateItems($scope.path);
+
+ // =================== Sensitivity ===================
+ $scope.markSensitivity = function(item) {
+ $scope._oriItem = item;
+ $scope._markItem = {
+ prefix: $scope.viewConfig.prefix,
+ tags: {
+ site: Site.current().tags.site
+ },
+ sensitivityType: ""
+ };
+ $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
+ $("#sensitivityMDL").modal();
+ };
+ $scope.confirmUpateSensitivity = function() {
+ $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+ Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+ Entities.dialog(data);
+ });
+ $("#sensitivityMDL").modal('hide');
+ };
+ $scope.unmarkSensitivity = function(item) {
+ $.dialog({
+ title: "Unmark Confirm",
+ content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ var _cond = {site: Site.current().tags.site};
+ _cond[$scope.viewConfig.keys[0]] = item.resource;
+ Entities.deleteEntities($scope.viewConfig.service, _cond);
+
+ item.sensitiveType = null;
+ $scope.$apply();
+ });
+ };
+ });
+
+ // =============================================================
+ // = Sensitivity - Table =
+ // =============================================================
+ feature.controller('sensitivityViewTable', function(Site, $scope, Entities) {
+ $scope.databases = null;
+ $scope.table = null;
+
+ // Mark sensitivity
+ $scope._oriItem = {};
+ $scope._markItem = {};
+
+ // ======================= View =======================
+ var _fillAttr = function(list, key, target) {
+ list._promise.then(function() {
+ $.each(list, function(i, unit) {
+ unit[key] = unit[target];
+ });
+ });
+ return list._promise;
+ };
+
+ $scope.loadDatabases = function(database) {
+ var _dbs = Entities.query($scope.viewConfig.api.database, {site: Site.current().tags.site});
+ return _fillAttr(_dbs, "database", $scope.viewConfig.mapping.database).then(function() {
+ if($scope.databases) {
+ $.each($scope.databases, function(i, oriDB) {
+ var db = common.array.find(oriDB.resource, _dbs, "resource");
+ if(db) {
+ db.show = oriDB.show;
+ db.tables = oriDB.tables;
+ }
+ });
+ }
+ $scope.databases = _dbs;
+ });
+ };
+ $scope.loadDatabases();
+
+ $scope.loadTables = function(database, force) {
+ var _tables, _qry;
+ if(database.tables && !force) return;
+ _qry = {
+ site: Site.current().tags.site
+ };
+ _qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
+ _tables = Entities.query($scope.viewConfig.api.table, _qry);
+ if(!database.tables) database.tables = _tables;
+ _fillAttr(_tables, "table", $scope.viewConfig.mapping.table);
+ return _fillAttr(_tables, "database", $scope.viewConfig.mapping.database).then(function() {
+ database.tables = _tables;
+ });
+ };
+
+ $scope.loadColumns = function(database, table) {
+ $scope.table = table;
+
+ if(table.columns) return;
+ var _qry = {
+ site: Site.current().tags.site
+ };
+ _qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
+ _qry[$scope.viewConfig.mapping.table] = table[$scope.viewConfig.mapping.table];
+ table.columns = Entities.query($scope.viewConfig.api.column, _qry);
+ _fillAttr(table.columns, "column", $scope.viewConfig.mapping.column);
+ };
+
+ $scope.refreshData = function() {
+ $scope.loadDatabases().then(function() {
+ if(!$scope.table) return;
+
+ var _table = $scope.table;
+ var _db = common.array.find($scope.table.database, $scope.databases, "database");
+ if(_db) {
+ $scope.loadTables(_db, true).then(function() {
+ $scope.table = common.array.find(_table.table, _db.tables, "table");
+ $scope.table.columns = _table.columns;
+ });
+ }
+ });
+ };
+
+ // =================== Sensitivity ===================
+ $scope.markSensitivity = function(item, event) {
+ if(event) event.stopPropagation();
+
+ $scope._oriItem = item;
+ $scope._markItem = {
+ prefix: $scope.viewConfig.prefix,
+ tags: {
+ site: Site.current().tags.site
+ },
+ sensitivityType: ""
+ };
+ $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
+ $("#sensitivityMDL").modal();
+ };
+ $scope.confirmUpateSensitivity = function() {
+ $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+ Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+ Entities.dialog(data);
+ $scope.refreshData();
+ });
+ $("#sensitivityMDL").modal('hide');
+ };
+ $scope.unmarkSensitivity = function(item, event) {
+ if(event) event.stopPropagation();
+
+ $.dialog({
+ title: "Unmark Confirm",
+ content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+ confirm: true
+ }, function(ret) {
+ if(!ret) return;
+
+ var _qry = {
+ site: Site.current().tags.site
+ };
+ _qry[$scope.viewConfig.keys[0]] = item.resource;
+ Entities.deleteEntities($scope.viewConfig.service, _qry)._promise.then(function() {
+ $scope.refreshData();
+ });
+
+ item.sensitiveType = null;
+ $scope.$apply();
+ });
+ };
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
new file mode 100644
index 0000000..41fb291
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
@@ -0,0 +1,40 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-folder-open"></i>
+ <h3 class="box-title ng-binding">{{Application.current().displayName}}</h3>
+ <div class="box-tools pull-right" ng-if="viewConfig">
+ <div class="btn-group">
+ <button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
+ <span class="fa fa-wrench"></span>
+ </button>
+ <ul class="dropdown-menu" role="menu">
+ <li><a ng-click="import()"><span class="fa fa-cloud-upload"></span> Import</a></li>
+ <li><a ng-click="export()"><span class="fa fa-cloud-download"></span> Export</a></li>
+ <li class="divider"></li>
+ <li class="danger"><a ng-click="deleteAll()"><span class="fa fa-trash"></span> Delete All</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <div class="box-body">
+ <ng-include ng-if="viewConfig" src="'public/feature/classification/page/sensitivity/' + viewConfig.type + '.html?_=' + ajaxId"></ng-include>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
new file mode 100644
index 0000000..cfefffa
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
@@ -0,0 +1,110 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div ng-controller="classification_sensitivityViewFolder">
+ <ul class="list-inline path">
+ <li>Path:</li>
+ <li ng-repeat="unit in pathUnitList">
+ <a ng-click="updateItems(unit.path)" class="label bg-black">{{unit.name}}</a>
+ </li>
+ </ul>
+
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th width="15%">File Name</th>
+ <th width="10%">Owner</th>
+ <th width="10%">Group</th>
+ <th>Sensitivity Type</th>
+ <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-show="items._promise.$$state.status !== 1">
+ <td colspan="5">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </td>
+ </tr>
+ <tr ng-show="items._promise.$$state.status === 1 && !items.length">
+ <td colspan="5">
+ <span class="fa fa-exclamation-triangle"> </span>
+ Empty Folder
+ </td>
+ </tr>
+ <tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
+ <td>
+ <span ng-show="!item.isdir">
+ <span class="fa fa-file"> </span>
+ {{getFileName(item)}}
+ </span>
+ <a ng-show="item.isdir" ng-click="updateItems(item.resource)">
+ <span class="fa fa-folder"> </span>
+ {{getFileName(item)}}
+ </a>
+
+ <span class="pull-right" ng-show="item.childSensitiveTypes.length">
+ <span class="fa fa-dot-circle-o text-muted" uib-tooltip="Contain child sensitivity defination"> </span>
+ </span>
+ </td>
+ <td>{{item.owner}}</td>
+ <td>{{item.groupName}}</td>
+ <td>{{item.sensitiveType}}</td>
+ <td ng-show="Auth.isRole('ROLE_ADMIN')">
+ <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
+ uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
+ <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
+ uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <!-- Modal: Create / Edit site -->
+ <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog" 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">×</span>
+ </button>
+ <h4 class="modal-title">Mark Sensitivity Data</h4>
+ </div>
+ <div class="modal-body">
+ <div class="form-group">
+ <label>Resource</label>
+ <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.filedir" />
+ </div>
+ <div class="form-group">
+ <label>* Sensitivity Type</label>
+ <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
+ Update
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
new file mode 100644
index 0000000..05d70da
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
@@ -0,0 +1,92 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div ng-controller="classification_sensitivityViewJob">
+ <ul class="list-inline path">
+ <li>Oozie CoordinatorJob:</li>
+ </ul>
+
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th width="15%">JobId</th>
+ <th width="10%">AppName</th>
+ <th>Sensitivity Type</th>
+ <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-show="items._promise.$$state.status !== 1">
+ <td colspan="5">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </td>
+ </tr>
+ <tr ng-show="items._promise.$$state.status === 1 && !items.length">
+ <td colspan="5">
+ <span class="fa fa-exclamation-triangle"> </span>
+ Empty
+ </td>
+ </tr>
+ <tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
+ <td>{{item.jobId}}</td>
+ <td>{{item.name}}</td>
+ <td>{{item.sensitiveType}}</td>
+ <td ng-show="Auth.isRole('ROLE_ADMIN')">
+ <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
+ uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
+ <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
+ uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <!-- Modal: Create / Edit site -->
+ <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog" 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">×</span>
+ </button>
+ <h4 class="modal-title">Mark Sensitivity Data</h4>
+ </div>
+ <div class="modal-body">
+ <div class="form-group">
+ <label>Resource</label>
+ <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.oozieResource" />
+ </div>
+ <div class="form-group">
+ <label>* Sensitivity Type</label>
+ <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
+ Update
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>
\ No newline at end of file
[02/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/app.time.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/app.time.js b/eagle-webservice/src/main/webapp/app/public/js/app.time.js
deleted file mode 100644
index d61506a..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/app.time.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Time Zone
-(function() {
- "use strict";
-
- app.time = {
- UTC_OFFSET: 0,
- now: function() {
- return app.time.offset();
- },
- offset: function(time) {
- // Parse string number
- if(typeof time === "string" && !isNaN(+time)) {
- time = +time;
- }
-
- var _mom = new moment(time);
- _mom.utcOffset(app.time.UTC_OFFSET);
- return _mom;
- },
- /*
- * Return the moment object which use server time zone and keep the time.
- */
- srvZone: function(time) {
- var _timezone = time._isAMomentObject ? time.utcOffset() : new moment().utcOffset();
- var _mom = app.time.offset(time);
- _mom.subtract(app.time.UTC_OFFSET, "m").add(_timezone, "m");
- return _mom;
- },
-
- refreshInterval: 1000 * 10
- };
-
- // Moment update
- moment.fn.toISO = function() {
- return this.format("YYYY-MM-DDTHH:mm:ss.000Z");
- };
-
- // Force convert date
- var _toDate = moment.fn.toDate;
- moment.fn.toDate = function(ignoreTimeZone) {
- if(!ignoreTimeZone) return _toDate.bind(this)();
- return new Date(
- this.year(),
- this.month(),
- this.date(),
- this.hour(),
- this.minute(),
- this.second(),
- this.millisecond()
- );
- };
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/app.ui.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/app.ui.js b/eagle-webservice/src/main/webapp/app/public/js/app.ui.js
deleted file mode 100644
index ca494d3..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/app.ui.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-(function() {
- // ================== AdminLTE Update ==================
- var _boxSelect = $.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors;
-
- // Box collapse
- $(document).on("click", _boxSelect.collapse, function(e) {
- if(common.getValueByPath($._data(this), "events.click")) return;
-
- e.preventDefault();
- $.AdminLTE.boxWidget.collapse($(this));
- });
-
- // Box remove
- $(document).on("click", _boxSelect.remove, function(e) {
- if(common.getValueByPath($._data(this), "events.click")) return;
-
- e.preventDefault();
- $.AdminLTE.boxWidget.remove($(this));
- });
-
- // =================== jQuery Update ===================
- // Slide Toggle
- var _slideToggle = $.fn.slideToggle;
- $.fn.slideToggle = function(showOrHide) {
- if(arguments.length === 1 && typeof showOrHide === "boolean") {
- if(showOrHide) {
- this.slideDown();
- } else {
- this.slideUp();
- }
- } else {
- _slideToggle.apply(this, arguments);
- }
- };
-
- // Fade Toggle
- var _fadeToggle = $.fn.fadeToggle;
- $.fn.fadeToggle = function(showOrHide) {
- if(arguments.length === 1 && typeof showOrHide === "boolean") {
- if(showOrHide) {
- this.fadeIn();
- } else {
- this.fadeOut();
- }
- } else {
- _fadeToggle.apply(this, arguments);
- }
- };
-
- // Modal
- var _modal = $.fn.modal;
- $.fn.modal = function() {
- setTimeout(function() {
- $(this).find("input[type='text'], textarea").filter(':not([readonly]):enabled:visible:first').focus();
- }.bind(this), 500);
- _modal.apply(this, arguments);
- return this;
- };
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/common.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/common.js b/eagle-webservice/src/main/webapp/app/public/js/common.js
deleted file mode 100644
index 4c5e82f..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/common.js
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var common = {};
-
-common.template = function (str, list) {
- $.each(list, function(key, value) {
- var _regex = new RegExp("\\$\\{" + key + "\\}", "g");
- str = str.replace(_regex, value);
- });
- return str;
-};
-
-common.getValueByPath = function (unit, path, defaultValue) {
- if(unit === null || unit === undefined) throw "Unit or path can't be empty!";
- if(path === "" || path === null || path === undefined) return unit;
-
- path = path.replace(/\[(\d+)\]/g, ".$1").replace(/^\./, "").split(/\./);
- $.each(path, function(i, path) {
- unit = unit[path];
- if(unit === null || unit === undefined) {
- unit = null;
- return false;
- }
- });
- if(unit === null && defaultValue !== undefined) {
- unit = defaultValue;
- }
- return unit;
-};
-
-common.setValueByPath = function(unit, path, value) {
- if(!unit || typeof path !== "string" || path === "") throw "Unit or path can't be empty!";
-
- var _inArray = false;
- var _end = 0;
- var _start = 0;
- var _unit = unit;
-
- function _nextPath(array) {
- var _key = path.slice(_start, _end);
- if(_inArray) {
- _key = _key.slice(0, -1);
- }
- if(!_unit[_key]) {
- if(array) {
- _unit[_key] = [];
- } else {
- _unit[_key] = {};
- }
- }
- _unit = _unit[_key];
- }
-
- for(; _end < path.length ; _end += 1) {
- if(path[_end] === ".") {
- _nextPath(false);
- _start = _end + 1;
- _inArray = false;
- } else if(path[_end] === "[") {
- _nextPath(true);
- _start = _end + 1;
- _inArray = true;
- }
- }
-
- _unit[path.slice(_start, _inArray ? -1 : _end)] = value;
-
- return unit;
-};
-
-common.parseJSON = function (str, defaultVal) {
- try {
- str = (str + "").trim();
- if(Number(str).toString() === str) throw "Number format";
- return JSON.parse(str);
- } catch(err) {
- if(defaultVal === undefined) {
- console.warn("Can't parse JSON: " + str);
- }
- }
- return defaultVal === undefined ? null : defaultVal;
-};
-
-common.stringify = function(json) {
- return JSON.stringify(json, function(key, value) {
- if(/^(_|\$)/.test(key)) return undefined;
- return value;
- });
-};
-
-common.isEmpty = function(val) {
- if($.isArray(val)) {
- return val.length === 0;
- } else {
- return val === null || val === undefined;
- }
-};
-
-common.extend = function(target, origin) {
- $.each(origin, function(key, value) {
- if(/^(_|\$)/.test(key)) return;
-
- target[key] = value;
- });
- return target;
-};
-
-// ====================== Format ======================
-common.format = {};
-
-/*
- * Format date to string. Support number, string, Date instance. Will auto convert time zone offset(Moment instance will keep time zone).
- */
-common.format.date = function(val, type) {
- if(val === undefined || val === null) return "";
-
- if(typeof val === "number" || typeof val === "string" || val instanceof Date) {
- val = app.time.offset(val);
- }
- switch(type) {
- case 'date':
- return val.format("YYYY-MM-DD");
- case 'time':
- return val.format("HH:mm:ss");
- case 'datetime':
- return val.format("YYYY-MM-DD HH:mm:ss");
- case 'mixed':
- return val.format("YYYY-MM-DD HH:mm");
- default:
- return val.format("YYYY-MM-DD HH:mm:ss") + (val.utcOffset() === 0 ? '[UTC]' : '');
- }
-};
-
-// ===================== Property =====================
-common.properties = {};
-
-common.properties.parse = function (str, defaultValue) {
- var regex = /\s*([\w\.]+)\s*=\s*(.*?)\s*([\r\n]+|$)/g;
- var match, props = {};
- var hasValue = false;
- while((match = regex.exec(str)) !== null) {
- props[match[1]] = match[2];
- hasValue = true;
- }
- props = hasValue ? props : defaultValue;
- props.getValueByPath = function (path) {
- if(props[path] !== undefined) return props[path];
- var subProps = {};
- var prefixPath = path + ".";
- $.each(props, function (key, value) {
- if(typeof value === "string" && key.indexOf(prefixPath) === 0) {
- subProps[key.replace(prefixPath, "")] = value;
- }
- });
- return subProps;
- };
-
- return props;
-};
-
-common.properties.check = function (str) {
- var pass = true;
- var regex = /^\s*[\w\.]+\s*=(.*)$/;
- $.each((str || "").trim().split(/[\r\n\s]+/g), function (i, line) {
- if(!regex.test(line)) {
- pass = false;
- return false;
- }
- });
- return pass;
-};
-
-// ====================== Array =======================
-common.array = {};
-
-common.array.sum = function(list, path) {
- var _sum = 0;
- if(list) {
- $.each(list, function(i, unit) {
- var _val = common.getValueByPath(unit, path);
- if(typeof _val === "number") {
- _sum += _val;
- }
- });
- }
- return _sum;
-};
-
-common.array.max = function(list, path) {
- var _max = null;
- if(list) {
- $.each(list, function(i, unit) {
- var _val = common.getValueByPath(unit, path);
- if(typeof _val === "number" && (_max === null || _max < _val)) {
- _max = _val;
- }
- });
- }
- return _max;
-};
-
-common.array.bottom = function(list, path, count) {
- var _list = list.slice();
-
- _list.sort(function(a, b) {
- var _a = common.getValueByPath(a, path, null);
- var _b = common.getValueByPath(b, path, null);
-
- if(_a === _b) return 0;
- if(_a < _b || _a === null) {
- return -1;
- } else {
- return 1;
- }
- });
- return !count ? _list : _list.slice(0, count);
-};
-common.array.top = function(list, path, count) {
- var _list = common.array.bottom(list, path);
- _list.reverse();
- return !count ? _list : _list.slice(0, count);
-};
-
-common.array.find = function(val, list, path, findAll, caseSensitive) {
- path = path || "";
- val = caseSensitive === false ? (val || "").toUpperCase() : val;
-
- var _list = $.grep(list, function(unit) {
- if(caseSensitive === false) {
- return val === (common.getValueByPath(unit, path) || "").toUpperCase();
- } else {
- return val === common.getValueByPath(unit, path);
- }
- });
- return findAll ? _list : (_list.length === 0 ? null : _list[0]);
-};
-
-common.array.filter = function(val, list, path) {
- return common.array.find(val, list, path, true);
-};
-
-common.array.count = function(list, val, path) {
- if(arguments.length === 1) {
- return list.length;
- } else {
- return common.array.find(val, list, path, true).length;
- }
-};
-
-common.array.remove = function(val, list) {
- for(var i = 0 ; i < list.length ; i += 1) {
- if(list[i] === val) {
- list.splice(i, 1);
- i -= 1;
- }
- }
-};
-
-common.array.insert = function(val, list, index) {
- list.splice(index, 0, val);
-};
-
-common.array.moveOffset = function(item, list, offset) {
- var _index = $.inArray(item, list);
- var _tgtPos = _index + offset;
- if(_tgtPos < 0 || _tgtPos >= list.length) return;
-
- common.array.remove(item, list);
- common.array.insert(item, list, _index + offset);
-};
-
-// ======================= Map ========================
-common.map = {};
-
-common.map.toArray = function(map) {
- return $.map(map, function(unit) {
- return unit;
- });
-};
-
-// ======================= Math =======================
-common.math = {};
-
-common.math.distance = function(x1,y1,x2,y2) {
- var a = x1 - x2;
- var b = y1 - y2;
- return Math.sqrt(a * a + b * b);
-};
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/charts/line3d.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/charts/line3d.js b/eagle-webservice/src/main/webapp/app/public/js/components/charts/line3d.js
deleted file mode 100644
index eb6e6d1..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/charts/line3d.js
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.directive('line3dChart', function($compile) {
- 'use strict';
-
- return {
- restrict : 'AE',
- scope : {
- title : "@",
- data : "=",
-
- height : "=?height"
- },
- controller : function(line3dCharts, $scope, $element, $attrs) {
- $scope.height = $scope.height || 200;
-
- var _chart = line3dCharts($scope);
-
- var _chartBody = $element.find(".chart-body");
- _chartBody.height($scope.height);
-
- _chart.gen($element.find(".chart-body"), $attrs.data, {
- height : $scope.height,
- });
- },
- template : '<div class="chart">' + '<div class="chart-header">' + '<h3>{{title}}</h3>' + '</div>' + '<div class="chart-body">' + '</div>' + '</div>',
- replace : true
- };
-});
-
-eagleComponents.service('line3dCharts', function() {
- 'use strict';
-
- var charts = function($scope) {
- return {
- gen : function(ele, series, config) {
- // ======================= Initialization =======================
- ele = $(ele);
-
- series = series || [];
- config = config || {};
-
- var _bounds = [{min:-10, max: 10},{min:-10, max: 10},{min:-10, max: 10}];
- var _scale = 1;
-
- // ======================= Set Up D3 View =======================
- var width = ele.innerWidth(), height = config.height;
-
- var color = ["#7cb5ec", "#f7a35c", "#90ee7e", "#7798BF", "#aaeeee"];
-
- var svg = d3.select(ele[0]).append("svg").attr("width", width).attr("height", height);
-
- // ========================== Function ==========================
- var yaw=0.5,pitch=0.5,drag;
- var transformPrecalc = [];
-
- var offsetPoint = function(point) {
- var _point = [
- +point[0] - (_bounds[0].max + _bounds[0].min) / 2,
- -point[1] + (_bounds[1].max + _bounds[1].min) / 2,
- -point[2] + (_bounds[2].max + _bounds[2].min) / 2
- ];
- return [_point[0] * _scale, _point[1] * _scale, _point[2] * _scale];
- };
-
- var transfromPointX = function(point) {
- point = offsetPoint(point);
- return transformPrecalc[0] * point[0] + transformPrecalc[1] * point[1] + transformPrecalc[2] * point[2] + width / 2;
- };
- var transfromPointY = function(point) {
- point = offsetPoint(point);
- return transformPrecalc[3] * point[0] + transformPrecalc[4] * point[1] + transformPrecalc[5] * point[2] + height / 2;
- };
- var transfromPointZ = function(point) {
- point = offsetPoint(point);
- return transformPrecalc[6] * point[0] + transformPrecalc[7] * point[1] + transformPrecalc[8] * point[2];
- };
- var transformPoint2D = function(point) {
- var _point = [point[0], point[1], point[2]];
- return transfromPointX(_point).toFixed(10) + "," + transfromPointY(_point).toFixed(10);
- };
-
- var setTurtable = function(yaw, pitch, update) {
- var cosA = Math.cos(pitch);
- var sinA = Math.sin(pitch);
- var cosB = Math.cos(yaw);
- var sinB = Math.sin(yaw);
- transformPrecalc[0] = cosB;
- transformPrecalc[1] = 0;
- transformPrecalc[2] = sinB;
- transformPrecalc[3] = sinA * sinB;
- transformPrecalc[4] = cosA;
- transformPrecalc[5] = -sinA * cosB;
- transformPrecalc[6] = -sinB * cosA;
- transformPrecalc[7] = sinA;
- transformPrecalc[8] = cosA * cosB;
-
- if(update) _update();
- };
- setTurtable(0.4,0.4);
-
- // =========================== Redraw ===========================
- var _coordinateList = [];
- var _axisText = [];
- var coordinate = svg.selectAll(".axis");
- var axisText = svg.selectAll(".axis-text");
- var lineList = svg.selectAll(".line");
-
- function _redraw(series) {
- var _desX, _desY, _desZ, _step;
-
- // Bounds
- if(series) {
- _bounds = [{},{},{}];
- $.each(series, function(j, series) {
- // Points
- $.each(series.data, function(k, point) {
- for(var i = 0 ; i < 3 ; i += 1) {
- // Minimum
- if(_bounds[i].min === undefined || point[i] < _bounds[i].min) {
- _bounds[i].min = point[i];
- }
- // Maximum
- if(_bounds[i].max === undefined || _bounds[i].max < point[i]) {
- _bounds[i].max = point[i];
- }
- }
- });
- });
- }
-
- _desX = _bounds[0].max - _bounds[0].min;
- _desY = _bounds[1].max - _bounds[1].min;
- _desZ = _bounds[2].max - _bounds[2].min;
-
- // Step
- (function() {
- var _stepX = _desX / 10;
- var _stepY = _desY / 10;
- var _stepZ = _desZ / 10;
-
- _step = Math.min(_stepX, _stepY, _stepZ);
- _step = Math.max(_step, 0.5);
- _step = 0.5;
- })();
-
- // Scale
- (function() {
- var _scaleX = width / _desX;
- var _scaleY = height / _desY / 2;
- var _scaleZ = width / _desZ;
- _scale = Math.min(_scaleX, _scaleY, _scaleZ) / 2;
- })();
-
- // Coordinate
- // > Basic
- _coordinateList = [
- {color: "rgba(0,0,0,0.1)", data: [[0,0,-100],[0,0,100]]},
- {color: "rgba(0,0,0,0.1)", data: [[0,-100,0],[0,100,0]]},
- {color: "rgba(0,0,0,0.1)", data: [[-100,0,0],[100,0,0]]},
-
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].min,_bounds[2].min]]},
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].min,_bounds[1].min,_bounds[2].max]]},
-
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].max],[_bounds[0].max,_bounds[1].min,_bounds[2].max]]},
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].max,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].min,_bounds[2].max]]},
-
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].max,_bounds[2].min],[_bounds[0].max,_bounds[1].max,_bounds[2].min]]},
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].max,_bounds[2].min],[_bounds[0].min,_bounds[1].max,_bounds[2].max]]},
-
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].min,_bounds[1].max,_bounds[2].min]]},
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].max,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].max,_bounds[2].min]]},
- {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].max],[_bounds[0].min,_bounds[1].max,_bounds[2].max]]},
- ];
-
- _axisText = [];
- function _axisPoint(point, dimension, number) {
- // Coordinate
- if(dimension === 1) {
- _coordinateList.push({
- color: "rgba(0,0,0,0.2)",
- data:[[_step/5,point[1],0],[0,point[1],0],[0,point[1],_step/5]]
- });
- } else {
- _coordinateList.push({
- color: "rgba(0,0,0,0.2)",
- data:[[point[0],-_step/8,point[2]], [point[0],_step/8,point[2]]]
- });
- }
-
- // Axis Text
- if(number.toFixed(0) == number + "") {
- point.text = number;
- point.dimension = dimension;
- _axisText.push(point);
- }
- }
- function _axisPoints(dimension, bound) {
- var i, _unit;
- for(i = _step ; i < bound.max + _step ; i += _step) {
- _unit = [0,0,0];
- _unit[dimension] = i;
- _axisPoint(_unit, dimension, i);
- }
- for(i = -_step ; i > bound.min - _step ; i -= _step) {
- _unit = [0,0,0];
- _unit[dimension] = i;
- _axisPoint(_unit, dimension, i);
- }
- }
- // > Steps
- _axisPoint([0,0,0],1,0);
- _axisPoints(0, _bounds[0]);
- _axisPoints(1, _bounds[1]);
- _axisPoints(2, _bounds[2]);
-
- // > Draw
- coordinate = coordinate.data(_coordinateList);
- coordinate.enter()
- .append("path")
- .attr("fill", "none")
- .attr("stroke", function(d) {return d.color;});
- coordinate.exit().remove();
-
- // Axis Text
- axisText = axisText.data(_axisText);
- axisText.enter()
- .append("text")
- .classed("noSelect", true)
- .attr("fill", "rgba(0,0,0,0.5)")
- .attr("text-anchor", function(d) {return d.dimension === 1 ? "start" : "middle";})
- .text(function(d) {return d.text;});
- axisText.transition()
- .attr("text-anchor", function(d) {return d.dimension === 1 ? "end" : "middle";})
- .text(function(d) {return d.text;});
- axisText.exit().remove();
-
- // Lines
- lineList = lineList.data(series || []);
- lineList.enter()
- .append("path")
- .attr("fill", "none")
- .attr("stroke", function(d) {return d.color;});
- lineList.exit().remove();
-
- _update();
- }
-
- function _update() {
- coordinate
- .attr("d", function(d) {
- var path = "";
- $.each(d.data, function(i, point) {
- path += (i === 0 ? "M" : "L") + transformPoint2D(point);
- });
- return path;
- });
-
- axisText
- .attr("x", function(d) {return transfromPointX(d) + (d.dimension === 1 ? -3 : 0);})
- .attr("y", function(d) {return transfromPointY(d) + (d.dimension === 1 ? 0 : -5);});
-
- lineList
- .attr("d", function(d, index) {
- var path = "";
- $.each(d.data, function(i, point) {
- path += (i === 0 ? "M" : "L") + transformPoint2D(point);
- });
- return path;
- });
- }
-
-
- svg.on("mousedown", function() {
- drag = [d3.mouse(this), yaw, pitch];
- }).on("mouseup", function() {
- drag = false;
- }).on("mousemove", function() {
- if (drag) {
- var mouse = d3.mouse(this);
- yaw = drag[1] - (mouse[0] - drag[0][0]) / 50;
- pitch = drag[2] + (mouse[1] - drag[0][1]) / 50;
- pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch));
- setTurtable(yaw, pitch, true);
- }
- });
-
- // =========================== Render ===========================
- _redraw();
-
- function _render() {
- // ======== Parse Data ========
- var _series = typeof series === "string" ? $scope.data : series;
- if(!_series) return;
-
- // Clone
- _series = $.map(_series, function(series) {
- return {
- name: series.name,
- color: series.color,
- data: series.data
- };
- });
-
- // Colors
- $.each(_series, function(i, series) {
- series.color = series.color || color[i % color.length];
- });
-
- // Render
- _redraw(_series);
- }
-
- // ======================= Dynamic Detect =======================
- if(typeof series === "string") {
- $scope.$parent.$watch(series, function() {
- _render();
- }, true);
- } else {
- _render();
- }
-
-
- // ========================== Clean Up ==========================
- $scope.$on('$destroy', function() {
- svg.remove();
- });
- },
- };
- };
- return charts;
-});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/file.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/file.js b/eagle-webservice/src/main/webapp/app/public/js/components/file.js
deleted file mode 100644
index 82927a3..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/file.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.directive('file', function($compile) {
- 'use strict';
-
- return {
- restrict : 'A',
- scope: {
- filepath: "=?filepath",
- },
- controller: function($scope, $element, $attrs) {
- // Watch change(Only support clean the data)
- if($attrs.filepath) {
- $scope.$parent.$watch($attrs.filepath, function(value) {
- if(!value) $element.val(value);
- });
- }
-
- // Bind changed value
- $element.on("change", function() {
- var _path = $(this).val();
- if($attrs.filepath) {
- common.setValueByPath($scope.$parent, $attrs.filepath, _path);
- $scope.$parent.$apply();
- }
- });
-
- $scope.$on('$destroy',function(){
- $element.off("change");
- });
- },
- replace: false
- };
-});
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/main.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/main.js b/eagle-webservice/src/main/webapp/app/public/js/components/main.js
deleted file mode 100644
index 6b0e21f..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/main.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var eagleComponents = angular.module('eagle.components', []);
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/nvd3.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/nvd3.js b/eagle-webservice/src/main/webapp/app/public/js/components/nvd3.js
deleted file mode 100644
index 8687c78..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/nvd3.js
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.service('nvd3', function() {
- var nvd3 = {
- charts: [],
- colors: [
- "#7CB5EC", "#F7A35C", "#90EE7E", "#7798BF", "#AAEEEE"
- ]
- };
-
- // ============================================
- // = Format Convert =
- // ============================================
-
- /***
- * Format: [series:{key:name, value: [{x, y}]}]
- */
-
- nvd3.convert = {};
- nvd3.convert.eagle = function(seriesList) {
- return $.map(seriesList, function(series) {
- var seriesObj = $.isArray(series) ? {values: series} : series;
- if(!seriesObj.key) seriesObj.key = "value";
- return seriesObj;
- });
- };
-
- nvd3.convert.druid = function(seriesList) {
- var _seriesList = [];
-
- $.each(seriesList, function(i, series) {
- if(!series.length) return;
-
- // Fetch keys
- var _measure = series[0];
- var _keys = $.map(_measure.event, function(value, key) {
- return key !== "metric" ? key : null;
- });
-
- // Parse series
- _seriesList.push.apply(_seriesList, $.map(_keys, function(key) {
- return {
- key: key,
- values: $.map(series, function(unit) {
- return {
- x: new moment(unit.timestamp).valueOf(),
- y: unit.event[key]
- };
- })
- };
- }));
- });
-
- return _seriesList;
- };
-
- // ============================================
- // = UI =
- // ============================================
- // Resize with refresh
- function chartResize() {
- $.each(nvd3.charts, function(i, chart) {
- if(chart) chart.nvd3Update();
- });
- }
- $(window).on("resize.components.nvd3", chartResize);
- $("body").on("collapsed.pushMenu expanded.pushMenu", function() {
- setTimeout(chartResize, 300);
- });
-
- return nvd3;
-});
-
-/**
- * config:
- * chart: Defined chart type: line, column, area
- * xTitle: Defined x axis title.
- * yTitle: Defined y axis title.
- * xType: Defined x axis label type: number, decimal, time
- * yType: Defined y axis label type
- * yMin: Defined minimum of y axis
- * yMax: Defined maximum of y axis
- * displayType: Defined the chart display type. Each chart has own type.
- */
-eagleComponents.directive('nvd3', function(nvd3) {
- 'use strict';
-
- return {
- restrict: 'AE',
- scope: {
- nvd3: "=",
- title: "@?title", // title
- chart: "@?chart", // Same as config.chart
- config: "=?config",
- watching: "@?watching", // Default watching data(nvd3) only. true will also watching chart & config. false do not watching.
-
- holder: "=?holder" // Container for holder to call the chart function
- },
- controller: function($scope, $element, $attrs, $timeout) {
- var _config, _chartType;
- var _chart;
- var _chartCntr;
- var _holder, _holder_updateTimes;
-
- // Destroy
- function destroy() {
- var _index = $.inArray(_chart, nvd3.charts);
- if(!_chartCntr) return _index;
-
- // Clean events
- d3.select(_chartCntr)
- .on("touchmove",null)
- .on("mousemove",null, true)
- .on("mouseout" ,null,true)
- .on("dblclick" ,null)
- .on("click", null);
-
- // Clean elements
- d3.select(_chartCntr).selectAll("*").remove();
- $element.find(".nvtooltip").remove();
- $(_chartCntr).remove();
-
- // Clean chart in nvd3 pool
- nvd3.charts[_index] = null;
- _chart = null;
-
- return _index;
- }
-
- // Setup chart environment. Will clean old chart and build new chart if recall.
- function initChart() {
- // Clean up if already have chart
- var _preIndex = destroy();
-
- // Initialize
- _config = $.extend({}, $scope.config);
- _chartType = $scope.chart || _config.chart;
- _chartCntr = $(document.createElementNS("http://www.w3.org/2000/svg", "svg"))
- .css("min-height", 50)
- .attr("_rnd", Math.random())
- .appendTo($element)[0];
-
- // Size
- if(_config.height) {
- $(_chartCntr).css("height", _config.height);
- }
-
- switch(_chartType) {
- case "line":
- _chart = nv.models.lineChart()
- .useInteractiveGuideline(true)
- .showLegend(true)
- .showYAxis(true)
- .showXAxis(true)
- .options({
- duration: 350
- });
- break;
- case "column":
- _chart = nv.models.multiBarChart()
- .groupSpacing(0.1)
- .options({
- duration: 350
- });
- break;
- case "area":
- _chart = nv.models.stackedAreaChart()
- .useInteractiveGuideline(true)
- .showLegend(true)
- .showYAxis(true)
- .showXAxis(true)
- .options({
- duration: 350
- });
- break;
- case "pie":
- _chart = nv.models.dimensionalPieChart()
- .x(function(d) { return d.key; })
- .y(function(d) { return d.values[d.values.length - 1].y; });
- break;
- default :
- throw "Type not defined: " + _chartType;
- }
-
- // nvd3 display Type
- // TODO: support type define
-
- // Define title
- if(_chartType !== 'pie') {
- if(_config.xTitle) _chart.xAxis.axisLabel(_config.xTitle);
- if(_config.yTitle) _chart.yAxis.axisLabel(_config.yTitle);
- }
-
- // Define label type
- var _tickMultiFormat = d3.time.format.multi([
- ["%-I:%M%p", function(d) { return d.getMinutes(); }],
- ["%-I%p", function(d) { return d.getHours(); }],
- ["%b %-d", function(d) { return d.getDate() != 1; }],
- ["%b %-d", function(d) { return d.getMonth(); }],
- ["%Y", function() { return true; }]
- ]);
-
- function _defineLabelType(axis, type) {
- if(!_chart) return;
-
- var _axis = _chart[axis + "Axis"];
- if(!_axis) return;
-
- switch(type) {
- case "decimal":
- case "decimals":
- _axis.tickFormat(d3.format('.02f'));
- break;
- case "text":
- if(axis === "x") {
- _chart.rotateLabels(10);
- _chart.reduceXTicks(false).staggerLabels(true);
- }
- _axis.tickFormat(function(d) {
- return d;
- });
- break;
- case "time":
- if(_chartType !== 'column') {
- _chart[axis + "Scale"](d3.time.scale());
- (function () {
- var measureSeries = null;
- $.each($scope.nvd3 || [], function(i, series) {
- var _len = (series.values || []).length;
- if(_len === 0) return;
- if(measureSeries === null || measureSeries.values.length < _len) measureSeries = series;
- });
-
- var width = $element.width() - 35;// Use default nvd3 margin. Hard code.
- if(!measureSeries || width <= 0) return;
- var count = Math.floor(width / 80);
- var countDes = Math.floor(measureSeries.values.length / count);
- var tickValues = [];
- for(var loc = 0 ; loc < measureSeries.values.length ; loc += countDes) {
- tickValues.push(measureSeries.values[loc].x);
- }
- _chart[axis + "Axis"].tickValues(tickValues);
- })();
- }
- _axis.tickFormat(function(d) {
- return _tickMultiFormat(app.time.offset(d).toDate(true));
- });
- break;
- case "number":
- /* falls through */
- default:
- _axis.tickFormat(d3.format(',r'));
- }
- }
-
- if(_chartType !== 'pie') {
- _defineLabelType("x", _config.xType || "number");
- _defineLabelType("y", _config.yType || "decimal");
- }
-
- // Global chart list update
- if(_preIndex === -1) {
- nvd3.charts.push(_chart);
- } else {
- nvd3.charts[_preIndex] = _chart;
- }
-
- // Use customize update function to update the view
- _chart.nvd3Update = function() {
- if(_config.xType === "time") _defineLabelType("x", _config.xType);
- _chart.update();
- };
-
- updateData();
- }
-
- // Update chart data
- function updateData() {
- var _min = null, _max = null;
-
- // Copy series to prevent Angular loop watching
- var _data = $.map($scope.nvd3 || [], function(series, i) {
- var _series = $.extend(true, {}, series);
- _series.color = _series.color || nvd3.colors[i % nvd3.colors.length];
- return _series;
- });
-
- // Chart Y value
- if(($scope.chart || _config.chart) !== "pie") {
- $.each(_data, function(i, series) {
- $.each(series.values, function(j, unit) {
- if(_min === null || unit.y < _min) _min = unit.y;
- if(_max === null || unit.y > _max) _max = unit.y;
- });
- });
-
- if(_min === 0 && _max === 0) {
- _chart.forceY([0, 10]);
- } else if(_config.yMin !== undefined || _config.yMax !== undefined) {
- _chart.forceY([_config.yMin, _config.yMax]);
- } else {
- _chart.forceY([]);
- }
- }
-
- // Update data
- d3.select(_chartCntr) //Select the <svg> element you want to render the chart in.
- .datum(_data) //Populate the <svg> element with chart data...
- .call(_chart); //Finally, render the chart!
-
- setTimeout(_chart.nvd3Update, 10);
- }
-
- // ================================================================
- // = Watching =
- // ================================================================
- // Ignore initial checking
- $timeout(function() {
- if ($scope.watching !== "false") {
- $scope.$watch(function() {
- if(!$scope.nvd3) return [];
-
- var _hashList = $.map($scope.nvd3, function(series) {
- if(!series.values) return 0;
- var unit = {
- x: 0,
- y: 0
- };
-
- $.each(series.values, function(j, item) {
- unit.x += item.x;
- unit.y += item.y;
- });
-
- return unit;
- });
-
- return _hashList;
- }, function() {
- updateData();
- }, true);
-
- // All watching mode
- if ($scope.watching === "true") {
- $scope.$watch("[chart, config]", function(newValue, oldValue) {
- if(angular.equals(newValue, oldValue)) return;
- initChart();
- }, true);
- }
- }
- });
-
- // Holder inject
- _holder_updateTimes = 0;
- _holder = {
- element: $element,
- refresh: function() {
- setTimeout(function() {
- updateData();
- }, 0);
- },
- refreshAll: function() {
- setTimeout(function() {
- initChart();
- }, 0);
- }
- };
-
- Object.defineProperty(_holder, 'chart', {
- get: function() {return _chart;}
- });
-
- $scope.$watch("holder", function() {
- // Holder times update
- setTimeout(function() {
- _holder_updateTimes = 0;
- }, 0);
- _holder_updateTimes += 1;
- if(_holder_updateTimes > 100) throw "Holder conflict";
-
- $scope.holder = _holder;
- });
-
- // ================================================================
- // = Start Up =
- // ================================================================
- initChart();
-
- // ================================================================
- // = Clean Up =
- // ================================================================
- $scope.$on('$destroy', function() {
- destroy();
- });
- },
- template :
- '<div>' +
- '<h3 title="{{title || config.title}}">{{title || config.title}}</h3>' +
- '</div>',
- replace: true
- };
-});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/sortTable.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/sortTable.js b/eagle-webservice/src/main/webapp/app/public/js/components/sortTable.js
deleted file mode 100644
index 86b89b5..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/sortTable.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.directive('sorttable', function($compile) {
- 'use strict';
-
- return {
- restrict : 'AE',
- scope: {
- source: '=',
- search: '=?search',
- searchfunc: "=?searchfunc",
-
- orderKey: "@?sort",
-
- maxSize: "=?maxSize",
- },
- controller: function($scope, $element, $attrs) {
- // Initialization
- $scope.app = app;
- $scope.common = common;
- $scope._parent = $scope.$parent;
-
- $scope.pageNumber = $scope.pageNumber || 1;
- $scope.pageSize = $scope.pageSize || 10;
-
- $scope.maxSize = $scope.maxSize || 10;
-
- // Search box
- if($scope.search !== false) {
- var $search = $(
- '<div style="overflow:hidden;">' +
- '<div class="row">' +
- '<div class="col-xs-4">' +
- '<div class="search-box">' +
- '<input type="search" class="form-control input-sm" placeholder="Search" ng-model="search" />' +
- '<span class="fa fa-search"></span>' +
- '</div>' +
- '</div>' +
- '</div>' +
- '</div>'
- ).prependTo($element);
- $compile($search)($scope);
- }
-
- // List head
- $scope.doSort = function(path) {
- if($scope.orderKey === path) {
- $scope.orderKey = "-" + path;
- } else {
- $scope.orderKey = path;
- }
- };
- $scope.checkSortClass = function(key) {
- if($scope.orderKey === key) {
- return "fa fa-sort-asc";
- } else if($scope.orderKey === "-" + key) {
- return "fa fa-sort-desc";
- }
- return "fa fa-sort";
- };
-
- var $listHead = $element.find("thead > tr");
- $listHead.find("> th").each(function() {
- var $th = $(this);
- $th.addClass("noSelect");
-
- var _sortpath = $th.attr("sortpath");
- if(_sortpath) {
- $th.attr("ng-click", "doSort('" + _sortpath + "')");
- $th.prepend('<span ng-class="checkSortClass(\'' + _sortpath + '\')"></span>');
- }
- });
- $compile($listHead)($scope);
-
- // List body
- var $listBody = $element.find("tbody > tr");
- $listBody.attr("ng-repeat", 'item in (filteredList = (source | filter: ' + ($scope.searchfunc ? 'searchfunc' : 'search') + ' | orderBy: orderKey)).slice((pageNumber - 1) * pageSize, pageNumber * pageSize)');
- $compile($listBody)($scope);
-
- // Navigation
- var $navigation = $(
- '<div style="overflow:hidden;">' +
- '<div class="row">' +
- '<div class="col-xs-5">' +
- 'show {{(pageNumber - 1) * pageSize + 1}} to {{pageNumber * pageSize}} of {{filteredList.length}} items' +
- '</div>' +
- '<div class="col-xs-7 text-right">' +
- '<uib-pagination total-items="filteredList.length" ng-model="pageNumber" boundary-links="true" items-per-page="pageSize" num-pages="numPages" max-size="maxSize"></uib-pagination>' +
- '</div>' +
- '</div>' +
- '</div>'
- ).appendTo($element);
- $compile($navigation)($scope);
- },
- replace: false
- };
-});
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js b/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
deleted file mode 100644
index c98c732..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/sortable.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.directive('uieSortable', function($rootScope) {
- 'use strict';
-
- var COLLECTION_MATCH = /^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/;
-
- var _move = false;
- var _selectElement;
- var _overElement;
- var _mockElement;
- var _offsetX, _offsetY;
- var _mouseDownPageX, _mouseDownPageY;
-
- function doMock(element, event) {
- var _offset = element.offset();
- if(_mockElement) _mockElement.remove();
-
- // Create mock element
- _mockElement = element.clone(false).appendTo("body");
- _mockElement.addClass("sortable-mock-element");
- _mockElement.css({
- display: "block",
- position: "absolute",
- "pointer-events": "none",
- "z-index": 10001,
- padding: element.css("padding"),
- margin: element.css("margin")
- });
- _mockElement.width(element.width());
- _mockElement.height(element.height());
-
- _mockElement.offset(_offset);
- _offsetX = event.pageX - _offset.left;
- _offsetY = event.pageY - _offset.top;
- }
-
- $(window).on("mousemove", function(event) {
- if(!_move) return;
- event.preventDefault();
-
- _mockElement.offset({
- left: event.pageX - _offsetX,
- top: event.pageY - _offsetY
- });
- });
-
- $(window).on("mouseup", function() {
- if(!_move) {
- _overElement = null;
- _selectElement = null;
- _mockElement = null;
- return;
- }
- _move = false;
-
- if(_overElement) {
- _overElement.removeClass("sortable-enter");
-
- if(_overElement[0] !== _selectElement[0]) {
- // Process switch
- var _oriHolder = _selectElement.holder;
- var _tgtHolder = _overElement.holder;
- var _oriSortableScope = _oriHolder.scope;
- var _tgtSortableScope = _tgtHolder.scope;
- var _oriScope = angular.element(_selectElement).scope();
- var _tgtScope = angular.element(_overElement).scope();
-
- var _oriRepeat = _selectElement.closest("[ng-repeat]").attr("ng-repeat");
- var _tgtRepeat = _overElement.closest("[ng-repeat]").attr("ng-repeat");
- var _oriMatch = _oriRepeat.match(COLLECTION_MATCH)[2];
- var _tgtMatch = _tgtRepeat.match(COLLECTION_MATCH)[2];
- var _oriCollection = _oriScope.$parent.$eval(_oriMatch);
- var _tgtCollection = _tgtScope.$parent.$eval(_tgtMatch);
- var _oriIndex = $.inArray(_oriCollection[_oriScope.$index], _oriSortableScope.ngModel);
- var _tgtIndex = $.inArray(_tgtCollection[_tgtScope.$index], _tgtSortableScope.ngModel);
-
- var _oriUnit = _oriSortableScope.ngModel[_oriIndex];
- var _tgtUnit = _tgtSortableScope.ngModel[_tgtIndex];
- _oriSortableScope.ngModel[_oriIndex] = _tgtUnit;
- _tgtSortableScope.ngModel[_tgtIndex] = _oriUnit;
-
- // Trigger event
- _oriHolder.change(_oriUnit, _tgtUnit);
- if (_oriHolder !== _tgtHolder) _tgtHolder.change(_oriUnit, _tgtUnit);
-
- $rootScope.$apply();
- }
- }
-
- if(_mockElement) _mockElement.remove();
-
- _overElement = null;
- _selectElement = null;
- _mockElement = null;
- });
-
- return {
- require: 'ngModel',
- restrict : 'AE',
- scope: {
- ngModel: "=",
- sortableEnabled: "=?sortableEnabled",
- sortableUpdateFunc: "=?sortableUpdateFunc"
- },
- link: function($scope, $element, $attrs, $ctrl) {
- var _holder = {
- scope: $scope,
- change: function(source, target) {
- if($scope.sortableUpdateFunc) $scope.sortableUpdateFunc(source, target);
- }
- };
-
- $element.on("mousedown", ">", function(event) {
- if($scope.sortableEnabled === false) return;
-
- _selectElement = $(this);
- _selectElement.holder = _holder;
-
- _mouseDownPageX = event.pageX;
- _mouseDownPageY = event.pageY;
-
- event.preventDefault();
- });
-
- $element.on("mousemove", ">", function(event) {
- if(_selectElement && !_move && common.math.distance(_mouseDownPageX, _mouseDownPageY, event.pageX, event.pageY) > 10) {
- _move = true;
- _overElement = _selectElement;
- _overElement.addClass("sortable-enter");
-
- doMock(_selectElement, event);
- }
- });
-
- $element.on("mouseenter", ">", function() {
- if(!_move) return;
- _overElement = $(this);
- _overElement.holder = _holder;
- _overElement.addClass("sortable-enter");
- });
- $element.on("mouseleave", ">", function() {
- if(!_move) return;
- $(this).removeClass("sortable-enter");
- _overElement = null;
- });
- },
- replace: false
- };
-});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/components/tabs.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/components/tabs.js b/eagle-webservice/src/main/webapp/app/public/js/components/tabs.js
deleted file mode 100644
index 21c4a4a..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/components/tabs.js
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-eagleComponents.directive('tabs', function() {
- 'use strict';
-
- return {
- restrict: 'AE',
- transclude: {
- 'header': '?header',
- 'pane': 'pane',
- 'footer': '?footer'
- },
- scope : {
- title: "@?title",
- icon: "@",
- selected: "@?selected",
- holder: "=?holder",
- sortableModel: "=?sortableModel",
-
- menuList: "=?menu"
- },
-
- controller: function($scope, $element, $attrs, $timeout) {
- var transDuration = $.fn.tab.Constructor.TRANSITION_DURATION;
- var transTimer = null;
- var _holder, _holder_updateTimes;
-
- var $header, $footer;
-
- $scope.paneList = [];
- $scope.selectedPane = null;
- $scope.inPane = null;
- $scope.activePane = null;
-
- // ================== Function ==================
- $scope.getPaneList = function() {
- return !$scope.title ? $scope.paneList : $scope.paneList.slice().reverse();
- };
-
- $scope.setSelect = function(pane) {
- if(typeof pane === "string") {
- pane = common.array.find(pane, $scope.paneList, "title");
- } else if(typeof pane === "number") {
- pane = (pane + $scope.paneList.length) % $scope.paneList.length;
- pane = $scope.paneList[pane];
- }
-
- $scope.activePane = $scope.selectedPane || pane;
- $scope.selectedPane = pane;
- $scope.inPane = null;
-
- if(transTimer) $timeout.cancel(transTimer);
- transTimer = $timeout(function() {
- $scope.activePane = $scope.selectedPane;
- $scope.inPane = $scope.selectedPane;
- }, transDuration);
- };
-
- $scope.getMenuList = function() {
- if($scope.selectedPane && $scope.selectedPane.menuList) {
- return $scope.selectedPane.menuList;
- }
- return $scope.menuList;
- };
-
- $scope.tabSwitchUpdate = function (src, tgt) {
- var _srcIndex = $.inArray(src.data, $scope.sortableModel);
- var _tgtIndex = $.inArray(tgt.data, $scope.sortableModel);
-
- if(_srcIndex !== -1 && _tgtIndex !== -1) {
- $scope.sortableModel[_srcIndex] = tgt.data;
- $scope.sortableModel[_tgtIndex] = src.data;
- }
- };
-
- // =================== Linker ===================
- function _linkerProperties(pane) {
- Object.defineProperties(pane, {
- selected: {
- get: function () {
- return $scope.selectedPane === this;
- }
- },
- active: {
- get: function () {
- return $scope.activePane === this;
- }
- },
- in: {
- get: function () {
- return $scope.inPane === this;
- }
- }
- });
- }
-
- this.addPane = function(pane) {
- $scope.paneList.push(pane);
-
- // Register properties
- _linkerProperties(pane);
-
- // Update select pane
- if(pane.title === $scope.selected || !$scope.selectedPane) {
- $scope.setSelect(pane);
- }
- };
-
- this.deletePane = function(pane) {
- common.array.remove(pane, $scope.paneList);
-
- if($scope.selectedPane === pane) {
- $scope.selectedPane = $scope.activePane = $scope.inPane = $scope.paneList[0];
- }
- };
-
- // ===================== UI =====================
- $header = $element.find("> .nav-tabs-custom > .box-body");
- $footer = $element.find("> .nav-tabs-custom > .box-footer");
-
- $scope.hasHeader = function() {
- return !!$header.children().length;
- };
- $scope.hasFooter = function() {
- return !!$footer.children().length;
- };
-
- // ================= Interface ==================
- _holder_updateTimes = 0;
- _holder = {
- scope: $scope,
- element: $element,
- setSelect: $scope.setSelect
- };
-
- Object.defineProperty(_holder, 'selectedPane', {
- get: function() {return $scope.selectedPane;}
- });
-
- $scope.$watch("holder", function(newValue, oldValue) {
- // Holder times update
- setTimeout(function() {
- _holder_updateTimes = 0;
- }, 0);
- _holder_updateTimes += 1;
- if(_holder_updateTimes > 100) throw "Holder conflict";
-
- $scope.holder = _holder;
- });
- },
-
- template :
- '<div class="nav-tabs-custom">' +
- // Menu
- '<div class="box-tools pull-right" ng-if="getMenuList() && getMenuList().length">' +
- '<div ng-repeat="menu in getMenuList() track by $index" class="inline">' +
- // Button
- '<button class="btn btn-box-tool" ng-click="menu.func($event)" ng-if="!menu.list"' +
- ' uib-tooltip="{{menu.title}}" tooltip-enable="menu.title" tooltip-append-to-body="true">' +
- '<span class="fa fa-{{menu.icon}}"></span>' +
- '</button>' +
-
- // Dropdown Group
- '<div class="btn-group" ng-if="menu.list">' +
- '<button class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown"' +
- ' uib-tooltip="{{menu.title}}" tooltip-enable="menu.title" tooltip-append-to-body="true">' +
- '<span class="fa fa-{{menu.icon}}"></span>' +
- '</button>' +
- '<ul class="dropdown-menu left" role="menu">' +
- '<li ng-repeat="item in menu.list track by $index" ng-class="{danger: item.danger, disabled: item.disabled}">' +
- '<a ng-click="!item.disabled && item.func($event)" ng-class="{strong: item.strong}">' +
- '<span class="fa fa-{{item.icon}}"></span> {{item.title}}' +
- '</a>' +
- '</li>' +
- '</ul>' +
- '</div>' +
- '</div>' +
- '</div>' +
-
- '<ul uie-sortable sortable-enabled="!!sortableModel" sortable-update-func="tabSwitchUpdate" ng-model="paneList" class="nav nav-tabs" ng-class="{\'pull-right\': title}">' +
- // Tabs
- '<li ng-repeat="pane in getPaneList() track by $index" ng-class="{active: selectedPane === pane}">' +
- '<a ng-click="setSelect(pane);">{{pane.title}}</a>' +
- '</li>' +
-
- // Title
- '<li class="pull-left header" ng-if="title">' +
- '<i class="fa fa-{{icon}}" ng-if="icon"></i> {{title}}' +
- '</li>' +
-
- '</ul>' +
- '<div class="box-body" ng-transclude="header" ng-show="paneList.length && hasHeader()"></div>' +
- '<div class="tab-content" ng-transclude="pane"></div>' +
- '<div class="box-footer" ng-transclude="footer" ng-show="paneList.length && hasFooter()"></div>' +
- '</div>'
- };
-}).directive('pane', function() {
- 'use strict';
-
- return {
- require : '^tabs',
- restrict : 'AE',
- transclude : true,
- scope : {
- title : '@',
- data: '=?data',
- menuList: "=?menu"
- },
- link : function(scope, element, attrs, tabsController) {
- tabsController.addPane(scope);
- scope.$on('$destroy', function() {
- tabsController.deletePane(scope);
- });
- },
- template : '<div class="tab-pane fade" ng-class="{active: active, in: in}" ng-transclude></div>',
- replace : true
- };
-}).directive('footer', function() {
- 'use strict';
-
- return {
- require : '^tabs',
- restrict : 'AE',
- transclude : true,
- scope : {},
- controller: function($scope, $element) {
- },
- template : '<div ng-transclude></div>',
- replace : true
- };
-});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/ctrl/authController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/authController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/authController.js
deleted file mode 100644
index dbdb704..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/ctrl/authController.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var eagleControllers = angular.module('eagleControllers');
- // =============================================================
- // = User Profile List =
- // =============================================================
- eagleControllers.controller('authLoginCtrl', function (PageConfig, Site, Authorization, Application, $scope) {
- PageConfig.hideSidebar = true;
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- PageConfig.hideUser = true;
-
- $scope.username = "";
- $scope.password = "";
- $scope.lock = false;
- $scope.loginSuccess = false;
-
- if(localStorage) {
- $scope.rememberUser = localStorage.getItem("rememberUser") !== "false";
-
- if($scope.rememberUser) {
- $scope.username = localStorage.getItem("username");
- $scope.password = localStorage.getItem("password");
- }
- }
-
- // UI
- setTimeout(function () {
- $("#username").focus();
- });
-
- // Login
- $scope.login = function (event, forceSubmit) {
- if ($scope.lock) return;
-
- if (event.which === 13 || forceSubmit) {
- $scope.lock = true;
-
- Authorization.login($scope.username, $scope.password).then(function (success) {
- if (success) {
- // Check user remember
- localStorage.setItem("rememberUser", $scope.rememberUser);
- if($scope.rememberUser) {
- localStorage.setItem("username", $scope.username);
- localStorage.setItem("password", $scope.password);
- } else {
- localStorage.removeItem("username");
- localStorage.removeItem("password");
- }
-
- // Initial environment
- $scope.loginSuccess = true;
- console.log("[Login] Login success! Reload data...");
- Authorization.reload().then(function() {}, function() {console.warn("Site error!");});
- Application.reload().then(function() {}, function() {console.warn("Site error!");});
- Site.reload().then(function() {}, function() {console.warn("Site error!");});
- Authorization.path(true);
- } else {
- $.dialog({
- title: "OPS",
- content: "User name or password not correct."
- }).on("hidden.bs.modal", function () {
- $("#username").focus();
- });
- }
- }).finally(function () {
- $scope.lock = false;
- });
- }
- };
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/ctrl/configurationController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/configurationController.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/configurationController.js
deleted file mode 100644
index e59198d..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/ctrl/configurationController.js
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var eagleControllers = angular.module('eagleControllers');
-
- // =============================================================
- // = Function =
- // =============================================================
- function watchEdit($scope, key) {
- $scope.changed = false;
- setTimeout(function() {
- var _func = $scope.$watch(key, function(newValue, oldValue) {
- if(angular.equals(newValue, oldValue)) return;
- $scope.changed = true;
- _func();
- }, true);
- }, 100);
- }
-
- // =============================================================
- // = Configuration =
- // =============================================================
- // ========================== Feature ==========================
- eagleControllers.controller('configFeatureCtrl', function ($scope, PageConfig, Application, Entities, UI) {
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- $scope._pageLock = false;
-
- PageConfig
- .addNavPath("Home", "/")
- .addNavPath("Feature Config");
-
- // ================== Feature ==================
- // Current feature
- $scope.feature = Application.featureList[0];
- $scope.setFeature = function (feature) {
- $scope.feature = feature;
- };
-
- // Feature list
- $scope.features = {};
- $.each(Application.featureList, function(i, feature) {
- $scope.features[feature.tags.feature] = $.extend({}, feature, true);
- });
-
- // Create feature
- $scope.newFeature = function() {
- UI.createConfirm("Feature", {}, [
- {name: "Feature Name", field: "name"}
- ], function(entity) {
- if(entity.name && $.map($scope.features, function(feature, name) {
- return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
- }).length) {
- return "Feature name conflict!";
- }
- }).then(null, null, function(holder) {
- Entities.updateEntity(
- "FeatureDescService",
- {tags: {feature: holder.entity.name}},
- {timestamp: false}
- )._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // Delete feature
- $scope.deleteFeature = function(feature) {
- UI.deleteConfirm(feature.tags.feature).then(null, null, function(holder) {
- Entities.delete("FeatureDescService", {feature: feature.tags.feature})._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // Save feature
- $scope.saveAll = function() {
- $scope._pageLock = true;
- var _list = $.map($scope.features, function(feature) {
- return feature;
- });
- Entities.updateEntity("FeatureDescService", _list, {timestamp: false})._promise.success(function() {
- location.reload();
- }).finally(function() {
- $scope._pageLock = false;
- });
- };
-
- // Watch config update
- watchEdit($scope, "features");
- });
-
- // ======================== Application ========================
- eagleControllers.controller('configApplicationCtrl', function ($scope, $timeout, PageConfig, Application, Entities, Feature, UI) {
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- $scope._pageLock = false;
-
- PageConfig
- .addNavPath("Home", "/")
- .addNavPath("Application Config");
-
- // ================ Application ================
- // Current application
- $scope.application = Application.list[0];
- $scope.setApplication = function (application) {
- $scope.application = application;
- };
-
- // Application list
- $scope.applications = {};
- $.each(Application.list, function(i, application) {
- var _application = $scope.applications[application.tags.application] = $.extend({}, application, {features: application.features.slice()}, true);
- _application.optionalFeatures = $.map(Application.featureList, function(feature) {
- var featurePlugin = Feature.get(feature.tags.feature);
- if(featurePlugin.config.global) return null;
- if(!common.array.find(feature.tags.feature, _application.features)) {
- return feature.tags.feature;
- }
- });
- });
-
- // Create application
- $scope.newApplication = function() {
- UI.createConfirm("Application", {}, [
- {name: "Application Name", field: "name"}
- ], function(entity) {
- if(entity.name && $.map($scope.applications, function(application, name) {
- return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
- }).length) {
- return "Application name conflict!";
- }
- }).then(null, null, function(holder) {
- Entities.updateEntity(
- "ApplicationDescService",
- {tags: {application: holder.entity.name}},
- {timestamp: false}
- )._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // Delete application
- $scope.deleteApplication = function(application) {
- UI.deleteConfirm(application.tags.application).then(null, null, function(holder) {
- Entities.delete("ApplicationDescService", {application: application.tags.application})._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // ================= Function ==================
- // Configuration check
- $scope.configCheck = function(config) {
- if(config && !common.parseJSON(config, false)) {
- return "Invalid JSON format";
- }
- };
-
- // Feature
- $scope._feature = "";
- function highlightFeature(feature) {
- $scope._feature = feature;
-
- $timeout(function() {
- $scope._feature = "";
- }, 100);
- }
-
- $scope.addFeature = function(feature, application) {
- application.features.push(feature);
- common.array.remove(feature, application.optionalFeatures);
- highlightFeature(feature);
- $scope.changed = true;
- };
-
- $scope.removeFeature = function(feature, application) {
- application.optionalFeatures.push(feature);
- common.array.remove(feature, application.features);
- $scope.changed = true;
- };
-
- $scope.moveFeature = function(feature, list, offset) {
- common.array.moveOffset(feature, list, offset);
- highlightFeature(feature);
- $scope.changed = true;
- };
-
- // Save feature
- $scope.saveAll = function() {
- $scope._pageLock = true;
-
- var _list = $.map($scope.applications, function(application) {
- return application;
- });
- Entities.updateEntity("ApplicationDescService", _list, {timestamp: false})._promise.success(function() {
- location.reload();
- }).finally(function() {
- $scope._pageLock = false;
- });
- };
-
- // Watch config update
- watchEdit($scope, "applications");
- });
-
- // ============================ Site ===========================
- eagleControllers.controller('configSiteCtrl', function ($scope, $timeout, PageConfig, Site, Application, Entities, UI) {
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- $scope._pageLock = false;
-
- PageConfig
- .addNavPath("Home", "/")
- .addNavPath("Site Config");
-
- // =================== Site ====================
- // Current site
- $scope.site = Site.list[0];
- $scope.setSite = function (site) {
- $scope.site = site;
- };
-
-
- // Site list
- $scope.sites = {};
- $.each(Site.list, function(i, site) {
- var _site = $scope.sites[site.tags.site] = $.extend({}, site, true);
- var _applications = [];
- var _optionalApplications = [];
-
- Object.defineProperties(_site, {
- applications: {
- get: function() {return _applications;}
- },
- optionalApplications: {
- get: function() {return _optionalApplications;}
- }
- });
-
- $.each(Application.list, function(i, application) {
- var _application = site.applicationList.set[application.tags.application];
- if(_application && _application.enabled) {
- _site.applications.push(_application);
- } else {
- if(_application) {
- _site.optionalApplications.push(_application);
- } else {
- _site.optionalApplications.push({
- prefix: "eagleSiteApplication",
- config: "",
- enabled: false,
- tags: {
- application: application.tags.application,
- site: site.tags.site
- }
- });
- }
- }
- });
- });
-
- // Create site
- $scope.newSite = function() {
- UI.createConfirm("Site", {}, [
- {name: "Site Name", field: "name"}
- ], function(entity) {
- if(entity.name && $.map($scope.sites, function(site, name) {
- return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
- }).length) {
- return "Site name conflict!";
- }
- }).then(null, null, function(holder) {
- Entities.updateEntity(
- "SiteDescService",
- {enabled: true, tags: {site: holder.entity.name}},
- {timestamp: false}
- )._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // Delete site
- $scope.deleteSite = function(site) {
- UI.deleteConfirm(site.tags.site).then(null, null, function(holder) {
- Entities.delete("SiteDescService", {site: site.tags.site})._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- // ================= Function ==================
- $scope._application = "";
- function highlightApplication(application) {
- $scope._application = application;
-
- $timeout(function() {
- $scope._application = "";
- }, 100);
- }
-
- $scope.addApplication = function(application, site) {
- site.applications.push(application);
- common.array.remove(application, site.optionalApplications);
- application.enabled = true;
- highlightApplication(application);
- $scope.changed = true;
- };
-
- $scope.removeApplication = function(application, site) {
- site.optionalApplications.push(application);
- common.array.remove(application, site.applications);
- application.enabled = false;
- $scope.changed = true;
- };
-
- $scope.setApplication = function(application) {
- var _oriConfig = application.config;
- UI.updateConfirm("Application", {config: _oriConfig}, [
- {name: "Configuration", field: "config", type: "blob"}
- ], function(entity) {
- if(entity.config !== "" && !common.properties.check(entity.config)) {
- return "Invalid Properties format";
- }
- }).then(null, null, function(holder) {
- application.config = holder.entity.config;
- holder.closeFunc();
- if(_oriConfig !== application.config) $scope.changed = true;
- });
- };
-
- // Save feature
- $scope.saveAll = function() {
- $scope._pageLock = true;
-
- var _list = $.map($scope.sites, function(site) {
- var _clone = $.extend({applications: site.applications.concat(site.optionalApplications)}, site);
- return _clone;
- });
-
- Entities.updateEntity("SiteDescService", _list, {timestamp: false, hook: true})._promise.success(function() {
- location.reload();
- }).finally(function() {
- $scope._pageLock = false;
- });
- };
-
- // Watch config update
- watchEdit($scope, "sites");
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/ctrl/main.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/ctrl/main.js b/eagle-webservice/src/main/webapp/app/public/js/ctrl/main.js
deleted file mode 100644
index 5064a1d..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/ctrl/main.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var eagleControllers = angular.module('eagleControllers', ['ui.bootstrap', 'eagle.components', 'eagle.service']);
-
- // ===========================================================
- // = Controller =
- // ===========================================================
- eagleControllers.controller('landingCtrl', function($scope, $wrapState, Site, Application, PageConfig, FeaturePageConfig, Feature) {
- var _app = Application.current();
-
- PageConfig.pageTitle = _app ? _app.displayName : 'OPS';
- PageConfig.pageSubTitle = Site.current().tags.site;
-
- $scope.Application = Application;
-
- var _navItemList = FeaturePageConfig.pageList;
- if(_navItemList.length) {
- console.log("[Landing] Auto redirect.", FeaturePageConfig);
- var _match = _navItemList[0].url.match(/#\/([^\/]+)\/([^\/]+)/);
- Feature.go(_match[1], _match[2]);
- }
- });
-})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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
deleted file mode 100644
index 187adb4..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/srv/applicationSrv.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var serviceModule = angular.module('eagle.service');
- var eagleApp = angular.module('eagleApp');
-
- 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, reload) {
- if(arguments.length && _current !== app) {
- var _prev = _current;
- _current = app;
-
- if(sessionStorage && _current) {
- sessionStorage.setItem("application", _current.tags.application);
- }
-
- if(_prev && reload !== false) {
- 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?_=" + eagleApp._TRS();
- 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
- group: {
- get: function () {
- return this.groupName || "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
[07/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/app.time.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/app.time.js b/eagle-webservice/src/main/webapp/_app/public/js/app.time.js
new file mode 100644
index 0000000..f5f41c1
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/app.time.js
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+// Time Zone
+(function() {
+ "use strict";
+
+ app.time = {
+ UTC_OFFSET: 0,
+ now: function() {
+ return app.time.offset();
+ },
+ offset: function(time) {
+ // Parse string number
+ if(typeof time === "string" && !isNaN(+time)) {
+ time = +time;
+ }
+
+ var _mom = new moment(time);
+ _mom.utcOffset(app.time.UTC_OFFSET);
+ return _mom;
+ },
+ /*
+ * Return the moment object which use server time zone and keep the time.
+ */
+ srvZone: function(time) {
+ var _timezone = time._isAMomentObject ? time.utcOffset() : new moment().utcOffset();
+ var _mom = app.time.offset(time);
+ _mom.subtract(app.time.UTC_OFFSET, "m").add(_timezone, "m");
+ return _mom;
+ },
+
+ refreshInterval: 1000 * 10
+ };
+
+ // Moment update
+ moment.fn.toISO = function() {
+ return this.format("YYYY-MM-DDTHH:mm:ss.000Z");
+ };
+
+ // Force convert date
+ var _toDate = moment.fn.toDate;
+ moment.fn.toDate = function(ignoreTimeZone) {
+ if(!ignoreTimeZone) return _toDate.bind(this)();
+ return new Date(
+ this.year(),
+ this.month(),
+ this.date(),
+ this.hour(),
+ this.minute(),
+ this.second(),
+ this.millisecond()
+ );
+ };
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/app.ui.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/app.ui.js b/eagle-webservice/src/main/webapp/_app/public/js/app.ui.js
new file mode 100644
index 0000000..ca494d3
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/app.ui.js
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+(function() {
+ // ================== AdminLTE Update ==================
+ var _boxSelect = $.AdminLTE.options.boxWidgetOptions.boxWidgetSelectors;
+
+ // Box collapse
+ $(document).on("click", _boxSelect.collapse, function(e) {
+ if(common.getValueByPath($._data(this), "events.click")) return;
+
+ e.preventDefault();
+ $.AdminLTE.boxWidget.collapse($(this));
+ });
+
+ // Box remove
+ $(document).on("click", _boxSelect.remove, function(e) {
+ if(common.getValueByPath($._data(this), "events.click")) return;
+
+ e.preventDefault();
+ $.AdminLTE.boxWidget.remove($(this));
+ });
+
+ // =================== jQuery Update ===================
+ // Slide Toggle
+ var _slideToggle = $.fn.slideToggle;
+ $.fn.slideToggle = function(showOrHide) {
+ if(arguments.length === 1 && typeof showOrHide === "boolean") {
+ if(showOrHide) {
+ this.slideDown();
+ } else {
+ this.slideUp();
+ }
+ } else {
+ _slideToggle.apply(this, arguments);
+ }
+ };
+
+ // Fade Toggle
+ var _fadeToggle = $.fn.fadeToggle;
+ $.fn.fadeToggle = function(showOrHide) {
+ if(arguments.length === 1 && typeof showOrHide === "boolean") {
+ if(showOrHide) {
+ this.fadeIn();
+ } else {
+ this.fadeOut();
+ }
+ } else {
+ _fadeToggle.apply(this, arguments);
+ }
+ };
+
+ // Modal
+ var _modal = $.fn.modal;
+ $.fn.modal = function() {
+ setTimeout(function() {
+ $(this).find("input[type='text'], textarea").filter(':not([readonly]):enabled:visible:first').focus();
+ }.bind(this), 500);
+ _modal.apply(this, arguments);
+ return this;
+ };
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/common.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/common.js b/eagle-webservice/src/main/webapp/_app/public/js/common.js
new file mode 100644
index 0000000..4c5e82f
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/common.js
@@ -0,0 +1,304 @@
+/*
+ * 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 common = {};
+
+common.template = function (str, list) {
+ $.each(list, function(key, value) {
+ var _regex = new RegExp("\\$\\{" + key + "\\}", "g");
+ str = str.replace(_regex, value);
+ });
+ return str;
+};
+
+common.getValueByPath = function (unit, path, defaultValue) {
+ if(unit === null || unit === undefined) throw "Unit or path can't be empty!";
+ if(path === "" || path === null || path === undefined) return unit;
+
+ path = path.replace(/\[(\d+)\]/g, ".$1").replace(/^\./, "").split(/\./);
+ $.each(path, function(i, path) {
+ unit = unit[path];
+ if(unit === null || unit === undefined) {
+ unit = null;
+ return false;
+ }
+ });
+ if(unit === null && defaultValue !== undefined) {
+ unit = defaultValue;
+ }
+ return unit;
+};
+
+common.setValueByPath = function(unit, path, value) {
+ if(!unit || typeof path !== "string" || path === "") throw "Unit or path can't be empty!";
+
+ var _inArray = false;
+ var _end = 0;
+ var _start = 0;
+ var _unit = unit;
+
+ function _nextPath(array) {
+ var _key = path.slice(_start, _end);
+ if(_inArray) {
+ _key = _key.slice(0, -1);
+ }
+ if(!_unit[_key]) {
+ if(array) {
+ _unit[_key] = [];
+ } else {
+ _unit[_key] = {};
+ }
+ }
+ _unit = _unit[_key];
+ }
+
+ for(; _end < path.length ; _end += 1) {
+ if(path[_end] === ".") {
+ _nextPath(false);
+ _start = _end + 1;
+ _inArray = false;
+ } else if(path[_end] === "[") {
+ _nextPath(true);
+ _start = _end + 1;
+ _inArray = true;
+ }
+ }
+
+ _unit[path.slice(_start, _inArray ? -1 : _end)] = value;
+
+ return unit;
+};
+
+common.parseJSON = function (str, defaultVal) {
+ try {
+ str = (str + "").trim();
+ if(Number(str).toString() === str) throw "Number format";
+ return JSON.parse(str);
+ } catch(err) {
+ if(defaultVal === undefined) {
+ console.warn("Can't parse JSON: " + str);
+ }
+ }
+ return defaultVal === undefined ? null : defaultVal;
+};
+
+common.stringify = function(json) {
+ return JSON.stringify(json, function(key, value) {
+ if(/^(_|\$)/.test(key)) return undefined;
+ return value;
+ });
+};
+
+common.isEmpty = function(val) {
+ if($.isArray(val)) {
+ return val.length === 0;
+ } else {
+ return val === null || val === undefined;
+ }
+};
+
+common.extend = function(target, origin) {
+ $.each(origin, function(key, value) {
+ if(/^(_|\$)/.test(key)) return;
+
+ target[key] = value;
+ });
+ return target;
+};
+
+// ====================== Format ======================
+common.format = {};
+
+/*
+ * Format date to string. Support number, string, Date instance. Will auto convert time zone offset(Moment instance will keep time zone).
+ */
+common.format.date = function(val, type) {
+ if(val === undefined || val === null) return "";
+
+ if(typeof val === "number" || typeof val === "string" || val instanceof Date) {
+ val = app.time.offset(val);
+ }
+ switch(type) {
+ case 'date':
+ return val.format("YYYY-MM-DD");
+ case 'time':
+ return val.format("HH:mm:ss");
+ case 'datetime':
+ return val.format("YYYY-MM-DD HH:mm:ss");
+ case 'mixed':
+ return val.format("YYYY-MM-DD HH:mm");
+ default:
+ return val.format("YYYY-MM-DD HH:mm:ss") + (val.utcOffset() === 0 ? '[UTC]' : '');
+ }
+};
+
+// ===================== Property =====================
+common.properties = {};
+
+common.properties.parse = function (str, defaultValue) {
+ var regex = /\s*([\w\.]+)\s*=\s*(.*?)\s*([\r\n]+|$)/g;
+ var match, props = {};
+ var hasValue = false;
+ while((match = regex.exec(str)) !== null) {
+ props[match[1]] = match[2];
+ hasValue = true;
+ }
+ props = hasValue ? props : defaultValue;
+ props.getValueByPath = function (path) {
+ if(props[path] !== undefined) return props[path];
+ var subProps = {};
+ var prefixPath = path + ".";
+ $.each(props, function (key, value) {
+ if(typeof value === "string" && key.indexOf(prefixPath) === 0) {
+ subProps[key.replace(prefixPath, "")] = value;
+ }
+ });
+ return subProps;
+ };
+
+ return props;
+};
+
+common.properties.check = function (str) {
+ var pass = true;
+ var regex = /^\s*[\w\.]+\s*=(.*)$/;
+ $.each((str || "").trim().split(/[\r\n\s]+/g), function (i, line) {
+ if(!regex.test(line)) {
+ pass = false;
+ return false;
+ }
+ });
+ return pass;
+};
+
+// ====================== Array =======================
+common.array = {};
+
+common.array.sum = function(list, path) {
+ var _sum = 0;
+ if(list) {
+ $.each(list, function(i, unit) {
+ var _val = common.getValueByPath(unit, path);
+ if(typeof _val === "number") {
+ _sum += _val;
+ }
+ });
+ }
+ return _sum;
+};
+
+common.array.max = function(list, path) {
+ var _max = null;
+ if(list) {
+ $.each(list, function(i, unit) {
+ var _val = common.getValueByPath(unit, path);
+ if(typeof _val === "number" && (_max === null || _max < _val)) {
+ _max = _val;
+ }
+ });
+ }
+ return _max;
+};
+
+common.array.bottom = function(list, path, count) {
+ var _list = list.slice();
+
+ _list.sort(function(a, b) {
+ var _a = common.getValueByPath(a, path, null);
+ var _b = common.getValueByPath(b, path, null);
+
+ if(_a === _b) return 0;
+ if(_a < _b || _a === null) {
+ return -1;
+ } else {
+ return 1;
+ }
+ });
+ return !count ? _list : _list.slice(0, count);
+};
+common.array.top = function(list, path, count) {
+ var _list = common.array.bottom(list, path);
+ _list.reverse();
+ return !count ? _list : _list.slice(0, count);
+};
+
+common.array.find = function(val, list, path, findAll, caseSensitive) {
+ path = path || "";
+ val = caseSensitive === false ? (val || "").toUpperCase() : val;
+
+ var _list = $.grep(list, function(unit) {
+ if(caseSensitive === false) {
+ return val === (common.getValueByPath(unit, path) || "").toUpperCase();
+ } else {
+ return val === common.getValueByPath(unit, path);
+ }
+ });
+ return findAll ? _list : (_list.length === 0 ? null : _list[0]);
+};
+
+common.array.filter = function(val, list, path) {
+ return common.array.find(val, list, path, true);
+};
+
+common.array.count = function(list, val, path) {
+ if(arguments.length === 1) {
+ return list.length;
+ } else {
+ return common.array.find(val, list, path, true).length;
+ }
+};
+
+common.array.remove = function(val, list) {
+ for(var i = 0 ; i < list.length ; i += 1) {
+ if(list[i] === val) {
+ list.splice(i, 1);
+ i -= 1;
+ }
+ }
+};
+
+common.array.insert = function(val, list, index) {
+ list.splice(index, 0, val);
+};
+
+common.array.moveOffset = function(item, list, offset) {
+ var _index = $.inArray(item, list);
+ var _tgtPos = _index + offset;
+ if(_tgtPos < 0 || _tgtPos >= list.length) return;
+
+ common.array.remove(item, list);
+ common.array.insert(item, list, _index + offset);
+};
+
+// ======================= Map ========================
+common.map = {};
+
+common.map.toArray = function(map) {
+ return $.map(map, function(unit) {
+ return unit;
+ });
+};
+
+// ======================= Math =======================
+common.math = {};
+
+common.math.distance = function(x1,y1,x2,y2) {
+ var a = x1 - x2;
+ var b = y1 - y2;
+ return Math.sqrt(a * a + b * b);
+};
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/charts/line3d.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/charts/line3d.js b/eagle-webservice/src/main/webapp/_app/public/js/components/charts/line3d.js
new file mode 100644
index 0000000..c2bf23b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/charts/line3d.js
@@ -0,0 +1,348 @@
+/*
+ * 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.
+ */
+
+eagleComponents.directive('line3dChart', function($compile) {
+ 'use strict';
+
+ return {
+ restrict : 'AE',
+ scope : {
+ title : "@",
+ data : "=",
+
+ height : "=?height"
+ },
+ controller : function(line3dCharts, $scope, $element, $attrs) {
+ $scope.height = $scope.height || 200;
+
+ var _chart = line3dCharts($scope);
+
+ var _chartBody = $element.find(".chart-body");
+ _chartBody.height($scope.height);
+
+ _chart.gen($element.find(".chart-body"), $attrs.data, {
+ height : $scope.height,
+ });
+ },
+ template : '<div class="chart">' + '<div class="chart-header">' + '<h3>{{title}}</h3>' + '</div>' + '<div class="chart-body">' + '</div>' + '</div>',
+ replace : true
+ };
+});
+
+eagleComponents.service('line3dCharts', function() {
+ 'use strict';
+
+ var charts = function($scope) {
+ return {
+ gen : function(ele, series, config) {
+ // ======================= Initialization =======================
+ ele = $(ele);
+
+ series = series || [];
+ config = config || {};
+
+ var _bounds = [{min:-10, max: 10},{min:-10, max: 10},{min:-10, max: 10}];
+ var _scale = 1;
+
+ // ======================= Set Up D3 View =======================
+ var width = ele.innerWidth(), height = config.height;
+
+ var color = ["#7cb5ec", "#f7a35c", "#90ee7e", "#7798BF", "#aaeeee"];
+
+ var svg = d3.select(ele[0]).append("svg").attr("width", width).attr("height", height);
+
+ // ========================== Function ==========================
+ var yaw=0.5,pitch=0.5,drag;
+ var transformPrecalc = [];
+
+ var offsetPoint = function(point) {
+ var _point = [
+ +point[0] - (_bounds[0].max + _bounds[0].min) / 2,
+ -point[1] + (_bounds[1].max + _bounds[1].min) / 2,
+ -point[2] + (_bounds[2].max + _bounds[2].min) / 2
+ ];
+ return [_point[0] * _scale, _point[1] * _scale, _point[2] * _scale];
+ };
+
+ var transfromPointX = function(point) {
+ point = offsetPoint(point);
+ return transformPrecalc[0] * point[0] + transformPrecalc[1] * point[1] + transformPrecalc[2] * point[2] + width / 2;
+ };
+ var transfromPointY = function(point) {
+ point = offsetPoint(point);
+ return transformPrecalc[3] * point[0] + transformPrecalc[4] * point[1] + transformPrecalc[5] * point[2] + height / 2;
+ };
+ var transfromPointZ = function(point) {
+ point = offsetPoint(point);
+ return transformPrecalc[6] * point[0] + transformPrecalc[7] * point[1] + transformPrecalc[8] * point[2];
+ };
+ var transformPoint2D = function(point) {
+ var _point = [point[0], point[1], point[2]];
+ return transfromPointX(_point).toFixed(10) + "," + transfromPointY(_point).toFixed(10);
+ };
+
+ var setTurtable = function(yaw, pitch, update) {
+ var cosA = Math.cos(pitch);
+ var sinA = Math.sin(pitch);
+ var cosB = Math.cos(yaw);
+ var sinB = Math.sin(yaw);
+ transformPrecalc[0] = cosB;
+ transformPrecalc[1] = 0;
+ transformPrecalc[2] = sinB;
+ transformPrecalc[3] = sinA * sinB;
+ transformPrecalc[4] = cosA;
+ transformPrecalc[5] = -sinA * cosB;
+ transformPrecalc[6] = -sinB * cosA;
+ transformPrecalc[7] = sinA;
+ transformPrecalc[8] = cosA * cosB;
+
+ if(update) _update();
+ };
+ setTurtable(0.4,0.4);
+
+ // =========================== Redraw ===========================
+ var _coordinateList = [];
+ var _axisText = [];
+ var coordinate = svg.selectAll(".axis");
+ var axisText = svg.selectAll(".axis-text");
+ var lineList = svg.selectAll(".line");
+
+ function _redraw(series) {
+ var _desX, _desY, _desZ, _step;
+
+ // Bounds
+ if(series) {
+ _bounds = [{},{},{}];
+ $.each(series, function(j, series) {
+ // Points
+ $.each(series.data, function(k, point) {
+ for(var i = 0 ; i < 3 ; i += 1) {
+ // Minimum
+ if(_bounds[i].min === undefined || point[i] < _bounds[i].min) {
+ _bounds[i].min = point[i];
+ }
+ // Maximum
+ if(_bounds[i].max === undefined || _bounds[i].max < point[i]) {
+ _bounds[i].max = point[i];
+ }
+ }
+ });
+ });
+ }
+
+ _desX = _bounds[0].max - _bounds[0].min;
+ _desY = _bounds[1].max - _bounds[1].min;
+ _desZ = _bounds[2].max - _bounds[2].min;
+
+ // Step
+ (function() {
+ var _stepX = _desX / 10;
+ var _stepY = _desY / 10;
+ var _stepZ = _desZ / 10;
+
+ _step = Math.min(_stepX, _stepY, _stepZ);
+ _step = Math.max(_step, 0.5);
+ _step = 0.5;
+ })();
+
+ // Scale
+ (function() {
+ var _scaleX = width / _desX;
+ var _scaleY = height / _desY / 2;
+ var _scaleZ = width / _desZ;
+ _scale = Math.min(_scaleX, _scaleY, _scaleZ) / 2;
+ })();
+
+ // Coordinate
+ // > Basic
+ _coordinateList = [
+ {color: "rgba(0,0,0,0.1)", data: [[0,0,-100],[0,0,100]]},
+ {color: "rgba(0,0,0,0.1)", data: [[0,-100,0],[0,100,0]]},
+ {color: "rgba(0,0,0,0.1)", data: [[-100,0,0],[100,0,0]]},
+
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].min,_bounds[2].min]]},
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].min,_bounds[1].min,_bounds[2].max]]},
+
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].max],[_bounds[0].max,_bounds[1].min,_bounds[2].max]]},
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].max,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].min,_bounds[2].max]]},
+
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].max,_bounds[2].min],[_bounds[0].max,_bounds[1].max,_bounds[2].min]]},
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].max,_bounds[2].min],[_bounds[0].min,_bounds[1].max,_bounds[2].max]]},
+
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].min],[_bounds[0].min,_bounds[1].max,_bounds[2].min]]},
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].max,_bounds[1].min,_bounds[2].min],[_bounds[0].max,_bounds[1].max,_bounds[2].min]]},
+ {color: "rgba(0,0,255,0.3)", data: [[_bounds[0].min,_bounds[1].min,_bounds[2].max],[_bounds[0].min,_bounds[1].max,_bounds[2].max]]},
+ ];
+
+ _axisText = [];
+ function _axisPoint(point, dimension, number) {
+ // Coordinate
+ if(dimension === 1) {
+ _coordinateList.push({
+ color: "rgba(0,0,0,0.2)",
+ data:[[_step/5,point[1],0],[0,point[1],0],[0,point[1],_step/5]]
+ });
+ } else {
+ _coordinateList.push({
+ color: "rgba(0,0,0,0.2)",
+ data:[[point[0],-_step/8,point[2]], [point[0],_step/8,point[2]]]
+ });
+ }
+
+ // Axis Text
+ if(number.toFixed(0) == number + "") {
+ point.text = number;
+ point.dimension = dimension;
+ _axisText.push(point);
+ }
+ }
+ function _axisPoints(dimension, bound) {
+ var i, _unit;
+ for(i = _step ; i < bound.max + _step ; i += _step) {
+ _unit = [0,0,0];
+ _unit[dimension] = i;
+ _axisPoint(_unit, dimension, i);
+ }
+ for(i = -_step ; i > bound.min - _step ; i -= _step) {
+ _unit = [0,0,0];
+ _unit[dimension] = i;
+ _axisPoint(_unit, dimension, i);
+ }
+ }
+ // > Steps
+ _axisPoint([0,0,0],1,0);
+ _axisPoints(0, _bounds[0]);
+ _axisPoints(1, _bounds[1]);
+ _axisPoints(2, _bounds[2]);
+
+ // > Draw
+ coordinate = coordinate.data(_coordinateList);
+ coordinate.enter()
+ .append("path")
+ .attr("fill", "none")
+ .attr("stroke", function(d) {return d.color;});
+ coordinate.exit().remove();
+
+ // Axis Text
+ axisText = axisText.data(_axisText);
+ axisText.enter()
+ .append("text")
+ .classed("noSelect", true)
+ .attr("fill", "rgba(0,0,0,0.5)")
+ .attr("text-anchor", function(d) {return d.dimension === 1 ? "start" : "middle";})
+ .text(function(d) {return d.text;});
+ axisText.transition()
+ .attr("text-anchor", function(d) {return d.dimension === 1 ? "end" : "middle";})
+ .text(function(d) {return d.text;});
+ axisText.exit().remove();
+
+ // Lines
+ lineList = lineList.data(series || []);
+ lineList.enter()
+ .append("path")
+ .attr("fill", "none")
+ .attr("stroke", function(d) {return d.color;});
+ lineList.exit().remove();
+
+ _update();
+ }
+
+ function _update() {
+ coordinate
+ .attr("d", function(d) {
+ var path = "";
+ $.each(d.data, function(i, point) {
+ path += (i === 0 ? "M" : "L") + transformPoint2D(point);
+ });
+ return path;
+ });
+
+ axisText
+ .attr("x", function(d) {return transfromPointX(d) + (d.dimension === 1 ? -3 : 0);})
+ .attr("y", function(d) {return transfromPointY(d) + (d.dimension === 1 ? 0 : -5);});
+
+ lineList
+ .attr("d", function(d, index) {
+ var path = "";
+ $.each(d.data, function(i, point) {
+ path += (i === 0 ? "M" : "L") + transformPoint2D(point);
+ });
+ return path;
+ });
+ }
+
+
+ svg.on("mousedown", function() {
+ drag = [d3.mouse(this), yaw, pitch];
+ }).on("mouseup", function() {
+ drag = false;
+ }).on("mousemove", function() {
+ if (drag) {
+ var mouse = d3.mouse(this);
+ yaw = drag[1] - (mouse[0] - drag[0][0]) / 50;
+ pitch = drag[2] + (mouse[1] - drag[0][1]) / 50;
+ pitch = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, pitch));
+ setTurtable(yaw, pitch, true);
+ }
+ });
+
+ // =========================== Render ===========================
+ _redraw();
+
+ function _render() {
+ // ======== Parse Data ========
+ var _series = typeof series === "string" ? $scope.data : series;
+ if(!_series) return;
+
+ // Clone
+ _series = $.map(_series, function(series) {
+ return {
+ name: series.name,
+ color: series.color,
+ data: series.data
+ };
+ });
+
+ // Colors
+ $.each(_series, function(i, series) {
+ series.color = series.color || color[i % color.length];
+ });
+
+ // Render
+ _redraw(_series);
+ }
+
+ // ======================= Dynamic Detect =======================
+ if(typeof series === "string") {
+ $scope.$parent.$watch(series, function() {
+ _render();
+ }, true);
+ } else {
+ _render();
+ }
+
+
+ // ========================== Clean Up ==========================
+ $scope.$on('$destroy', function() {
+ svg.remove();
+ });
+ },
+ };
+ };
+ return charts;
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/file.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/file.js b/eagle-webservice/src/main/webapp/_app/public/js/components/file.js
new file mode 100644
index 0000000..a8d78db
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/file.js
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+eagleComponents.directive('file', function($compile) {
+ 'use strict';
+
+ return {
+ restrict : 'A',
+ scope: {
+ filepath: "=?filepath",
+ },
+ controller: function($scope, $element, $attrs) {
+ // Watch change(Only support clean the data)
+ if($attrs.filepath) {
+ $scope.$parent.$watch($attrs.filepath, function(value) {
+ if(!value) $element.val(value);
+ });
+ }
+
+ // Bind changed value
+ $element.on("change", function() {
+ var _path = $(this).val();
+ if($attrs.filepath) {
+ common.setValueByPath($scope.$parent, $attrs.filepath, _path);
+ $scope.$parent.$apply();
+ }
+ });
+
+ $scope.$on('$destroy',function(){
+ $element.off("change");
+ });
+ },
+ replace: false
+ };
+});
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/main.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/main.js b/eagle-webservice/src/main/webapp/_app/public/js/components/main.js
new file mode 100644
index 0000000..a0d9f9f
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/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.
+ */
+
+var eagleComponents = angular.module('eagle.components', []);
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/nvd3.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/nvd3.js b/eagle-webservice/src/main/webapp/_app/public/js/components/nvd3.js
new file mode 100644
index 0000000..8687c78
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/nvd3.js
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+eagleComponents.service('nvd3', function() {
+ var nvd3 = {
+ charts: [],
+ colors: [
+ "#7CB5EC", "#F7A35C", "#90EE7E", "#7798BF", "#AAEEEE"
+ ]
+ };
+
+ // ============================================
+ // = Format Convert =
+ // ============================================
+
+ /***
+ * Format: [series:{key:name, value: [{x, y}]}]
+ */
+
+ nvd3.convert = {};
+ nvd3.convert.eagle = function(seriesList) {
+ return $.map(seriesList, function(series) {
+ var seriesObj = $.isArray(series) ? {values: series} : series;
+ if(!seriesObj.key) seriesObj.key = "value";
+ return seriesObj;
+ });
+ };
+
+ nvd3.convert.druid = function(seriesList) {
+ var _seriesList = [];
+
+ $.each(seriesList, function(i, series) {
+ if(!series.length) return;
+
+ // Fetch keys
+ var _measure = series[0];
+ var _keys = $.map(_measure.event, function(value, key) {
+ return key !== "metric" ? key : null;
+ });
+
+ // Parse series
+ _seriesList.push.apply(_seriesList, $.map(_keys, function(key) {
+ return {
+ key: key,
+ values: $.map(series, function(unit) {
+ return {
+ x: new moment(unit.timestamp).valueOf(),
+ y: unit.event[key]
+ };
+ })
+ };
+ }));
+ });
+
+ return _seriesList;
+ };
+
+ // ============================================
+ // = UI =
+ // ============================================
+ // Resize with refresh
+ function chartResize() {
+ $.each(nvd3.charts, function(i, chart) {
+ if(chart) chart.nvd3Update();
+ });
+ }
+ $(window).on("resize.components.nvd3", chartResize);
+ $("body").on("collapsed.pushMenu expanded.pushMenu", function() {
+ setTimeout(chartResize, 300);
+ });
+
+ return nvd3;
+});
+
+/**
+ * config:
+ * chart: Defined chart type: line, column, area
+ * xTitle: Defined x axis title.
+ * yTitle: Defined y axis title.
+ * xType: Defined x axis label type: number, decimal, time
+ * yType: Defined y axis label type
+ * yMin: Defined minimum of y axis
+ * yMax: Defined maximum of y axis
+ * displayType: Defined the chart display type. Each chart has own type.
+ */
+eagleComponents.directive('nvd3', function(nvd3) {
+ 'use strict';
+
+ return {
+ restrict: 'AE',
+ scope: {
+ nvd3: "=",
+ title: "@?title", // title
+ chart: "@?chart", // Same as config.chart
+ config: "=?config",
+ watching: "@?watching", // Default watching data(nvd3) only. true will also watching chart & config. false do not watching.
+
+ holder: "=?holder" // Container for holder to call the chart function
+ },
+ controller: function($scope, $element, $attrs, $timeout) {
+ var _config, _chartType;
+ var _chart;
+ var _chartCntr;
+ var _holder, _holder_updateTimes;
+
+ // Destroy
+ function destroy() {
+ var _index = $.inArray(_chart, nvd3.charts);
+ if(!_chartCntr) return _index;
+
+ // Clean events
+ d3.select(_chartCntr)
+ .on("touchmove",null)
+ .on("mousemove",null, true)
+ .on("mouseout" ,null,true)
+ .on("dblclick" ,null)
+ .on("click", null);
+
+ // Clean elements
+ d3.select(_chartCntr).selectAll("*").remove();
+ $element.find(".nvtooltip").remove();
+ $(_chartCntr).remove();
+
+ // Clean chart in nvd3 pool
+ nvd3.charts[_index] = null;
+ _chart = null;
+
+ return _index;
+ }
+
+ // Setup chart environment. Will clean old chart and build new chart if recall.
+ function initChart() {
+ // Clean up if already have chart
+ var _preIndex = destroy();
+
+ // Initialize
+ _config = $.extend({}, $scope.config);
+ _chartType = $scope.chart || _config.chart;
+ _chartCntr = $(document.createElementNS("http://www.w3.org/2000/svg", "svg"))
+ .css("min-height", 50)
+ .attr("_rnd", Math.random())
+ .appendTo($element)[0];
+
+ // Size
+ if(_config.height) {
+ $(_chartCntr).css("height", _config.height);
+ }
+
+ switch(_chartType) {
+ case "line":
+ _chart = nv.models.lineChart()
+ .useInteractiveGuideline(true)
+ .showLegend(true)
+ .showYAxis(true)
+ .showXAxis(true)
+ .options({
+ duration: 350
+ });
+ break;
+ case "column":
+ _chart = nv.models.multiBarChart()
+ .groupSpacing(0.1)
+ .options({
+ duration: 350
+ });
+ break;
+ case "area":
+ _chart = nv.models.stackedAreaChart()
+ .useInteractiveGuideline(true)
+ .showLegend(true)
+ .showYAxis(true)
+ .showXAxis(true)
+ .options({
+ duration: 350
+ });
+ break;
+ case "pie":
+ _chart = nv.models.dimensionalPieChart()
+ .x(function(d) { return d.key; })
+ .y(function(d) { return d.values[d.values.length - 1].y; });
+ break;
+ default :
+ throw "Type not defined: " + _chartType;
+ }
+
+ // nvd3 display Type
+ // TODO: support type define
+
+ // Define title
+ if(_chartType !== 'pie') {
+ if(_config.xTitle) _chart.xAxis.axisLabel(_config.xTitle);
+ if(_config.yTitle) _chart.yAxis.axisLabel(_config.yTitle);
+ }
+
+ // Define label type
+ var _tickMultiFormat = d3.time.format.multi([
+ ["%-I:%M%p", function(d) { return d.getMinutes(); }],
+ ["%-I%p", function(d) { return d.getHours(); }],
+ ["%b %-d", function(d) { return d.getDate() != 1; }],
+ ["%b %-d", function(d) { return d.getMonth(); }],
+ ["%Y", function() { return true; }]
+ ]);
+
+ function _defineLabelType(axis, type) {
+ if(!_chart) return;
+
+ var _axis = _chart[axis + "Axis"];
+ if(!_axis) return;
+
+ switch(type) {
+ case "decimal":
+ case "decimals":
+ _axis.tickFormat(d3.format('.02f'));
+ break;
+ case "text":
+ if(axis === "x") {
+ _chart.rotateLabels(10);
+ _chart.reduceXTicks(false).staggerLabels(true);
+ }
+ _axis.tickFormat(function(d) {
+ return d;
+ });
+ break;
+ case "time":
+ if(_chartType !== 'column') {
+ _chart[axis + "Scale"](d3.time.scale());
+ (function () {
+ var measureSeries = null;
+ $.each($scope.nvd3 || [], function(i, series) {
+ var _len = (series.values || []).length;
+ if(_len === 0) return;
+ if(measureSeries === null || measureSeries.values.length < _len) measureSeries = series;
+ });
+
+ var width = $element.width() - 35;// Use default nvd3 margin. Hard code.
+ if(!measureSeries || width <= 0) return;
+ var count = Math.floor(width / 80);
+ var countDes = Math.floor(measureSeries.values.length / count);
+ var tickValues = [];
+ for(var loc = 0 ; loc < measureSeries.values.length ; loc += countDes) {
+ tickValues.push(measureSeries.values[loc].x);
+ }
+ _chart[axis + "Axis"].tickValues(tickValues);
+ })();
+ }
+ _axis.tickFormat(function(d) {
+ return _tickMultiFormat(app.time.offset(d).toDate(true));
+ });
+ break;
+ case "number":
+ /* falls through */
+ default:
+ _axis.tickFormat(d3.format(',r'));
+ }
+ }
+
+ if(_chartType !== 'pie') {
+ _defineLabelType("x", _config.xType || "number");
+ _defineLabelType("y", _config.yType || "decimal");
+ }
+
+ // Global chart list update
+ if(_preIndex === -1) {
+ nvd3.charts.push(_chart);
+ } else {
+ nvd3.charts[_preIndex] = _chart;
+ }
+
+ // Use customize update function to update the view
+ _chart.nvd3Update = function() {
+ if(_config.xType === "time") _defineLabelType("x", _config.xType);
+ _chart.update();
+ };
+
+ updateData();
+ }
+
+ // Update chart data
+ function updateData() {
+ var _min = null, _max = null;
+
+ // Copy series to prevent Angular loop watching
+ var _data = $.map($scope.nvd3 || [], function(series, i) {
+ var _series = $.extend(true, {}, series);
+ _series.color = _series.color || nvd3.colors[i % nvd3.colors.length];
+ return _series;
+ });
+
+ // Chart Y value
+ if(($scope.chart || _config.chart) !== "pie") {
+ $.each(_data, function(i, series) {
+ $.each(series.values, function(j, unit) {
+ if(_min === null || unit.y < _min) _min = unit.y;
+ if(_max === null || unit.y > _max) _max = unit.y;
+ });
+ });
+
+ if(_min === 0 && _max === 0) {
+ _chart.forceY([0, 10]);
+ } else if(_config.yMin !== undefined || _config.yMax !== undefined) {
+ _chart.forceY([_config.yMin, _config.yMax]);
+ } else {
+ _chart.forceY([]);
+ }
+ }
+
+ // Update data
+ d3.select(_chartCntr) //Select the <svg> element you want to render the chart in.
+ .datum(_data) //Populate the <svg> element with chart data...
+ .call(_chart); //Finally, render the chart!
+
+ setTimeout(_chart.nvd3Update, 10);
+ }
+
+ // ================================================================
+ // = Watching =
+ // ================================================================
+ // Ignore initial checking
+ $timeout(function() {
+ if ($scope.watching !== "false") {
+ $scope.$watch(function() {
+ if(!$scope.nvd3) return [];
+
+ var _hashList = $.map($scope.nvd3, function(series) {
+ if(!series.values) return 0;
+ var unit = {
+ x: 0,
+ y: 0
+ };
+
+ $.each(series.values, function(j, item) {
+ unit.x += item.x;
+ unit.y += item.y;
+ });
+
+ return unit;
+ });
+
+ return _hashList;
+ }, function() {
+ updateData();
+ }, true);
+
+ // All watching mode
+ if ($scope.watching === "true") {
+ $scope.$watch("[chart, config]", function(newValue, oldValue) {
+ if(angular.equals(newValue, oldValue)) return;
+ initChart();
+ }, true);
+ }
+ }
+ });
+
+ // Holder inject
+ _holder_updateTimes = 0;
+ _holder = {
+ element: $element,
+ refresh: function() {
+ setTimeout(function() {
+ updateData();
+ }, 0);
+ },
+ refreshAll: function() {
+ setTimeout(function() {
+ initChart();
+ }, 0);
+ }
+ };
+
+ Object.defineProperty(_holder, 'chart', {
+ get: function() {return _chart;}
+ });
+
+ $scope.$watch("holder", function() {
+ // Holder times update
+ setTimeout(function() {
+ _holder_updateTimes = 0;
+ }, 0);
+ _holder_updateTimes += 1;
+ if(_holder_updateTimes > 100) throw "Holder conflict";
+
+ $scope.holder = _holder;
+ });
+
+ // ================================================================
+ // = Start Up =
+ // ================================================================
+ initChart();
+
+ // ================================================================
+ // = Clean Up =
+ // ================================================================
+ $scope.$on('$destroy', function() {
+ destroy();
+ });
+ },
+ template :
+ '<div>' +
+ '<h3 title="{{title || config.title}}">{{title || config.title}}</h3>' +
+ '</div>',
+ replace: true
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/sortTable.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/sortTable.js b/eagle-webservice/src/main/webapp/_app/public/js/components/sortTable.js
new file mode 100644
index 0000000..bdcbca4
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/sortTable.js
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+eagleComponents.directive('sorttable', function($compile) {
+ 'use strict';
+
+ return {
+ restrict : 'AE',
+ scope: {
+ source: '=',
+ search: '=?search',
+ searchfunc: "=?searchfunc",
+
+ orderKey: "@?sort",
+
+ maxSize: "=?maxSize",
+ },
+ controller: function($scope, $element, $attrs) {
+ // Initialization
+ $scope.app = app;
+ $scope.common = common;
+ $scope._parent = $scope.$parent;
+
+ $scope.pageNumber = $scope.pageNumber || 1;
+ $scope.pageSize = $scope.pageSize || 10;
+
+ $scope.maxSize = $scope.maxSize || 10;
+
+ // Search box
+ if($scope.search !== false) {
+ var $search = $(
+ '<div style="overflow:hidden;">' +
+ '<div class="row">' +
+ '<div class="col-xs-4">' +
+ '<div class="search-box">' +
+ '<input type="search" class="form-control input-sm" placeholder="Search" ng-model="search" />' +
+ '<span class="fa fa-search"></span>' +
+ '</div>' +
+ '</div>' +
+ '</div>' +
+ '</div>'
+ ).prependTo($element);
+ $compile($search)($scope);
+ }
+
+ // List head
+ $scope.doSort = function(path) {
+ if($scope.orderKey === path) {
+ $scope.orderKey = "-" + path;
+ } else {
+ $scope.orderKey = path;
+ }
+ };
+ $scope.checkSortClass = function(key) {
+ if($scope.orderKey === key) {
+ return "fa fa-sort-asc";
+ } else if($scope.orderKey === "-" + key) {
+ return "fa fa-sort-desc";
+ }
+ return "fa fa-sort";
+ };
+
+ var $listHead = $element.find("thead > tr");
+ $listHead.find("> th").each(function() {
+ var $th = $(this);
+ $th.addClass("noSelect");
+
+ var _sortpath = $th.attr("sortpath");
+ if(_sortpath) {
+ $th.attr("ng-click", "doSort('" + _sortpath + "')");
+ $th.prepend('<span ng-class="checkSortClass(\'' + _sortpath + '\')"></span>');
+ }
+ });
+ $compile($listHead)($scope);
+
+ // List body
+ var $listBody = $element.find("tbody > tr");
+ $listBody.attr("ng-repeat", 'item in (filteredList = (source | filter: ' + ($scope.searchfunc ? 'searchfunc' : 'search') + ' | orderBy: orderKey)).slice((pageNumber - 1) * pageSize, pageNumber * pageSize)');
+ $compile($listBody)($scope);
+
+ // Navigation
+ var $navigation = $(
+ '<div style="overflow:hidden;">' +
+ '<div class="row">' +
+ '<div class="col-xs-5">' +
+ 'show {{(pageNumber - 1) * pageSize + 1}} to {{pageNumber * pageSize}} of {{filteredList.length}} items' +
+ '</div>' +
+ '<div class="col-xs-7 text-right">' +
+ '<uib-pagination total-items="filteredList.length" ng-model="pageNumber" boundary-links="true" items-per-page="pageSize" num-pages="numPages" max-size="maxSize"></uib-pagination>' +
+ '</div>' +
+ '</div>' +
+ '</div>'
+ ).appendTo($element);
+ $compile($navigation)($scope);
+ },
+ replace: false
+ };
+});
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/sortable.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/sortable.js b/eagle-webservice/src/main/webapp/_app/public/js/components/sortable.js
new file mode 100644
index 0000000..c98c732
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/sortable.js
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+eagleComponents.directive('uieSortable', function($rootScope) {
+ 'use strict';
+
+ var COLLECTION_MATCH = /^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/;
+
+ var _move = false;
+ var _selectElement;
+ var _overElement;
+ var _mockElement;
+ var _offsetX, _offsetY;
+ var _mouseDownPageX, _mouseDownPageY;
+
+ function doMock(element, event) {
+ var _offset = element.offset();
+ if(_mockElement) _mockElement.remove();
+
+ // Create mock element
+ _mockElement = element.clone(false).appendTo("body");
+ _mockElement.addClass("sortable-mock-element");
+ _mockElement.css({
+ display: "block",
+ position: "absolute",
+ "pointer-events": "none",
+ "z-index": 10001,
+ padding: element.css("padding"),
+ margin: element.css("margin")
+ });
+ _mockElement.width(element.width());
+ _mockElement.height(element.height());
+
+ _mockElement.offset(_offset);
+ _offsetX = event.pageX - _offset.left;
+ _offsetY = event.pageY - _offset.top;
+ }
+
+ $(window).on("mousemove", function(event) {
+ if(!_move) return;
+ event.preventDefault();
+
+ _mockElement.offset({
+ left: event.pageX - _offsetX,
+ top: event.pageY - _offsetY
+ });
+ });
+
+ $(window).on("mouseup", function() {
+ if(!_move) {
+ _overElement = null;
+ _selectElement = null;
+ _mockElement = null;
+ return;
+ }
+ _move = false;
+
+ if(_overElement) {
+ _overElement.removeClass("sortable-enter");
+
+ if(_overElement[0] !== _selectElement[0]) {
+ // Process switch
+ var _oriHolder = _selectElement.holder;
+ var _tgtHolder = _overElement.holder;
+ var _oriSortableScope = _oriHolder.scope;
+ var _tgtSortableScope = _tgtHolder.scope;
+ var _oriScope = angular.element(_selectElement).scope();
+ var _tgtScope = angular.element(_overElement).scope();
+
+ var _oriRepeat = _selectElement.closest("[ng-repeat]").attr("ng-repeat");
+ var _tgtRepeat = _overElement.closest("[ng-repeat]").attr("ng-repeat");
+ var _oriMatch = _oriRepeat.match(COLLECTION_MATCH)[2];
+ var _tgtMatch = _tgtRepeat.match(COLLECTION_MATCH)[2];
+ var _oriCollection = _oriScope.$parent.$eval(_oriMatch);
+ var _tgtCollection = _tgtScope.$parent.$eval(_tgtMatch);
+ var _oriIndex = $.inArray(_oriCollection[_oriScope.$index], _oriSortableScope.ngModel);
+ var _tgtIndex = $.inArray(_tgtCollection[_tgtScope.$index], _tgtSortableScope.ngModel);
+
+ var _oriUnit = _oriSortableScope.ngModel[_oriIndex];
+ var _tgtUnit = _tgtSortableScope.ngModel[_tgtIndex];
+ _oriSortableScope.ngModel[_oriIndex] = _tgtUnit;
+ _tgtSortableScope.ngModel[_tgtIndex] = _oriUnit;
+
+ // Trigger event
+ _oriHolder.change(_oriUnit, _tgtUnit);
+ if (_oriHolder !== _tgtHolder) _tgtHolder.change(_oriUnit, _tgtUnit);
+
+ $rootScope.$apply();
+ }
+ }
+
+ if(_mockElement) _mockElement.remove();
+
+ _overElement = null;
+ _selectElement = null;
+ _mockElement = null;
+ });
+
+ return {
+ require: 'ngModel',
+ restrict : 'AE',
+ scope: {
+ ngModel: "=",
+ sortableEnabled: "=?sortableEnabled",
+ sortableUpdateFunc: "=?sortableUpdateFunc"
+ },
+ link: function($scope, $element, $attrs, $ctrl) {
+ var _holder = {
+ scope: $scope,
+ change: function(source, target) {
+ if($scope.sortableUpdateFunc) $scope.sortableUpdateFunc(source, target);
+ }
+ };
+
+ $element.on("mousedown", ">", function(event) {
+ if($scope.sortableEnabled === false) return;
+
+ _selectElement = $(this);
+ _selectElement.holder = _holder;
+
+ _mouseDownPageX = event.pageX;
+ _mouseDownPageY = event.pageY;
+
+ event.preventDefault();
+ });
+
+ $element.on("mousemove", ">", function(event) {
+ if(_selectElement && !_move && common.math.distance(_mouseDownPageX, _mouseDownPageY, event.pageX, event.pageY) > 10) {
+ _move = true;
+ _overElement = _selectElement;
+ _overElement.addClass("sortable-enter");
+
+ doMock(_selectElement, event);
+ }
+ });
+
+ $element.on("mouseenter", ">", function() {
+ if(!_move) return;
+ _overElement = $(this);
+ _overElement.holder = _holder;
+ _overElement.addClass("sortable-enter");
+ });
+ $element.on("mouseleave", ">", function() {
+ if(!_move) return;
+ $(this).removeClass("sortable-enter");
+ _overElement = null;
+ });
+ },
+ replace: false
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/components/tabs.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/components/tabs.js b/eagle-webservice/src/main/webapp/_app/public/js/components/tabs.js
new file mode 100644
index 0000000..21c4a4a
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/components/tabs.js
@@ -0,0 +1,247 @@
+/*
+ * 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.
+ */
+
+eagleComponents.directive('tabs', function() {
+ 'use strict';
+
+ return {
+ restrict: 'AE',
+ transclude: {
+ 'header': '?header',
+ 'pane': 'pane',
+ 'footer': '?footer'
+ },
+ scope : {
+ title: "@?title",
+ icon: "@",
+ selected: "@?selected",
+ holder: "=?holder",
+ sortableModel: "=?sortableModel",
+
+ menuList: "=?menu"
+ },
+
+ controller: function($scope, $element, $attrs, $timeout) {
+ var transDuration = $.fn.tab.Constructor.TRANSITION_DURATION;
+ var transTimer = null;
+ var _holder, _holder_updateTimes;
+
+ var $header, $footer;
+
+ $scope.paneList = [];
+ $scope.selectedPane = null;
+ $scope.inPane = null;
+ $scope.activePane = null;
+
+ // ================== Function ==================
+ $scope.getPaneList = function() {
+ return !$scope.title ? $scope.paneList : $scope.paneList.slice().reverse();
+ };
+
+ $scope.setSelect = function(pane) {
+ if(typeof pane === "string") {
+ pane = common.array.find(pane, $scope.paneList, "title");
+ } else if(typeof pane === "number") {
+ pane = (pane + $scope.paneList.length) % $scope.paneList.length;
+ pane = $scope.paneList[pane];
+ }
+
+ $scope.activePane = $scope.selectedPane || pane;
+ $scope.selectedPane = pane;
+ $scope.inPane = null;
+
+ if(transTimer) $timeout.cancel(transTimer);
+ transTimer = $timeout(function() {
+ $scope.activePane = $scope.selectedPane;
+ $scope.inPane = $scope.selectedPane;
+ }, transDuration);
+ };
+
+ $scope.getMenuList = function() {
+ if($scope.selectedPane && $scope.selectedPane.menuList) {
+ return $scope.selectedPane.menuList;
+ }
+ return $scope.menuList;
+ };
+
+ $scope.tabSwitchUpdate = function (src, tgt) {
+ var _srcIndex = $.inArray(src.data, $scope.sortableModel);
+ var _tgtIndex = $.inArray(tgt.data, $scope.sortableModel);
+
+ if(_srcIndex !== -1 && _tgtIndex !== -1) {
+ $scope.sortableModel[_srcIndex] = tgt.data;
+ $scope.sortableModel[_tgtIndex] = src.data;
+ }
+ };
+
+ // =================== Linker ===================
+ function _linkerProperties(pane) {
+ Object.defineProperties(pane, {
+ selected: {
+ get: function () {
+ return $scope.selectedPane === this;
+ }
+ },
+ active: {
+ get: function () {
+ return $scope.activePane === this;
+ }
+ },
+ in: {
+ get: function () {
+ return $scope.inPane === this;
+ }
+ }
+ });
+ }
+
+ this.addPane = function(pane) {
+ $scope.paneList.push(pane);
+
+ // Register properties
+ _linkerProperties(pane);
+
+ // Update select pane
+ if(pane.title === $scope.selected || !$scope.selectedPane) {
+ $scope.setSelect(pane);
+ }
+ };
+
+ this.deletePane = function(pane) {
+ common.array.remove(pane, $scope.paneList);
+
+ if($scope.selectedPane === pane) {
+ $scope.selectedPane = $scope.activePane = $scope.inPane = $scope.paneList[0];
+ }
+ };
+
+ // ===================== UI =====================
+ $header = $element.find("> .nav-tabs-custom > .box-body");
+ $footer = $element.find("> .nav-tabs-custom > .box-footer");
+
+ $scope.hasHeader = function() {
+ return !!$header.children().length;
+ };
+ $scope.hasFooter = function() {
+ return !!$footer.children().length;
+ };
+
+ // ================= Interface ==================
+ _holder_updateTimes = 0;
+ _holder = {
+ scope: $scope,
+ element: $element,
+ setSelect: $scope.setSelect
+ };
+
+ Object.defineProperty(_holder, 'selectedPane', {
+ get: function() {return $scope.selectedPane;}
+ });
+
+ $scope.$watch("holder", function(newValue, oldValue) {
+ // Holder times update
+ setTimeout(function() {
+ _holder_updateTimes = 0;
+ }, 0);
+ _holder_updateTimes += 1;
+ if(_holder_updateTimes > 100) throw "Holder conflict";
+
+ $scope.holder = _holder;
+ });
+ },
+
+ template :
+ '<div class="nav-tabs-custom">' +
+ // Menu
+ '<div class="box-tools pull-right" ng-if="getMenuList() && getMenuList().length">' +
+ '<div ng-repeat="menu in getMenuList() track by $index" class="inline">' +
+ // Button
+ '<button class="btn btn-box-tool" ng-click="menu.func($event)" ng-if="!menu.list"' +
+ ' uib-tooltip="{{menu.title}}" tooltip-enable="menu.title" tooltip-append-to-body="true">' +
+ '<span class="fa fa-{{menu.icon}}"></span>' +
+ '</button>' +
+
+ // Dropdown Group
+ '<div class="btn-group" ng-if="menu.list">' +
+ '<button class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown"' +
+ ' uib-tooltip="{{menu.title}}" tooltip-enable="menu.title" tooltip-append-to-body="true">' +
+ '<span class="fa fa-{{menu.icon}}"></span>' +
+ '</button>' +
+ '<ul class="dropdown-menu left" role="menu">' +
+ '<li ng-repeat="item in menu.list track by $index" ng-class="{danger: item.danger, disabled: item.disabled}">' +
+ '<a ng-click="!item.disabled && item.func($event)" ng-class="{strong: item.strong}">' +
+ '<span class="fa fa-{{item.icon}}"></span> {{item.title}}' +
+ '</a>' +
+ '</li>' +
+ '</ul>' +
+ '</div>' +
+ '</div>' +
+ '</div>' +
+
+ '<ul uie-sortable sortable-enabled="!!sortableModel" sortable-update-func="tabSwitchUpdate" ng-model="paneList" class="nav nav-tabs" ng-class="{\'pull-right\': title}">' +
+ // Tabs
+ '<li ng-repeat="pane in getPaneList() track by $index" ng-class="{active: selectedPane === pane}">' +
+ '<a ng-click="setSelect(pane);">{{pane.title}}</a>' +
+ '</li>' +
+
+ // Title
+ '<li class="pull-left header" ng-if="title">' +
+ '<i class="fa fa-{{icon}}" ng-if="icon"></i> {{title}}' +
+ '</li>' +
+
+ '</ul>' +
+ '<div class="box-body" ng-transclude="header" ng-show="paneList.length && hasHeader()"></div>' +
+ '<div class="tab-content" ng-transclude="pane"></div>' +
+ '<div class="box-footer" ng-transclude="footer" ng-show="paneList.length && hasFooter()"></div>' +
+ '</div>'
+ };
+}).directive('pane', function() {
+ 'use strict';
+
+ return {
+ require : '^tabs',
+ restrict : 'AE',
+ transclude : true,
+ scope : {
+ title : '@',
+ data: '=?data',
+ menuList: "=?menu"
+ },
+ link : function(scope, element, attrs, tabsController) {
+ tabsController.addPane(scope);
+ scope.$on('$destroy', function() {
+ tabsController.deletePane(scope);
+ });
+ },
+ template : '<div class="tab-pane fade" ng-class="{active: active, in: in}" ng-transclude></div>',
+ replace : true
+ };
+}).directive('footer', function() {
+ 'use strict';
+
+ return {
+ require : '^tabs',
+ restrict : 'AE',
+ transclude : true,
+ scope : {},
+ controller: function($scope, $element) {
+ },
+ template : '<div ng-transclude></div>',
+ replace : true
+ };
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/ctrl/authController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/ctrl/authController.js b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/authController.js
new file mode 100644
index 0000000..dbdb704
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/authController.js
@@ -0,0 +1,91 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+ // =============================================================
+ // = User Profile List =
+ // =============================================================
+ eagleControllers.controller('authLoginCtrl', function (PageConfig, Site, Authorization, Application, $scope) {
+ PageConfig.hideSidebar = true;
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ PageConfig.hideUser = true;
+
+ $scope.username = "";
+ $scope.password = "";
+ $scope.lock = false;
+ $scope.loginSuccess = false;
+
+ if(localStorage) {
+ $scope.rememberUser = localStorage.getItem("rememberUser") !== "false";
+
+ if($scope.rememberUser) {
+ $scope.username = localStorage.getItem("username");
+ $scope.password = localStorage.getItem("password");
+ }
+ }
+
+ // UI
+ setTimeout(function () {
+ $("#username").focus();
+ });
+
+ // Login
+ $scope.login = function (event, forceSubmit) {
+ if ($scope.lock) return;
+
+ if (event.which === 13 || forceSubmit) {
+ $scope.lock = true;
+
+ Authorization.login($scope.username, $scope.password).then(function (success) {
+ if (success) {
+ // Check user remember
+ localStorage.setItem("rememberUser", $scope.rememberUser);
+ if($scope.rememberUser) {
+ localStorage.setItem("username", $scope.username);
+ localStorage.setItem("password", $scope.password);
+ } else {
+ localStorage.removeItem("username");
+ localStorage.removeItem("password");
+ }
+
+ // Initial environment
+ $scope.loginSuccess = true;
+ console.log("[Login] Login success! Reload data...");
+ Authorization.reload().then(function() {}, function() {console.warn("Site error!");});
+ Application.reload().then(function() {}, function() {console.warn("Site error!");});
+ Site.reload().then(function() {}, function() {console.warn("Site error!");});
+ Authorization.path(true);
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: "User name or password not correct."
+ }).on("hidden.bs.modal", function () {
+ $("#username").focus();
+ });
+ }
+ }).finally(function () {
+ $scope.lock = false;
+ });
+ }
+ };
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/ctrl/configurationController.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/ctrl/configurationController.js b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/configurationController.js
new file mode 100644
index 0000000..e59198d
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/configurationController.js
@@ -0,0 +1,377 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers');
+
+ // =============================================================
+ // = Function =
+ // =============================================================
+ function watchEdit($scope, key) {
+ $scope.changed = false;
+ setTimeout(function() {
+ var _func = $scope.$watch(key, function(newValue, oldValue) {
+ if(angular.equals(newValue, oldValue)) return;
+ $scope.changed = true;
+ _func();
+ }, true);
+ }, 100);
+ }
+
+ // =============================================================
+ // = Configuration =
+ // =============================================================
+ // ========================== Feature ==========================
+ eagleControllers.controller('configFeatureCtrl', function ($scope, PageConfig, Application, Entities, UI) {
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ $scope._pageLock = false;
+
+ PageConfig
+ .addNavPath("Home", "/")
+ .addNavPath("Feature Config");
+
+ // ================== Feature ==================
+ // Current feature
+ $scope.feature = Application.featureList[0];
+ $scope.setFeature = function (feature) {
+ $scope.feature = feature;
+ };
+
+ // Feature list
+ $scope.features = {};
+ $.each(Application.featureList, function(i, feature) {
+ $scope.features[feature.tags.feature] = $.extend({}, feature, true);
+ });
+
+ // Create feature
+ $scope.newFeature = function() {
+ UI.createConfirm("Feature", {}, [
+ {name: "Feature Name", field: "name"}
+ ], function(entity) {
+ if(entity.name && $.map($scope.features, function(feature, name) {
+ return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
+ }).length) {
+ return "Feature name conflict!";
+ }
+ }).then(null, null, function(holder) {
+ Entities.updateEntity(
+ "FeatureDescService",
+ {tags: {feature: holder.entity.name}},
+ {timestamp: false}
+ )._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // Delete feature
+ $scope.deleteFeature = function(feature) {
+ UI.deleteConfirm(feature.tags.feature).then(null, null, function(holder) {
+ Entities.delete("FeatureDescService", {feature: feature.tags.feature})._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // Save feature
+ $scope.saveAll = function() {
+ $scope._pageLock = true;
+ var _list = $.map($scope.features, function(feature) {
+ return feature;
+ });
+ Entities.updateEntity("FeatureDescService", _list, {timestamp: false})._promise.success(function() {
+ location.reload();
+ }).finally(function() {
+ $scope._pageLock = false;
+ });
+ };
+
+ // Watch config update
+ watchEdit($scope, "features");
+ });
+
+ // ======================== Application ========================
+ eagleControllers.controller('configApplicationCtrl', function ($scope, $timeout, PageConfig, Application, Entities, Feature, UI) {
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ $scope._pageLock = false;
+
+ PageConfig
+ .addNavPath("Home", "/")
+ .addNavPath("Application Config");
+
+ // ================ Application ================
+ // Current application
+ $scope.application = Application.list[0];
+ $scope.setApplication = function (application) {
+ $scope.application = application;
+ };
+
+ // Application list
+ $scope.applications = {};
+ $.each(Application.list, function(i, application) {
+ var _application = $scope.applications[application.tags.application] = $.extend({}, application, {features: application.features.slice()}, true);
+ _application.optionalFeatures = $.map(Application.featureList, function(feature) {
+ var featurePlugin = Feature.get(feature.tags.feature);
+ if(featurePlugin.config.global) return null;
+ if(!common.array.find(feature.tags.feature, _application.features)) {
+ return feature.tags.feature;
+ }
+ });
+ });
+
+ // Create application
+ $scope.newApplication = function() {
+ UI.createConfirm("Application", {}, [
+ {name: "Application Name", field: "name"}
+ ], function(entity) {
+ if(entity.name && $.map($scope.applications, function(application, name) {
+ return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
+ }).length) {
+ return "Application name conflict!";
+ }
+ }).then(null, null, function(holder) {
+ Entities.updateEntity(
+ "ApplicationDescService",
+ {tags: {application: holder.entity.name}},
+ {timestamp: false}
+ )._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // Delete application
+ $scope.deleteApplication = function(application) {
+ UI.deleteConfirm(application.tags.application).then(null, null, function(holder) {
+ Entities.delete("ApplicationDescService", {application: application.tags.application})._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // ================= Function ==================
+ // Configuration check
+ $scope.configCheck = function(config) {
+ if(config && !common.parseJSON(config, false)) {
+ return "Invalid JSON format";
+ }
+ };
+
+ // Feature
+ $scope._feature = "";
+ function highlightFeature(feature) {
+ $scope._feature = feature;
+
+ $timeout(function() {
+ $scope._feature = "";
+ }, 100);
+ }
+
+ $scope.addFeature = function(feature, application) {
+ application.features.push(feature);
+ common.array.remove(feature, application.optionalFeatures);
+ highlightFeature(feature);
+ $scope.changed = true;
+ };
+
+ $scope.removeFeature = function(feature, application) {
+ application.optionalFeatures.push(feature);
+ common.array.remove(feature, application.features);
+ $scope.changed = true;
+ };
+
+ $scope.moveFeature = function(feature, list, offset) {
+ common.array.moveOffset(feature, list, offset);
+ highlightFeature(feature);
+ $scope.changed = true;
+ };
+
+ // Save feature
+ $scope.saveAll = function() {
+ $scope._pageLock = true;
+
+ var _list = $.map($scope.applications, function(application) {
+ return application;
+ });
+ Entities.updateEntity("ApplicationDescService", _list, {timestamp: false})._promise.success(function() {
+ location.reload();
+ }).finally(function() {
+ $scope._pageLock = false;
+ });
+ };
+
+ // Watch config update
+ watchEdit($scope, "applications");
+ });
+
+ // ============================ Site ===========================
+ eagleControllers.controller('configSiteCtrl', function ($scope, $timeout, PageConfig, Site, Application, Entities, UI) {
+ PageConfig.hideApplication = true;
+ PageConfig.hideSite = true;
+ $scope._pageLock = false;
+
+ PageConfig
+ .addNavPath("Home", "/")
+ .addNavPath("Site Config");
+
+ // =================== Site ====================
+ // Current site
+ $scope.site = Site.list[0];
+ $scope.setSite = function (site) {
+ $scope.site = site;
+ };
+
+
+ // Site list
+ $scope.sites = {};
+ $.each(Site.list, function(i, site) {
+ var _site = $scope.sites[site.tags.site] = $.extend({}, site, true);
+ var _applications = [];
+ var _optionalApplications = [];
+
+ Object.defineProperties(_site, {
+ applications: {
+ get: function() {return _applications;}
+ },
+ optionalApplications: {
+ get: function() {return _optionalApplications;}
+ }
+ });
+
+ $.each(Application.list, function(i, application) {
+ var _application = site.applicationList.set[application.tags.application];
+ if(_application && _application.enabled) {
+ _site.applications.push(_application);
+ } else {
+ if(_application) {
+ _site.optionalApplications.push(_application);
+ } else {
+ _site.optionalApplications.push({
+ prefix: "eagleSiteApplication",
+ config: "",
+ enabled: false,
+ tags: {
+ application: application.tags.application,
+ site: site.tags.site
+ }
+ });
+ }
+ }
+ });
+ });
+
+ // Create site
+ $scope.newSite = function() {
+ UI.createConfirm("Site", {}, [
+ {name: "Site Name", field: "name"}
+ ], function(entity) {
+ if(entity.name && $.map($scope.sites, function(site, name) {
+ return name.toUpperCase() === entity.name.toUpperCase() ? true : null;
+ }).length) {
+ return "Site name conflict!";
+ }
+ }).then(null, null, function(holder) {
+ Entities.updateEntity(
+ "SiteDescService",
+ {enabled: true, tags: {site: holder.entity.name}},
+ {timestamp: false}
+ )._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // Delete site
+ $scope.deleteSite = function(site) {
+ UI.deleteConfirm(site.tags.site).then(null, null, function(holder) {
+ Entities.delete("SiteDescService", {site: site.tags.site})._promise.then(function() {
+ holder.closeFunc();
+ location.reload();
+ });
+ });
+ };
+
+ // ================= Function ==================
+ $scope._application = "";
+ function highlightApplication(application) {
+ $scope._application = application;
+
+ $timeout(function() {
+ $scope._application = "";
+ }, 100);
+ }
+
+ $scope.addApplication = function(application, site) {
+ site.applications.push(application);
+ common.array.remove(application, site.optionalApplications);
+ application.enabled = true;
+ highlightApplication(application);
+ $scope.changed = true;
+ };
+
+ $scope.removeApplication = function(application, site) {
+ site.optionalApplications.push(application);
+ common.array.remove(application, site.applications);
+ application.enabled = false;
+ $scope.changed = true;
+ };
+
+ $scope.setApplication = function(application) {
+ var _oriConfig = application.config;
+ UI.updateConfirm("Application", {config: _oriConfig}, [
+ {name: "Configuration", field: "config", type: "blob"}
+ ], function(entity) {
+ if(entity.config !== "" && !common.properties.check(entity.config)) {
+ return "Invalid Properties format";
+ }
+ }).then(null, null, function(holder) {
+ application.config = holder.entity.config;
+ holder.closeFunc();
+ if(_oriConfig !== application.config) $scope.changed = true;
+ });
+ };
+
+ // Save feature
+ $scope.saveAll = function() {
+ $scope._pageLock = true;
+
+ var _list = $.map($scope.sites, function(site) {
+ var _clone = $.extend({applications: site.applications.concat(site.optionalApplications)}, site);
+ return _clone;
+ });
+
+ Entities.updateEntity("SiteDescService", _list, {timestamp: false, hook: true})._promise.success(function() {
+ location.reload();
+ }).finally(function() {
+ $scope._pageLock = false;
+ });
+ };
+
+ // Watch config update
+ watchEdit($scope, "sites");
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/js/ctrl/main.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/js/ctrl/main.js b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/main.js
new file mode 100644
index 0000000..5064a1d
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/ctrl/main.js
@@ -0,0 +1,42 @@
+/*
+ * 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 eagleControllers = angular.module('eagleControllers', ['ui.bootstrap', 'eagle.components', 'eagle.service']);
+
+ // ===========================================================
+ // = Controller =
+ // ===========================================================
+ eagleControllers.controller('landingCtrl', function($scope, $wrapState, Site, Application, PageConfig, FeaturePageConfig, Feature) {
+ var _app = Application.current();
+
+ PageConfig.pageTitle = _app ? _app.displayName : 'OPS';
+ PageConfig.pageSubTitle = Site.current().tags.site;
+
+ $scope.Application = Application;
+
+ var _navItemList = FeaturePageConfig.pageList;
+ if(_navItemList.length) {
+ console.log("[Landing] Auto redirect.", FeaturePageConfig);
+ var _match = _navItemList[0].url.match(/#\/([^\/]+)\/([^\/]+)/);
+ Feature.go(_match[1], _match[2]);
+ }
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/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..187adb4
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/js/srv/applicationSrv.js
@@ -0,0 +1,170 @@
+/*
+ * 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');
+ var eagleApp = angular.module('eagleApp');
+
+ 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, reload) {
+ if(arguments.length && _current !== app) {
+ var _prev = _current;
+ _current = app;
+
+ if(sessionStorage && _current) {
+ sessionStorage.setItem("application", _current.tags.application);
+ }
+
+ if(_prev && reload !== false) {
+ 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?_=" + eagleApp._TRS();
+ 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
+ group: {
+ get: function () {
+ return this.groupName || "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
[05/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/css/main.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/css/main.css b/eagle-webservice/src/main/webapp/app/public/css/main.css
deleted file mode 100644
index a7eba4b..0000000
--- a/eagle-webservice/src/main/webapp/app/public/css/main.css
+++ /dev/null
@@ -1,805 +0,0 @@
-@CHARSET "UTF-8";
-/*
- * 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.
- */
-
-/* Frame */
-body.no-sidebar .content-wrapper {
- margin-left: 0;
-
- -webkit-transition: none;
- -moz-transition: none;
- -o-transition: none;
- transition: none;
-}
-
-body.no-sidebar .main-footer {
- margin-left: 0;
-}
-
-/* Navigation */
-.navbar-nav > .user-menu > .dropdown-menu > li.user-header .img-circle {
- display: inline-block;
- border: 3px solid;
- border-color: rgba(255,255,255,0.2);
- width: 90px;
- height: 90px;
- margin-top: 10px;
-}
-
-.navbar-nav > .user-menu > .dropdown-menu > li.user-header .fa {
- font-size: 60px;
- color: rgba(255,255,255,0.8);
- margin-top: 10px;
-}
-
- /* Common */
-a {
- cursor: pointer;
-}
-
-/* Table */
-.table.table-sm>tbody>tr>td,
-.table.table-sm>tbody>tr>th,
-.table.table-sm>tfoot>tr>td,
-.table.table-sm>tfoot>tr>th,
-.table.table-sm>thead>tr>td,
-.table.table-sm>thead>tr>th{
- padding: 3px 8px;
-}
-
-.table thead th .fa.fa-sort,
-.table thead th .fa.fa-sort-asc,
-.table thead th .fa.fa-sort-desc {
- margin-top: 5px;
- opacity: 0.3;
- float: right;
-}
-.table thead th:hover .fa.fa-sort,
-.table thead th:hover .fa.fa-sort-asc,
-.table thead th:hover .fa.fa-sort-desc {
- opacity: 0.8;
-}
-
-.table tr th,
-.table tr td {
- -webkit-transition: background .5s linear;
- -o-transition: background .5s linear;
- transition: background .5s linear;
-}
-
-.sortTable-cntr .pagination {
- margin-top: 0;
-}
-
-.table th.input-field,
-.table td.input-field {
- padding: 0;
- vertical-align: middle;
-}
-
-.table th.input-field > input,
-.table td.input-field > input,
-.table th.input-field > select,
-.table td.input-field > select {
- border: none;
- transition: border-color 0s;
-}
-
-.table th.input-field > input:focus,
-.table td.input-field > input:focus,
-.table th.input-field > select:focus,
-.table td.input-field > select:focus {
- box-shadow: inset 1px 1px 0px #3c8dbc, inset -1px -1px 0px #3c8dbc;
-}
-
-.table th.input-field > input.has-warning,
-.table td.input-field > input.has-warning {
- box-shadow: inset 1px 1px 0px #f39c12, inset -1px -1px 0px #f39c12;
-}
-
-/* Box */
-.small-box > a.inner {
- color: #FFF;
- display: block;
-}
-
-.small-box > a.inner h3 {
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- font-size: 32px;
-}
-
-.info-box.bg-gray,
-.info-box a {
- color: #FFFFFF;
-}
-.info-box a:hover {
- color: #FFFFFF;
- text-decoration: underline;
-}
-
-.info-box-content a.config {
- color: rgba(255,255,255,0.8);
-}
-.info-box-content a.config:hover {
- color: #FFFFFF;
-}
-
-.info-box-content.box-clickable {
- box-shadow: 0 0 3px;
-}
-.box-clickable {
- cursor: pointer;
-}
-
-.info-box-content .info-box-text.text-large {
- font-size: 26px;
- margin: 5px 0 10px 0;
-}
-
-/* inline group */
-.inline-group dl,
-.inline-group dl dt,
-.inline-group dl dd {
- display: inline-block;
-}
-
-.inline-group dl {
- margin-right: 35px;
-}
-.inline-group dl dt {
- margin-right: 20px;
-}
-
-.inline-group.form-inline {
- margin-top: 5px;
-}
-.inline-group dl {
- margin-right: 25px;
-}
-.inline-group dl dt {
- margin-right: 5px;
-}
-
-/* Search box */
-.search-box {
- position: relative;
- margin-bottom: 15px;
-}
-.search-box input[type="search"] {
- padding-left: 26px;
-}
-.search-box .fa.fa-search {
- position: absolute;
- top: 8px;
- left: 8px;
- z-index: 2;
- pointer-events: none;
- color: #999;
-}
-
-/* Navigation Tab */
-ul.nav.nav-tabs li .btn {
- margin-top: 1px;
-}
-
-.modal-body ul.nav.nav-tabs {
- border-bottom-color: #F4F4F4;
- margin-bottom: 15px;
-}
-
-.modal-body ul.nav.nav-tabs li {
- border-top: 3px solid #FFFFFF;
- margin-right: 3px;
-}
-.modal-body ul.nav.nav-tabs li.active {
- border-top-color: #3c8dbc;
-}
-
-.modal-body ul.nav.nav-tabs li > a,
-.modal-body ul.nav.nav-tabs li > a:active,
-.modal-body ul.nav.nav-tabs li > a:hover {
- border: none;
- border-radius: 0;
- margin: 0;
- padding: 6px 15px 8px 15px;
- color: #444;
-}
-.modal-body ul.nav.nav-tabs li:not(.active) > a:hover {
- background: rgba(0,0,0,0);
- color: #999;
-}
-.modal-body ul.nav.nav-tabs li.active > a {
- border-left: 1px solid #F4F4F4;
- border-right: 1px solid #F4F4F4;
-}
-
-/* Step Navigation */
-.step-cntr .step {
- background: #3c8dbc;
- margin: 0 0 20px 0;
- color: #FFF;
- height: 60px;
- border-radius: 3px;
- box-shadow: 0 1px 1px rgba(0,0,0,0.1);
- display: block;
-
- -webkit-transition: background .15s linear;
- -o-transition: background .15s linear;
- transition: background .15s linear;
-}
-.step-cntr .step.active {
- background: #f39c12;
-}
-
-.step-cntr .step h1,
-.step-cntr .step h2,
-.step-cntr .step p {
- margin: 0;
- padding: 0;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
-}
-
-.step-cntr .step h1 {
- display: inline-block;
- font-size: 30px;
- float: left;
- border-right: 2px solid rgba(255,255,255,0.2);
- width: 60px;
- height: 60px;
- text-align: center;
- padding-top: 12px;
- margin-right: 10px;
-}
-.step-cntr .step h2 {
- font-size: 18px;
- padding: 8px 0 5px 0;
-}
-
-/* Panel */
-.panel-group.panel-group-sm .panel .panel-heading {
- padding: 5px 6px 5px 10px;
-}
-.panel-group.panel-group-sm .panel .panel-heading h4 {
- font-size: 14px;
-}
-.panel-group.panel-group-sm .panel .panel-heading h4 a {
- display: block;
-}
-.panel-group.panel-group-sm .panel .panel-heading .pull-right {
- padding-left: 5px;
- padding-right: 5px;
- border-radius: 3px;
-}
-
-/* Drop Down */
-.dropdown-menu > li.danger > a {
- color: #dd4b39;
-}
-.dropdown-menu > li.danger > a:hover {
- color: #FFFFFF;
- background: #dd4b39;
-}
-
-/* Drop Down */
-.dropdown-menu.left {
- right: 0;
- left: auto;
-}
-
-.dropdown-submenu{position:relative;}
-.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
-.dropdown-submenu:hover>.dropdown-menu{display:block;}
-.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
-.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
-.dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
-
-/* Input Group */
-.input-group .input-group-btn select {
- width: auto;
-}
-
-/* Form group */
-.form-group .checkbox {
- display: inline;
- margin-right: 10px;
-}
-
-.form-group select.has-warning,
-.form-group input.has-warning {
- border-color: #f39c12;
- box-shadow: none;
-}
-
-.checkbox.noMargin {
- margin-top: 0;
- margin-bottom: 5px;
-}
-
-/* UL */
-ul.path {
- margin-left: 0;
-}
-
-ul.path li {
- padding: 0;
- margin-right: 5px;
-}
-ul.path li a {
- color: #FFFFFF;
-}
-
-ul.tree {
- padding: 0 0 0 5px;
-}
-
-ul.tree > li,
-ul.tree > li > ul > li {
- list-style-type: none;
-}
-
-ul.tree .tree-item .hover {
- display: none;
-}
-ul.tree .tree-item:hover .hover {
- display: inline-block;
-}
-
-ul.tree > li > ul {
- padding: 0 0 0 25px;
-}
-
-ul.tree > li > ul > li.active {
- background: #F4F4F4;
-}
-
-ul.tree.tree-bordered {
- border: 1px solid #f4f4f4;
-}
-ul.tree.tree-bordered,
-ul.tree.tree-bordered > li > ul {
- padding: 0;
-}
-ul.tree.tree-bordered > li:not(:last-child) {
- border-bottom: 1px solid #f4f4f4;
-}
-ul.tree.tree-bordered > li > ul > li {
- border-top: 1px solid #f4f4f4;
-}
-ul.tree.tree-bordered > li > span,
-ul.tree.tree-bordered > li > a {
- display: block;
- padding: 8px;
-}
-ul.tree.tree-bordered > li > ul > li > span,
-ul.tree.tree-bordered > li > ul > li > a {
- display: block;
- padding: 8px 8px 8px 30px;
-}
-
-.product-list-in-box > .item {
- -webkit-transition: background .5s linear;
- -o-transition: background .5s linear;
- transition: background .5s linear;
-}
-.product-list-in-box > .item.ng-animate {
- transition: 0s;
-}
-.product-list-in-box > .item.active {
- background: #F5FAFC;
- -webkit-transition: none;
- -o-transition: none;
- transition: none;
-}
-
-.nav.fixed-height,
-.products-list.fixed-height {
- height: 402px;
- overflow-y: auto;
-}
-
-.products-list .product-operation {
- float: left;
- border: 1px solid #9EC8E0;
- border-radius: 5px;
- overflow: hidden;
-}
-
-.products-list .product-operation .fa {
- display: block;
- padding: 4px 16px;
- color: #3c8dbc;
-}
-.products-list .product-operation a.fa:hover {
- color: #FFFFFF;
- background: #337ab7;
-}
-
-.products-list .product-operation.single .fa {
- padding: 12px 12px;
- font-size: 20px;
-}
-
-.products-list .item .product-info a.fa.fa-times {
- display: none;
-}
-.products-list .item:hover .product-info a.fa.fa-times {
- display: block;
-}
-
-/* Label */
-.label.label-default {
- color: #FFFFFF;
-}
-
-.label.label-sm {
- padding: .0em .4em .1em;
-}
-
-/* Row */
-.row.narrow {
- margin-left: -5px;
- margin-right: -5px;
- margin-bottom: -10px;
-}
-
-.row.narrow>.col-xs-1, .row.narrow>.col-sm-1, .row.narrow>.col-md-1, .row.narrow>.col-lg-1, .row.narrow>.col-xs-2, .row.narrow>.col-sm-2, .row.narrow>.col-md-2, .row.narrow>.col-lg-2, .row.narrow>.col-xs-3, .row.narrow>.col-sm-3, .row.narrow>.col-md-3, .row.narrow>.col-lg-3, .row.narrow>.col-xs-4, .row.narrow>.col-sm-4, .row.narrow>.col-md-4, .row.narrow>.col-lg-4, .row.narrow>.col-xs-5, .row.narrow>.col-sm-5, .row.narrow>.col-md-5, .row.narrow>.col-lg-5, .row.narrow>.col-xs-6, .row.narrow>.col-sm-6, .row.narrow>.col-md-6, .row.narrow>.col-lg-6, .row.narrow>.col-xs-7, .row.narrow>.col-sm-7, .row.narrow>.col-md-7, .row.narrow>.col-lg-7, .row.narrow>.col-xs-8, .row.narrow>.col-sm-8, .row.narrow>.col-md-8, .row.narrow>.col-lg-8, .row.narrow>.col-xs-9, .row.narrow>.col-sm-9, .row.narrow>.col-md-9, .row.narrow>.col-lg-9, .row.narrow>.col-xs-10, .row.narrow>.col-sm-10, .row.narrow>.col-md-10, .row.narrow>.col-lg-10, .row.narrow>.col-xs-11, .row.narrow>.col-sm-11, .row.narrow>.col-md-11, .
row.narrow>.col-lg-11, .row.narrow>.col-xs-12, .row.narrow>.col-sm-12, .row.narrow>.col-md-12, .row.narrow>.col-lg-12 {
- padding-left: 5px;
- padding-right: 5px;
-}
-
-.row.narrow > [class^="col-"],
-.row.narrow > [class*=" col-"] {
- margin-bottom: 10px;
-}
-
-/* Chart */
-.sortable-mock-element .nvd3-chart-wrapper {
- background: #FFFFFF;
- opacity: 0.8;
-}
-
-.sortable-enter .nvd3-chart-wrapper {
- border-color: #3c8dbc;
- pointer-events: none;
-}
-.sortable-enter .nvd3-chart-wrapper .nvtooltip {
- display: none;
-}
-
-.nvd3-chart-wrapper {
- position: relative;
- border: 1px solid rgba(0,0,0,0.1);
-}
-.nvd3-chart-wrapper:hover {
- //border-color: #F4F4F4;
-}
-
-.nvd3-chart-wrapper .nvd3-chart-config {
- position: absolute;
- top: 1px;
- right: 1px;
- display: none;
- border-radius: 0;
- padding: 0 5px;
- background: rgba(0,0,0,0.7);
-}
-.nvd3-chart-wrapper:hover .nvd3-chart-config {
- display: block;
-}
-
-.nvd3-chart-wrapper .nvd3-chart-config a {
- color: rgba(255,255,255, 0.9);
- padding: 5px 2px 4px 2px;
- font-size: 16px;
-}
-.nvd3-chart-wrapper .nvd3-chart-config a:hover {
- color: #FFFFFF;
-}
-
-.nvd3-chart-cntr {
- padding: 5px;
-}
-
-.nvd3-chart-cntr > h3 {
- text-align: center;
- font-size: 16px;
- font-weight: bolder;
- margin: 0;
- padding: 5px 0;
-
- overflow:hidden;
- text-overflow:ellipsis;
-
-}
-
-.nvd3-chart-cntr > svg.nvd3-svg {
- height: 200px;
-}
-
-.nvd3-chart-cntr.lg > svg.nvd3-svg {
- height: 400px;
-}
-
-/* Tab */
-body .tab-content>.tab-pane {
- display: block;
- height: 0px;
- overflow: hidden;
- position: relative;
-}
-body .tab-content>.tab-pane.active {
- height: auto;
- overflow-x: visible;
- overflow-y: visible;
-}
-
-body .modal-body .nav-pills > li > a,
-body .box-body .nav-pills > li > a {
- padding: 5px 15px;
- border: none;
-}
-
-body .modal-body .nav-stacked > li {
- border-bottom: 1px solid #f4f4f4;
- margin: 0;
-}
-body .modal-body .nav-stacked > li:last-child {
- border-bottom: none;
-}
-
-body .box-body .nav-tabs-custom {
- box-shadow: none;
- margin-bottom: 0;
-}
-body .box-body .nav-tabs-custom > .nav-tabs > li:first-of-type.active > a {
- border-left-color: #f4f4f4;
-}
-body .box-body .nav-tabs-custom > .nav-tabs > li > a {
- padding: 8px 15px;
-}
-body .box-body .nav-tabs-custom > .tab-content {
- padding: 10px 0;
-}
-
-/* Box */
-.box .guideline {
- margin-top: 0;
-}
-
-.box.inner-box {
- border: none;
- box-shadow: none;
- padding: 5px 10px;
- margin: 0;
- border-bottom: 1px solid #f4f4f4;
- position: relative;
- border-radius: 0;
-}
-
-.box.inner-box .box-title {
- margin: 0 5px 5px 0;
- padding: 0;
- font-size: 16px;
- font-weight: bolder;
- display: inline-block;
- word-break: break-all;
-}
-
-.box.inner-box .box-tools {
- position: absolute;
- top: 0;
- right: 0;
-}
-
-.box.inner-box:last-child {
- border-bottom: none;
-}
-
-/* Navigation Tab */
-.nav-tabs-custom {
- position: relative;
-}
-
-.nav-tabs-custom .box-tools {
- position: absolute;
- right: 15px;
- top: 8px;
-}
-
-.nav-tabs-custom .box-tools .strong {
- font-weight: bolder;
-}
-
-/* Customize */
-#content {
- position: relative;
-}
-
-.page-fixed {
- position: absolute;
- top: -45px;
- right: 0;
-}
-
-@media (max-width:991px) {
- .page-fixed {
- top: -70px;
- }
-}
-
-.fixed-right {
- position: absolute;
- right: 0;
- z-index: 3;
-}
-
-.main-header .logo img {
- height: 34px;
-}
-
-.main-header .navbar-toggle {
- float: none;
- border-radius: 0;
-}
-.main-header .navbar-toggle:hover {
- background: rgba(0, 0, 0, 0.1);
-}
-
-#moduleMenu > ul > li.active > a {
- border-top: 3px solid rgba(255,255,255,0.8);
- padding-top: 12px;
-}
-
-@media (max-width: 767px) {
- #moduleMenu > ul > li.active > a {
- padding: 10px 15px;
- border-top: none;
- border-left: 3px solid rgba(255,255,255,0.8);
- }
-
- .main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a {
- color: #333;
- }
- .main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a:hover {
- color: #FFF;
- }
-}
-
-#timeRangePickerCntr .navbar-form {
- display: inline-block;
- padding-right: 0;
-}
-
-#timeRangePickerCntr #timeRangePicker {
- min-width: 300px;
-}
-
-body .login-box, body .register-box {
- margin: 3% auto;
-}
-
-.content-header > .breadcrumb > li {
- font-size: 14px;
-}
-
-.daterangepicker .ranges {
- width: 110px!important;
-}
-.daterangepicker .daterangepicker_start_input,
-.daterangepicker .daterangepicker_end_input {
- display: block!important;
- padding: 0!important;
- float: none!important;
-}
-.daterangepicker .daterangepicker_start_input .input-mini,
-.daterangepicker .daterangepicker_end_input .input-mini {
- width: 110px!important;
-}
-
-.form-group.inner-icon {
- position: relative;
-}
-.form-group.inner-icon .fa {
- position: absolute;
- left: 10px;
- top: 10px;
-}
-.form-group.inner-icon input {
- padding-left: 35px;
-}
-
-#autoRefreshCntr > a {
- border: none;
- opacity: 0.3;
-}
-#autoRefreshCntr.autoRefresh > a {
- opacity: 1;
-}
-
-.table-responsive .row {
- margin: 0;
-}
-
-
-/* Misc */
-body .tooltip-inner {
- max-width: 500px;
-}
-
-.text-nowrap {
- white-space: nowrap;
-}
-
-.text-ellipsis,
-.label.text-ellipsis {
- overflow:hidden;
- text-overflow:ellipsis;
- display: inline-block;
- white-space: nowrap;
- max-width: 100%;
-}
-td.text-ellipsis {
- display: table-cell;
-}
-
-.text-breakall {
- max-width: 100%;
- display: inline-block;
- word-wrap: break-word;
-}
-
-.btn.btn-xs.sm {
- font-size: 12px;
- padding: 2px 6px;
-}
-
-.form-control.input-xs {
- height: 24px;
- padding: 2px 8px;
- font-size: 12px;
- line-height: 100%;
-}
-
-pre.noWrap {
- border: none;
- border-radius: 0;
- background: transparent;
- margin: 0;
- padding: 0;
-}
-
-.noSelect {
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
-}
-
-.blink {
- animation: blinker 1s linear infinite;
-}
-
-@keyframes blinker {
- 50% {opacity: 0.0;}
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/classification/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/classification/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/classification/controller.js
deleted file mode 100644
index 462b41b..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/classification/controller.js
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module('featureControllers');
- var feature = featureControllers.register("classification");
- var eagleApp = angular.module('eagleApp');
-
- // ==============================================================
- // = Function =
- // ==============================================================
-
- // =============================================================
- // = Sensitivity =
- // =============================================================
- feature.navItem("sensitivity", "Classification", "user-secret");
- feature.controller('sensitivity', function(PageConfig, Site, $scope, Application, Entities, UI) {
- PageConfig.pageTitle = "Data Classification";
- PageConfig.pageSubTitle = Site.current().tags.site;
- $scope.ajaxId = eagleApp._TRS();
- $scope.viewConfig = Application.current().configObj.view;
-
- if(!$scope.viewConfig) {
- $.dialog({
- title: "OPS",
- content: "View configuration not defined in Application."
- });
- return;
- }
-
- // ===================== Function =====================
- $scope.export = function() {
- var _data = {};
- UI.fieldConfirm({title: "Export Classification", confirm: false, size: "large"}, _data, [
- {name: "Data", field: "data", type: "blob", rows: 20, optional: true, readonly: true}]
- );
-
- Entities.queryEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function(data) {
- _data.data = JSON.stringify(data, null, "\t");
- });
- };
-
- $scope.import = function() {
- UI.fieldConfirm({title: "Import Classification", size: "large"}, {}, [
- {name: "Data", field: "data", type: "blob", rows: 20, optional: true}
- ], function(entity) {
- var _list = common.parseJSON(entity.data, false);
- if(!_list) {
- return "Invalid JSON format";
- }
- if(!$.isArray(_list)) {
- return "Not an array";
- }
- }).then(null, null, function(holder) {
- Entities.updateEntity($scope.viewConfig.service, common.parseJSON(holder.entity.data, []), {timestamp: false})._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
-
- $scope.deleteAll = function() {
- UI.deleteConfirm("All the Classification Data").then(null, null, function(holder) {
- Entities.deleteEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function() {
- holder.closeFunc();
- location.reload();
- });
- });
- };
- });
- // =============================================================
- // = Sensitivity - Job =
- // =============================================================
- feature.controller('sensitivityViewJob', function(Site, $scope, $wrapState, Entities) {
- $scope.items = [];
-
- // Mark sensitivity
- $scope._oriItem = {};
- $scope._markItem = {};
-
- // ======================= View =======================
- // Item
- $scope.updateItems = function() {
- $scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site});
- };
-
-
- $scope.updateItems();
-
- // =================== Sensitivity ===================
- $scope.markSensitivity = function(item) {
- $scope._oriItem = item;
- $scope._markItem = {
- prefix: $scope.viewConfig.prefix,
- tags: {
- site: Site.current().tags.site
- },
- sensitivityType: ""
- };
-
- $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.jobId;
- $("#sensitivityMDL").modal();
- };
- $scope.confirmUpateSensitivity = function() {
- $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
- Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
- Entities.dialog(data);
- });
- $("#sensitivityMDL").modal('hide');
- };
- $scope.unmarkSensitivity = function(item) {
- $.dialog({
- title: "Unmark Confirm",
- content: "Do you want to remove the sensitivity mark on '" + item.jobId + "'?",
- confirm: true
- }, function(ret) {
- if(!ret) return;
-
- var _cond = {site: Site.current().tags.site};
- _cond[$scope.viewConfig.keys[0]] = item.jobId;
- Entities.deleteEntities($scope.viewConfig.service, _cond);
-
- item.sensitiveType = null;
- $scope.$apply();
- });
- };
- });
- // =============================================================
- // = Sensitivity - Folder =
- // =============================================================
- feature.controller('sensitivityViewFolder', function(Site, $scope, $wrapState, Entities) {
- $scope.path = $wrapState.param.path || "/";
- $scope.pathUnitList = [];
- $scope.items = [];
-
- // Mark sensitivity
- $scope._oriItem = {};
- $scope._markItem = {};
-
- // ======================= View =======================
- // Path
- function _refreshPathUnitList(_path) {
- var _start,_current, _unitList = [];
- _path = _path + (_path.match(/\/$/) ? "" : "/");
- for(_current = _start = 0 ; _current < _path.length ; _current += 1) {
- if(_path[_current] === "/") {
- _unitList.push({
- name: _path.substring(_start, _current + (_current === 0 ? 1 : 0)),
- path: _path.substring(0, _current === 0 ? 1 : _current)
- });
- _start = _current + 1;
- }
- }
- $scope.pathUnitList = _unitList;
- }
-
- // Item
- $scope.updateItems = function(path) {
- if(path) $scope.path = path;
-
- $scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site, path: $scope.path});
- $scope.items._promise.success(function(data) {
- Entities.dialog(data, function() {
- if($scope.path !== "/") $scope.updateItems("/");
- });
- });
- _refreshPathUnitList($scope.path);
- };
-
- $scope.getFileName = function(item) {
- return (item.resource + "").replace(/^.*\//, "");
- };
-
- $scope.updateItems($scope.path);
-
- // =================== Sensitivity ===================
- $scope.markSensitivity = function(item) {
- $scope._oriItem = item;
- $scope._markItem = {
- prefix: $scope.viewConfig.prefix,
- tags: {
- site: Site.current().tags.site
- },
- sensitivityType: ""
- };
- $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
- $("#sensitivityMDL").modal();
- };
- $scope.confirmUpateSensitivity = function() {
- $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
- Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
- Entities.dialog(data);
- });
- $("#sensitivityMDL").modal('hide');
- };
- $scope.unmarkSensitivity = function(item) {
- $.dialog({
- title: "Unmark Confirm",
- content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
- confirm: true
- }, function(ret) {
- if(!ret) return;
-
- var _cond = {site: Site.current().tags.site};
- _cond[$scope.viewConfig.keys[0]] = item.resource;
- Entities.deleteEntities($scope.viewConfig.service, _cond);
-
- item.sensitiveType = null;
- $scope.$apply();
- });
- };
- });
-
- // =============================================================
- // = Sensitivity - Table =
- // =============================================================
- feature.controller('sensitivityViewTable', function(Site, $scope, Entities) {
- $scope.databases = null;
- $scope.table = null;
-
- // Mark sensitivity
- $scope._oriItem = {};
- $scope._markItem = {};
-
- // ======================= View =======================
- var _fillAttr = function(list, key, target) {
- list._promise.then(function() {
- $.each(list, function(i, unit) {
- unit[key] = unit[target];
- });
- });
- return list._promise;
- };
-
- $scope.loadDatabases = function(database) {
- var _dbs = Entities.query($scope.viewConfig.api.database, {site: Site.current().tags.site});
- return _fillAttr(_dbs, "database", $scope.viewConfig.mapping.database).then(function() {
- if($scope.databases) {
- $.each($scope.databases, function(i, oriDB) {
- var db = common.array.find(oriDB.resource, _dbs, "resource");
- if(db) {
- db.show = oriDB.show;
- db.tables = oriDB.tables;
- }
- });
- }
- $scope.databases = _dbs;
- });
- };
- $scope.loadDatabases();
-
- $scope.loadTables = function(database, force) {
- var _tables, _qry;
- if(database.tables && !force) return;
- _qry = {
- site: Site.current().tags.site
- };
- _qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
- _tables = Entities.query($scope.viewConfig.api.table, _qry);
- if(!database.tables) database.tables = _tables;
- _fillAttr(_tables, "table", $scope.viewConfig.mapping.table);
- return _fillAttr(_tables, "database", $scope.viewConfig.mapping.database).then(function() {
- database.tables = _tables;
- });
- };
-
- $scope.loadColumns = function(database, table) {
- $scope.table = table;
-
- if(table.columns) return;
- var _qry = {
- site: Site.current().tags.site
- };
- _qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
- _qry[$scope.viewConfig.mapping.table] = table[$scope.viewConfig.mapping.table];
- table.columns = Entities.query($scope.viewConfig.api.column, _qry);
- _fillAttr(table.columns, "column", $scope.viewConfig.mapping.column);
- };
-
- $scope.refreshData = function() {
- $scope.loadDatabases().then(function() {
- if(!$scope.table) return;
-
- var _table = $scope.table;
- var _db = common.array.find($scope.table.database, $scope.databases, "database");
- if(_db) {
- $scope.loadTables(_db, true).then(function() {
- $scope.table = common.array.find(_table.table, _db.tables, "table");
- $scope.table.columns = _table.columns;
- });
- }
- });
- };
-
- // =================== Sensitivity ===================
- $scope.markSensitivity = function(item, event) {
- if(event) event.stopPropagation();
-
- $scope._oriItem = item;
- $scope._markItem = {
- prefix: $scope.viewConfig.prefix,
- tags: {
- site: Site.current().tags.site
- },
- sensitivityType: ""
- };
- $scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
- $("#sensitivityMDL").modal();
- };
- $scope.confirmUpateSensitivity = function() {
- $scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
- Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
- Entities.dialog(data);
- $scope.refreshData();
- });
- $("#sensitivityMDL").modal('hide');
- };
- $scope.unmarkSensitivity = function(item, event) {
- if(event) event.stopPropagation();
-
- $.dialog({
- title: "Unmark Confirm",
- content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
- confirm: true
- }, function(ret) {
- if(!ret) return;
-
- var _qry = {
- site: Site.current().tags.site
- };
- _qry[$scope.viewConfig.keys[0]] = item.resource;
- Entities.deleteEntities($scope.viewConfig.service, _qry)._promise.then(function() {
- $scope.refreshData();
- });
-
- item.sensitiveType = null;
- $scope.$apply();
- });
- };
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity.html b/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity.html
deleted file mode 100644
index 41fb291..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-folder-open"></i>
- <h3 class="box-title ng-binding">{{Application.current().displayName}}</h3>
- <div class="box-tools pull-right" ng-if="viewConfig">
- <div class="btn-group">
- <button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
- <span class="fa fa-wrench"></span>
- </button>
- <ul class="dropdown-menu" role="menu">
- <li><a ng-click="import()"><span class="fa fa-cloud-upload"></span> Import</a></li>
- <li><a ng-click="export()"><span class="fa fa-cloud-download"></span> Export</a></li>
- <li class="divider"></li>
- <li class="danger"><a ng-click="deleteAll()"><span class="fa fa-trash"></span> Delete All</a></li>
- </ul>
- </div>
- </div>
- </div>
- <div class="box-body">
- <ng-include ng-if="viewConfig" src="'public/feature/classification/page/sensitivity/' + viewConfig.type + '.html?_=' + ajaxId"></ng-include>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/folder.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/folder.html b/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/folder.html
deleted file mode 100644
index cfefffa..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/folder.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div ng-controller="classification_sensitivityViewFolder">
- <ul class="list-inline path">
- <li>Path:</li>
- <li ng-repeat="unit in pathUnitList">
- <a ng-click="updateItems(unit.path)" class="label bg-black">{{unit.name}}</a>
- </li>
- </ul>
-
- <table class="table table-bordered">
- <thead>
- <tr>
- <th width="15%">File Name</th>
- <th width="10%">Owner</th>
- <th width="10%">Group</th>
- <th>Sensitivity Type</th>
- <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-show="items._promise.$$state.status !== 1">
- <td colspan="5">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </td>
- </tr>
- <tr ng-show="items._promise.$$state.status === 1 && !items.length">
- <td colspan="5">
- <span class="fa fa-exclamation-triangle"> </span>
- Empty Folder
- </td>
- </tr>
- <tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
- <td>
- <span ng-show="!item.isdir">
- <span class="fa fa-file"> </span>
- {{getFileName(item)}}
- </span>
- <a ng-show="item.isdir" ng-click="updateItems(item.resource)">
- <span class="fa fa-folder"> </span>
- {{getFileName(item)}}
- </a>
-
- <span class="pull-right" ng-show="item.childSensitiveTypes.length">
- <span class="fa fa-dot-circle-o text-muted" uib-tooltip="Contain child sensitivity defination"> </span>
- </span>
- </td>
- <td>{{item.owner}}</td>
- <td>{{item.groupName}}</td>
- <td>{{item.sensitiveType}}</td>
- <td ng-show="Auth.isRole('ROLE_ADMIN')">
- <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
- uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
- <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
- uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
- </td>
- </tr>
- </tbody>
- </table>
-
-
- <!-- Modal: Create / Edit site -->
- <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog" 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">×</span>
- </button>
- <h4 class="modal-title">Mark Sensitivity Data</h4>
- </div>
- <div class="modal-body">
- <div class="form-group">
- <label>Resource</label>
- <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.filedir" />
- </div>
- <div class="form-group">
- <label>* Sensitivity Type</label>
- <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
- Update
- </button>
- </div>
- </div>
- </div>
- </div>
-
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/job.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/job.html b/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/job.html
deleted file mode 100644
index 05d70da..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/job.html
+++ /dev/null
@@ -1,92 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div ng-controller="classification_sensitivityViewJob">
- <ul class="list-inline path">
- <li>Oozie CoordinatorJob:</li>
- </ul>
-
- <table class="table table-bordered">
- <thead>
- <tr>
- <th width="15%">JobId</th>
- <th width="10%">AppName</th>
- <th>Sensitivity Type</th>
- <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-show="items._promise.$$state.status !== 1">
- <td colspan="5">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </td>
- </tr>
- <tr ng-show="items._promise.$$state.status === 1 && !items.length">
- <td colspan="5">
- <span class="fa fa-exclamation-triangle"> </span>
- Empty
- </td>
- </tr>
- <tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
- <td>{{item.jobId}}</td>
- <td>{{item.name}}</td>
- <td>{{item.sensitiveType}}</td>
- <td ng-show="Auth.isRole('ROLE_ADMIN')">
- <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
- uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
- <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
- uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
- </td>
- </tr>
- </tbody>
- </table>
-
-
- <!-- Modal: Create / Edit site -->
- <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog" 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">×</span>
- </button>
- <h4 class="modal-title">Mark Sensitivity Data</h4>
- </div>
- <div class="modal-body">
- <div class="form-group">
- <label>Resource</label>
- <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.oozieResource" />
- </div>
- <div class="form-group">
- <label>* Sensitivity Type</label>
- <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
- Update
- </button>
- </div>
- </div>
- </div>
- </div>
-
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/table.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/table.html b/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/table.html
deleted file mode 100644
index 13d5807..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/classification/page/sensitivity/table.html
+++ /dev/null
@@ -1,150 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div ng-controller="classification_sensitivityViewTable">
- <p ng-show="databases._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div ng-show="databases._promise.$$state.status === 1" class="row">
- <div class="col-md-4">
- <label>
- Databases
- ({{databases.length}})
- </label>
- <ul class="tree tree-bordered" style="max-height: 500px; overflow-y: auto;">
- <li ng-repeat="db in databases">
- <span class="tree-item box-clickable text-primary" ng-click="db.show = !db.show; loadTables(db);">
- <span ng-class="{'text-warning' : db.sensitiveType}">
- <span class="fa fa-database"> </span>
- {{db.database}}
- <span ng-show="db.tables._promise.$$state.status === 1">({{db.tables.length}})</span>
-
- <span ng-show="Auth.isRole('ROLE_ADMIN')">
- <a class="fa fa-eye text-muted hover" ng-click="markSensitivity(db, $event)" ng-show="!db.sensitiveType"
- uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="right"></a>
- <a class="fa fa-eye-slash text-muted hover" ng-click="unmarkSensitivity(db, $event)" ng-show="db.sensitiveType"
- uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="right"></a>
- </span>
-
- <span class="pull-right" ng-show="db.childSensitiveTypes.length">
- <span class="fa fa-dot-circle-o" uib-tooltip="Contain child sensitivity defination" tooltip-placement="right" tooltip-append-to-body="true"> </span>
- </span>
- <span ng-show="db.sensitiveType" class="pull-right">[{{db.sensitiveType}}]</span>
- </span>
- </span>
- <ul ng-show="db.show">
- <li ng-show="db.tables._promise.$$state.status !== 1">
- <span>
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </span>
- </li>
- <li ng-repeat="tb in db.tables" ng-class="{active : tb === table}">
- <span class="tree-item box-clickable text-primary" ng-click="loadColumns(db, tb)">
- <span ng-class="{'text-warning' : tb.sensitiveType}">
- <span class="fa fa-table"> </span>
- {{tb.table}}
-
- <span ng-show="Auth.isRole('ROLE_ADMIN')">
- <a class="fa fa-eye text-muted hover" ng-click="markSensitivity(tb, $event)" ng-show="!tb.sensitiveType"
- uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="right"></a>
- <a class="fa fa-eye-slash text-muted hover" ng-click="unmarkSensitivity(tb, $event)" ng-show="tb.sensitiveType"
- uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="right"></a>
- </span>
-
- <span class="pull-right" ng-show="tb.childSensitiveTypes.length">
- <span class="fa fa-dot-circle-o" uib-tooltip="Contain child sensitivity defination" tooltip-placement="right" tooltip-append-to-body="true"> </span>
- </span>
- <span ng-show="tb.sensitiveType" class="pull-right">[{{tb.sensitiveType}}]</span>
- </span>
- </span>
- </li>
- </ul>
- </li>
- </ul>
- </div>
- <div class="col-md-8">
- <label ng-show="table">Route: {{table.database}} > {{table.table}}</label>
- <p ng-show="table && table.columns._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
- <div ng-show="table && table.columns._promise.$$state.status === 1">
- <table class="table table-bordered">
- <thead>
- <tr>
- <th width="40%">Column Name</th>
- <th>Sensitivity Type</th>
- <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="col in table.columns" ng-class="{warning : col.sensitiveType}">
- <td>{{col.column}}</td>
- <td>{{col.sensitiveType}}</td>
- <td ng-show="Auth.isRole('ROLE_ADMIN')">
- <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(col)" ng-show="!col.sensitiveType"
- uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
- <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(col)" ng-show="col.sensitiveType"
- uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
-
-
-
-
-
-
- <!-- Modal: Create / Edit site -->
- <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog" 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">×</span>
- </button>
- <h4 class="modal-title">Mark Sensitivity Data</h4>
- </div>
- <div class="modal-body">
- <div class="form-group">
- <label>Resource</label>
- <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags[viewConfig.keys[0]]" />
- </div>
- <div class="form-group">
- <label>* Sensitivity Type</label>
- <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
- Update
- </button>
- </div>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
[09/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/table.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/table.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/table.html
new file mode 100644
index 0000000..13d5807
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/table.html
@@ -0,0 +1,150 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div ng-controller="classification_sensitivityViewTable">
+ <p ng-show="databases._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+
+ <div ng-show="databases._promise.$$state.status === 1" class="row">
+ <div class="col-md-4">
+ <label>
+ Databases
+ ({{databases.length}})
+ </label>
+ <ul class="tree tree-bordered" style="max-height: 500px; overflow-y: auto;">
+ <li ng-repeat="db in databases">
+ <span class="tree-item box-clickable text-primary" ng-click="db.show = !db.show; loadTables(db);">
+ <span ng-class="{'text-warning' : db.sensitiveType}">
+ <span class="fa fa-database"> </span>
+ {{db.database}}
+ <span ng-show="db.tables._promise.$$state.status === 1">({{db.tables.length}})</span>
+
+ <span ng-show="Auth.isRole('ROLE_ADMIN')">
+ <a class="fa fa-eye text-muted hover" ng-click="markSensitivity(db, $event)" ng-show="!db.sensitiveType"
+ uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="right"></a>
+ <a class="fa fa-eye-slash text-muted hover" ng-click="unmarkSensitivity(db, $event)" ng-show="db.sensitiveType"
+ uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="right"></a>
+ </span>
+
+ <span class="pull-right" ng-show="db.childSensitiveTypes.length">
+ <span class="fa fa-dot-circle-o" uib-tooltip="Contain child sensitivity defination" tooltip-placement="right" tooltip-append-to-body="true"> </span>
+ </span>
+ <span ng-show="db.sensitiveType" class="pull-right">[{{db.sensitiveType}}]</span>
+ </span>
+ </span>
+ <ul ng-show="db.show">
+ <li ng-show="db.tables._promise.$$state.status !== 1">
+ <span>
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </span>
+ </li>
+ <li ng-repeat="tb in db.tables" ng-class="{active : tb === table}">
+ <span class="tree-item box-clickable text-primary" ng-click="loadColumns(db, tb)">
+ <span ng-class="{'text-warning' : tb.sensitiveType}">
+ <span class="fa fa-table"> </span>
+ {{tb.table}}
+
+ <span ng-show="Auth.isRole('ROLE_ADMIN')">
+ <a class="fa fa-eye text-muted hover" ng-click="markSensitivity(tb, $event)" ng-show="!tb.sensitiveType"
+ uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="right"></a>
+ <a class="fa fa-eye-slash text-muted hover" ng-click="unmarkSensitivity(tb, $event)" ng-show="tb.sensitiveType"
+ uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="right"></a>
+ </span>
+
+ <span class="pull-right" ng-show="tb.childSensitiveTypes.length">
+ <span class="fa fa-dot-circle-o" uib-tooltip="Contain child sensitivity defination" tooltip-placement="right" tooltip-append-to-body="true"> </span>
+ </span>
+ <span ng-show="tb.sensitiveType" class="pull-right">[{{tb.sensitiveType}}]</span>
+ </span>
+ </span>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <div class="col-md-8">
+ <label ng-show="table">Route: {{table.database}} > {{table.table}}</label>
+ <p ng-show="table && table.columns._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+ <div ng-show="table && table.columns._promise.$$state.status === 1">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th width="40%">Column Name</th>
+ <th>Sensitivity Type</th>
+ <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="col in table.columns" ng-class="{warning : col.sensitiveType}">
+ <td>{{col.column}}</td>
+ <td>{{col.sensitiveType}}</td>
+ <td ng-show="Auth.isRole('ROLE_ADMIN')">
+ <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(col)" ng-show="!col.sensitiveType"
+ uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
+ <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(col)" ng-show="col.sensitiveType"
+ uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+
+
+
+
+
+ <!-- Modal: Create / Edit site -->
+ <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
+ <div class="modal-dialog" 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">×</span>
+ </button>
+ <h4 class="modal-title">Mark Sensitivity Data</h4>
+ </div>
+ <div class="modal-body">
+ <div class="form-group">
+ <label>Resource</label>
+ <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags[viewConfig.keys[0]]" />
+ </div>
+ <div class="form-group">
+ <label>* Sensitivity Type</label>
+ <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">
+ Close
+ </button>
+ <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
+ Update
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/common/controller.js
new file mode 100644
index 0000000..207c8df
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/controller.js
@@ -0,0 +1,1224 @@
+/*
+ * 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 featureControllers = angular.module("featureControllers");
+ var feature = featureControllers.register("common");
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+ feature.service("Policy", function(Entities) {
+ var Policy = function () {};
+
+ Policy.updatePolicyStatus = function(policy, status) {
+ $.dialog({
+ title: "Confirm",
+ content: "Do you want to " + (status ? "enable" : "disable") + " policy[" + policy.tags.policyId + "]?",
+ confirm: true
+ }, function(ret) {
+ if(ret) {
+ policy.enabled = status;
+ Entities.updateEntity("AlertDefinitionService", policy);
+ }
+ });
+ };
+ Policy.deletePolicy = function(policy, callback) {
+ $.dialog({
+ title: "Confirm",
+ content: "Do you want to delete policy[" + policy.tags.policyId + "]?",
+ confirm: true
+ }, function(ret) {
+ if(ret) {
+ policy.enabled = status;
+ Entities.deleteEntity("AlertDefinitionService", policy)._promise.finally(function() {
+ if(callback) {
+ callback(policy);
+ }
+ });
+ }
+ });
+ };
+ return Policy;
+ });
+
+ feature.service("Notification", function(Entities) {
+ var Notification = function () {};
+ Notification.map = {};
+
+ Notification.list = Entities.queryEntities("AlertNotificationService");
+ Notification.list._promise.then(function () {
+ $.each(Notification.list, function (i, notification) {
+ // Parse fields
+ notification.fieldList = common.parseJSON(notification.fields, []);
+
+ // Fill map
+ Notification.map[notification.tags.notificationType] = notification;
+ });
+ });
+
+ Notification.promise = Notification.list._promise;
+
+ return Notification;
+ });
+
+ // ==============================================================
+ // = Policies =
+ // ==============================================================
+
+ // ========================= Policy List ========================
+ feature.navItem("policyList", "Policies", "list");
+ feature.controller('policyList', function(PageConfig, Site, $scope, Application, Entities, Policy) {
+ PageConfig.pageTitle = "Policy List";
+ PageConfig.pageSubTitle = Site.current().tags.site;
+
+ // Initial load
+ $scope.policyList = [];
+ $scope.application = Application.current();
+
+ // List policies
+ var _policyList = Entities.queryEntities("AlertDefinitionService", {site: Site.current().tags.site, application: $scope.application.tags.application});
+ _policyList._promise.then(function() {
+ $.each(_policyList, function(i, policy) {
+ policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
+
+ $scope.policyList.push(policy);
+ });
+ });
+ $scope.policyList._promise = _policyList._promise;
+
+ // Function
+ $scope.searchFunc = function(item) {
+ var key = $scope.search;
+ if(!key) return true;
+
+ var _key = key.toLowerCase();
+ function _hasKey(item, path) {
+ var _value = common.getValueByPath(item, path, "").toLowerCase();
+ return _value.indexOf(_key) !== -1;
+ }
+ return _hasKey(item, "tags.policyId") || _hasKey(item, "__expression") || _hasKey(item, "description") || _hasKey(item, "owner");
+ };
+
+ $scope.updatePolicyStatus = Policy.updatePolicyStatus;
+ $scope.deletePolicy = function(policy) {
+ Policy.deletePolicy(policy, function(policy) {
+ var _index = $scope.policyList.indexOf(policy);
+ $scope.policyList.splice(_index, 1);
+ });
+ };
+ });
+
+ // ======================= Policy Detail ========================
+ feature.controller('policyDetail', function(PageConfig, Site, $scope, $wrapState, $interval, Entities, Policy, nvd3) {
+ var MAX_PAGESIZE = 10000;
+ var seriesRefreshInterval;
+
+ PageConfig.pageTitle = "Policy Detail";
+ PageConfig.lockSite = true;
+ PageConfig
+ .addNavPath("Policy List", "/common/policyList")
+ .addNavPath("Policy Detail");
+
+ $scope.chartConfig = {
+ chart: "line",
+ xType: "time"
+ };
+
+ // Query policy
+ if($wrapState.param.filter) {
+ $scope.policyList = Entities.queryEntity("AlertDefinitionService", $wrapState.param.filter);
+ } else {
+ $scope.policyList = Entities.queryEntities("AlertDefinitionService", {
+ policyId: $wrapState.param.policy,
+ site: $wrapState.param.site,
+ alertExecutorId: $wrapState.param.executor
+ });
+ }
+
+ $scope.policyList._promise.then(function() {
+ var policy = null;
+
+ if($scope.policyList.length === 0) {
+ $.dialog({
+ title: "OPS!",
+ content: "Policy not found!"
+ }, function() {
+ location.href = "#/common/policyList";
+ });
+ return;
+ } else {
+ policy = $scope.policyList[0];
+
+ policy.__notificationList = common.parseJSON(policy.notificationDef, []);
+
+ policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
+
+ $scope.policy = policy;
+ Site.current(Site.find($scope.policy.tags.site));
+ console.log($scope.policy);
+ }
+
+ // Visualization
+ var _intervalType = 0;
+ var _intervalList = [
+ ["Daily", 1440, function() {
+ var _endTime = app.time.now().hour(23).minute(59).second(59).millisecond(0);
+ var _startTime = _endTime.clone().subtract(1, "month").hour(0).minute(0).second(0).millisecond(0);
+ return [_startTime, _endTime];
+ }],
+ ["Hourly", 60, function() {
+ var _endTime = app.time.now().minute(59).second(59).millisecond(0);
+ var _startTime = _endTime.clone().subtract(48, "hour").minute(0).second(0).millisecond(0);
+ return [_startTime, _endTime];
+ }],
+ ["Every 5 minutes", 5, function() {
+ var _endTime = app.time.now().second(59).millisecond(0);
+ var _minute = Math.floor(_endTime.minute() / 5) * 5;
+ var _startTime = _endTime.clone().minute(_minute).subtract(5 * 30, "minute").second(0).millisecond(0);
+ _endTime.minute(_minute + 4);
+ return [_startTime, _endTime];
+ }]
+ ];
+
+ function _loadSeries(seriesName, metricName, condition) {
+ var list = Entities.querySeries("GenericMetricService", $.extend({_metricName: metricName}, condition), "@site", "sum(value)", _intervalList[_intervalType][1]);
+ var seriesList = nvd3.convert.eagle([list]);
+ if(!$scope[seriesName]) $scope[seriesName] = seriesList;
+ list._promise.then(function() {
+ $scope[seriesName] = seriesList;
+ });
+ }
+
+ function refreshSeries() {
+ var _timeRange = _intervalList[_intervalType][2]();
+ var _startTime = _timeRange[0];
+ var _endTime = _timeRange[1];
+ var _cond = {
+ application: policy.tags.application,
+ policyId: policy.tags.policyId,
+ _startTime: _startTime,
+ _endTime: _endTime
+ };
+ console.log("Range:", common.format.date(_startTime, "datetime"), common.format.date(_endTime, "datetime"));
+
+ // > eagle.policy.eval.count
+ _loadSeries("policyEvalSeries", "eagle.policy.eval.count", _cond);
+
+ // > eagle.policy.eval.fail.count
+ _loadSeries("policyEvalFailSeries", "eagle.policy.eval.fail.count", _cond);
+
+ // > eagle.alert.count
+ _loadSeries("alertSeries", "eagle.alert.count", _cond);
+
+ // > eagle.alert.fail.count
+ _loadSeries("alertFailSeries", "eagle.alert.fail.count", _cond);
+ }
+
+ // Alert list
+ $scope.alertList = Entities.queryEntities("AlertService", {
+ site: Site.current().tags.site,
+ application: policy.tags.application,
+ policyId: policy.tags.policyId,
+ _pageSize: MAX_PAGESIZE,
+ _duration: 1000 * 60 * 60 * 24 * 30,
+ __ETD: 1000 * 60 * 60 * 24
+ });
+
+ refreshSeries();
+ seriesRefreshInterval = $interval(refreshSeries, 60000);
+
+ // Menu
+ $scope.visualizationMenu = [
+ {icon: "clock-o", title: "Interval", list:
+ $.map(_intervalList, function(item, index) {
+ var _item = {icon: "clock-o", title: item[0], func: function() {
+ _intervalType = index;
+ refreshSeries();
+ }};
+ Object.defineProperty(_item, 'strong', {
+ get: function() {return _intervalType === index;}
+ });
+ return _item;
+ })
+ }
+ ];
+ });
+
+ // Function
+ $scope.updatePolicyStatus = Policy.updatePolicyStatus;
+ $scope.deletePolicy = function(policy) {
+ Policy.deletePolicy(policy, function() {
+ location.href = "#/common/policyList";
+ });
+ };
+
+ // Clean up
+ $scope.$on('$destroy', function() {
+ $interval.cancel(seriesRefreshInterval);
+ });
+ });
+
+ // ======================== Policy Edit =========================
+ function policyCtrl(create, PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
+ PageConfig.pageTitle = create ? "Policy Create" : "Policy Edit";
+ PageConfig.pageSubTitle = Site.current().tags.site;
+ PageConfig
+ .addNavPath("Policy List", "/common/policyList")
+ .addNavPath("Policy Edit");
+
+ var _winTimeDesc = "Number unit[millisecond, sec, min, hour, day, month, year]. e.g. 23 sec";
+ var _winTimeRegex = /^\d+\s+(millisecond|milliseconds|second|seconds|sec|minute|minutes|min|hour|hours|day|days|week|weeks|month|months|year|years)$/;
+ var _winTimeDefaultValue = '10 min';
+ $scope.config = {
+ window: [
+ // Display name, window type, required columns[Title, Description || "LONG_FIELD", Regex check, default value]
+ {
+ title: "Message Time Slide",
+ description: "Using timestamp filed from input is used as event's timestamp",
+ type: "externalTime",
+ fields:[
+ {title: "Field", defaultValue: "timestamp", hide: true},
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+ ]
+ },
+ {
+ title: "System Time Slide",
+ description: "Using System time is used as timestamp for event's timestamp",
+ type: "time",
+ fields:[
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+ ]
+ },
+ {
+ title: "System Time Batch",
+ description: "Same as System Time Window except the policy is evaluated at fixed duration",
+ type: "timeBatch",
+ fields:[
+ {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+ ]
+ },
+ {
+ title: "Length Slide",
+ description: "The slide window has a fixed length",
+ type: "length",
+ fields:[
+ {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/}
+ ]
+ },
+ {
+ title: "Length Batch",
+ description: "Same as Length window except the policy is evaluated in batch mode when fixed event count reached",
+ type: "lengthBatch",
+ fields:[
+ {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/}
+ ]
+ }
+ ]
+ };
+
+ $scope.Notification = Notification;
+
+ $scope.create = create;
+ $scope.encodedRowkey = $wrapState.param.filter;
+
+ $scope.step = 0;
+ $scope.applications = {};
+ $scope.policy = {};
+
+ // ==========================================
+ // = Notification =
+ // ==========================================
+ $scope.notificationTabHolder = null;
+
+ $scope.newNotification = function (notificationType) {
+ var __notification = {
+ notificationType: notificationType
+ };
+
+ $.each(Notification.map[notificationType].fieldList, function (i, field) {
+ __notification[field.name] = field.value || "";
+ });
+
+ $scope.policy.__.notification.push(__notification);
+ };
+
+ Notification.promise.then(function () {
+ $scope.menu = Authorization.isRole('ROLE_ADMIN') ? [
+ {icon: "cog", title: "Configuration", list: [
+ {icon: "trash", title: "Delete", danger: true, func: function () {
+ var notification = $scope.notificationTabHolder.selectedPane.data;
+ UI.deleteConfirm(notification.notificationType).then(null, null, function(holder) {
+ common.array.remove(notification, $scope.policy.__.notification);
+ holder.closeFunc();
+ });
+ }}
+ ]},
+ {icon: "plus", title: "New", list: $.map(Notification.list, function(notification) {
+ return {
+ icon: "plus",
+ title: notification.tags.notificationType,
+ func: function () {
+ $scope.newNotification(notification.tags.notificationType);
+ setTimeout(function() {
+ $scope.notificationTabHolder.setSelect(-1);
+ $scope.$apply();
+ }, 0);
+ }
+ };
+ })}
+ ] : [];
+ });
+
+ // ==========================================
+ // = Data Preparation =
+ // ==========================================
+ // Steam list
+ var _streamList = Entities.queryEntities("AlertStreamService", {application: Application.current().tags.application});
+ var _executorList = Entities.queryEntities("AlertExecutorService", {application: Application.current().tags.application});
+ $scope.streamList = _streamList;
+ $scope.executorList = _executorList;
+ $scope.streamReady = false;
+
+ $q.all([_streamList._promise, _executorList._promise]).then(function() {
+ // Map executor with stream
+ $.each(_executorList, function(i, executor) {
+ $.each(_streamList, function(j, stream) {
+ if(stream.tags.application === executor.tags.application && stream.tags.streamName === executor.tags.streamName) {
+ stream.alertExecutor = executor;
+ return false;
+ }
+ });
+ });
+
+ // Fill stream list
+ $.each(_streamList, function(i, unit) {
+ var _srcStreamList = $scope.applications[unit.tags.application] = $scope.applications[unit.tags.application] || [];
+ _srcStreamList.push(unit);
+ });
+
+ $scope.streamReady = true;
+
+ // ==========================================
+ // = Function =
+ // ==========================================
+ function _findStream(application, streamName) {
+ var _streamList = $scope.applications[application];
+ if(!_streamList) return null;
+
+ for(var i = 0 ; i < _streamList.length ; i += 1) {
+ if(_streamList[i].tags.streamName === streamName) {
+ return _streamList[i];
+ }
+ }
+ return null;
+ }
+
+ // ==========================================
+ // = Step control =
+ // ==========================================
+ $scope.steps = [
+ // >> Select stream
+ {
+ title: "Select Stream",
+ ready: function() {
+ return $scope.streamReady;
+ },
+ init: function() {
+ $scope.policy.__.streamName = $scope.policy.__.streamName ||
+ common.array.find($scope.policy.tags.application, _streamList, "tags.application").tags.streamName;
+ },
+ nextable: function() {
+ var _streamName = common.getValueByPath($scope.policy, "__.streamName");
+ if(!_streamName) return false;
+
+ // Detect stream in current data source list
+ return !!common.array.find(_streamName, $scope.applications[$scope.policy.tags.application], "tags.streamName");
+ }
+ },
+
+ // >> Define Alert Policy
+ {
+ title: "Define Alert Policy",
+ init: function() {
+ // Normal mode will fetch meta list
+ if(!$scope.policy.__.advanced) {
+ var _stream = _findStream($scope.policy.tags.application, $scope.policy.__.streamName);
+ $scope._stream = _stream;
+
+ if(!_stream) {
+ $.dialog({
+ title: "OPS",
+ content: "Stream not found! Current application don't match any stream."
+ });
+ return;
+ }
+
+ if(!_stream.metas) {
+ _stream.metas = Entities.queryEntities("AlertStreamSchemaService", {application: $scope.policy.tags.application, streamName: $scope.policy.__.streamName});
+ _stream.metas._promise.then(function() {
+ _stream.metas.sort(function(a, b) {
+ if(a.tags.attrName < b.tags.attrName) {
+ return -1;
+ } else if(a.tags.attrName > b.tags.attrName) {
+ return 1;
+ }
+ return 0;
+ });
+ });
+ }
+ }
+ },
+ ready: function() {
+ if(!$scope.policy.__.advanced) {
+ return $scope._stream.metas._promise.$$state.status === 1;
+ }
+ return true;
+ },
+ nextable: function() {
+ if($scope.policy.__.advanced) {
+ // Check stream source
+ $scope._stream = null;
+ $.each($scope.applications[$scope.policy.tags.application], function(i, stream) {
+ if(($scope.policy.__._expression || "").indexOf(stream.tags.streamName) !== -1) {
+ $scope._stream = stream;
+ return false;
+ }
+ });
+ return $scope._stream;
+ } else {
+ // Window
+ if($scope.policy.__.windowConfig) {
+ var _winMatch = true;
+ var _winConds = $scope.getWindow().fields;
+ $.each(_winConds, function(i, cond) {
+ if(!(cond.val || "").match(cond.regex)) {
+ _winMatch = false;
+ return false;
+ }
+ });
+ if(!_winMatch) return false;
+
+ // Aggregation
+ if($scope.policy.__.groupAgg) {
+ if(!$scope.policy.__.groupAggPath ||
+ !$scope.policy.__.groupCondOp ||
+ !$scope.policy.__.groupCondVal) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ },
+
+ // >> Configuration & Notification
+ {
+ title: "Configuration & Notification",
+ nextable: function() {
+ return !!$scope.policy.tags.policyId;
+ }
+ }
+ ];
+
+ // ==========================================
+ // = Policy Logic =
+ // ==========================================
+ _streamList._promise.then(function() {
+ // Initial policy entity
+ if(create) {
+ $scope.policy = {
+ __: {
+ toJSON: jQuery.noop,
+ conditions: {},
+ notification: [],
+ dedupe: {
+ alertDedupIntervalMin: 10,
+ fields: []
+ },
+ _dedupTags: {},
+ policy: {},
+ window: "externalTime",
+ group: "",
+ groupAgg: "count",
+ groupAggPath: "timestamp",
+ groupCondOp: ">=",
+ groupCondVal: "2"
+ },
+ description: "",
+ enabled: true,
+ prefix: "alertdef",
+ remediationDef: "",
+ tags: {
+ application: Application.current().tags.application,
+ policyType: "siddhiCEPEngine"
+ }
+ };
+
+ // If configured data source
+ if($wrapState.param.app) {
+ $scope.policy.tags.application = $wrapState.param.app;
+ if(common.array.find($wrapState.param.app, Site.current().applicationList, "tags.application")) {
+ setTimeout(function() {
+ $scope.changeStep(0, 2, false);
+ $scope.$apply();
+ }, 1);
+ }
+ }
+
+ // Start step
+ $scope.changeStep(0, 1, false);
+ console.log($scope.policy);
+ } else {
+ var _policy = Entities.queryEntity("AlertDefinitionService", $scope.encodedRowkey);
+ _policy._promise.then(function() {
+ if(_policy.length) {
+ $scope.policy = _policy[0];
+ $scope.policy.__ = {
+ toJSON: jQuery.noop
+ };
+
+ Site.current(Site.find($scope.policy.tags.site));
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: "Policy not found!"
+ }, function() {
+ $wrapState.path("/common/policyList");
+ $scope.$apply();
+ });
+ return;
+ }
+
+ var _application = Application.current();
+ if(_application.tags.application !== $scope.policy.tags.application) {
+ _application = Application.find($scope.policy.tags.application);
+ if(_application) {
+ Application.current(_application, false);
+ console.log("Application not match. Do reload...");
+ $wrapState.reload();
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: "Application not found! Current policy don't match any application."
+ }, function() {
+ $location.path("/common/policyList");
+ $scope.$apply();
+ });
+ }
+ return;
+ }
+
+ // === Revert inner data ===
+ // >> De-dupe
+ $scope.policy.__._dedupTags = {};
+ $scope.policy.__.dedupe = common.parseJSON($scope.policy.dedupeDef, {});
+ $.each($scope.policy.__.dedupe.fields || [], function (i, field) {
+ $scope.policy.__._dedupTags[field] = true;
+ });
+
+ // >> Notification
+ $scope.policy.__.notification = common.parseJSON($scope.policy.notificationDef, []);
+
+ // >> Policy
+ var _policyUnit = $scope.policy.__.policy = common.parseJSON($scope.policy.policyDef);
+
+ // >> Parse expression
+ $scope.policy.__.conditions = {};
+ var _condition = _policyUnit.expression.match(/from\s+(\w+)(\[(.*)])?(#window[^\)]*\))?\s+(select (\w+, )?(\w+)\((\w+)\) as [\w\d_]+ (group by (\w+) )?having ([\w\d_]+) ([<>=]+) ([^\s]+))?/);
+ var _cond_stream = _condition[1];
+ var _cond_query = _condition[3] || "";
+ var _cond_window = _condition[4];
+ var _cond_group = _condition[5];
+ var _cond_groupUnit = _condition.slice(7,14);
+
+ if(!_condition) {
+ $scope.policy.__.advanced = true;
+ } else {
+ // > StreamName
+ var _streamName = _cond_stream;
+ var _cond = _cond_query;
+
+ $scope.policy.__.streamName = _streamName;
+
+ // > Conditions
+ // Loop condition groups
+ if(_cond.trim() !== "" && /^\(.*\)$/.test(_cond)) {
+ var _condGrps = _cond.substring(1, _cond.length - 1).split(/\)\s+and\s+\(/);
+ $.each(_condGrps, function(i, line) {
+ // Loop condition cells
+ var _condCells = line.split(/\s+or\s+/);
+ $.each(_condCells, function(i, cell) {
+ var _opMatch = cell.match(/(\S*)\s*(==|!=|>|<|>=|<=|contains)\s*('(?:[^'\\]|\\.)*'|[\w\d]+)/);
+ if(!common.isEmpty(_opMatch)) {
+ var _key = _opMatch[1];
+ var _op = _opMatch[2];
+ var _val = _opMatch[3];
+ var _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
+ var _type = "";
+ if(_val.match(/'.*'/)) {
+ _val = _val.slice(1, -1);
+ _type = "string";
+ } else if(_val === "true" || _val === "false") {
+ var _regexMatch = _key.match(/^str:regexp\((\w+),'(.*)'\)/);
+ var _containsMatch = _key.match(/^str:contains\((\w+),'(.*)'\)/);
+ var _mathes = _regexMatch || _containsMatch;
+ if(_mathes) {
+ _key = _mathes[1];
+ _val = _mathes[2];
+ _type = "string";
+ _op = _regexMatch ? "regex" : "contains";
+ _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
+ } else {
+ _type = "bool";
+ }
+ } else {
+ _type = "number";
+ }
+ _conds.push($scope._CondUnit(_key, _op, _val, _type));
+ }
+ });
+ });
+ } else if(_cond_query !== "") {
+ $scope.policy.__.advanced = true;
+ }
+ }
+
+ if($scope.policy.__.advanced) {
+ $scope.policy.__._expression = _policyUnit.expression;
+ } else {
+ // > window
+ if(!_cond_window) {
+ $scope.policy.__.window = "externalTime";
+ $scope.policy.__.group = "";
+ $scope.policy.__.groupAgg = "count";
+ $scope.policy.__.groupAggPath = "timestamp";
+ $scope.policy.__.groupCondOp = ">=";
+ $scope.policy.__.groupCondVal = "2";
+ } else {
+ try {
+ $scope.policy.__.windowConfig = true;
+
+ var _winCells = _cond_window.match(/\.(\w+)\((.*)\)/);
+ $scope.policy.__.window = _winCells[1];
+ var _winConds = $scope.getWindow().fields;
+ $.each(_winCells[2].split(","), function(i, val) {
+ _winConds[i].val = val;
+ });
+
+ // Group
+ if(_cond_group) {
+ $scope.policy.__.group = _cond_groupUnit[3];
+ $scope.policy.__.groupAgg = _cond_groupUnit[0];
+ $scope.policy.__.groupAggPath = _cond_groupUnit[1];
+ $scope.policy.__.groupAggAlias = _cond_groupUnit[4] === "aggValue" ? "" : _cond_groupUnit[4];
+ $scope.policy.__.groupCondOp = _cond_groupUnit[5];
+ $scope.policy.__.groupCondVal = _cond_groupUnit[6];
+ } else {
+ $scope.policy.__.group = "";
+ $scope.policy.__.groupAgg = "count";
+ $scope.policy.__.groupAggPath = "timestamp";
+ $scope.policy.__.groupCondOp = ">=";
+ $scope.policy.__.groupCondVal = "2";
+ }
+ } catch(err) {
+ $scope.policy.__.window = "externalTime";
+ }
+ }
+ }
+
+ $scope.changeStep(0, 2, false);
+ console.log($scope.policy);
+ });
+ }
+ });
+
+ // ==========================================
+ // = Function =
+ // ==========================================
+ // UI: Highlight select step
+ $scope.stepSelect = function(step) {
+ return step === $scope.step ? "active" : "";
+ };
+
+ // UI: Collapse all
+ $scope.collapse = function(cntr) {
+ var _list = $(cntr).find(".collapse").css("height", "auto");
+ if(_list.hasClass("in")) {
+ _list.removeClass("in");
+ } else {
+ _list.addClass("in");
+ }
+ };
+
+ // Step process. Will fetch target step attribute and return boolean
+ function _check(key, step) {
+ var _step = $scope.steps[step - 1];
+ if(!_step) return;
+
+ var _value = _step[key];
+ if(typeof _value === "function") {
+ return _value();
+ } else if(typeof _value === "boolean") {
+ return _value;
+ }
+ return true;
+ }
+ // Check step is ready. Otherwise will display load animation
+ $scope.stepReady = function(step) {
+ return _check("ready", step);
+ };
+ // Check whether process next step. Otherwise will disable next button
+ $scope.checkNextable = function(step) {
+ return !_check("nextable", step);
+ };
+ // Switch step
+ $scope.changeStep = function(step, targetStep, check) {
+ if(check === false || _check("checkStep", step)) {
+ $scope.step = targetStep;
+
+ _check("init", targetStep);
+ }
+ };
+
+ // Window
+ $scope.getWindow = function() {
+ if(!$scope.policy || !$scope.policy.__) return null;
+ return common.array.find($scope.policy.__.window, $scope.config.window, "type");
+ };
+
+ // Aggregation
+ $scope.groupAggPathList = function() {
+ return $.grep(common.getValueByPath($scope, "_stream.metas", []), function(meta) {
+ return $.inArray(meta.attrType, ['long','integer','number', 'double', 'float']) !== -1;
+ });
+ };
+
+ $scope.updateGroupAgg = function() {
+ $scope.policy.__.groupAggPath = $scope.policy.__.groupAggPath || common.getValueByPath($scope.groupAggPathList()[0], "tags.attrName");
+
+ if($scope.policy.__.groupAgg === 'count') {
+ $scope.policy.__.groupAggPath = 'timestamp';
+ }
+ };
+
+ // Resolver
+ $scope.resolverTypeahead = function(value, resolver) {
+ var _resolverList = Entities.query("stream/attributeresolve", {
+ site: Site.current().tags.site,
+ resolver: resolver,
+ query: value
+ });
+ return _resolverList._promise.then(function() {
+ return _resolverList;
+ });
+ };
+
+ // Used for input box when pressing enter
+ $scope.conditionPress = function(event) {
+ if(event.which == 13) {
+ setTimeout(function() {
+ $(event.currentTarget).closest(".input-group").find("button").click();
+ }, 1);
+ }
+ };
+ // Check whether has condition
+ $scope.hasCondition = function(key, type) {
+ var _list = common.getValueByPath($scope.policy.__.conditions, key, []);
+ if(_list.length === 0) return false;
+
+ if(type === "bool") {
+ return !_list[0].ignored();
+ }
+ return true;
+ };
+ // Condition unit definition
+ $scope._CondUnit = function(key, op, value, type) {
+ return {
+ key: key,
+ op: op,
+ val: value,
+ type: type,
+ ignored: function() {
+ return this.type === "bool" && this.val === "none";
+ },
+ getVal: function() {
+ return this.type === "string" ? "'" + this.val + "'" : this.val;
+ },
+ toString: function() {
+ return this.op + " " + this.getVal();
+ },
+ toCondString: function() {
+ var _op = this.op === "=" ? "==" : this.op;
+ if(_op === "regex") {
+ return "str:regexp(" + this.key + "," + this.getVal() + ")==true";
+ } else if(_op === "contains") {
+ return "str:contains(" + this.key + "," + this.getVal() + ")==true";
+ } else {
+ return this.key + " " + _op + " " + this.getVal();
+ }
+ }
+ };
+ };
+
+ //for maprfs, if key is status or volume or src/dst, convert these names to id.
+ $scope.convertToID = function(_condList, key, op, name, type, site){
+ if(key == "dst" || key == "src") {
+ Entities.maprfsNameToID("fNameResolver", name, site)._promise.then(
+ function(response){
+ console.log("success");
+ console.log(response);
+ _condList.push($scope._CondUnit(key, op, response.data, type));
+ },
+ function(error, status){
+ console.log("error: " + status);
+ }
+ );
+ }
+ else if (key == "status"){
+ Entities.maprfsNameToID("sNameResolver", name, site)._promise.then(
+ function(response){
+ console.log("success");
+ console.log(response);
+ _condList.push($scope._CondUnit(key, op, response.data, type));
+ },
+ function(error, status){
+ console.log("error: " + status);
+ }
+ );
+ }
+ else if (key == "volume") {
+ Entities.maprfsNameToID("vNameResolver", name, site)._promise.then(
+ function(response){
+ console.log("success");
+ console.log(response);
+ _condList.push($scope._CondUnit(key, op, response.data, type));
+ },
+ function(error, status){
+ console.log("error: " + status);
+ }
+ );
+ }
+ };
+
+ // Add condition for policy
+ $scope.addCondition = function(key, op, value, type) {
+ if(value === "" || value === undefined) return false;
+
+ var _condList = $scope.policy.__.conditions[key] = $scope.policy.__.conditions[key] || [];
+ _condList.push($scope._CondUnit(key, op, value, type));
+
+ //if it is mapr application, covert human readable name to ids
+ if(Application.current().tags.application == "maprFSAuditLog") {
+ if ( key == "dst" || key == "src" || key == "volume" || key == "status") {
+ $scope.convertToID(_condList, key, op, value , type, Site.current().tags.site);
+ }
+ }
+
+ return true;
+ };
+ // Convert condition list to description string
+ $scope.parseConditionDesc = function(key) {
+ return $.map($scope.policy.__.conditions[key] || [], function(cond) {
+ if(!cond.ignored()) return "[" + cond.toString() + "]";
+ }).join(" or ");
+ };
+
+ // To query
+ $scope.toQuery = function() {
+ if(!$scope.policy.__) return "";
+
+ if($scope.policy.__.advanced) return $scope.policy.__._expression;
+
+ // > Query
+ var _query = $.map(common.getValueByPath($scope.policy, "__.conditions", {}), function(list) {
+ var _conds = $.map(list, function(cond) {
+ if(!cond.ignored()) return cond.toCondString();
+ });
+ if(_conds.length) {
+ return "(" + _conds.join(" or ") + ")";
+ }
+ }).join(" and ");
+ if(_query) {
+ _query = "[" + _query + "]";
+ }
+
+ // > Window
+ var _window = $scope.getWindow();
+ var _windowStr = "";
+ if($scope.policy.__.windowConfig) {
+ _windowStr = $.map(_window.fields, function(field) {
+ return field.val;
+ }).join(",");
+ _windowStr = "#window." + _window.type + "(" + _windowStr + ")";
+
+ // > Group
+ if($scope.policy.__.group) {
+ _windowStr += common.template(" select ${group}, ${groupAgg}(${groupAggPath}) as ${groupAggAlias} group by ${group} having ${groupAggAlias} ${groupCondOp} ${groupCondVal}", {
+ group: $scope.policy.__.group,
+ groupAgg: $scope.policy.__.groupAgg,
+ groupAggPath: $scope.policy.__.groupAggPath,
+ groupCondOp: $scope.policy.__.groupCondOp,
+ groupCondVal: $scope.policy.__.groupCondVal,
+ groupAggAlias: $scope.policy.__.groupAggAlias || "aggValue"
+ });
+ } else {
+ _windowStr += common.template(" select ${groupAgg}(${groupAggPath}) as ${groupAggAlias} having ${groupAggAlias} ${groupCondOp} ${groupCondVal}", {
+ groupAgg: $scope.policy.__.groupAgg,
+ groupAggPath: $scope.policy.__.groupAggPath,
+ groupCondOp: $scope.policy.__.groupCondOp,
+ groupCondVal: $scope.policy.__.groupCondVal,
+ groupAggAlias: $scope.policy.__.groupAggAlias || "aggValue"
+ });
+ }
+ } else {
+ _windowStr = " select *";
+ }
+
+ return common.template("from ${stream}${query}${window} insert into outputStream;", {
+ stream: $scope.policy.__.streamName,
+ query: _query,
+ window: _windowStr
+ });
+ };
+
+ // ==========================================
+ // = Update Policy =
+ // ==========================================
+ // dedupeDef notificationDef policyDef
+ $scope.finishPolicy = function() {
+ $scope.lock = true;
+
+ // dedupeDef
+ $scope.policy.__.dedupe.fields = $.map($scope.policy.__._dedupTags, function (value, key) {
+ if(value) return key;
+ });
+ $scope.policy.dedupeDef = JSON.stringify($scope.policy.__.dedupe);
+
+ // notificationDef
+ $scope.policy.__.notification = $scope.policy.__.notification || [];
+
+ $scope.policy.notificationDef = JSON.stringify($scope.policy.__.notification);
+
+ // policyDef
+ $scope.policy.__.policy = {
+ expression: $scope.toQuery(),
+ type: "siddhiCEPEngine"
+ };
+ $scope.policy.policyDef = JSON.stringify($scope.policy.__.policy);
+
+ // alertExecutorId
+ if($scope._stream.alertExecutor) {
+ $scope.policy.tags.alertExecutorId = $scope._stream.alertExecutor.tags.alertExecutorId;
+ } else {
+ $scope.lock = false;
+ $.dialog({
+ title: "OPS!",
+ content: "Alert Executor not defined! Please check 'AlertExecutorService'!"
+ });
+ return;
+ }
+
+ // site
+ $scope.policy.tags.site = $scope.policy.tags.site || Site.current().tags.site;
+
+ // owner
+ $scope.policy.owner = Authorization.userProfile.username;
+
+ // Update function
+ function _updatePolicy() {
+ Entities.updateEntity("AlertDefinitionService", $scope.policy)._promise.success(function(data) {
+ $.dialog({
+ title: "Success",
+ content: (create ? "Create" : "Update") + " success!"
+ }, function() {
+ if(data.success) {
+ location.href = "#/common/policyList";
+ } else {
+ $.dialog({
+ title: "OPS",
+ content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data)
+ });
+ }
+ });
+
+ $scope.create = create = false;
+ $scope.encodedRowkey = data.obj[0];
+ }).error(function(data) {
+ $.dialog({
+ title: "OPS",
+ content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data)
+ });
+ }).then(function() {
+ $scope.lock = false;
+ });
+ }
+
+ // Check if already exist
+ if($scope.create) {
+ var _checkList = Entities.queryEntities("AlertDefinitionService", {
+ alertExecutorId: $scope.policy.tags.alertExecutorId,
+ policyId: $scope.policy.tags.policyId,
+ policyType: "siddhiCEPEngine",
+ application: $scope.policy.tags.application
+ });
+ _checkList._promise.then(function() {
+ if(_checkList.length) {
+ $.dialog({
+ title: "Override Confirm",
+ content: "Already exists PolicyID '" + $scope.policy.tags.policyId + "'. Do you want to override?",
+ confirm: true
+ }, function(ret) {
+ if(ret) {
+ _updatePolicy();
+ } else {
+ $scope.lock = false;
+ $scope.$apply();
+ }
+ });
+ } else {
+ _updatePolicy();
+ }
+ });
+ } else {
+ _updatePolicy();
+ }
+ };
+ });
+ }
+
+ feature.controller('policyCreate', function(PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
+ var _args = [true];
+ _args.push.apply(_args, arguments);
+ policyCtrl.apply(this, _args);
+ }, "policyEdit");
+ feature.controller('policyEdit', function(PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
+ PageConfig.lockSite = true;
+ var _args = [false];
+ _args.push.apply(_args, arguments);
+ policyCtrl.apply(this, _args);
+ });
+
+ // ==============================================================
+ // = Alerts =
+ // ==============================================================
+
+ // ========================= Alert List =========================
+ feature.navItem("alertList", "Alerts", "exclamation-triangle");
+ feature.controller('alertList', function(PageConfig, Site, $scope, $wrapState, $interval, $timeout, Entities, Application) {
+ PageConfig.pageSubTitle = Site.current().tags.site;
+
+ var MAX_PAGESIZE = 10000;
+
+ // Initial load
+ $scope.application = Application.current();
+
+ $scope.alertList = [];
+ $scope.alertList.ready = false;
+
+ // Load data
+ function _loadAlerts() {
+ if($scope.alertList._promise) {
+ $scope.alertList._promise.abort();
+ }
+
+ var _list = Entities.queryEntities("AlertService", {
+ site: Site.current().tags.site,
+ application: $scope.application.tags.application,
+ _pageSize: MAX_PAGESIZE,
+ _duration: 1000 * 60 * 60 * 24 * 30,
+ __ETD: 1000 * 60 * 60 * 24
+ });
+ $scope.alertList._promise = _list._promise;
+ _list._promise.then(function() {
+ var index;
+
+ if($scope.alertList[0]) {
+ // List new alerts
+ for(index = 0 ; index < _list.length ; index += 1) {
+ var _alert = _list[index];
+ _alert.__new = true;
+ if(_alert.encodedRowkey === $scope.alertList[0].encodedRowkey) {
+ break;
+ }
+ }
+
+ if(index > 0) {
+ $scope.alertList.unshift.apply($scope.alertList, _list.slice(0, index));
+
+ // Clean up UI highlight
+ $timeout(function() {
+ $.each(_list, function(i, alert) {
+ delete alert.__new;
+ });
+ }, 100);
+ }
+ } else {
+ // List all alerts
+ $scope.alertList.push.apply($scope.alertList, _list);
+ }
+
+ $scope.alertList.ready = true;
+ });
+ }
+
+ _loadAlerts();
+ var _loadInterval = $interval(_loadAlerts, app.time.refreshInterval);
+ $scope.$on('$destroy',function(){
+ $interval.cancel(_loadInterval);
+ });
+ });
+
+ // ======================== Alert Detail ========================
+ feature.controller('alertDetail', function(PageConfig, Site, $scope, $wrapState, Entities) {
+ PageConfig.pageTitle = "Alert Detail";
+ PageConfig.lockSite = true;
+ PageConfig
+ .addNavPath("Alert List", "/common/alertList")
+ .addNavPath("Alert Detail");
+
+ $scope.common = common;
+
+ // Query policy
+ $scope.alertList = Entities.queryEntity("AlertService", $wrapState.param.filter);
+ $scope.alertList._promise.then(function() {
+ if($scope.alertList.length === 0) {
+ $.dialog({
+ title: "OPS!",
+ content: "Alert not found!"
+ }, function() {
+ location.href = "#/common/alertList";
+ });
+ } else {
+ $scope.alert = $scope.alertList[0];
+ $scope.alert.rawAlertContext = JSON.stringify($scope.alert.alertContext, null, "\t");
+ Site.current(Site.find($scope.alert.tags.site));
+ console.log($scope.alert);
+ }
+ });
+
+ // UI
+ $scope.getMessageTime = function(alert) {
+ var _time = common.getValueByPath(alert, "alertContext.timestamp");
+ return Number(_time);
+ };
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertDetail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertDetail.html b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertDetail.html
new file mode 100644
index 0000000..309fac3
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertDetail.html
@@ -0,0 +1,67 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title" id="policyId">
+ {{alert.tags.policyId}}
+ <small>{{common.format.date(alert.timestamp)}}</small>
+ </h3>
+ </div><!-- /.box-header -->
+
+ <div class="box-body">
+ <a class="btn btn-primary pull-right" href="#/common/policyDetail/?policy={{alert.tags.policyId}}&site={{alert.tags.site}}&executor={{alert.tags.alertExecutorId}}">View Policy</a>
+
+ <div class="inline-group">
+ <dl><dt>Site</dt><dd>{{alert.tags.site}}</dd></dl>
+ <dl><dt>Data Source</dt><dd>{{alert.tags.application}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Alert Time</dt><dd>{{common.format.date(alert.timestamp)}}</dd></dl>
+ <dl><dt>Message Time</dt><dd>{{common.format.date(alert.alertContext.properties.timestamp)}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Stream Name</dt><dd>{{alert.tags.sourceStreams}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Alert Source</dt><dd>{{alert.tags.alertSource}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>User</dt><dd>{{alert.alertContext.properties.user}}</dd></dl>
+ <dl><dt>Host</dt><dd>{{alert.alertContext.properties.host}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Event</dt><dd>{{alert.alertContext.properties.alertEvent}}</dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Message</dt><dd>{{alert.alertContext.properties.alertMessage}}</dd></dl>
+ </div>
+ </div><!-- /.box-body -->
+
+ <div class="overlay" ng-hide="alertList._promise.$$state.status === 1;">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+
+ <div class="box-footer clearfix">
+ <a data-toggle="collapse" href="[data-id='rawAlertContext']">
+ Raw Alert Context
+ </a>
+ <div data-id="rawAlertContext" class="collapse">
+ <pre>{{alert.rawAlertContext}}</pre>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertList.html b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertList.html
new file mode 100644
index 0000000..0415cc0
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/alertList.html
@@ -0,0 +1,67 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-list-alt"> </i>
+ <h3 class="box-title">
+ {{application.displayName}}
+ </h3>
+ </div>
+ <div class="box-body">
+ <p ng-show="!alertList.ready">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+
+ <div sorttable source="alertList" sort="-timestamp">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th width="170" sortpath="timestamp">Alert Time</th>
+ <th width="170" sortpath="alertContext.properties.timestamp">Message Time</th>
+ <th width="105" sortpath="tags.application">Application</th>
+ <th width="150" sortpath="tags.policyId">Policy Name</th>
+ <th width="60" sortpath="alertContext.properties.user">User</th>
+ <th width="150" sortpath="tags.alertSource">Source</th>
+ <th sortpath="alertContext.properties.emailMessage">Description</th>
+ <th width="50"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-class="{info : item.__new}">
+ <td>{{common.format.date(item.timestamp)}}</td>
+ <td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
+ <td>{{item.tags.application}}</td>
+ <td class="text-nowrap">
+ <a class="fa fa-share-square-o" ng-show="item.tags.policyId"
+ href="#/common/policyDetail/?policy={{item.tags.policyId}}&site={{item.tags.site}}&executor={{item.tags.alertExecutorId}}"> </a>
+ {{item.tags.policyId}}
+ </td>
+ <td>{{item.alertContext.properties.user}}</td>
+ <td>{{item.tags.alertSource}}</td>
+ <td>{{item.alertContext.properties.alertMessage}}</td>
+ <td><a href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ </div>
+ <!--div class="box-footer clearfix">
+ </div-->
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyDetail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyDetail.html b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyDetail.html
new file mode 100644
index 0000000..cdddc43
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyDetail.html
@@ -0,0 +1,173 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ {{policy.tags.policyId}}
+ <small>{{policy.tags.site}}</small>
+ </h3>
+ </div>
+
+ <div class="box-body">
+ <div class="row">
+ <div class="col-xs-8">
+ <div class="inline-group">
+ <dl><dt>Data Source</dt><dd>{{policy.tags.application}}</dd></dl>
+ <dl><dt>Status</dt><dd>
+ <span ng-show="policy.enabled" class="text-muted"><i class="fa fa-square text-green"></i> Enabled</span>
+ <span ng-show="!policy.enabled" class="text-muted"><i class="fa fa-square text-muted"></i> Disabled</span>
+ </dd></dl>
+ </div>
+ <div class="inline-group">
+ <dl><dt>Description</dt><dd>{{policy.description}}</dd></dl>
+ </div>
+ <!--div class="inline-group">
+ <dl><dt>Alert</dt><dd>
+ <a href="mailto:{{mail}}" ng-repeat="mail in policy.__mailList track by $index" style="margin-right: 10px;">{{mail}}</a>
+ <div tabs>
+ <pane ng-repeat="notification in policy.__notificationList track by $index" data-title="{{notification.notificationType}}">
+ </pane>
+ </div>
+ </dd></dl>
+ </div-->
+ <label>Notification</label>
+ <div tabs>
+ <pane ng-repeat="notification in policy.__notificationList track by $index" data-title="{{notification.notificationType}}">
+ <table class="table table-bordered">
+ <tbody>
+ <tr ng-repeat="(key, value) in notification track by $index">
+ <th width="30%">{{key}}</th>
+ <td>{{value}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </pane>
+ </div>
+ </div>
+ <div class="col-xs-4 text-right" ng-show="Auth.isRole('ROLE_ADMIN')">
+ <a class="btn btn-primary" href="#/common/policyEdit/{{policy.encodedRowkey}}">Edit</a>
+ <button class="btn btn-warning" ng-show="!policy.enabled" ng-click="updatePolicyStatus(policy, true)">Enable</button>
+ <button class="btn btn-warning" ng-show="policy.enabled" ng-click="updatePolicyStatus(policy, false)">Disable</button>
+ <button class="btn btn-danger" ng-click="deletePolicy(policy)">Delete</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="overlay" ng-hide="policyList._promise.$$state.status === 1;">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+
+ <div class="box-footer clearfix">
+ <a data-toggle="collapse" href="[data-id='query']">
+ View Query
+ </a>
+ <div data-id="query" class="collapse in">
+ <pre>{{policy.__expression}}</pre>
+ </div>
+ </div>
+</div>
+
+<div tabs>
+ <pane data-title="Visualization" menu="visualizationMenu">
+ <div class="row">
+ <div class="col-xs-6">
+ <div nvd3="policyEvalSeries" data-title="Policy Eval Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
+ </div>
+ <div class="col-xs-6">
+ <div nvd3="policyEvalFailSeries" data-title="Policy Eval Fail Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
+ </div>
+ <div class="col-xs-6">
+ <div nvd3="alertSeries" data-title="Alert Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
+ </div>
+ <div class="col-xs-6">
+ <div nvd3="alertFailSeries" data-title="Alert Fail Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
+ </div>
+ </div>
+ </pane>
+ <pane data-title="Statistics">
+ <div class="row">
+ <div class="col-xs-3">
+ <div class="info-box bg-aqua">
+ <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
+ <div class="info-box-content">
+ <span class="info-box-text">Policy Eval Count</span>
+ <span class="info-box-number">{{common.array.sum(policyEvalSeries, "1")}} <small>(Monthly)</small></span>
+ <span class="info-box-number">{{policyEvalSeries[policyEvalSeries.length - 1][1]}} <small>(Daily)</small></span>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="info-box bg-red">
+ <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
+ <div class="info-box-content">
+ <span class="info-box-text">Policy Eval Fail Count</span>
+ <span class="info-box-number">{{common.array.sum(policyEvalFailSeries, "1")}} <small>(Monthly)</small></span>
+ <span class="info-box-number">{{policyEvalFailSeries[policyEvalFailSeries.length - 1][1]}} <small>(Daily)</small></span>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="info-box bg-aqua">
+ <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
+ <div class="info-box-content">
+ <span class="info-box-text">Alert Count</span>
+ <span class="info-box-number">{{common.array.sum(alertSeries, "1")}} <small>(Monthly)</small></span>
+ <span class="info-box-number">{{alertSeries[alertSeries.length - 1][1]}} <small>(Daily)</small></span>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="info-box bg-red">
+ <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
+ <div class="info-box-content">
+ <span class="info-box-text">Alert Fail Count</span>
+ <span class="info-box-number">{{common.array.sum(alertFailSeries, "1")}} <small>(Monthly)</small></span>
+ <span class="info-box-number">{{alertFailSeries[alertFailSeries.length - 1][1]}} <small>(Daily)</small></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </pane>
+ <pane data-title="Alerts">
+ <div sorttable source="alertList" sort="-timestamp">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th width="170" sortpath="timestamp">Alert Time</th>
+ <th width="170" sortpath="alertContext.properties.timestamp">Message Time</th>
+ <th width="60" sortpath="alertContext.properties.user">User</th>
+ <th width="150" sortpath="alertContext.properties.host">Host</th>
+ <th sortpath="alertContext.properties.emailMessage">Description</th>
+ <th width="50"> </th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-class="{info : item.__new}">
+ <td>{{common.format.date(item.timestamp)}}</td>
+ <td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
+ <td>{{item.alertContext.properties.user}}</td>
+ <td>{{item.alertContext.properties.host}}</td>
+ <td>{{item.alertContext.properties.alertMessage}}</td>
+ <td><a href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </pane>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyEdit.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyEdit.html b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyEdit.html
new file mode 100644
index 0000000..33d4cde
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyEdit.html
@@ -0,0 +1,346 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<div class="progress active" ng-show="!streamReady">
+ <div class="progress-bar progress-bar-primary progress-bar-striped" style="width: 100%">
+ </div>
+</div>
+
+<!-- Step navigation -->
+<div ng-show="streamReady">
+ <div class="row step-cntr">
+ <div class="col-sm-4" ng-repeat="step in steps">
+ <div class="step" ng-class="stepSelect($index + 1)">
+ <h1>{{$index + 1}}</h1>
+ <h2>Step {{$index + 1}}</h2>
+ <p title="{{step.title}}">{{step.title}}</p>
+ </div>
+ </div>
+ </div>
+
+ <!-- Step container -->
+ <div class="box box-info">
+ <div class="box-header with-border">
+ <h3 class="box-title">Step {{step}} - {{steps[step - 1].title}}</h3>
+ </div><!-- /.box-header -->
+
+ <div class="box-body">
+ <!-- ---------------------- Step Body Start ---------------------- -->
+
+ <!-- Step 1: Stream -->
+ <div ng-show="step === 1">
+ <div class="pull-right" ng-show="policy.__.advanced === undefined">
+ <span class="text-muted">or</span>
+ <a ng-click="policy.__.advanced = true;">Advanced</a>
+ </div>
+
+ <div class="form-group">
+ <label>Select Stream</label>
+ <select class="form-control" ng-model="policy.__.streamName" ng-show="!policy.__.advanced">
+ <option ng-repeat="stream in applications[policy.tags.application]">{{stream.tags.streamName}}</option>
+ </select>
+ <select class="form-control" ng-show="policy.__.advanced" disabled="disabled">
+ <option>[Advanced Mode]</option>
+ </select>
+ </div>
+
+ <div class="checkbox" ng-show="policy.__.advanced !== undefined">
+ <label>
+ <input type="checkbox" ng-model="policy.__.advanced">
+ Advanced Mode
+ </label>
+ </div>
+ </div>
+
+ <!-- Step 2: Define Alert Policy -->
+ <div ng-show="step === 2 && !policy.__.advanced">
+ <!-- Criteria -->
+ <div>
+ <label>Match Criteria</label>
+ <a ng-click="collapse('.panel-group')">expand / collapse all</a>
+
+ <div class="panel-group panel-group-sm" role="tablist">
+ <div class="panel panel-default"
+ ng-repeat="meta in _stream.metas"
+ ng-init="op = '=='; val = null; type = (meta.attrType || '').toLowerCase();">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <span class="bg-navy disabled color-palette pull-right">
+ {{parseConditionDesc(meta.tags.attrName)}}
+ </span>
+
+ <a role="button" data-toggle="collapse" href="[data-name='{{meta.tags.attrName}}']" class="collapsed">
+ <span class="fa fa-square" ng-class="hasCondition(meta.tags.attrName, type) ? 'text-green' : 'text-muted'"> </span>
+ {{meta.attrDisplayName || meta.tags.attrName}}
+ <span class="fa fa-question-circle" ng-show="meta.attrDescription"
+ uib-tooltip="{{meta.attrDescription}}" tooltip-placement="right" tooltip-animation="false"> </span>
+ </a>
+ </h4>
+ </div>
+ <div data-name="{{meta.tags.attrName}}" data-type="{{meta.attrType}}" role="tabpanel" class="collapse">
+ <div class="panel-body">
+ <ul ng-show="type !== 'bool'">
+ <li ng-repeat="cond in policy.__.conditions[meta.tags.attrName]">
+ [<a ng-click="policy.__.conditions[meta.tags.attrName].splice($index, 1)">X</a>]
+ {{cond.toString()}}
+ </li>
+ </ul>
+
+ <!-- String -->
+ <div ng-if="type == 'string'">
+ <div class="input-group" style="max-width: 450px;">
+ <div class="input-group-btn">
+ <select class="form-control" ng-model="op">
+ <option ng-repeat="mark in ['==','!=','contains','regex']">{{mark}}</option>
+ </select>
+ </div>
+
+ <!-- With resolver -->
+ <input type="text" class="form-control" autocomplete="off" ng-model="val" ng-show="meta.attrValueResolver"
+ ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)"
+ uib-typeahead="item for item in resolverTypeahead($viewValue, meta.attrValueResolver)">
+ <!-- Without resolver -->
+ <input type="text" class="form-control" autocomplete="off" ng-model="val" ng-show="!meta.attrValueResolver"
+ ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)">
+
+ <span class="input-group-btn">
+ <button class="btn btn-info btn-flat" type="button" ng-click="addCondition(meta.tags.attrName, op, val, type);val=null;">Add</button>
+ </span>
+ </div>
+ </div>
+
+ <!-- Number -->
+ <div ng-if="type == 'long' || type == 'integer' || type == 'number' || type == 'double' || type == 'float'">
+ <div class="input-group" style="max-width: 450px;">
+ <div class="input-group-btn">
+ <select class="form-control" ng-model="op">
+ <option ng-repeat="mark in ['==','!=','>','>=','<','<=']">{{mark}}</option>
+ </select>
+ </div>
+
+ <input type="number" class="form-control" autocomplete="off" placeholder="Number Only..." ng-model="val" ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)">
+ <span class="input-group-btn">
+ <button class="btn btn-info btn-flat" type="button" ng-click="addCondition(meta.tags.attrName, op, val, type) ? val=null : void(0);">Add</button>
+ </span>
+ </div>
+ </div>
+
+ <!-- Boolean -->
+ <div ng-if="type == 'bool'" ng-init="policy.__.conditions[meta.tags.attrName] = policy.__.conditions[meta.tags.attrName] || [_CondUnit(meta.tags.attrName, '==', 'none', 'bool')]">
+ <select class="form-control" ng-model="policy.__.conditions[meta.tags.attrName][0].val" style="max-width: 100px;">
+ <option ng-repeat="bool in ['none','true','false']">{{bool}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Window -->
+ <div class="checkbox">
+ <label>
+ <input type="checkbox" ng-checked="policy.__.windowConfig" ng-click="policy.__.windowConfig = !policy.__.windowConfig"> Slide Window
+ </label>
+ </div>
+ <div ng-show="policy.__.windowConfig">
+ <div class="row">
+ <div class="col-md-4">
+ <div class="form-group">
+ <label>Window</label>
+ <select class="form-control" ng-model="policy.__.window"
+ uib-tooltip="{{getWindow().description}}" tooltip-animation="false">
+ <option ng-repeat="item in config.window" value="{{item.type}}">{{item.title}}</option>
+ </select>
+ </div>
+ </div>
+
+ <!-- fields -->
+ <div class="col-md-4" ng-repeat="field in getWindow().fields" ng-init="field.val = field.val || (field.defaultValue || '');" ng-hide="field.hide">
+ <div class="form-group" ng-class="{'has-warning' : !field.val || !field.val.match(field.regex)}">
+ <label>Window - {{field.title}}</label>
+ <input type="text" class="form-control" autocomplete="off" placeholder="{{field.description}}" ng-model="field.val" title="{{field.description}}">
+ </div>
+ </div>
+ </div>
+
+ <!-- Aggregation -->
+ <div class="row">
+ <div class="col-md-4">
+ <div class="form-group" ng-class="{'text-yellow' : (policy.__.groupAgg && !policy.__.groupAggPath)}">
+ <label>Aggregation</label>
+ <div class="input-group">
+ <div class="input-group-btn">
+ <select class="form-control" ng-model="policy.__.groupAgg" ng-change="updateGroupAgg()">
+ <option ng-repeat="op in ['max','min','avg','count', 'sum']">{{op}}</option>
+ </select>
+ </div>
+ <select class="form-control" ng-model="policy.__.groupAggPath" ng-class="{'has-warning' : !policy.__.groupAggPath}" id="groupAggPath"
+ ng-show="policy.__.groupAgg" ng-disabled="policy.__.groupAgg === 'count'">
+ <option ng-repeat="meta in groupAggPathList()">{{meta.tags.attrName}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-md-4">
+ <div class="form-group" ng-class="{'text-yellow' : (!policy.__.groupCondOp || !policy.__.groupCondVal)}">
+ <label>Condition</label>
+ <div class="input-group">
+ <div class="input-group-btn">
+ <select class="form-control" ng-model="policy.__.groupCondOp" ng-class="{'has-warning' : !policy.__.groupCondOp}">
+ <option ng-repeat="op in ['>','<','>=','<=','==']">{{op}}</option>
+ </select>
+ </div>
+ <input type="text" class="form-control" ng-model="policy.__.groupCondVal" ng-class="{'has-warning' : !policy.__.groupCondVal}" />
+ </div>
+ </div>
+ </div>
+
+ <div class="col-md-4">
+ <div class="form-group">
+ <label>Alias (Optional)</label>
+ <input type="text" class="form-control" ng-model="policy.__.groupAggAlias" placeholder="Default: aggValue" />
+ </div>
+ </div>
+ </div>
+
+ <!-- Group -->
+ <div class="row">
+ <div class="col-md-4">
+ <div class="form-group">
+ <label>Group By</label>
+ <select class="form-control" ng-model="policy.__.group">
+ <option value="">None</option>
+ <option ng-repeat="meta in _stream.metas">{{meta.tags.attrName}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Step 2: Define Alert Policy -->
+ <div ng-show="step === 2 && policy.__.advanced">
+ <div class="form-group">
+ <label>Query Expression</label>
+ <textarea class="form-control" ng-model="policy.__._expression"
+ placeholder="Query expression. e.g. from hdfsAuditLogEventStream[(cmd=='open') and (host=='localhost' or host=='127.0.0.1')]#window.time(2 sec) select * insert into outputStream;" rows="5"></textarea>
+ </div>
+ </div>
+
+ <!-- Step 3: Email Notification -->
+ <div ng-show="step === 3">
+ <div class="row">
+ <div class="col-xs-4">
+ <div class="form-group" ng-class="{'has-warning' : !policy.tags.policyId}">
+ <label>Policy Name</label>
+ <input type="text" class="form-control" placeholder="" ng-model="policy.tags.policyId" ng-disabled="!create">
+ </div>
+ </div>
+ <div class="col-xs-3">
+ <div class="form-group">
+ <label>
+ Alert De-Dup Interval(min)
+ <span class="fa fa-question-circle" uib-tooltip="Same type alert will be De-dup in configured interval"> </span>
+ </label>
+ <input type="number" class="form-control" ng-model="policy.__.dedupe.alertDedupIntervalMin" placeholder="[Minute] Number only. Suggestion: 720">
+ </div>
+ </div>
+ <div class="col-xs-2">
+ <div class="form-group">
+ <label>
+ Enabled
+ </label>
+ <p>
+ <input type="checkbox" checked="checked" ng-model="policy.enabled">
+ </p>
+ </div>
+ </div>
+ </div>
+
+ <div>
+ <a data-toggle="collapse" href="[data-id='advancedDeDup']">Advanced De-Dup</a>
+ <div data-id='advancedDeDup' class="collapse">
+ <label>
+ De-Dup Key
+ <span class="fa fa-question-circle" uib-tooltip="Type of grouping alerts. If you don't know how to config, leave it default."> </span>
+ </label>
+ <div class="form-group">
+ <div class="checkbox" ng-repeat="meta in _stream.metas" ng-init="create ? policy.__._dedupTags[meta.tags.attrName] = !!meta.usedAsTag : void(0);">
+ <label>
+ <input type="checkbox" ng-model="policy.__._dedupTags[meta.tags.attrName]">
+ {{meta.tags.attrName}}
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <hr/>
+
+ <label>Notification</label>
+ <div tabs menu="menu" holder="notificationTabHolder" ng-show="policy.__.notification.length">
+ <pane ng-repeat="notification in policy.__.notification track by $index" data-data="notification" data-title="{{notification.notificationType}}">
+ <div class="form-group" ng-repeat="field in Notification.map[notification.notificationType].fieldList track by $index">
+ <label>{{field.name}}</label>
+ <input type="text" class="form-control" ng-model="notification[field.name]">
+ </div>
+ <p class="text-muted" ng-if="Notification.map[notification.notificationType].fieldList.length === 0">No configuration required</p>
+ </pane>
+ </div>
+ <ul ng-show="policy.__.notification.length === 0">
+ <li ng-repeat="notification in Notification.list track by $index">
+ <a ng-click="newNotification(notification.tags.notificationType)">+ New {{notification.tags.notificationType}} Notification</a>
+ </li>
+ </ul>
+
+ <hr/>
+
+ <div class="form-group">
+ <label>Description</label>
+ <textarea class="form-control" placeholder="Policy description" ng-model="policy.description"></textarea>
+ </div>
+
+ <a data-toggle="collapse" href="[data-id='policyQuery']">
+ View Query
+ </a>
+ <div class="collapse in" data-id="policyQuery">
+ <pre>{{toQuery()}}</pre>
+ </div>
+ </div>
+
+ <!-- ----------------------- Step Body End ----------------------- -->
+ </div><!-- /.box-body -->
+
+ <div class="overlay" ng-hide="stepReady(step)">
+ <span class="fa fa-refresh fa-spin"> </span>
+ </div>
+
+ <div class="box-footer text-right">
+ <button class="btn btn-info" ng-show="step > 1" ng-click="changeStep(step, step - 1, false)" ng-disabled="lock">
+ Prev <span class="fa fa-arrow-circle-o-left"> </span>
+ </button>
+ <button class="btn btn-info" ng-show="step < steps.length" ng-click="changeStep(step, step + 1)" ng-disabled="checkNextable(step) || lock">
+ Next <span class="fa fa-arrow-circle-o-right"> </span>
+ </button>
+ <button class="btn btn-info" ng-show="step === steps.length" ng-click="finishPolicy()" ng-disabled="checkNextable(step) || lock">
+ Done <span class="fa fa-check-circle-o"> </span>
+ </button>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyList.html b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyList.html
new file mode 100644
index 0000000..20a38dd
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/common/page/policyList.html
@@ -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.
+ -->
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <i class="fa fa-list-alt"> </i>
+ <h3 class="box-title">
+ {{application.displayName}}
+ </h3>
+ </div>
+ <div class="box-body">
+ <div class="row">
+ <div class="col-xs-3">
+ <div class="search-box">
+ <input type="search" class="form-control input-sm" placeholder="Search" ng-model="search" />
+ <span class="fa fa-search"> </span>
+ </div>
+ </div>
+ <div class="col-xs-6">
+ <div class="inline-group form-inline text-muted">
+ <dl><dt><i class="fa fa-square text-green"> </i></dt><dd>Enabled</dd></dl>
+ <dl><dt><i class="fa fa-square text-muted"> </i></dt><dd>Disabled</dd></dl>
+ </div>
+ </div>
+ <div class="col-xs-3 text-right">
+ <a class="btn btn-primary" href="#/common/policyCreate/{{!application ? '' : '?app=' + application.tags.application}}" ng-show="Auth.isRole('ROLE_ADMIN')">
+ New Policy
+ <i class="fa fa-plus-circle"> </i>
+ </a>
+ </div>
+ </div>
+
+ <p ng-show="policyList._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+
+ <div sorttable source="policyList" ng-show="policyList._promise.$$state.status === 1" search="false" searchfunc="searchFunc">
+ <table class="table table-bordered" ng-non-bindable>
+ <thead>
+ <tr>
+ <th width="30" sortpath="enabled"> </th>
+ <th width="200" sortpath="tags.policyId">Policy Name</th>
+ <th sortpath="description">Description</th>
+ <th width="150" sortpath="owner">Owner</th>
+ <th width="170" sortpath="lastModifiedDate">Last Modified</th>
+ <th width="95" ng-show="_parent.Auth.isRole('ROLE_ADMIN')">Action</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class='fa fa-square' ng-class="item.enabled ? 'text-green' : 'text-muted'"> </span></td>
+ <td><a href="#/common/policyDetail/{{item.encodedRowkey}}" style="width: 200px;" class="text-breakall">{{item.tags.policyId}}</a></td>
+ <td>{{item.description}}</td>
+ <td>{{item.owner}}</td>
+ <td>{{common.format.date(item.lastModifiedDate) || "-"}}</td>
+ <td ng-show="_parent.Auth.isRole('ROLE_ADMIN')">
+ <a class="fa fa-pencil btn btn-default btn-xs" uib-tooltip="Edit" tooltip-animation="false" href="#/common/policyEdit/{{item.encodedRowkey}}"> </a>
+ <button class="fa fa-play sm btn btn-default btn-xs" uib-tooltip="Enable" tooltip-animation="false" ng-show="!item.enabled" ng-click="_parent.updatePolicyStatus(item, true)"> </button>
+ <button class="fa fa-pause sm btn btn-default btn-xs" uib-tooltip="Disable" tooltip-animation="false" ng-show="item.enabled" ng-click="_parent.updatePolicyStatus(item, false)"> </button>
+ <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="_parent.deletePolicy(item)"> </button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ <!--div class="box-footer clearfix">
+ </div-->
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/metadata/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/metadata/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/metadata/controller.js
new file mode 100644
index 0000000..c17d612
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/metadata/controller.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.
+ */
+
+(function() {
+ 'use strict';
+
+ var featureControllers = angular.module('featureControllers');
+ var feature = featureControllers.register("metadata");
+
+ // ==============================================================
+ // = Function =
+ // ==============================================================
+
+ // ==============================================================
+ // = Metadata =
+ // ==============================================================
+
+ // ======================= Metadata List ========================
+ feature.navItem("streamList", "Metadata", "bullseye");
+ feature.controller('streamList', function(PageConfig, Site, $scope, $q, Application, Entities) {
+ PageConfig.hideSite = true;
+
+ $scope.streams = {};
+ $scope._streamEntity = null;
+ $scope._streamEntityLock = false;
+
+ // =========================================== List ===========================================
+ var _streamList = Entities.queryEntities("AlertStreamService", {application: Application.current().tags.application});
+ var _streamSchemaList = Entities.queryEntities("AlertStreamSchemaService", {application: Application.current().tags.application});
+ $scope.streamList = _streamList;
+ $scope.streamSchemaList = _streamSchemaList;
+
+ _streamList._promise.then(function() {
+ $.each(_streamList, function(i, stream) {
+ stream.metaList = [];
+ $scope.streams[stream.tags.application + "_" + stream.tags.streamName] = stream;
+ });
+ });
+
+ $q.all([_streamList._promise, _streamSchemaList._promise]).then(function() {
+ $.each(_streamSchemaList, function(i, meta) {
+ var _stream = $scope.streams[meta.tags.application + "_" + meta.tags.streamName];
+ if(_stream) {
+ _stream.metaList.push(meta);
+ } else {
+ console.warn("[Meta] Stream not match:", meta.tags.application + "_" + meta.tags.streamName);
+ }
+ });
+ });
+ });
+})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/metadata/page/streamList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/metadata/page/streamList.html b/eagle-webservice/src/main/webapp/_app/public/feature/metadata/page/streamList.html
new file mode 100644
index 0000000..2b73300
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/metadata/page/streamList.html
@@ -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.
+ -->
+<p ng-show="streamList._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+</p>
+
+<div class="box box-primary" ng-repeat="stream in streams">
+ <div class="box-header with-border">
+ <h3 class="box-title">{{stream.tags.streamName}}</h3>
+ </div>
+ <div class="box-body">
+ <div class="inline-group">
+ <dl>
+ <dt>
+ Data Source
+ </dt>
+ <dd>
+ {{stream.tags.application}}
+ </dd>
+ </dl>
+ <dl>
+ <dt>
+ Stream Name
+ </dt>
+ <dd>
+ {{stream.tags.streamName}}
+ </dd>
+ </dl>
+ </div>
+ <div>
+ <dl>
+ <dt>
+ Description
+ </dt>
+ <dd>
+ {{stream.description}}
+ </dd>
+ </dl>
+ </div>
+
+ <p ng-show="streamSchemaList._promise.$$state.status !== 1">
+ <span class="fa fa-refresh fa-spin"> </span>
+ Loading...
+ </p>
+
+ <div class="list" ng-show="streamSchemaList._promise.$$state.status === 1">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th width="10%">Attribute Name</th>
+ <th width="12%">Display Name</th>
+ <th width="8%">Type</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="meta in stream.metaList">
+ <td>{{meta.tags.attrName}}</td>
+ <td>{{meta.attrDisplayName}}</td>
+ <td><span class="label label-warning">{{meta.attrType}}</span></td>
+ <td>{{meta.attrDescription}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div><!-- /.box-body -->
+ <!-- Loading (remove the following to stop the loading)-->
+</div>
\ No newline at end of file
[14/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
[EAGLE-574] UI refactor for support 0.5 api
Eagle 0.5 updates the rest api interface and UI need also update for support the api.
* Remove feature
* Support app provider
* Create JPM UI application
* Redesign site logic
* Widget support
Author: jiljiang <ji...@ebay.com>
Closes #460 from zombieJ/ui.
Project: http://git-wip-us.apache.org/repos/asf/incubator-eagle/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-eagle/commit/afb89794
Tree: http://git-wip-us.apache.org/repos/asf/incubator-eagle/tree/afb89794
Diff: http://git-wip-us.apache.org/repos/asf/incubator-eagle/diff/afb89794
Branch: refs/heads/master
Commit: afb897940021caec79982e29f7194f2b6c733b49
Parents: 1fa490e
Author: zombiej <ji...@apache.org>
Authored: Wed Sep 28 13:38:17 2016 +0800
Committer: jiljiang <ji...@ebay.com>
Committed: Wed Sep 28 13:38:17 2016 +0800
----------------------------------------------------------------------
.../webapp/app/apps/jpm/ctrl/compareCtrl.js | 380 ++++++
.../main/webapp/app/apps/jpm/ctrl/detailCtrl.js | 192 +++
.../webapp/app/apps/jpm/ctrl/jobTaskCtrl.js | 551 ++++++++
.../main/webapp/app/apps/jpm/ctrl/listCtrl.js | 239 ++++
.../webapp/app/apps/jpm/ctrl/overviewCtrl.js | 140 ++
.../webapp/app/apps/jpm/ctrl/statisticCtrl.js | 386 ++++++
.../src/main/webapp/app/apps/jpm/index.js | 489 +++++++
.../app/apps/jpm/partials/job/compare.html | 274 ++++
.../app/apps/jpm/partials/job/detail.html | 256 ++++
.../webapp/app/apps/jpm/partials/job/list.html | 131 ++
.../app/apps/jpm/partials/job/overview.html | 347 +++++
.../app/apps/jpm/partials/job/statistic.html | 120 ++
.../webapp/app/apps/jpm/partials/job/task.html | 149 +++
.../main/webapp/app/apps/jpm/style/index.css | 76 ++
.../webapp/app/apps/jpm/widget/jobStatistic.js | 108 ++
eagle-server/.gitignore | 7 +
eagle-server/pom.xml | 23 +-
eagle-server/src/main/webapp/app/.editorconfig | 27 +
eagle-server/src/main/webapp/app/Gruntfile.js | 190 +++
eagle-server/src/main/webapp/app/README.md | 4 +
eagle-server/src/main/webapp/app/build/index.js | 144 +++
eagle-server/src/main/webapp/app/dev/index.html | 250 ++++
.../webapp/app/dev/partials/alert/list.html | 21 +
.../webapp/app/dev/partials/alert/main.html | 29 +
.../app/dev/partials/alert/policyEdit.back.html | 108 ++
.../app/dev/partials/alert/policyEdit.html | 29 +
.../app/dev/partials/alert/policyList.html | 63 +
.../src/main/webapp/app/dev/partials/home.html | 60 +
.../partials/integration/applicationList.html | 80 ++
.../app/dev/partials/integration/main.html | 29 +
.../app/dev/partials/integration/site.html | 95 ++
.../app/dev/partials/integration/siteList.html | 60 +
.../dev/partials/integration/streamList.html | 52 +
.../src/main/webapp/app/dev/partials/setup.html | 46 +
.../webapp/app/dev/public/css/animation.css | 47 +
.../src/main/webapp/app/dev/public/css/main.css | 317 +++++
.../webapp/app/dev/public/css/sortTable.css | 61 +
.../webapp/app/dev/public/images/favicon.png | Bin 0 -> 4209 bytes
.../app/dev/public/images/favicon_white.png | Bin 0 -> 1621 bytes
.../src/main/webapp/app/dev/public/js/app.js | 311 +++++
.../src/main/webapp/app/dev/public/js/common.js | 387 ++++++
.../app/dev/public/js/components/chart.js | 188 +++
.../webapp/app/dev/public/js/components/main.js | 24 +
.../app/dev/public/js/components/sortTable.js | 231 ++++
.../app/dev/public/js/components/widget.js | 52 +
.../webapp/app/dev/public/js/ctrls/alertCtrl.js | 119 ++
.../app/dev/public/js/ctrls/integrationCtrl.js | 226 ++++
.../main/webapp/app/dev/public/js/ctrls/main.js | 27 +
.../webapp/app/dev/public/js/ctrls/mainCtrl.js | 62 +
.../webapp/app/dev/public/js/ctrls/siteCtrl.js | 33 +
.../src/main/webapp/app/dev/public/js/index.js | 326 +++++
.../dev/public/js/services/applicationSrv.js | 71 +
.../app/dev/public/js/services/entitySrv.js | 135 ++
.../webapp/app/dev/public/js/services/main.js | 23 +
.../app/dev/public/js/services/pageSrv.js | 137 ++
.../app/dev/public/js/services/siteSrv.js | 111 ++
.../app/dev/public/js/services/timeSrv.js | 277 ++++
.../webapp/app/dev/public/js/services/uiSrv.js | 276 ++++
.../app/dev/public/js/services/widgetSrv.js | 79 ++
.../app/dev/public/js/services/wrapStateSrv.js | 119 ++
.../app/dev/public/js/worker/sortTableFunc.js | 93 ++
.../app/dev/public/js/worker/sortTableWorker.js | 32 +
eagle-server/src/main/webapp/app/index.html | 11 +-
eagle-server/src/main/webapp/app/package.json | 47 +
eagle-server/src/main/webapp/package.json | 0
eagle-server/ui-build.sh | 40 +
eagle-webservice/src/main/webapp/Gruntfile.js | 175 ---
eagle-webservice/src/main/webapp/README.md | 4 -
.../src/main/webapp/_app/index.html | 281 ++++
.../_app/partials/config/application.html | 124 ++
.../webapp/_app/partials/config/feature.html | 85 ++
.../main/webapp/_app/partials/config/site.html | 115 ++
.../src/main/webapp/_app/partials/landing.html | 30 +
.../src/main/webapp/_app/partials/login.html | 54 +
.../main/webapp/_app/public/css/animation.css | 46 +
.../src/main/webapp/_app/public/css/main.css | 805 ++++++++++++
.../public/feature/classification/controller.js | 358 +++++
.../classification/page/sensitivity.html | 40 +
.../classification/page/sensitivity/folder.html | 110 ++
.../classification/page/sensitivity/job.html | 92 ++
.../classification/page/sensitivity/table.html | 150 +++
.../_app/public/feature/common/controller.js | 1224 ++++++++++++++++++
.../public/feature/common/page/alertDetail.html | 67 +
.../public/feature/common/page/alertList.html | 67 +
.../feature/common/page/policyDetail.html | 173 +++
.../public/feature/common/page/policyEdit.html | 346 +++++
.../public/feature/common/page/policyList.html | 84 ++
.../_app/public/feature/metadata/controller.js | 66 +
.../feature/metadata/page/streamList.html | 84 ++
.../_app/public/feature/metrics/controller.js | 571 ++++++++
.../public/feature/metrics/page/dashboard.html | 250 ++++
.../_app/public/feature/topology/controller.js | 257 ++++
.../feature/topology/page/management.html | 52 +
.../feature/topology/page/monitoring.html | 151 +++
.../public/feature/userProfile/controller.js | 268 ++++
.../public/feature/userProfile/page/detail.html | 87 ++
.../public/feature/userProfile/page/list.html | 138 ++
.../main/webapp/_app/public/images/favicon.png | Bin 0 -> 4209 bytes
.../webapp/_app/public/images/favicon_white.png | Bin 0 -> 1621 bytes
.../main/webapp/_app/public/js/app.config.js | 126 ++
.../src/main/webapp/_app/public/js/app.js | 499 +++++++
.../src/main/webapp/_app/public/js/app.time.js | 70 +
.../src/main/webapp/_app/public/js/app.ui.js | 76 ++
.../src/main/webapp/_app/public/js/common.js | 304 +++++
.../_app/public/js/components/charts/line3d.js | 348 +++++
.../webapp/_app/public/js/components/file.js | 50 +
.../webapp/_app/public/js/components/main.js | 19 +
.../webapp/_app/public/js/components/nvd3.js | 418 ++++++
.../_app/public/js/components/sortTable.js | 113 ++
.../_app/public/js/components/sortable.js | 166 +++
.../webapp/_app/public/js/components/tabs.js | 247 ++++
.../_app/public/js/ctrl/authController.js | 91 ++
.../public/js/ctrl/configurationController.js | 377 ++++++
.../src/main/webapp/_app/public/js/ctrl/main.js | 42 +
.../webapp/_app/public/js/srv/applicationSrv.js | 170 +++
.../_app/public/js/srv/authorizationSrv.js | 143 ++
.../webapp/_app/public/js/srv/entitiesSrv.js | 301 +++++
.../src/main/webapp/_app/public/js/srv/main.js | 72 ++
.../main/webapp/_app/public/js/srv/pageSrv.js | 131 ++
.../main/webapp/_app/public/js/srv/siteSrv.js | 193 +++
.../src/main/webapp/_app/public/js/srv/uiSrv.js | 247 ++++
.../webapp/_app/public/js/srv/wrapStateSrv.js | 109 ++
eagle-webservice/src/main/webapp/app/index.html | 281 ----
.../webapp/app/partials/config/application.html | 124 --
.../webapp/app/partials/config/feature.html | 85 --
.../main/webapp/app/partials/config/site.html | 115 --
.../src/main/webapp/app/partials/landing.html | 30 -
.../src/main/webapp/app/partials/login.html | 54 -
.../main/webapp/app/public/css/animation.css | 46 -
.../src/main/webapp/app/public/css/main.css | 805 ------------
.../public/feature/classification/controller.js | 358 -----
.../classification/page/sensitivity.html | 40 -
.../classification/page/sensitivity/folder.html | 110 --
.../classification/page/sensitivity/job.html | 92 --
.../classification/page/sensitivity/table.html | 150 ---
.../app/public/feature/common/controller.js | 1224 ------------------
.../public/feature/common/page/alertDetail.html | 67 -
.../public/feature/common/page/alertList.html | 67 -
.../feature/common/page/policyDetail.html | 173 ---
.../public/feature/common/page/policyEdit.html | 346 -----
.../public/feature/common/page/policyList.html | 84 --
.../app/public/feature/metadata/controller.js | 66 -
.../feature/metadata/page/streamList.html | 84 --
.../app/public/feature/metrics/controller.js | 571 --------
.../public/feature/metrics/page/dashboard.html | 250 ----
.../app/public/feature/topology/controller.js | 257 ----
.../feature/topology/page/management.html | 52 -
.../feature/topology/page/monitoring.html | 151 ---
.../public/feature/userProfile/controller.js | 268 ----
.../public/feature/userProfile/page/detail.html | 87 --
.../public/feature/userProfile/page/list.html | 138 --
.../main/webapp/app/public/images/favicon.png | Bin 4209 -> 0 bytes
.../webapp/app/public/images/favicon_white.png | Bin 1621 -> 0 bytes
.../src/main/webapp/app/public/js/app.config.js | 126 --
.../src/main/webapp/app/public/js/app.js | 499 -------
.../src/main/webapp/app/public/js/app.time.js | 70 -
.../src/main/webapp/app/public/js/app.ui.js | 76 --
.../src/main/webapp/app/public/js/common.js | 304 -----
.../app/public/js/components/charts/line3d.js | 348 -----
.../webapp/app/public/js/components/file.js | 50 -
.../webapp/app/public/js/components/main.js | 19 -
.../webapp/app/public/js/components/nvd3.js | 418 ------
.../app/public/js/components/sortTable.js | 113 --
.../webapp/app/public/js/components/sortable.js | 166 ---
.../webapp/app/public/js/components/tabs.js | 247 ----
.../webapp/app/public/js/ctrl/authController.js | 91 --
.../public/js/ctrl/configurationController.js | 377 ------
.../src/main/webapp/app/public/js/ctrl/main.js | 42 -
.../webapp/app/public/js/srv/applicationSrv.js | 170 ---
.../app/public/js/srv/authorizationSrv.js | 143 --
.../webapp/app/public/js/srv/entitiesSrv.js | 301 -----
.../src/main/webapp/app/public/js/srv/main.js | 72 --
.../main/webapp/app/public/js/srv/pageSrv.js | 131 --
.../main/webapp/app/public/js/srv/siteSrv.js | 193 ---
.../src/main/webapp/app/public/js/srv/uiSrv.js | 240 ----
.../webapp/app/public/js/srv/wrapStateSrv.js | 109 --
eagle-webservice/src/main/webapp/grunt.json | 42 -
eagle-webservice/src/main/webapp/index.html | 28 -
eagle-webservice/src/main/webapp/package.json | 48 -
eagle-webservice/ui-build.sh | 23 +-
180 files changed, 19503 insertions(+), 10801 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/compareCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/compareCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/compareCtrl.js
new file mode 100644
index 0000000..121e80e
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/compareCtrl.js
@@ -0,0 +1,380 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ /**
+ * @param {{}} $scope
+ * @param {{}} $scope.trendChart
+ */
+ jpmApp.controller("compareCtrl", function ($q, $wrapState, $scope, PageConfig, Time, Entity, JPM) {
+ $scope.site = $wrapState.param.siteId;
+ $scope.jobDefId = $wrapState.param.jobDefId;
+
+ $scope.jobTrendCategory = [];
+
+ $scope.fromJob = null;
+ $scope.toJob = null;
+
+ PageConfig.title = "Job History";
+ PageConfig.subTitle = $scope.jobDefId;
+
+ $scope.getStateClass = JPM.getStateClass;
+
+ var browserAction = true;
+
+ // ==================================================================
+ // = Fetch Job List =
+ // ==================================================================
+ var jobList = $scope.jobList = JPM.findMRJobs($scope.site, $scope.jobDefId);
+ jobList._promise.then(function () {
+ if(jobList.length <= 1) {
+ $.dialog({
+ title: "No statistic info",
+ content: "Current job do not have enough statistic info. Please check 'jobDefId' if your job have run many times."
+ });
+ }
+
+ function findJob(jobId) {
+ return common.array.find(jobId, jobList, "tags.jobId");
+ }
+
+ var TASK_BUCKET_TIMES = [0, 30, 60, 120, 300, 600, 1800, 3600, 7200, 18000];
+ function taskDistribution(jobId) {
+ return JPM.taskDistribution($scope.site, jobId, TASK_BUCKET_TIMES.join(",")).then(
+ /**
+ * @param {{}} res
+ * @param {{}} res.data
+ * @param {[]} res.data.finishedTaskCount
+ * @param {[]} res.data.runningTaskCount
+ */
+ function (res) {
+ var result = {};
+ var data = res.data;
+ var finishedTaskCount = data.finishedTaskCount;
+ var runningTaskCount = data.runningTaskCount;
+
+ /**
+ * @param {number} item.taskCount
+ */
+ var finishedTaskData = $.map(finishedTaskCount, function (item) {
+ return item.taskCount;
+ });
+ /**
+ * @param {number} item.taskCount
+ */
+ var runningTaskData = $.map(runningTaskCount, function (item) {
+ return item.taskCount;
+ });
+
+ result.taskSeries = [{
+ name: "Finished Tasks",
+ type: "bar",
+ stack: jobId,
+ data: finishedTaskData
+ }, {
+ name: "Running Tasks",
+ type: "bar",
+ stack: jobId,
+ data: runningTaskData
+ }];
+
+ result.finishedTaskCount = finishedTaskCount;
+ result.runningTaskCount = runningTaskCount;
+
+ return result;
+ });
+ }
+
+ var taskDistributionCategory = $.map(TASK_BUCKET_TIMES, function (current, i) {
+ var curDes = Time.diffStr(TASK_BUCKET_TIMES[i] * 1000);
+ var nextDes = Time.diffStr(TASK_BUCKET_TIMES[i + 1] * 1000);
+
+ if(!curDes && nextDes) {
+ return "<" + nextDes;
+ } else if(nextDes) {
+ return curDes + "\n~\n" + nextDes;
+ }
+ return ">" + curDes;
+ });
+
+ // ========================= Job Trend ==========================
+ function refreshParam() {
+ browserAction = false;
+ $wrapState.go(".", {
+ from: common.getValueByPath($scope.fromJob, "tags.jobId"),
+ to: common.getValueByPath($scope.toJob, "tags.jobId")
+ });
+ setTimeout(function () {
+ browserAction = true;
+ }, 0);
+ }
+
+ function getMarkPoint(name, x, y, color) {
+ return {
+ name: name,
+ silent: true,
+ coord: [x, y],
+ symbolSize: 20,
+ label: {
+ normal: { show: false },
+ emphasis: { show: false }
+ }, itemStyle: {
+ normal: { color: color }
+ }
+ };
+ }
+
+ function refreshTrendMarkPoint() {
+ var fromX = null, fromY = null;
+ var toX = null, toY = null;
+ $.each(jobList, function (index, job) {
+ if($scope.fromJob && $scope.fromJob.tags.jobId === job.tags.jobId) {
+ fromX = index;
+ fromY = job.durationTime;
+ }
+ if($scope.toJob && $scope.toJob.tags.jobId === job.tags.jobId) {
+ toX = index;
+ toY = job.durationTime;
+ }
+ });
+
+ markPoint.data = [];
+ if(!common.isEmpty(fromX)) {
+ markPoint.data.push(getMarkPoint("<From Job>", fromX, fromY, "#00c0ef"));
+ }
+ if(!common.isEmpty(toX)) {
+ markPoint.data.push(getMarkPoint("<To Job>", toX, toY, "#3c8dbc"));
+ }
+
+ $scope.trendChart.refresh();
+ }
+
+ var jobListTrend = $.map(jobList, function (job) {
+ var time = Time.format(job.startTime);
+ $scope.jobTrendCategory.push(time);
+ return job.durationTime;
+ });
+
+ $scope.jobTrendOption = {
+ yAxis: [{
+ axisLabel: {
+ formatter: function (value) {
+ return Time.diffStr(value);
+ }
+ }
+ }],
+ tooltip: {
+ formatter: function (points) {
+ var point = points[0];
+ return point.name + "<br/>" +
+ '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+ point.seriesName + ": " + Time.diffStr(point.value);
+ }
+ }
+ };
+
+ var markPoint = {
+ data: [],
+ silent: true
+ };
+
+ $scope.jobTrendSeries = [{
+ name: "Job Duration",
+ type: "line",
+ data: jobListTrend,
+ symbolSize: 10,
+ showSymbol: false,
+ markPoint: markPoint
+ }];
+
+ $scope.compareJobSelect = function (e, job) {
+ var index = e.dataIndex;
+ job = job || jobList[index];
+ if(!job) return;
+
+ var event = e.event ? e.event.event : e;
+
+ if(event.ctrlKey) {
+ $scope.fromJob = job;
+ } else if(event.shiftKey) {
+ $scope.toJob = job;
+ } else {
+ if ($scope.fromJob) {
+ $scope.toJob = $scope.fromJob;
+ }
+ $scope.fromJob = job;
+ }
+
+ refreshTrendMarkPoint();
+ refreshParam();
+ refreshComparisonDashboard();
+ };
+
+ $scope.exchangeJobs = function () {
+ var tmpJob = $scope.fromJob;
+ $scope.fromJob = $scope.toJob;
+ $scope.toJob = tmpJob;
+ refreshTrendMarkPoint();
+ refreshParam();
+ refreshComparisonDashboard();
+ };
+
+ // ======================= Job Comparison =======================
+ $scope.taskOption = {
+ xAxis: {axisTick: { show: true }}
+ };
+
+ function getComparedValue(path) {
+ var val1 = common.getValueByPath($scope.fromJob, path);
+ var val2 = common.getValueByPath($scope.toJob, path);
+ return common.number.compare(val1, val2);
+ }
+
+ $scope.jobCompareClass = function (path) {
+ var diff = getComparedValue(path);
+ if(typeof diff !== "number" || Math.abs(diff) < 0.01) return "hide";
+ if(diff < 0.05) return "label label-success";
+ if(diff < 0.15) return "label label-warning";
+ return "label label-danger";
+ };
+
+ $scope.jobCompareValue = function (path) {
+ var diff = getComparedValue(path);
+ return (diff >= 0 ? "+" : "") + Math.floor(diff * 100) + "%";
+ };
+
+ /**
+ * get 2 interval data list category. (minutes level)
+ * @param {[]} list1
+ * @param {[]} list2
+ * @return {Array}
+ */
+ function intervalCategories(list1, list2) {
+ var len = Math.max(list1.length, list2.length);
+ var category = [];
+ for(var i = 0 ; i < len ; i += 1) {
+ category.push((i + 1) + "min");
+ }
+ return category;
+ }
+
+ function refreshComparisonDashboard() {
+ if(!$scope.fromJob || !$scope.toJob) return;
+
+ var fromJobCond = {
+ jobId: $scope.fromJob.tags.jobId,
+ site: $scope.site
+ };
+ var toJobCond = {
+ jobId: $scope.toJob.tags.jobId,
+ site: $scope.site
+ };
+
+ var from_startTime = Time($scope.fromJob.startTime).subtract(1, "hour");
+ var from_endTime = ($scope.fromJob.endTime ? Time($scope.fromJob.endTime) : Time()).add(1, "hour");
+ var to_startTime = Time($scope.toJob.startTime).subtract(1, "hour");
+ var to_endTime = ($scope.toJob.endTime ? Time($scope.toJob.endTime) : Time()).add(1, "hour");
+
+ $scope.fromJob._cache = $scope.fromJob._cache || {};
+ $scope.toJob._cache = $scope.toJob._cache || {};
+
+ /**
+ * Generate metric level chart series
+ * @param {string} metric
+ * @param {string} seriesName
+ */
+ function metricComparison(metric, seriesName) {
+ var from_metric = $scope.fromJob._cache[metric] =
+ $scope.fromJob._cache[metric] || JPM.metrics(fromJobCond, metric, from_startTime, from_endTime);
+ var to_metric = $scope.toJob._cache[metric] =
+ $scope.toJob._cache[metric] || JPM.metrics(toJobCond, metric, to_startTime, to_endTime);
+
+ var holder = {};
+
+ $q.all([from_metric._promise, to_metric._promise]).then(function () {
+ from_metric = JPM.metricsToInterval(from_metric, 1000 * 60);
+ to_metric = JPM.metricsToInterval(to_metric, 1000 * 60);
+
+ var series_from = JPM.metricsToSeries("from job " + seriesName, from_metric, true);
+ var series_to = JPM.metricsToSeries("to job " + seriesName, to_metric, true);
+
+ holder.categories = intervalCategories(from_metric, to_metric);
+ holder.series = [series_from, series_to];
+ });
+
+ return holder;
+ }
+
+ // Dashboard1: Containers metrics
+ $scope.comparisonChart_Container = metricComparison("hadoop.job.runningcontainers", "running containers");
+
+ // Dashboard 2: Allocated
+ $scope.comparisonChart_allocatedMB = metricComparison("hadoop.job.allocatedmb", "allocated MB");
+
+ // Dashboard 3: vCores
+ $scope.comparisonChart_vCores = metricComparison("hadoop.job.allocatedvcores", "vCores");
+
+ // Dashboard 4: Task distribution
+ var from_distributionPromise = $scope.fromJob._cache.distributionPromise =
+ $scope.fromJob._cache.distributionPromise || taskDistribution($scope.fromJob.tags.jobId);
+ var to_distributionPromise = $scope.toJob._cache.distributionPromise =
+ $scope.toJob._cache.distributionPromise || taskDistribution($scope.toJob.tags.jobId);
+ var comparisonChart_taskDistribution = $scope.comparisonChart_taskDistribution = {
+ categories: taskDistributionCategory
+ };
+
+ $q.all([from_distributionPromise, to_distributionPromise]).then(function (args) {
+ var from_data = args[0];
+ var to_data = args[1];
+
+ var from_taskSeries = $.map(from_data.taskSeries, function (series) {
+ return $.extend({}, series, {name: "From " + series.name});
+ });
+ var to_taskSeries = $.map(to_data.taskSeries, function (series) {
+ return $.extend({}, series, {name: "To " + series.name});
+ });
+
+ comparisonChart_taskDistribution.series = from_taskSeries.concat(to_taskSeries);
+ });
+ }
+
+ // ======================== Job Refresh =========================
+ function jobRefresh() {
+ $scope.fromJob = findJob($wrapState.param.from);
+ $scope.toJob = findJob($wrapState.param.to);
+ $(window).resize();
+ refreshTrendMarkPoint();
+ refreshComparisonDashboard();
+ }
+
+ // ======================= Initialization =======================
+ jobRefresh();
+
+ $scope.$on('$locationChangeSuccess', function() {
+ if(browserAction) {
+ jobRefresh();
+ }
+ });
+ });
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/detailCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/detailCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/detailCtrl.js
new file mode 100644
index 0000000..be7631f
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/detailCtrl.js
@@ -0,0 +1,192 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ jpmApp.controller("detailCtrl", function ($q, $wrapState, $element, $scope, PageConfig, Time, Entity, JPM) {
+ var TASK_BUCKET_TIMES = [0, 30, 60, 120, 300, 600, 1800, 3600, 7200, 18000];
+ var i;
+ var startTime, endTime;
+ var metric_allocatedMB, metric_allocatedVCores, metric_runningContainers;
+ var nodeTaskCountList;
+
+ $scope.site = $wrapState.param.siteId;
+ $scope.jobId = $wrapState.param.jobId;
+
+ PageConfig.title = "Job";
+ PageConfig.subTitle = $scope.jobId;
+
+ $scope.getStateClass = JPM.getStateClass;
+ $scope.compareChart = null;
+
+ var jobCond = {
+ jobId: $scope.jobId,
+ site: $scope.site
+ };
+
+ function taskDistribution(jobId) {
+ return JPM.taskDistribution($scope.site, jobId, TASK_BUCKET_TIMES.join(",")).then(
+ /**
+ * @param {{}} res
+ * @param {{}} res.data
+ * @param {[]} res.data.finishedTaskCount
+ * @param {[]} res.data.runningTaskCount
+ */
+ function (res) {
+ var result = {};
+ var data = res.data;
+ var finishedTaskCount = data.finishedTaskCount;
+ var runningTaskCount = data.runningTaskCount;
+
+ /**
+ * @param {number} item.taskCount
+ */
+ var finishedTaskData = $.map(finishedTaskCount, function (item) {
+ return item.taskCount;
+ });
+ /**
+ * @param {number} item.taskCount
+ */
+ var runningTaskData = $.map(runningTaskCount, function (item) {
+ return item.taskCount;
+ });
+
+ result.taskSeries = [{
+ name: "Finished Tasks",
+ type: "bar",
+ stack: jobId,
+ data: finishedTaskData
+ }, {
+ name: "Running Tasks",
+ type: "bar",
+ stack: jobId,
+ data: runningTaskData
+ }];
+
+ result.finishedTaskCount = finishedTaskCount;
+ result.runningTaskCount = runningTaskCount;
+
+ return result;
+ });
+ }
+
+ $scope.taskCategory = $.map(TASK_BUCKET_TIMES, function (current, i) {
+ var curDes = Time.diffStr(TASK_BUCKET_TIMES[i] * 1000);
+ var nextDes = Time.diffStr(TASK_BUCKET_TIMES[i + 1] * 1000);
+
+ if(!curDes && nextDes) {
+ return "<" + nextDes;
+ } else if(nextDes) {
+ return curDes + "\n~\n" + nextDes;
+ }
+ return ">" + curDes;
+ });
+
+ // =========================================================================
+ // = Fetch Job =
+ // =========================================================================
+ JPM.findMRJobs($scope.site, undefined, $scope.jobId)._promise.then(function (list) {
+ $scope.job = list[list.length - 1];
+ console.log("[JPM] Fetch job:", $scope.job);
+
+ if(!$scope.job) {
+ $.dialog({
+ title: "OPS!",
+ content: "Job not found!"
+ }, function () {
+ $wrapState.go("jpmList", {siteId: $scope.site});
+ });
+ return;
+ }
+
+ startTime = Time($scope.job.startTime).subtract(1, "hour");
+ endTime = Time().add(1, "hour");
+ $scope.startTimestamp = $scope.job.startTime;
+ $scope.endTimestamp = $scope.job.endTime;
+ $scope.isRunning = !$scope.job.currentState || ($scope.job.currentState || "").toUpperCase() === 'RUNNING';
+
+ // Dashboard 1: Allocated MB
+ metric_allocatedMB = JPM.metrics(jobCond, "hadoop.job.allocatedmb", startTime, endTime);
+
+ metric_allocatedMB._promise.then(function () {
+ var series_allocatedMB = JPM.metricsToSeries("allocated MB", metric_allocatedMB);
+ $scope.allocatedSeries = [series_allocatedMB];
+ });
+
+ // Dashboard 2: vCores & Containers metrics
+ metric_allocatedVCores = JPM.metrics(jobCond, "hadoop.job.allocatedvcores", startTime, endTime);
+ metric_runningContainers = JPM.metrics(jobCond, "hadoop.job.runningcontainers", startTime, endTime);
+
+ $q.all([metric_allocatedVCores._promise, metric_runningContainers._promise]).then(function () {
+ var series_allocatedVCores = JPM.metricsToSeries("vCores", metric_allocatedVCores);
+ var series_runningContainers = JPM.metricsToSeries("running containers", metric_runningContainers);
+ $scope.vCoresSeries = [series_allocatedVCores, series_runningContainers];
+ });
+
+ // Dashboard 3: Task duration
+ taskDistribution($scope.job.tags.jobId).then(function (data) {
+ $scope.taskSeries = data.taskSeries;
+
+ $scope.taskSeriesClick = function (e) {
+ var taskCount = e.seriesIndex === 0 ? data.finishedTaskCount : data.runningTaskCount;
+ $scope.taskBucket = taskCount[e.dataIndex];
+ };
+
+ $scope.backToTaskSeries = function () {
+ $scope.taskBucket = null;
+ setTimeout(function () {
+ $(window).resize();
+ }, 0);
+ };
+ });
+
+ // Dashboard 4: Running task
+ nodeTaskCountList = JPM.groups(
+ $scope.isRunning ? "RunningTaskExecutionService" : "TaskExecutionService",
+ jobCond, ["hostname"], "count", null, startTime, endTime, null, 1000000);
+ nodeTaskCountList._promise.then(function () {
+ var nodeTaskCountMap = [];
+
+ $.each(nodeTaskCountList, function (i, obj) {
+ var count = obj.value[0];
+ nodeTaskCountMap[count] = (nodeTaskCountMap[count] || 0) + 1;
+ });
+
+ $scope.nodeTaskCountCategory = [];
+ for(i = 0 ; i < nodeTaskCountMap.length ; i += 1) {
+ nodeTaskCountMap[i] = nodeTaskCountMap[i] || 0;
+ $scope.nodeTaskCountCategory.push(i + " tasks");
+ }
+
+ nodeTaskCountMap.splice(0, 1);
+ $scope.nodeTaskCountCategory.splice(0, 1);
+
+ $scope.nodeTaskCountSeries = [{
+ name: "node count",
+ type: "bar",
+ data: nodeTaskCountMap
+ }];
+ });
+
+ });
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/jobTaskCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/jobTaskCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/jobTaskCtrl.js
new file mode 100644
index 0000000..9f0e7f4
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/jobTaskCtrl.js
@@ -0,0 +1,551 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ var TREND_INTERVAL = 60;
+ var SCHEDULE_BUCKET_COUNT = 30;
+ var TASK_STATUS = ["SUCCEEDED", "FAILED", "KILLED"];
+ var TASK_TYPE = ["MAP", "REDUCE"];
+ var DURATION_BUCKETS = [0, 30 * 1000, 60 * 1000, 120 * 1000, 300 * 1000, 600 * 1000, 1800 * 1000, 3600 * 1000, 2 * 3600 * 1000, 3 * 3600 * 1000];
+ var TASK_FIELDS = [
+ "rack",
+ "hostname",
+ "taskType",
+ "taskId",
+ "taskStatus",
+ "startTime",
+ "endTime",
+ "jobCounters"
+ ];
+
+ function getCommonHeatMapOption(categoryList, maxCount) {
+ return {
+ animation: false,
+ tooltip: {
+ trigger: 'item'
+ },
+ xAxis: {splitArea: {show: true}},
+ yAxis: [{
+ type: 'category',
+ data: categoryList,
+ splitArea: {show: true},
+ axisTick: {show: false}
+ }],
+ grid: { bottom: "50" },
+ visualMap: {
+ min: 0,
+ max: maxCount,
+ calculable: true,
+ orient: 'horizontal',
+ left: 'right',
+ inRange: {
+ color: ["#00a65a", "#ffdc62", "#dd4b39"]
+ }
+ }
+ };
+ }
+
+ function getCommonHeatMapSeries(name, data) {
+ return {
+ name: name,
+ type: "heatmap",
+ data: data,
+ itemStyle: {
+ normal: {
+ borderColor: "#FFF"
+ }
+ }
+ };
+ }
+
+ /**
+ * @typedef {{}} Task
+ * @property {string} taskStatus
+ * @property {number} startTime
+ * @property {number} endTime
+ * @property {{}} jobCounters
+ * @property {{}} jobCounters.counters
+ * @property {{}} tags
+ * @property {string} tags.taskType
+ * @property {number} _bucket
+ * @property {number} _bucketStart
+ * @property {number} _bucketEnd
+ * @property {number} _duration
+ * @property {number} _durationBucket
+ */
+
+ jpmApp.controller("jobTaskCtrl", function ($wrapState, $scope, PageConfig, Time, JPM) {
+ $scope.site = $wrapState.param.siteId;
+ $scope.jobId = $wrapState.param.jobId;
+
+ var startTime = Number($wrapState.param.startTime);
+ var endTime = Number($wrapState.param.endTime);
+
+ PageConfig.title = "Task";
+ PageConfig.subTitle = $scope.jobId;
+
+ var timeDiff = endTime - startTime;
+ var timeDes = Math.ceil(timeDiff / SCHEDULE_BUCKET_COUNT);
+
+ $scope.bucketScheduleCategory = [];
+ for(var i = 0 ; i < SCHEDULE_BUCKET_COUNT ; i += 1) {
+ $scope.bucketScheduleCategory.push(Time.format(startTime + i * timeDes, "HH:mm:SS") + "\n~\n" + Time.format(startTime + (i + 1) * timeDes, "HH:mm:SS"));
+ }
+
+ $scope.bucketDurationCategory = [];
+ $.each(DURATION_BUCKETS, function (i, start) {
+ var end = DURATION_BUCKETS[i + 1];
+ if(!start) {
+ $scope.bucketDurationCategory.push("<" + Time.diffStr(end));
+ } else if(!end) {
+ $scope.bucketDurationCategory.push(">" + Time.diffStr(start));
+ } else {
+ $scope.bucketDurationCategory.push(Time.diffStr(start) + "\n~\n" + Time.diffStr(end));
+ }
+ });
+
+ // ============================================================================
+ // ============================================================================
+ // == Fetch Task ==
+ // ============================================================================
+ // ============================================================================
+ $scope.list = JPM.list("TaskExecutionService", {site: $scope.site, jobId: $scope.jobId}, startTime, endTime, TASK_FIELDS, 1000000);
+ $scope.list._promise.then(function () {
+ var i;
+
+ // ========================= Schedule Trend =========================
+ var trend_map_countList = [];
+ var trend_reduce_countList = [];
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var _task = {
+ _bucketStart: Math.floor((task.startTime - startTime) / TREND_INTERVAL),
+ _bucketEnd: Math.floor((task.endTime - startTime) / TREND_INTERVAL)
+ };
+
+ switch (task.tags.taskType) {
+ case "MAP":
+ fillBucket(trend_map_countList, _task);
+ break;
+ case "REDUCE":
+ fillBucket(trend_reduce_countList, _task);
+ break;
+ default:
+ console.warn("Task type not match:", task.tags.taskType, task);
+ }
+ });
+
+ $scope.scheduleCategory = [];
+ for(i = 0 ; i < Math.max(trend_map_countList.length, trend_reduce_countList.length) ; i += 1) {
+ $scope.scheduleCategory.push(Time.format(startTime + i * TREND_INTERVAL).replace(" ", "\n"));
+ }
+
+ $scope.scheduleSeries = [{
+ name: "Map Task Count",
+ type: "line",
+ showSymbol: false,
+ areaStyle: {normal: {}},
+ data: trend_map_countList
+ }, {
+ name: "Reduce Task Count",
+ type: "line",
+ showSymbol: false,
+ areaStyle: {normal: {}},
+ data: trend_reduce_countList
+ }];
+
+ // ======================= Bucket Distribution ======================
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ task._bucketStart = Math.floor((task.startTime - startTime) / timeDes);
+ task._bucketEnd = Math.floor((task.endTime - startTime) / timeDes);
+ task._duration = task.endTime - task.startTime;
+ task._durationBucket = common.number.inRange(DURATION_BUCKETS, task._duration);
+ });
+
+ // ==================================================================
+ // = Schedule Distribution =
+ // ==================================================================
+ function fillBucket(countList, task, maxCount) {
+ for(var bucketId = task._bucketStart ; bucketId <= task._bucketEnd ; bucketId += 1) {
+ var count = countList[bucketId] = (countList[bucketId] || 0) + 1;
+ maxCount = Math.max(maxCount, count);
+ }
+ return maxCount;
+ }
+
+ function getHeatMapOption(categoryList, maxCount) {
+ var option = getCommonHeatMapOption(categoryList, maxCount);
+ return common.merge(option, {
+ tooltip: {
+ formatter: function (point) {
+ if(point.data) {
+ return categoryList[point.data[1]] + ":<br/>" +
+ '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+ $scope.bucketScheduleCategory[point.data[0]] + ": " +
+ point.data[2];
+ }
+ return "";
+ }
+ }
+ });
+ }
+
+ function bucketToSeries(categoryList, buckets, name) {
+ var bucket_data = $.map(categoryList, function (category, index) {
+ var list = [];
+ var dataList = buckets[category] || [];
+ for(var i = 0 ; i < SCHEDULE_BUCKET_COUNT ; i += 1) {
+ list.push([i, index, dataList[i] || 0]);
+ }
+ return list;
+ });
+
+ return [common.merge(getCommonHeatMapSeries(name, bucket_data), {
+ label: {
+ normal: {
+ show: true,
+ formatter: function (point) {
+ if(point.data[2] === 0) return "-";
+ return " ";
+ }
+ }
+ }
+ })];
+ }
+
+ // ======================== Status Statistic ========================
+ var bucket_status = {};
+ var bucket_status_maxCount = 0;
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var countList = bucket_status[task.taskStatus] = (bucket_status[task.taskStatus] || []);
+
+ bucket_status_maxCount = fillBucket(countList, task, bucket_status_maxCount);
+ });
+
+ $scope.statusSeries = bucketToSeries(TASK_STATUS, bucket_status, "Task Status");
+ $scope.statusOption = getHeatMapOption(TASK_STATUS, bucket_status_maxCount);
+
+ // ======================= Duration Statistic =======================
+ var TASK_DURATION = [0, 120 * 1000, 300 * 1000, 600 * 1000, 1800 * 1000, 3600 * 1000];
+ var bucket_durations = {};
+ var bucket_durations_maxCount = 0;
+
+ var TASK_DURATION_DISTRIBUTION = $.map(TASK_DURATION, function (start, i) {
+ var end = TASK_DURATION[i + 1];
+ if(i === 0) {
+ return "<" + Time.diffStr(end);
+ } else if(end) {
+ return Time.diffStr(start) + "~" + Time.diffStr(end);
+ }
+ return ">" + Time.diffStr(start);
+ });
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_DURATION_DISTRIBUTION[common.number.inRange(TASK_DURATION, task._duration)];
+ var countList = bucket_durations[durationBucket] = (bucket_durations[durationBucket] || []);
+
+ bucket_durations_maxCount = fillBucket(countList, task, bucket_durations_maxCount);
+ });
+
+ $scope.durationSeries = bucketToSeries(TASK_DURATION_DISTRIBUTION, bucket_durations, "Task Duration Distribution");
+ $scope.durationOption = getHeatMapOption(TASK_DURATION_DISTRIBUTION, bucket_durations_maxCount);
+
+ // ======================= HDFS Read Statistic ======================
+ var TASK_HDFS_BYTES = [0, 5 * 1024 * 1024, 20 * 1024 * 1024, 100 * 1024 * 1024, 256 * 1024 * 1024, 1024 * 1024 * 1024];
+ var bucket_hdfs_reads = {};
+ var bucket_hdfs_reads_maxCount = 0;
+
+ var TASK_HDFS_DISTRIBUTION = $.map(TASK_HDFS_BYTES, function (start, i) {
+ var end = TASK_HDFS_BYTES[i + 1];
+ if(i === 0) {
+ return "<" + common.number.abbr(end, true);
+ } else if(end) {
+ return common.number.abbr(start, true) + "~" + common.number.abbr(end, true);
+ }
+ return ">" + common.number.abbr(start, true);
+ });
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_HDFS_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)];
+ var countList = bucket_hdfs_reads[durationBucket] = (bucket_hdfs_reads[durationBucket] || []);
+
+ bucket_hdfs_reads_maxCount = fillBucket(countList, task, bucket_hdfs_reads_maxCount);
+ });
+
+ $scope.hdfsReadSeries = bucketToSeries(TASK_HDFS_DISTRIBUTION, bucket_hdfs_reads, "Task HDFS Read Distribution");
+ $scope.hdfsReadOption = getHeatMapOption(TASK_HDFS_DISTRIBUTION, bucket_hdfs_reads_maxCount);
+
+ // ====================== HDFS Write Statistic ======================
+ var bucket_hdfs_writes = {};
+ var bucket_hdfs_writes_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_HDFS_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)];
+ var countList = bucket_hdfs_writes[durationBucket] = (bucket_hdfs_writes[durationBucket] || []);
+
+ bucket_hdfs_writes_maxCount = fillBucket(countList, task, bucket_hdfs_writes_maxCount);
+ });
+
+ $scope.hdfsWriteSeries = bucketToSeries(TASK_HDFS_DISTRIBUTION, bucket_hdfs_writes, "Task HDFS Write Distribution");
+ $scope.hdfsWriteOption = getHeatMapOption(TASK_HDFS_DISTRIBUTION, bucket_hdfs_writes_maxCount);
+
+ // ====================== Local Read Statistic ======================
+ var TASK_LOCAL_BYTES = [0, 20 * 1024 * 1024, 100 * 1024 * 1024, 256 * 1024 * 1024, 1024 * 1024 * 1024, 2 * 1024 * 1024 * 1024];
+ var bucket_local_reads = {};
+ var bucket_local_reads_maxCount = 0;
+
+ var TASK_LOCAL_DISTRIBUTION = $.map(TASK_LOCAL_BYTES, function (start, i) {
+ var end = TASK_LOCAL_BYTES[i + 1];
+ if(i === 0) {
+ return "<" + common.number.abbr(end, true);
+ } else if(end) {
+ return common.number.abbr(start, true) + "~" + common.number.abbr(end, true);
+ }
+ return ">" + common.number.abbr(start, true);
+ });
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_LOCAL_DISTRIBUTION[common.number.inRange(TASK_LOCAL_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_READ)];
+ var countList = bucket_local_reads[durationBucket] = (bucket_local_reads[durationBucket] || []);
+
+ bucket_local_reads_maxCount = fillBucket(countList, task, bucket_local_reads_maxCount);
+ });
+
+ $scope.localReadSeries = bucketToSeries(TASK_LOCAL_DISTRIBUTION, bucket_local_reads, "Task Local Read Distribution");
+ $scope.localReadOption = getHeatMapOption(TASK_LOCAL_DISTRIBUTION, bucket_local_reads_maxCount);
+
+ // ====================== Local Write Statistic =====================
+ var bucket_local_writes = {};
+ var bucket_local_writes_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_LOCAL_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_WRITTEN)];
+ var countList = bucket_local_writes[durationBucket] = (bucket_local_writes[durationBucket] || []);
+
+ bucket_local_writes_maxCount = fillBucket(countList, task, bucket_local_writes_maxCount);
+ });
+
+ $scope.localWriteSeries = bucketToSeries(TASK_LOCAL_DISTRIBUTION, bucket_local_writes, "Task Local Write Distribution");
+ $scope.localWriteOption = getHeatMapOption(TASK_LOCAL_DISTRIBUTION, bucket_local_writes_maxCount);
+
+ // ==================================================================
+ // = Duration Distribution =
+ // ==================================================================
+ function fillDurationBucket(countList, task, maxCount) {
+ var count = countList[task._durationBucket] = (countList[task._durationBucket] || 0) + 1;
+ maxCount = Math.max(maxCount, count);
+ return maxCount;
+ }
+
+ function getDurationHeatMapOption(categoryList, maxCount) {
+ var option = getCommonHeatMapOption(categoryList, maxCount);
+ return common.merge(option, {
+ tooltip: {
+ formatter: function (point) {
+ if(point.data) {
+ return categoryList[point.data[1]] + ":<br/>" +
+ '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+ $scope.bucketDurationCategory[point.data[0]] + ": " +
+ point.data[2];
+ }
+ return "";
+ }
+ }
+ });
+ }
+
+ function bucketToDurationSeries(categoryList, buckets, name) {
+ var bucket_data = $.map(categoryList, function (category, index) {
+ var list = [];
+ var dataList = buckets[category] || [];
+ for(var i = 0 ; i < DURATION_BUCKETS.length ; i += 1) {
+ list.push([i, index, dataList[i] || 0]);
+ }
+ return list;
+ });
+
+ return [common.merge(getCommonHeatMapSeries(name, bucket_data), {
+ label: {
+ normal: {
+ show: true,
+ formatter: function (point) {
+ if(point.data[2] === 0) return "-";
+ return point.data[2] + "";
+ }
+ }
+ }
+ })];
+ }
+
+ // ======================== Status Statistic ========================
+ var duration_status = {};
+ var duration_status_maxCount = 0;
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var countList = duration_status[task.taskStatus] = (duration_status[task.taskStatus] || []);
+
+ duration_status_maxCount = fillDurationBucket(countList, task, duration_status_maxCount);
+ });
+
+ $scope.durationStatusSeries = bucketToDurationSeries(TASK_STATUS, duration_status, "Task Status");
+ $scope.durationStatusOption = getDurationHeatMapOption(TASK_STATUS, duration_status_maxCount);
+
+ // ===================== Map / Reduce Statistic =====================
+ var mapReduce_status = {};
+ var mapReduce_status_maxCount = 0;
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var countList = mapReduce_status[task.tags.taskType] = (mapReduce_status[task.tags.taskType] || []);
+
+ mapReduce_status_maxCount = fillDurationBucket(countList, task, mapReduce_status_maxCount);
+ });
+
+ $scope.durationMapReduceSeries = bucketToDurationSeries(TASK_TYPE, mapReduce_status, "Task Type");
+ $scope.durationMapReduceOption = getDurationHeatMapOption(TASK_TYPE, mapReduce_status_maxCount);
+
+ // ======================= HDFS Read Statistic ======================
+ var duration_hdfs_reads = {};
+ var duration_hdfs_reads_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_HDFS_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)];
+ var countList = duration_hdfs_reads[durationBucket] = (duration_hdfs_reads[durationBucket] || []);
+
+ duration_hdfs_reads_maxCount = fillDurationBucket(countList, task, duration_hdfs_reads_maxCount);
+ });
+
+ $scope.durationHdfsReadSeries = bucketToDurationSeries(TASK_HDFS_DISTRIBUTION, duration_hdfs_reads, "Task HDFS Read Distribution");
+ $scope.durationHdfsReadOption = getDurationHeatMapOption(TASK_HDFS_DISTRIBUTION, duration_hdfs_reads_maxCount);
+
+ // ====================== HDFS Write Statistic ======================
+ var duration_hdfs_writes = {};
+ var duration_hdfs_writes_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_HDFS_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)];
+ var countList = duration_hdfs_writes[durationBucket] = (duration_hdfs_writes[durationBucket] || []);
+
+ duration_hdfs_writes_maxCount = fillDurationBucket(countList, task, duration_hdfs_writes_maxCount);
+ });
+
+ $scope.durationHdfsWriteSeries = bucketToDurationSeries(TASK_HDFS_DISTRIBUTION, duration_hdfs_writes, "Task HDFS Write Distribution");
+ $scope.durationHdfsWriteOption = getDurationHeatMapOption(TASK_HDFS_DISTRIBUTION, duration_hdfs_writes_maxCount);
+
+ // ====================== Local Read Statistic ======================
+ var duration_local_reads = {};
+ var duration_local_reads_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_LOCAL_DISTRIBUTION[common.number.inRange(TASK_LOCAL_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_READ)];
+ var countList = duration_local_reads[durationBucket] = (duration_local_reads[durationBucket] || []);
+
+ duration_local_reads_maxCount = fillDurationBucket(countList, task, duration_local_reads_maxCount);
+ });
+
+ $scope.durationLocalReadSeries = bucketToDurationSeries(TASK_LOCAL_DISTRIBUTION, duration_local_reads, "Task Local Read Distribution");
+ $scope.durationLocalReadOption = getDurationHeatMapOption(TASK_LOCAL_DISTRIBUTION, duration_local_reads_maxCount);
+
+ // ====================== Local Write Statistic =====================
+ var duration_local_writes = {};
+ var duration_local_writes_maxCount = 0;
+
+ $.each($scope.list,
+ /**
+ * @param {number} i
+ * @param {Task} task
+ */
+ function (i, task) {
+ var durationBucket = TASK_LOCAL_DISTRIBUTION[common.number.inRange(TASK_HDFS_BYTES, task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_WRITTEN)];
+ var countList = duration_local_writes[durationBucket] = (duration_local_writes[durationBucket] || []);
+
+ duration_local_writes_maxCount = fillDurationBucket(countList, task, duration_local_writes_maxCount);
+ });
+
+ $scope.durationLocalWriteSeries = bucketToDurationSeries(TASK_LOCAL_DISTRIBUTION, duration_local_writes, "Task Local Write Distribution");
+ $scope.durationLocalWriteOption = getDurationHeatMapOption(TASK_LOCAL_DISTRIBUTION, duration_local_writes_maxCount);
+ });
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/listCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/listCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/listCtrl.js
new file mode 100644
index 0000000..ff9ed5e
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/listCtrl.js
@@ -0,0 +1,239 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ var JOB_STATES = ["NEW", "NEW_SAVING", "SUBMITTED", "ACCEPTED", "RUNNING", "FINISHED", "SUCCEEDED", "FAILED", "KILLED"];
+
+ jpmApp.controller("listCtrl", function ($wrapState, $element, $scope, $q, PageConfig, Time, Entity, JPM) {
+ // Initialization
+ PageConfig.title = "YARN Jobs";
+ $scope.getStateClass = JPM.getStateClass;
+ $scope.tableScope = {};
+
+ $scope.site = $wrapState.param.siteId;
+ $scope.searchPathList = [["tags", "jobId"], ["tags", "user"], ["tags", "queue"], ["currentState"]];
+
+ function getCommonOption(left) {
+ return {
+ grid: {
+ left: left,
+ bottom: 20,
+ containLabel: false
+ }
+ };
+ }
+
+ $scope.chartLeftOption = getCommonOption(45);
+ $scope.chartRightOption = getCommonOption(80);
+
+ $scope.fillSearch = function (key) {
+ $("#jobList").find(".search-box input").val(key).trigger('input');
+ };
+
+ $scope.refreshList = function () {
+ var startTime = Time.startTime();
+ var endTime = Time.endTime();
+
+ // ==========================================================
+ // = Job List =
+ // ==========================================================
+
+ /**
+ * @namespace
+ * @property {[]} jobList
+ * @property {{}} jobList.tags unique job key
+ * @property {string} jobList.tags.jobId Job Id
+ * @property {string} jobList.tags.user Submit user
+ * @property {string} jobList.tags.queue Queue
+ * @property {string} jobList.currentState Job state
+ * @property {string} jobList.submissionTime Submission time
+ * @property {string} jobList.startTime Start time
+ * @property {string} jobList.endTime End time
+ * @property {string} jobList.numTotalMaps Maps count
+ * @property {string} jobList.numTotalReduces Reduce count
+ * @property {string} jobList.runningContainers Running container count
+ */
+
+ $scope.jobList = Entity.merge($scope.jobList, JPM.jobList({site: $scope.site}, startTime, endTime, [
+ "jobId",
+ "jobDefId",
+ "jobName",
+ "jobExecId",
+ "currentState",
+ "user",
+ "queue",
+ "submissionTime",
+ "startTime",
+ "endTime",
+ "numTotalMaps",
+ "numTotalReduces",
+ "runningContainers"
+ ], 100000));
+ $scope.jobStateList = [];
+
+ $scope.jobList._then(function () {
+ var now = Time();
+ var jobStates = {};
+ $.each($scope.jobList, function (i, job) {
+ jobStates[job.currentState] = (jobStates[job.currentState] || 0) + 1;
+ job.duration = Time.diff(job.startTime, job.endTime || now);
+ });
+
+ $scope.jobStateList = $.map(JOB_STATES, function (state) {
+ var value = jobStates[state];
+ delete jobStates[state];
+ if(!value) return null;
+ return {
+ key: state,
+ value: value
+ };
+ });
+
+ $.each(jobStates, function (key, value) {
+ $scope.jobStateList.push({
+ key: key,
+ value: value
+ });
+ });
+ });
+
+ // ===========================================================
+ // = Statistic Trend =
+ // ===========================================================
+ var interval = Time.diffInterval(startTime, endTime);
+ var intervalMin = interval / 1000 / 60;
+ var trendStartTime = Time.align(startTime, interval);
+ var trendEndTime = Time.align(endTime, interval);
+ var trendStartTimestamp = trendStartTime.valueOf();
+
+ // ==================== Running Job Trend ====================
+ JPM.get(JPM.getQuery("MR_JOB_COUNT"), {
+ site: $scope.site,
+ intervalInSecs: interval / 1000,
+ durationBegin: Time.format(trendStartTime),
+ durationEnd: Time.format(trendEndTime)
+ }).then(
+ /**
+ * @param {{}} res
+ * @param {{}} res.data
+ * @param {[]} res.data.jobCounts
+ */
+ function (res) {
+ var data = res.data;
+ var jobCounts = data.jobCounts;
+ var jobTypesData = {};
+ $.each(jobCounts,
+ /**
+ * @param index
+ * @param {{}} jobCount
+ * @param {{}} jobCount.timeBucket
+ * @param {{}} jobCount.jobCountByType
+ */
+ function (index, jobCount) {
+ $.each(jobCount.jobCountByType, function (type, count) {
+ var countList = jobTypesData[type] = jobTypesData[type] || [];
+ countList[index] = count;
+ });
+ });
+
+ $scope.runningTrendSeries = $.map(jobTypesData, function (countList, type) {
+ var dataList = [];
+ for(var i = 0 ; i < jobCounts.length ; i += 1) {
+ dataList[i] = {
+ x: trendStartTimestamp + i * interval,
+ y: countList[i] || 0
+ };
+ }
+
+ return {
+ name: type,
+ type: "line",
+ stack: "job",
+ showSymbol: false,
+ areaStyle: {normal: {}},
+ data: dataList
+ };
+ });
+ });
+
+ // ================= Running Container Trend =================
+ JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, "hadoop.cluster.runningcontainers", ["site"], "max(value)", intervalMin, trendStartTime, trendEndTime),
+ true)._promise.then(function (list) {
+ $scope.runningContainersSeries = [JPM.metricsToSeries("Running Containers", list, {areaStyle: {normal: {}}})];
+ });
+
+ // ================= Allocated vCores Trend ==================
+ JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, "hadoop.cluster.allocatedvcores", ["site"], "max(value)", intervalMin, trendStartTime, trendEndTime),
+ true)._promise.then(function (list) {
+ $scope.allocatedvcoresSeries = [JPM.metricsToSeries("Allocated vCores", list, {areaStyle: {normal: {}}})];
+ });
+
+ // ==================== AllocatedMB Trend ====================
+ var allocatedMBEntities = JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, "hadoop.cluster.allocatedmb", ["site"], "max(value)", intervalMin, trendStartTime, trendEndTime),
+ true);
+
+ var totalMemoryEntities = JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, "hadoop.cluster.totalmemory", ["site"], "max(value)", intervalMin, trendStartTime, trendEndTime),
+ true);
+
+ $q.all([allocatedMBEntities._promise, totalMemoryEntities._promise]).then(function (args) {
+ var allocatedMBList = args[0];
+ var totalMemoryList = args[1];
+
+ var mergedList = $.map(allocatedMBList, function (obj, index) {
+ var value = obj.value[0] / totalMemoryList[index].value[0] * 100 || 0;
+ return $.extend({}, obj, {
+ value: [value]
+ });
+ });
+
+ $scope.allocatedMBSeries = [JPM.metricsToSeries("Allocated GB", mergedList, {areaStyle: {normal: {}}})];
+ $scope.allocatedMBOption = $.extend({}, $scope.chartRightOption, {
+ yAxis: [{
+ axisLabel: {
+ formatter: "{value}%"
+ },
+ max: 100
+ }],
+ tooltip: {
+ formatter: function (points) {
+ var point = points[0];
+ var index = point.dataIndex;
+ return point.name + "<br/>" +
+ '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+ point.seriesName + ": " + common.number.format(allocatedMBList[index].value[0] / 1024, 2);
+ }
+ }
+ });
+ });
+ };
+
+ Time.onReload($scope.refreshList, $scope);
+
+ // Load list
+ $scope.refreshList();
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/overviewCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/overviewCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/overviewCtrl.js
new file mode 100644
index 0000000..7d7b949
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/overviewCtrl.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.
+ */
+
+(function () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ jpmApp.controller("overviewCtrl", function ($q, $wrapState, $element, $scope, $timeout, PageConfig, Time, Entity, JPM) {
+ var cache = {};
+ $scope.aggregationMap = {
+ job: "jobId",
+ user: "user",
+ jobType: "jobType"
+ };
+
+ $scope.site = $wrapState.param.siteId;
+
+ PageConfig.title = "Overview";
+
+ $scope.type = "job";
+
+ $scope.commonOption = {
+ animation: false,
+ tooltip: {
+ formatter: function (points) {
+ return points[0].name + "<br/>" +
+ $.map(points, function (point) {
+ return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+ point.seriesName + ": " +
+ common.number.format(point.value, true);
+ }).reverse().join("<br/>");
+ }
+ },
+ grid: {
+ top: 70
+ },
+ yAxis: [{
+ axisLabel: {formatter: function (value) {
+ return common.number.abbr(value, true);
+ }}
+ }]
+ };
+
+ // ======================================================================
+ // = Refresh Overview =
+ // ======================================================================
+ $scope.typeChange = function () {
+ $timeout($scope.refresh, 1);
+ };
+
+ // TODO: Optimize the chart count
+ // TODO: ECharts dynamic refresh series bug: https://github.com/ecomfe/echarts/issues/4033
+ $scope.refresh = function () {
+ var startTime = Time.startTime();
+ var endTime = Time.endTime();
+ var intervalMin = Time.diffInterval(startTime, endTime) / 1000 / 60;
+
+ function getTopList(metric, scopeVariable) {
+ var deferred = $q.defer();
+
+ metric = common.template(metric, {
+ type: $scope.type.toLocaleLowerCase()
+ });
+
+ if(scopeVariable) {
+ $scope[scopeVariable] = [];
+ $scope[scopeVariable]._done = false;
+ $scope[scopeVariable + "List"] = [];
+ }
+
+ var aggregation = $scope.aggregationMap[$scope.type];
+
+ var aggPromise = cache[metric] = cache[metric] || JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, metric, [aggregation], "avg(value), sum(value) desc", intervalMin, startTime, endTime, 10)
+ )._promise.then(function (list) {
+ var series = $.map(list, function (metrics) {
+ return JPM.metricsToSeries(metrics[0].tags[aggregation], metrics, {
+ stack: "stack",
+ areaStyle: {normal: {}}
+ });
+ });
+
+ var topList = $.map(series, function (series) {
+ return {
+ name: series.name,
+ total: common.number.sum(series.data, "y") * intervalMin
+ };
+ }).sort(function (a, b) {
+ return b.total - a.total;
+ });
+
+ return [series, topList];
+ });
+
+ aggPromise.then(function (args) {
+ if(scopeVariable) {
+ $scope[scopeVariable] = args[0];
+ $scope[scopeVariable]._done = true;
+ $scope[scopeVariable + "List"] = args[1];
+ }
+ });
+
+ return deferred.promise;
+ }
+
+ getTopList("hadoop.${type}.history.minute.cpu_milliseconds", "cpuUsageSeries");
+ getTopList("hadoop.${type}.history.minute.physical_memory_bytes", "physicalMemorySeries");
+ getTopList("hadoop.${type}.history.minute.virtual_memory_bytes", "virtualMemorySeries");
+ getTopList("hadoop.${type}.history.minute.hdfs_bytes_read", "hdfsBtyesReadSeries");
+ getTopList("hadoop.${type}.history.minute.hdfs_bytes_written", "hdfsBtyesWrittenSeries");
+ getTopList("hadoop.${type}.history.minute.hdfs_read_ops", "hdfsReadOpsSeries");
+ getTopList("hadoop.${type}.history.minute.hdfs_write_ops", "hdfsWriteOpsSeries");
+ getTopList("hadoop.${type}.history.minute.file_bytes_read", "fileBytesReadSeries");
+ getTopList("hadoop.${type}.history.minute.file_bytes_written", "fileBytesWrittenSeries");
+ };
+
+ Time.onReload(function () {
+ cache = {};
+ $scope.refresh();
+ }, $scope);
+ $scope.refresh();
+ });
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/statisticCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/statisticCtrl.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/statisticCtrl.js
new file mode 100644
index 0000000..6dff7a1
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/ctrl/statisticCtrl.js
@@ -0,0 +1,386 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ var colorMap = {
+ "SUCCEEDED": "#00a65a",
+ "FAILED": "#dd4b39",
+ "KILLED": "#CCCCCC",
+ "ERROR": "#f39c12"
+ };
+
+ var DURATION_BUCKETS = [0, 30, 60, 120, 300, 600, 1800, 3600, 2 * 3600, 3 * 3600];
+
+ jpmApp.controller("statisticCtrl", function ($wrapState, $element, $scope, PageConfig, Time, Entity, JPM, Chart) {
+ $scope.site = $wrapState.param.siteId;
+
+ PageConfig.title = "Job Statistics";
+
+ $scope.type = "hourly";
+
+ $scope.switchType = function (type) {
+ $scope.type = type;
+ $scope.refreshDistribution();
+ };
+
+ // ===============================================================
+ // = Time Level Distribution =
+ // ===============================================================
+ function parseDayBuckets(startTime, endTime) {
+ startTime = startTime.clone().hour(0).minute(0).second(0);
+ endTime = endTime.clone().hour(0).minute(0).second(0);
+
+ var _buckets = [];
+ var _start = startTime.clone();
+
+ do {
+ var _end = _start.clone().date(1).add(1, "month").date(0);
+ if (_end.isAfter(endTime)) {
+ _end = endTime.clone();
+ }
+ var _dayDes = moment.duration(_end.diff(_start)).asDays() + 1;
+ _buckets.push(_dayDes);
+
+ _start = _end.clone().add(1, "day");
+ } while (!_start.isAfter(endTime));
+
+ return _buckets;
+ }
+
+ var distributionCache = {};
+ $scope.distributionSelectedType = "";
+ $scope.distributionSelectedIndex = -1;
+
+ $scope.jobDistributionSeriesOption = {};
+
+ $scope.refreshDistribution = function () {
+ var type = $scope.type;
+ var startTime, endTime;
+ var metric, minInterval, field;
+ $scope.distributionSelectedIndex = -1;
+
+ switch (type) {
+ case "monthly":
+ endTime = Time("monthEnd");
+ startTime = endTime.clone().subtract(365, "day").date(1).hours(0).minutes(0).seconds(0);
+ metric = "hadoop.job.history.day.count";
+ minInterval = 1440;
+ field = "max(value)";
+ break;
+ case "weekly":
+ endTime = Time("weekEnd");
+ startTime = Time("week").subtract(7 * 12, "day");
+ metric = "hadoop.job.history.day.count";
+ minInterval = 1440 * 7;
+ field = "sum(value)";
+ break;
+ case "daily":
+ endTime = Time("dayEnd");
+ startTime = Time("day").subtract(31, "day");
+ metric = "hadoop.job.history.day.count";
+ minInterval = 1440;
+ field = "max(value)";
+ break;
+ case "hourly":
+ endTime = Time("hourEnd");
+ startTime = Time("day").subtract(2, "day");
+ metric = "hadoop.job.history.hour.count";
+ minInterval = 60;
+ field = "sum(value)";
+ break;
+ }
+
+ $scope.jobDistributionSeries = [];
+ $scope.jobDistributionCategoryFunc = function (value) {
+ if(type === "hourly") {
+ return Time.format(value, "HH:mm");
+ }
+ return Time.format(value, "MM-DD");
+ };
+ var promise = distributionCache[type] = distributionCache[type] || JPM.aggMetricsToEntities(
+ JPM.aggMetrics({site: $scope.site}, metric, ["jobStatus"], field, minInterval, startTime, endTime)
+ )._promise.then(function (list) {
+ if(type === "monthly") {
+ var buckets = parseDayBuckets(startTime, endTime);
+ list = $.map(list, function (units) {
+ // Merge by day buckets
+ units = units.concat();
+ return [$.map(buckets, function (dayCount) {
+ var subUnits = units.splice(0, dayCount);
+ var sum = common.number.sum(subUnits, ["value", 0]);
+
+ return $.extend({}, subUnits[0], {
+ value: [sum]
+ });
+ })];
+ });
+ }
+ return list;
+ }).then(function(list) {
+ /**
+ * @param {Object[]} metrics
+ * @param {{}} metrics[].tags
+ * @param {string} metrics[].tags.jobStatus
+ */
+ var series = $.map(list, function (metrics) {
+ return JPM.metricsToSeries(metrics[0].tags.jobStatus, metrics, {
+ stack: "stack",
+ type: "bar",
+ itemStyle: {
+ normal: {
+ borderWidth: 2
+ }
+ }
+ });
+ });
+ common.array.doSort(series, "name", true, ["SUCCEEDED", "FAILED", "KILLED", "ERROR"]);
+ $scope.jobDistributionSeriesOption.color = $.map(series, function (series) {
+ return colorMap[series.name];
+ });
+
+ return series;
+ });
+
+ promise.then(function(series) {
+ $scope.jobDistributionSeries = series;
+ });
+ };
+
+ // ==============================================================
+ // = Drill Down =
+ // ==============================================================
+ $scope.commonChartOption = {
+ grid: {
+ left: 42,
+ bottom: 60,
+ containLabel: false
+ },
+ yAxis: [{
+ minInterval: 1
+ }],
+ xAxis: {
+ axisLabel: {
+ interval: 0
+ }
+ }
+ };
+ $scope.commonTrendChartOption = {
+ yAxis: [{
+ minInterval: 1
+ }],
+ grid: {
+ top: 60,
+ left: 42,
+ bottom: 20,
+ containLabel: false
+ }
+ };
+
+ $scope.topUserJobCountSeries = [];
+ $scope.topTypeJobCountSeries = [];
+
+ $scope.drillDownCategoryFunc = function (value) {
+ switch ($scope.type) {
+ case "monthly":
+ return Time.format(value, "MM-DD");
+ case "weekly":
+ case "daily":
+ return Time.format(value, "MM-DD HH:mm");
+ default:
+ return Time.format(value, "HH:mm");
+ }
+ };
+
+ $scope.bucketDurationCategory = [];
+ $.each(DURATION_BUCKETS, function (i, start) {
+ var end = DURATION_BUCKETS[i + 1];
+
+ start *= 1000;
+ end *= 1000;
+
+ if(!start) {
+ $scope.bucketDurationCategory.push("<" + Time.diffStr(end));
+ } else if(!end) {
+ $scope.bucketDurationCategory.push(">" + Time.diffStr(start));
+ } else {
+ $scope.bucketDurationCategory.push(Time.diffStr(start) + "\n~\n" + Time.diffStr(end));
+ }
+ });
+
+ function flattenTrendSeries(name, series) {
+ var len = series.length;
+ var category = [];
+ var data = [];
+ var needBreakLine = series.length > 6;
+ $.each(series, function (i, series) {
+ category.push((needBreakLine && i % 2 !== 0 ? "\n" : "") + series.name);
+ data.push(common.number.sum(series.data, ["y"]));
+ });
+ return {
+ category: category.reverse(),
+ series: [{
+ name: name,
+ data: data.reverse(),
+ type: "bar",
+ itemStyle: {
+ normal: {
+ color: function (point) {
+ return Chart.color[len - point.dataIndex - 1];
+ }
+ }
+ }
+ }]
+ };
+ }
+
+ $scope.distributionClick = function (event) {
+ if(event.componentType !== "series") return;
+
+ $scope.distributionSelectedType = event.seriesName;
+ $scope.distributionSelectedIndex = event.dataIndex;
+ var timestamp = 0;
+
+ // Highlight logic
+ $.each($scope.jobDistributionSeries, function (i, series) {
+ timestamp = series.data[$scope.distributionSelectedIndex].x;
+
+ common.merge(series, {
+ itemStyle: {
+ normal: {
+ color: function (point) {
+ if(point.seriesName === $scope.distributionSelectedType && point.dataIndex === $scope.distributionSelectedIndex) {
+ return "#60C0DD";
+ }
+ return colorMap[point.seriesName];
+ }
+ }
+ }
+ });
+ });
+
+ // Data fetch
+ var startTime = Time(timestamp);
+ var endTime;
+
+ switch ($scope.type) {
+ case "monthly":
+ endTime = startTime.clone().add(1, "month").subtract(1, "s");
+ break;
+ case "weekly":
+ endTime = startTime.clone().add(7, "day").subtract(1, "s");
+ break;
+ case "daily":
+ endTime = startTime.clone().add(1, "day").subtract(1, "s");
+ break;
+ case "hourly":
+ endTime = startTime.clone().add(1, "hour").subtract(1, "s");
+ break;
+ }
+
+ var intervalMin = Time.diffInterval(startTime, endTime) / 1000 / 60;
+
+ // ===================== Top User Job Count =====================
+ $scope.topUserJobCountSeries = [];
+ $scope.topUserJobCountTrendSeries = [];
+ JPM.aggMetricsToEntities(
+ JPM.groups("JobExecutionService", {site: $scope.site, currentState: $scope.distributionSelectedType}, ["user"], "count desc", intervalMin, startTime, endTime, 10, 1000000)
+ )._promise.then(function (list) {
+ $scope.topUserJobCountTrendSeries = $.map(list, function (subList) {
+ return JPM.metricsToSeries(subList[0].tags.user, subList, {
+ stack: "user",
+ areaStyle: {normal: {}}
+ });
+ });
+
+ var flatten = flattenTrendSeries("User", $scope.topUserJobCountTrendSeries);
+ $scope.topUserJobCountSeries = flatten.series;
+ $scope.topUserJobCountSeriesCategory = flatten.category;
+ });
+
+ // ===================== Top Type Job Count =====================
+ $scope.topTypeJobCountSeries = [];
+ $scope.topTypeJobCountTrendSeries = [];
+ JPM.aggMetricsToEntities(
+ JPM.groups("JobExecutionService", {site: $scope.site, currentState: $scope.distributionSelectedType}, ["jobType"], "count desc", intervalMin, startTime, endTime, 10, 1000000)
+ )._promise.then(function (list) {
+ $scope.topTypeJobCountTrendSeries = $.map(list, function (subList) {
+ return JPM.metricsToSeries(subList[0].tags.jobType, subList, {
+ stack: "type",
+ areaStyle: {normal: {}}
+ });
+ });
+
+ var flatten = flattenTrendSeries("Job Type", $scope.topTypeJobCountTrendSeries);
+ $scope.topTypeJobCountSeries = flatten.series;
+ $scope.topTypeJobCountSeriesCategory = flatten.category;
+ });
+
+ if($scope.distributionSelectedType === "FAILED") {
+ // ====================== Failure Job List ======================
+ $scope.jobList = JPM.jobList({site: $scope.site, currentState: "FAILED"}, startTime, endTime, [
+ "jobId",
+ "jobName",
+ "user",
+ "startTime",
+ "jobType"
+ ]);
+ } else {
+ // ============== Job Duration Distribution Count ===============
+ $scope.jobList = null;
+
+ $scope.jobDurationDistributionSeries = [];
+ /**
+ * @param {{}} res
+ * @param {{}} res.data
+ * @param {[]} res.data.jobTypes
+ * @param {[]} res.data.jobCounts
+ */
+ JPM.jobDistribution($scope.site, $scope.type, DURATION_BUCKETS.join(","), startTime, endTime).then(function (res) {
+ var data = res.data;
+ var jobTypes = {};
+ $.each(data.jobTypes, function (i, type) {
+ jobTypes[type] = [];
+ });
+ $.each(data.jobCounts, function (index, statistic) {
+ $.each(statistic.jobCountByType, function (type, value) {
+ jobTypes[type][index] = value;
+ });
+ });
+
+ $scope.jobDurationDistributionSeries = $.map(jobTypes, function (list, type) {
+ return {
+ name: type,
+ data: list,
+ type: "bar",
+ stack: "jobType"
+ };
+ });
+ });
+ }
+
+ return true;
+ };
+
+ $scope.refreshDistribution();
+ });
+ });
+})();
[03/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
deleted file mode 100644
index d717ad1..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/metrics/controller.js
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module('featureControllers');
- var feature = featureControllers.register("metrics");
-
- // ==============================================================
- // = Initialization =
- // ==============================================================
-
- // ==============================================================
- // = Function =
- // ==============================================================
- // Format dashboard unit. Will adjust format with old version and add miss attributes.
- feature.service("DashboardFormatter", function() {
- return {
- parse: function(unit) {
- unit = unit || {};
- unit.groups = unit.groups || [];
-
- $.each(unit.groups, function (i, group) {
- group.charts = group.charts || [];
- $.each(group.charts, function (i, chart) {
- if (!chart.metrics && chart.metric) {
- chart.metrics = [{
- aggregations: chart.aggregations,
- dataSource: chart.dataSource,
- metric: chart.metric
- }];
-
- delete chart.aggregations;
- delete chart.dataSource;
- delete chart.metric;
- } else if (!chart.metrics) {
- chart.metrics = [];
- }
- });
- });
-
- return unit;
- }
- };
- });
-
- // ==============================================================
- // = Controller =
- // ==============================================================
-
- // ========================= Dashboard ==========================
- feature.navItem("dashboard", "Metrics", "line-chart");
-
- feature.controller('dashboard', function(PageConfig, $scope, $http, $q, UI, Site, Authorization, Application, Entities, DashboardFormatter) {
- var _siteApp = Site.currentSiteApplication();
- var _druidConfig = _siteApp.configObj.getValueByPath("web.druid");
- var _refreshInterval;
-
- var _menu_newChart;
-
- $scope.lock = false;
-
- $scope.dataSourceListReady = false;
- $scope.dataSourceList = [];
- $scope.dashboard = {
- groups: []
- };
- $scope.dashboardEntity = null;
- $scope.dashboardReady = false;
-
- $scope._newMetricFilter = "";
- $scope._newMetricDataSrc = null;
- $scope._newMetricDataMetric = null;
-
- $scope.tabHolder = {};
-
- $scope.endTime = app.time.now();
- $scope.startTime = $scope.endTime.clone();
-
- // =================== Initialization ===================
- if(!_druidConfig || !_druidConfig.coordinator || !_druidConfig.broker) {
- $.dialog({
- title: "OPS",
- content: "Druid configuration can't be empty!"
- });
- return;
- }
-
- $scope.autoRefreshList = [
- {title: "Last 1 Month", timeDes: "day", getStartTime: function(endTime) {return endTime.clone().subtract(1, "month");}},
- {title: "Last 1 Day", timeDes: "thirty_minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "day");}},
- {title: "Last 6 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(6, "hour");}},
- {title: "Last 2 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(2, "hour");}},
- {title: "Last 1 Hour", timeDes: "minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "hour");}}
- ];
- $scope.autoRefreshSelect = $scope.autoRefreshList[2];
-
- // ====================== Function ======================
- $scope.setAuthRefresh = function(item) {
- $scope.autoRefreshSelect = item;
- $scope.refreshAllChart(true);
- };
-
- $scope.refreshTimeDisplay = function() {
- PageConfig.pageSubTitle = common.format.date($scope.startTime) + " ~ " + common.format.date($scope.endTime) + " [refresh interval: 30s]";
- };
- $scope.refreshTimeDisplay();
-
- // ======================= Metric =======================
- // Fetch metric data
- $http.get(_druidConfig.coordinator + "/druid/coordinator/v1/metadata/datasources", {withCredentials: false}).then(function(data) {
- var _endTime = new moment();
- var _startTime = _endTime.clone().subtract(1, "day");
- var _intervals = _startTime.toISOString() + "/" + _endTime.toISOString();
-
- $scope.dataSourceList = $.map(data.data, function(dataSrc) {
- return {
- dataSource: dataSrc,
- metricList: []
- };
- });
-
- // List dataSource metrics
- var _metrixList_promiseList = $.map($scope.dataSourceList, function(dataSrc) {
- var _data = JSON.stringify({
- "queryType": "groupBy",
- "dataSource": dataSrc.dataSource,
- "granularity": "all",
- "dimensions": ["metric"],
- "aggregations": [
- {
- "type":"count",
- "name":"count"
- }
- ],
- "intervals": [_intervals]
- });
-
- return $http.post(_druidConfig.broker + "/druid/v2", _data, {withCredentials: false}).then(function(response) {
- dataSrc.metricList = $.map(response.data, function(entity) {
- return entity.event.metric;
- });
- });
- });
-
- $q.all(_metrixList_promiseList).finally(function() {
- $scope.dataSourceListReady = true;
-
- $scope._newMetricDataSrc = $scope.dataSourceList[0];
- $scope._newMetricDataMetric = common.getValueByPath($scope._newMetricDataSrc, "metricList.0");
- });
- }, function() {
- $.dialog({
- title: "OPS",
- content: "Fetch data source failed. Please check Site Application Metrics configuration."
- });
- });
-
- // Filter data source
- $scope.dataSourceMetricList = function(dataSrc, filter) {
- filter = (filter || "").toLowerCase().trim().split(/\s+/);
- return $.grep((dataSrc && dataSrc.metricList) || [], function(metric) {
- for(var i = 0 ; i < filter.length ; i += 1) {
- if(metric.toLowerCase().indexOf(filter[i]) === -1) return false;
- }
- return true;
- });
- };
-
- // New metric select
- $scope.newMetricSelectDataSource = function(dataSrc) {
- if(dataSrc !== $scope._newMetricDataMetric) $scope._newMetricDataMetric = dataSrc.metricList[0];
- $scope._newMetricDataSrc = dataSrc;
- };
- $scope.newMetricSelectMetric = function(metric) {
- $scope._newMetricDataMetric = metric;
- };
-
- // Confirm new metric
- $scope.confirmSelectMetric = function() {
- var group = $scope.tabHolder.selectedPane.data;
- var metric = {
- dataSource: $scope._newMetricDataSrc.dataSource,
- metric: $scope._newMetricDataMetric,
- aggregations: ["max"]
- };
- $("#metricMDL").modal('hide');
-
- if($scope.metricForConfigChart) {
- $scope.configPreviewChart.metrics.push(metric);
- $scope.refreshChart($scope.configPreviewChart, true, true);
- } else {
- group.charts.push({
- chart: "line",
- metrics: [metric]
- });
- $scope.refreshAllChart();
- }
- };
-
- // ======================== Menu ========================
- function _checkGroupName(entity) {
- if(common.array.find(entity.name, $scope.dashboard.groups, "name")) {
- return "Group name conflict";
- }
- }
-
- $scope.newGroup = function() {
- if($scope.lock) return;
-
- UI.createConfirm("Group", {}, [{field: "name"}], _checkGroupName).then(null, null, function(holder) {
- $scope.dashboard.groups.push({
- name: holder.entity.name,
- charts: []
- });
- holder.closeFunc();
-
- setTimeout(function() {
- $scope.tabHolder.setSelect(holder.entity.name);
- }, 0);
- });
- };
-
- function renameGroup() {
- var group = $scope.tabHolder.selectedPane.data;
- UI.updateConfirm("Group", {}, [{field: "name", name: "New Name"}], _checkGroupName).then(null, null, function(holder) {
- group.name = holder.entity.name;
- holder.closeFunc();
- });
- }
-
- function deleteGroup() {
- var group = $scope.tabHolder.selectedPane.data;
- UI.deleteConfirm(group.name).then(null, null, function(holder) {
- common.array.remove(group, $scope.dashboard.groups);
- holder.closeFunc();
- });
- }
-
- _menu_newChart = {title: "Add Metric", func: function() {$scope.newChart();}};
- Object.defineProperties(_menu_newChart, {
- icon: {
- get: function() {return $scope.dataSourceListReady ? 'plus' : 'refresh fa-spin';}
- },
- disabled: {
- get: function() {return !$scope.dataSourceListReady;}
- }
- });
-
- $scope.menu = Authorization.isRole('ROLE_ADMIN') ? [
- {icon: "cog", title: "Configuration", list: [
- _menu_newChart,
- {icon: "pencil", title: "Rename Group", func: renameGroup},
- {icon: "trash", title: "Delete Group", danger: true, func: deleteGroup}
- ]},
- {icon: "plus", title: "New Group", func: $scope.newGroup}
- ] : [];
-
- // ===================== Dashboard ======================
- $scope.dashboardList = Entities.queryEntities("GenericResourceService", {
- site: Site.current().tags.site,
- application: Application.current().tags.application
- });
- $scope.dashboardList._promise.then(function(list) {
- $scope.dashboardEntity = list[0];
- $scope.dashboard = DashboardFormatter.parse(common.parseJSON($scope.dashboardEntity.value));
- $scope.refreshAllChart();
- }).finally(function() {
- $scope.dashboardReady = true;
- });
-
- $scope.saveDashboard = function() {
- $scope.lock = true;
-
- if(!$scope.dashboardEntity) {
- $scope.dashboardEntity = {
- tags: {
- site: Site.current().tags.site,
- application: Application.current().tags.application,
- name: "/metric_dashboard/dashboard/default"
- }
- };
- }
- $scope.dashboardEntity.value = common.stringify($scope.dashboard);
-
- Entities.updateEntity("GenericResourceService", $scope.dashboardEntity)._promise.then(function() {
- $.dialog({
- title: "Done",
- content: "Save success!"
- });
- }, function() {
- $.dialog({
- title: "POS",
- content: "Save failed. Please retry."
- });
- }).finally(function() {
- $scope.lock = false;
- });
- };
-
- // ======================= Chart ========================
- $scope.configTargetChart = null;
- $scope.configPreviewChart = null;
- $scope.metricForConfigChart = false;
- $scope.viewChart = null;
-
- $scope.chartConfig = {
- xType: "time"
- };
-
- $scope.chartTypeList = [
- {icon: "line-chart", chart: "line"},
- {icon: "area-chart", chart: "area"},
- {icon: "bar-chart", chart: "column"},
- {icon: "pie-chart", chart: "pie"}
- ];
-
- $scope.chartSeriesList = [
- {name: "Min", series: "min"},
- {name: "Max", series: "max"},
- {name: "Avg", series: "avg"},
- {name: "Count", series: "count"},
- {name: "Sum", series: "sum"}
- ];
-
- $scope.newChart = function() {
- $scope.metricForConfigChart = false;
- $("#metricMDL").modal();
- };
-
- $scope.configPreviewChartMinimumCheck = function() {
- $scope.configPreviewChart.min = $scope.configPreviewChart.min === 0 ? undefined : 0;
- };
-
- $scope.seriesChecked = function(metric, series) {
- if(!metric) return false;
- return $.inArray(series, metric.aggregations || []) !== -1;
- };
- $scope.seriesCheckClick = function(metric, series, chart) {
- if(!metric || !chart) return;
- if($scope.seriesChecked(metric, series)) {
- common.array.remove(series, metric.aggregations);
- } else {
- metric.aggregations.push(series);
- }
- $scope.chartSeriesUpdate(chart);
- };
-
- $scope.chartSeriesUpdate = function(chart) {
- chart._data = $.map(chart._oriData, function(groupData, i) {
- var metric = chart.metrics[i];
- return $.map(groupData, function(series) {
- if($.inArray(series._key, metric.aggregations) !== -1) return series;
- });
- });
- };
-
- $scope.configAddMetric = function() {
- $scope.metricForConfigChart = true;
- $("#metricMDL").modal();
- };
-
- $scope.configRemoveMetric = function(metric) {
- common.array.remove(metric, $scope.configPreviewChart.metrics);
- };
-
- $scope.getChartConfig = function(chart) {
- if(!chart) return null;
-
- var _config = chart._config = chart._config || $.extend({}, $scope.chartConfig);
- _config.yMin = chart.min;
-
- return _config;
- };
-
- $scope.configChart = function(chart) {
- $scope.configTargetChart = chart;
- $scope.configPreviewChart = $.extend({}, chart);
- $scope.configPreviewChart.metrics = $.map(chart.metrics, function(metric) {
- return $.extend({}, metric, {aggregations: (metric.aggregations || []).slice()});
- });
- delete $scope.configPreviewChart._config;
- $("#chartMDL").modal();
- setTimeout(function() {
- $(window).resize();
- }, 200);
- };
-
- $scope.confirmUpdateChart = function() {
- $("#chartMDL").modal('hide');
- $.extend($scope.configTargetChart, $scope.configPreviewChart);
- $scope.chartSeriesUpdate($scope.configTargetChart);
- if($scope.configTargetChart._holder) $scope.configTargetChart._holder.refreshAll();
- $scope.configPreviewChart = null;
- };
-
- $scope.deleteChart = function(group, chart) {
- UI.deleteConfirm(chart.metric).then(null, null, function(holder) {
- common.array.remove(chart, group.charts);
- holder.closeFunc();
- $scope.refreshAllChart(false, true);
- });
- };
-
- $scope.showChart = function(chart) {
- $scope.viewChart = chart;
- $("#chartViewMDL").modal();
- setTimeout(function() {
- $(window).resize();
- }, 200);
- };
-
- $scope.refreshChart = function(chart, forceRefresh, refreshAll) {
- var _intervals = $scope.startTime.toISOString() + "/" + $scope.endTime.toISOString();
-
- function _refreshChart() {
- if (chart._holder) {
- if (refreshAll) {
- chart._holder.refreshAll();
- } else {
- chart._holder.refresh();
- }
- }
- }
-
- var _tmpData, _metricPromiseList;
-
- if (chart._data && !forceRefresh) {
- // Refresh chart without reload
- _refreshChart();
- } else {
- // Refresh chart with reload
- _tmpData = [];
- _metricPromiseList = $.map(chart.metrics, function (metric, k) {
- // Each Metric
- var _query = JSON.stringify({
- "queryType": "groupBy",
- "dataSource": metric.dataSource,
- "granularity": $scope.autoRefreshSelect.timeDes,
- "dimensions": ["metric"],
- "filter": {"type": "selector", "dimension": "metric", "value": metric.metric},
- "aggregations": [
- {
- "type": "max",
- "name": "max",
- "fieldName": "maxValue"
- },
- {
- "type": "min",
- "name": "min",
- "fieldName": "maxValue"
- },
- {
- "type": "count",
- "name": "count",
- "fieldName": "maxValue"
- },
- {
- "type": "longSum",
- "name": "sum",
- "fieldName": "maxValue"
- }
- ],
- "postAggregations" : [
- {
- "type": "javascript",
- "name": "avg",
- "fieldNames": ["sum", "count"],
- "function": "function(sum, cnt) { return sum / cnt;}"
- }
- ],
- "intervals": [_intervals]
- });
-
- return $http.post(_druidConfig.broker + "/druid/v2", _query, {withCredentials: false}).then(function (response) {
- var _data = nvd3.convert.druid([response.data]);
- _tmpData[k] = _data;
-
- // Process series name
- $.each(_data, function(i, series) {
- series._key = series.key;
- if(chart.metrics.length > 1) {
- series.key = metric.metric.replace(/^.*\./, "") + "-" +series._key;
- }
- });
- });
- });
-
- $q.all(_metricPromiseList).then(function() {
- chart._oriData = _tmpData;
- $scope.chartSeriesUpdate(chart);
- _refreshChart();
- });
- }
- };
-
- $scope.refreshAllChart = function(forceRefresh, refreshAll) {
- setTimeout(function() {
- $scope.endTime = app.time.now();
- $scope.startTime = $scope.autoRefreshSelect.getStartTime($scope.endTime);
-
- $scope.refreshTimeDisplay();
-
- $.each($scope.dashboard.groups, function (i, group) {
- $.each(group.charts, function (j, chart) {
- $scope.refreshChart(chart, forceRefresh, refreshAll);
- });
- });
-
- $(window).resize();
- }, 0);
- };
-
- $scope.chartSwitchRefresh = function(source, target) {
- var _oriSize = source.size;
- source.size = target.size;
- target.size = _oriSize;
-
- if(source._holder) source._holder.refreshAll();
- if(target._holder) target._holder.refreshAll();
-
- };
-
- _refreshInterval = setInterval(function() {
- if(!$scope.dashboardReady) return;
- $scope.refreshAllChart(true);
- }, 1000 * 30);
-
- // > Chart UI
- $scope.configChartSize = function(chart, sizeOffset) {
- chart.size = (chart.size || 6) + sizeOffset;
- if(chart.size <= 0) chart.size = 1;
- if(chart.size > 12) chart.size = 12;
- setTimeout(function() {
- $(window).resize();
- }, 1);
- };
-
- // ========================= UI =========================
- $("#metricMDL").on('hidden.bs.modal', function () {
- if($(".modal-backdrop").length) {
- $("body").addClass("modal-open");
- }
- });
-
- $("#chartViewMDL").on('hidden.bs.modal', function () {
- $scope.viewChart = null;
- });
-
- // ====================== Clean Up ======================
- $scope.$on('$destroy', function() {
- clearInterval(_refreshInterval);
- });
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html b/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
deleted file mode 100644
index 2acb954..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/metrics/page/dashboard.html
+++ /dev/null
@@ -1,250 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="page-fixed">
- <div class="dropdown inline">
- <button data-toggle="dropdown" class="btn btn-primary">
- <span class="fa fa-clock-o"></span> {{autoRefreshSelect.title}}
- </button>
- <ul class="dropdown-menu left">
- <li ng-repeat="item in autoRefreshList track by $index">
- <a ng-click="setAuthRefresh(item)">
- <span class="fa fa-clock-o"></span>
- <span ng-class="{'text-bold': item === autoRefreshSelect}">{{item.title}}</span>
- </a>
- </li>
- </ul>
- </div>
-
- <button class="btn btn-primary" ng-if="Auth.isRole('ROLE_ADMIN')"
- ng-click="saveDashboard()" ng-disabled="lock">
- <span class="fa fa-floppy-o"></span> Save
- </button>
-</div>
-
-<div class="box box-default" ng-if="!dashboard.groups.length">
- <div class="box-header with-border">
- <h3 class="box-title">Empty Dashboard</h3>
- </div>
- <div class="box-body">
- <div ng-show="!dashboardReady">
- Loading...
- </div>
- <div ng-show="dashboardReady">
- Current dashboard is empty.
- <span ng-if="Auth.isRole('ROLE_ADMIN')">Click <a ng-click="newGroup()">here</a> to create a new group.</span>
- </div>
- </div>
- <div class="overlay" ng-show="!dashboardReady">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-</div>
-
-<div tabs menu="menu" holder="tabHolder" ng-show="dashboard.groups.length" data-sortable-model="Auth.isRole('ROLE_ADMIN') ? dashboard.groups : null">
- <pane ng-repeat="group in dashboard.groups" data-data="group" data-title="{{group.name}}">
- <div uie-sortable ng-model="group.charts" class="row narrow" sortable-update-func="chartSwitchRefresh" ng-show="group.charts.length">
- <div ng-repeat="chart in group.charts track by $index" class="col-md-{{chart.size || 6}}">
- <div class="nvd3-chart-wrapper">
- <div nvd3="chart._data" data-holder="chart._holder" data-title="{{chart.title || chart.metrics[0].metric}}" data-watching="false"
- data-chart="{{chart.chart || 'line'}}" data-config="getChartConfig(chart)" class="nvd3-chart-cntr"></div>
- <div class="nvd3-chart-config">
- <a class="fa fa-expand" ng-click="showChart(chart, -1)"></a>
- <span ng-if="Auth.isRole('ROLE_ADMIN')">
- <a class="fa fa-minus" ng-click="configChartSize(chart, -1)"></a>
- <a class="fa fa-plus" ng-click="configChartSize(chart, 1)"></a>
- <a class="fa fa-cog" ng-click="configChart(chart)"></a>
- <a class="fa fa-trash" ng-click="deleteChart(group, chart)"></a>
- </span>
- </div>
- </div>
- </div>
- </div>
-
- <p ng-if="!group.charts.length">
- Empty group.
- <span ng-if="Auth.isRole('ROLE_ADMIN')">
- Click
- <span ng-hide="dataSourceListReady" class="fa fa-refresh fa-spin"></span>
- <a ng-show="dataSourceListReady" ng-click="newChart()">here</a>
- to add metric.
- </span>
- </p>
- </pane>
-</div>
-
-
-
-<!-- Modal: Chart configuration -->
-<div class="modal fade" id="chartMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog modal-lg" 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">×</span>
- </button>
- <h4 class="modal-title">Chart Configuration</h4>
- </div>
- <div class="modal-body">
- <div class="row">
- <div class="col-md-6">
- <div class="nvd3-chart-wrapper">
- <div nvd3="configPreviewChart._data" data-title="{{configPreviewChart.title || configPreviewChart.metrics[0].metric}}"
- data-watching="true" data-chart="{{configPreviewChart.chart || 'line'}}" data-config="getChartConfig(configPreviewChart)" class="nvd3-chart-cntr"></div>
- </div>
- </div>
- <div class="col-md-6">
- <!-- Chart Configuration -->
- <table class="table">
- <tbody>
- <tr>
- <th width="100">Name</th>
- <td><input type="text" class="form-control input-xs" ng-model="configPreviewChart.title" placeholder="Default: {{configPreviewChart.metrics[0].metric}}" /></td>
- </tr>
- <tr>
- <th>Chart Type</th>
- <td>
- <div class="btn-group" data-toggle="buttons">
- <label class="btn btn-default btn-xs" ng-class="{active: (configPreviewChart.chart || 'line') === type.chart}"
- ng-repeat="type in chartTypeList track by $index" ng-click="configPreviewChart.chart = type.chart;">
- <input type="radio" name="chartType" autocomplete="off"
- ng-checked="(configPreviewChart.chart || 'line') === type.chart">
- <span class="fa fa-{{type.icon}}"></span>
- </label>
- </div>
- </td>
- </tr>
- <tr>
- <th>Minimum</th>
- <td><input type="checkbox" ng-checked="configPreviewChart.min === 0" ng-disabled="configPreviewChart.chart === 'area' || configPreviewChart.chart === 'pie'"
- ng-click="configPreviewChartMinimumCheck()" /></td>
- </tr>
- <tr>
- <th>Metrics</th>
- <td>
- <div ng-repeat="metric in configPreviewChart.metrics" class="box inner-box">
- <div class="box-tools">
- <button class="btn btn-box-tool" ng-click="configRemoveMetric(metric)">
- <span class="fa fa-times"></span>
- </button>
- </div>
-
- <h3 class="box-title">{{metric.metric}}</h3>
- <div class="checkbox noMargin" ng-repeat="series in chartSeriesList track by $index">
- <label>
- <input type="checkbox" ng-checked="seriesChecked(metric, series.series)"
- ng-click="seriesCheckClick(metric, series.series, configPreviewChart)" />
- {{series.name}}
- </label>
- </div>
- </div>
- <a ng-click="configAddMetric()">+ Add Metric</a>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- <button type="button" class="btn btn-primary" ng-click="confirmUpdateChart()">
- Confirm
- </button>
- </div>
- </div>
- </div>
-</div>
-
-
-
-<!-- Modal: Metric selector -->
-<div class="modal fade" id="metricMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog modal-lg" 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">×</span>
- </button>
- <h4 class="modal-title">Select Metric</h4>
- </div>
- <div class="modal-body">
- <div class="input-group" style="margin-bottom: 10px;">
- <input type="text" class="form-control" placeholder="Filter..." ng-model="_newMetricFilter" />
- <span class="input-group-btn">
- <button class="btn btn-default btn-flat" ng-click="_newMetricFilter = '';"><span class="fa fa-times"></span></button>
- </span>
- </div>
-
- <div class="row">
- <div class="col-md-4">
- <ul class="nav nav-pills nav-stacked fixed-height">
- <li class="disabled"><a>Data Source</a></li>
- <li ng-repeat="dataSrc in dataSourceList track by $index" ng-class="{active: _newMetricDataSrc === dataSrc}">
- <a ng-click="newMetricSelectDataSource(dataSrc)">{{dataSrc.dataSource}}</a>
- </li>
- </ul>
- </div>
- <div class="col-md-8">
- <ul class="nav nav-pills nav-stacked fixed-height">
- <li class="disabled"><a>Metric</a></li>
- <li ng-repeat="metric in dataSourceMetricList(_newMetricDataSrc, _newMetricFilter) track by $index" ng-class="{active: _newMetricDataMetric === metric}">
- <a ng-click="newMetricSelectMetric(metric)">{{metric}}</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- <div class="modal-footer">
- <span class="text-primary pull-left">{{_newMetricDataSrc.dataSource}} <span class="fa fa-arrow-right"></span> {{_newMetricDataMetric}}</span>
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- <button type="button" class="btn btn-primary" ng-click="confirmSelectMetric()">
- Select
- </button>
- </div>
- </div>
- </div>
-</div>
-
-
-
-<!-- Modal: Chart View -->
-<div class="modal fade" id="chartViewMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog modal-lg" 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">×</span>
- </button>
- <h4 class="modal-title">{{viewChart.title || viewChart.metrics[0].metric}}</h4>
- </div>
- <div class="modal-body">
- <div nvd3="viewChart._data" data-title="{{viewChart.title || viewChart.metrics[0].metric}}"
- data-watching="true" data-chart="{{viewChart.chart || 'line'}}" data-config="getChartConfig(viewChart)" class="nvd3-chart-cntr lg"></div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/topology/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/topology/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/topology/controller.js
deleted file mode 100644
index 94886c9..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/topology/controller.js
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module('featureControllers');
- var feature = featureControllers.register("topology", {
- global: true // Global Feature needn't add to applications
- });
-
- // ==============================================================
- // = Initialization =
- // ==============================================================
-
- // ==============================================================
- // = Function =
- // ==============================================================
- //feature.service("DashboardFormatter", function() {
- //});
-
- // ==============================================================
- // = Controller =
- // ==============================================================
- feature.configNavItem("monitoring", "Topology", "usb");
-
- // ========================= Monitoring =========================
- feature.configController('monitoring', function(PageConfig, $scope, $interval, Entities, UI, Site, Application) {
- var topologyRefreshInterval;
-
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- PageConfig.pageTitle = "Topology Execution";
-
- $scope.topologyExecutionList = null;
-
- $scope.currentTopologyExecution = null;
- $scope.currentTopologyExecutionOptList = [];
-
- // ======================= Function =======================
- function refreshExecutionList() {
- var _list = Entities.queryEntities("TopologyExecutionService");
- _list._promise.then(function () {
- $scope.topologyExecutionList = _list;
- });
- }
-
- $scope.showTopologyDetail = function (topologyExecution) {
- $scope.currentTopologyExecution = topologyExecution;
- $("#topologyMDL").modal();
-
- $scope.currentTopologyExecutionOptList = Entities.queryEntities("TopologyOperationService", {
- site: topologyExecution.tags.site,
- application: topologyExecution.tags.application,
- topology: topologyExecution.tags.topology,
- _pageSize: 10,
- _duration: 1000 * 60 * 60 * 24 * 30
- });
- };
-
- $scope.getStatusClass = function (status) {
- switch (status) {
- case "NEW":
- return "info";
- case "STARTING":
- case "STOPPING":
- return "warning";
- case "STARTED":
- return "success";
- case "STOPPED":
- return "danger";
- default:
- return "default";
- }
- };
-
- // ==================== Initialization ====================
- refreshExecutionList();
- topologyRefreshInterval = $interval(refreshExecutionList, 10 * 1000);
-
- $scope.topologyList = Entities.queryEntities("TopologyDescriptionService");
- $scope.topologyList._promise.then(function () {
- $scope.topologyList = $.map($scope.topologyList, function (topology) {
- return topology.tags.topology;
- });
- });
-
- $scope.applicationList = $.map(Application.list, function (application) {
- return application.tags.application;
- });
-
- $scope.siteList = $.map(Site.list, function (site) {
- return site.tags.site;
- });
-
- // ================== Topology Execution ==================
- $scope.newTopologyExecution = function () {
- UI.createConfirm("Topology", {}, [
- {field: "site", type: "select", valueList: $scope.siteList},
- {field: "application", type: "select", valueList: $scope.applicationList},
- {field: "topology", type: "select", valueList: $scope.topologyList}
- ], function (entity) {
- for(var i = 0 ; i < $scope.topologyExecutionList.length; i += 1) {
- var _entity = $scope.topologyExecutionList[i].tags;
- if(_entity.site === entity.site && _entity.application === entity.application && _entity.topology === entity.topology) {
- return "Topology already exist!";
- }
- }
- }).then(null, null, function(holder) {
- var _entity = {
- tags: {
- site: holder.entity.site,
- application: holder.entity.application,
- topology: holder.entity.topology
- },
- status: "NEW"
- };
- Entities.updateEntity("TopologyExecutionService", _entity)._promise.then(function() {
- holder.closeFunc();
- $scope.topologyExecutionList.push(_entity);
- refreshExecutionList();
- });
- });
- };
-
- $scope.deleteTopologyExecution = function (topologyExecution) {
- UI.deleteConfirm(topologyExecution.tags.topology).then(null, null, function(holder) {
- Entities.deleteEntities("TopologyExecutionService", topologyExecution.tags)._promise.then(function() {
- holder.closeFunc();
- common.array.remove(topologyExecution, $scope.topologyExecutionList);
- });
- });
- };
-
- // ================== Topology Operation ==================
- $scope.doTopologyOperation = function (topologyExecution, operation) {
- $.dialog({
- title: operation + " Confirm",
- content: "Do you want to " + operation + " '" + topologyExecution.tags.topology + "'?",
- confirm: true
- }, function (ret) {
- if(!ret) return;
-
- var list = Entities.queryEntities("TopologyOperationService", {
- site: topologyExecution.tags.site,
- application: topologyExecution.tags.application,
- topology: topologyExecution.tags.topology,
- _pageSize: 20
- });
-
- list._promise.then(function () {
- var lastOperation = common.array.find(operation, list, "tags.operation");
- if(lastOperation && (lastOperation.status === "INITIALIZED" || lastOperation.status === "PENDING")) {
- refreshExecutionList();
- return;
- }
-
- Entities.updateEntity("rest/app/operation", {
- tags: {
- site: topologyExecution.tags.site,
- application: topologyExecution.tags.application,
- topology: topologyExecution.tags.topology,
- operation: operation
- },
- status: "INITIALIZED"
- }, {timestamp: false, hook: true});
- });
- });
- };
-
- $scope.startTopologyOperation = function (topologyExecution) {
- $scope.doTopologyOperation(topologyExecution, "START");
- };
- $scope.stopTopologyOperation = function (topologyExecution) {
- $scope.doTopologyOperation(topologyExecution, "STOP");
- };
-
- // ======================= Clean Up =======================
- $scope.$on('$destroy', function() {
- $interval.cancel(topologyRefreshInterval);
- });
- });
-
- // ========================= Management =========================
- feature.configController('management', function(PageConfig, $scope, Entities, UI) {
- PageConfig.hideApplication = true;
- PageConfig.hideSite = true;
- PageConfig.pageTitle = "Topology";
-
- var typeList = ["CLASS", "DYNAMIC"];
- var topologyDefineAttrs = [
- {field: "topology", name: "name"},
- {field: "type", type: "select", valueList: typeList},
- {field: "exeClass", name: "execution entry", description: function (entity) {
- if(entity.type === "CLASS") return "Class implements interface TopologyExecutable";
- if(entity.type === "DYNAMIC") return "DSL based topology definition";
- }, type: "blob", rows: 5},
- {field: "version", optional: true},
- {field: "description", optional: true, type: "blob"}
- ];
- var topologyUpdateAttrs = $.extend(topologyDefineAttrs.concat(), [{field: "topology", name: "name", readonly: true}]);
-
- $scope.topologyList = Entities.queryEntities("TopologyDescriptionService");
-
- $scope.newTopology = function () {
- UI.createConfirm("Topology", {}, topologyDefineAttrs, function (entity) {
- if(common.array.find(entity.topology, $scope.topologyList, "tags.topology", false, false)) {
- return "Topology name conflict!";
- }
- }).then(null, null, function(holder) {
- holder.entity.tags = {
- topology: holder.entity.topology
- };
- Entities.updateEntity("TopologyDescriptionService", holder.entity, {timestamp: false})._promise.then(function() {
- holder.closeFunc();
- $scope.topologyList.push(holder.entity);
- });
- });
- };
-
- $scope.updateTopology = function (topology) {
- UI.updateConfirm("Topology", $.extend({}, topology, {topology: topology.tags.topology}), topologyUpdateAttrs).then(null, null, function(holder) {
- holder.entity.tags = {
- topology: holder.entity.topology
- };
- Entities.updateEntity("TopologyDescriptionService", holder.entity, {timestamp: false})._promise.then(function() {
- holder.closeFunc();
- $.extend(topology, holder.entity);
- });
- });
- };
-
- $scope.deleteTopology = function (topology) {
- UI.deleteConfirm(topology.tags.topology).then(null, null, function(holder) {
- Entities.delete("TopologyDescriptionService", {topology: topology.tags.topology})._promise.then(function() {
- holder.closeFunc();
- common.array.remove(topology, $scope.topologyList);
- });
- });
- };
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/topology/page/management.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/topology/page/management.html b/eagle-webservice/src/main/webapp/app/public/feature/topology/page/management.html
deleted file mode 100644
index 9e22c84..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/topology/page/management.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-cog"></i>
- <a class="pull-right" href="#/config/topology/monitoring"><span class="fa fa-angle-right"></span> Monitoring</a>
- <h3 class="box-title">
- Management
- </h3>
- </div>
- <div class="box-body">
- <table class="table table-bordered">
- <thead>
- <tr>
- <th>Name</th>
- <th width="20%">Execution Class</th>
- <th>Type</th>
- <th width="50%">Description</th>
- <th>Version</th>
- <th width="70"></th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="item in topologyList">
- <td>{{item.tags.topology}}</td>
- <td><pre class="noWrap">{{item.exeClass}}</pre></td>
- <td>{{item.type}}</td>
- <td>{{item.description}}</td>
- <td>{{item.version}}</td>
- <td class="text-center">
- <button class="rm fa fa-pencil btn btn-default btn-xs" uib-tooltip="Edit" tooltip-animation="false" ng-click="updateTopology(item)"> </button>
- <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="deleteTopology(item)"> </button>
- </td>
- </tr>
- <tr ng-if="topologyList.length === 0">
- <td colspan="6">
- <span class="text-muted">Empty list</span>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-
- <div class="box-footer">
- <button class="btn btn-primary pull-right" ng-click="newTopology()">
- New Topology
- <i class="fa fa-plus-circle"> </i>
- </button>
- </div>
-
- <div class="overlay" ng-hide="topologyList._promise.$$state.status === 1;">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/topology/page/monitoring.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/topology/page/monitoring.html b/eagle-webservice/src/main/webapp/app/public/feature/topology/page/monitoring.html
deleted file mode 100644
index 0acb2c1..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/topology/page/monitoring.html
+++ /dev/null
@@ -1,151 +0,0 @@
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-eye"></i>
- <a class="pull-right" href="#/config/topology/management"><span class="fa fa-angle-right"></span> Management</a>
- <h3 class="box-title">
- Monitoring
- </h3>
- </div>
- <div class="box-body">
- <div sorttable source="topologyExecutionList">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="70" sortpath="status">Status</th>
- <th width="90" sortpath="tags.topology">Topology</th>
- <th width="60" sortpath="tags.site">Site</th>
- <th width="100" sortpath="tags.application">Application</th>
- <th width="60" sortpath="mode">Mode</th>
- <th sortpath="description">Description</th>
- <th width="70" style="min-width: 70px;"></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="text-center">
- <span class="label label-{{_parent.getStatusClass(item.status)}}">{{item.status}}</span>
- </td>
- <td><a ng-click="_parent.showTopologyDetail(item)">{{item.tags.topology}}</a></td>
- <td>{{item.tags.site}}</td>
- <td>{{item.tags.application}}</td>
- <td>{{item.mode}}</td>
- <td>{{item.description}}</td>
- <td class="text-center">
- <button ng-if="item.status === 'NEW' || item.status === 'STOPPED'" class="fa fa-play sm btn btn-default btn-xs" uib-tooltip="Start" tooltip-animation="false" ng-click="_parent.startTopologyOperation(item)"> </button>
- <button ng-if="item.status === 'STARTED'" class="fa fa-stop sm btn btn-default btn-xs" uib-tooltip="Stop" tooltip-animation="false" ng-click="_parent.stopTopologyOperation(item)"> </button>
- <button ng-if="item.status !== 'NEW' && item.status !== 'STARTED' && item.status !== 'STOPPED'" class="fa fa-ban sm btn btn-default btn-xs" disabled="disabled"> </button>
- <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="_parent.deleteTopologyExecution(item)"> </button>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
-
- <div class="box-footer">
- <button class="btn btn-primary pull-right" ng-click="newTopologyExecution()">
- New Topology Execution
- <i class="fa fa-plus-circle"> </i>
- </button>
- </div>
-
- <div class="overlay" ng-hide="topologyExecutionList._promise.$$state.status === 1;">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-</div>
-
-
-
-
-<!-- Modal: Topology Detail -->
-<div class="modal fade" id="topologyMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog modal-lg" 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">×</span>
- </button>
- <h4 class="modal-title">Topology Detail</h4>
- </div>
- <div class="modal-body">
- <h3>Detail</h3>
- <table class="table">
- <tbody>
- <tr>
- <th>Site</th>
- <td>{{currentTopologyExecution.tags.site}}</td>
- </tr>
- <tr>
- <th>Application</th>
- <td>{{currentTopologyExecution.tags.application}}</td>
- </tr>
- <tr>
- <th>Topology</th>
- <td>{{currentTopologyExecution.tags.topology}}</td>
- </tr>
- <tr>
- <th>Full Name</th>
- <td>{{currentTopologyExecution.fullName || "-"}}</td>
- </tr>
- <tr>
- <th>Status</th>
- <td>
- <span class="label label-{{getStatusClass(currentTopologyExecution.status)}}">{{currentTopologyExecution.status}}</span>
- </td>
- </tr>
- <tr>
- <th>Mode</th>
- <td>{{currentTopologyExecution.mode || "-"}}</td>
- </tr>
- <tr>
- <th>Environment</th>
- <td>{{currentTopologyExecution.environment || "-"}}</td>
- </tr>
- <tr>
- <th>Url</th>
- <td>
- <a ng-if="currentTopologyExecution.url" href="{{currentTopologyExecution.url}}" target="_blank">{{currentTopologyExecution.url}}</a>
- <span ng-if="!currentTopologyExecution.url">-</span>
- </td>
- </tr>
- <tr>
- <th>Description</th>
- <td>{{currentTopologyExecution.description || "-"}}</td>
- </tr>
- <tr>
- <th>Last Modified Date</th>
- <td>{{common.format.date(currentTopologyExecution.lastModifiedDate) || "-"}}</td>
- </tr>
- </tbody>
- </table>
-
- <h3>Latest Operations</h3>
- <div class="table-responsive">
- <table class="table table-bordered table-sm margin-bottom-none">
- <thead>
- <tr>
- <th>Date Time</th>
- <th>Operation</th>
- <th>Status</th>
- <th>Message</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="action in currentTopologyExecutionOptList track by $index">
- <td>{{common.format.date(action.lastModifiedDate) || "-"}}</td>
- <td>{{action.tags.operation}}</td>
- <td>{{action.status}}</td>
- <td><pre class="noWrap">{{action.message}}</pre></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/userProfile/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/userProfile/controller.js
deleted file mode 100644
index ed619d3..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/controller.js
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module('featureControllers');
- var feature = featureControllers.register("userProfile");
-
- // ==============================================================
- // = Function =
- // ==============================================================
-
- // ==============================================================
- // = User Profile =
- // ==============================================================
-
- // ======================== Profile List ========================
- //feature.navItem("list", "User Profiles", "graduation-cap");
- feature.controller('list', function(PageConfig, Site, $scope, $interval, Entities) {
- PageConfig.pageSubTitle = Site.current().tags.site;
-
- $scope.common = common;
- $scope.algorithms = [];
-
- // ======================================== Algorithms ========================================
- $scope.algorithmEntity = {};
- Entities.queryEntities("AlertDefinitionService", {site: Site.current().tags.site, application: "userProfile"})._promise.then(function(data) {
- $scope.algorithmEntity = common.getValueByPath(data, "obj[0]");
- $scope.algorithmEntity.policy = common.parseJSON($scope.algorithmEntity.policyDef);
- });
-
- // ======================================= User profile =======================================
- $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().tags.site}, ["user", "algorithm", "content", "version"]);
- $scope.profileList._promise.then(function() {
- var _algorithms = {};
- var _users = {};
-
- // Map user
- $.each($scope.profileList, function(i, unit) {
- _algorithms[unit.tags.algorithm] = unit.tags.algorithm;
- var _user = _users[unit.tags.user] = _users[unit.tags.user] || {user: unit.tags.user};
- _user[unit.tags.algorithm] = {
- version: unit.version
- };
-
- // DE
- if(unit.tags.algorithm === "DE") {
- var _statistics = common.parseJSON(unit.content);
- _statistics = common.getValueByPath(_statistics, "statistics", []);
- _user[unit.tags.algorithm].topCommands = $.map(common.array.top(_statistics, "mean"), function(command) {
- return command.commandName;
- });
- }
- });
-
- // Map algorithms
- $scope.algorithms = $.map(_algorithms, function(algorithm) {
- return algorithm;
- }).sort();
-
- $scope.profileList.splice(0);
- $scope.profileList.push.apply($scope.profileList, common.map.toArray(_users));
- });
-
- // =========================================== Task ===========================================
- $scope.tasks = [];
- function _loadTasks() {
- var _tasks = Entities.queryEntities("ScheduleTaskService", {
- site: Site.current().tags.site,
- _pageSize: 100,
- _duration: 1000 * 60 * 60 * 24 * 14,
- __ETD: 1000 * 60 * 60 * 24
- });
- _tasks._promise.then(function() {
- $scope.tasks.splice(0);
- $scope.tasks.push.apply($scope.tasks, _tasks);
-
- // Duration
- $.each($scope.tasks, function(i, data) {
- if(data.timestamp && data.updateTime) {
- var _ms = (new moment(data.updateTime)).diff(new moment(data.timestamp));
- var _d = moment.duration(_ms);
- data._duration = Math.floor(_d.asHours()) + moment.utc(_ms).format(":mm:ss");
- data.duration = _ms;
- } else {
- data._duration = "--";
- }
- });
- });
- }
-
- $scope.runningTaskCount = function () {
- return common.array.count($scope.tasks, "INITIALIZED", "status") +
- common.array.count($scope.tasks, "PENDING", "status") +
- common.array.count($scope.tasks, "EXECUTING", "status");
- };
-
- // Create task
- $scope.updateTask = function() {
- $.dialog({
- title: "Confirm",
- content: "Do you want to update now?",
- confirm: true
- }, function(ret) {
- if(!ret) return;
-
- var _entity = {
- status: "INITIALIZED",
- detail: "Newly created command",
- tags: {
- site: Site.current().tags.site,
- type: "USER_PROFILE_TRAINING"
- },
- timestamp: +new Date()
- };
- Entities.updateEntity("ScheduleTaskService", _entity, {timestamp: false})._promise.success(function(data) {
- if(!Entities.dialog(data)) {
- _loadTasks();
- }
- });
- });
- };
-
- // Show detail
- $scope.showTaskDetail = function(task) {
- var _content = $("<pre>").text(task.detail);
-
- var $mdl = $.dialog({
- title: "Detail",
- content: _content
- });
-
- _content.click(function(e) {
- if(!e.ctrlKey) return;
-
- $.dialog({
- title: "Confirm",
- content: "Remove this task?",
- confirm: true
- }, function(ret) {
- if(!ret) return;
-
- $mdl.modal('hide');
- Entities.deleteEntity("ScheduleTaskService", task)._promise.then(function() {
- _loadTasks();
- });
- });
- });
- };
-
- _loadTasks();
- var _loadInterval = $interval(_loadTasks, app.time.refreshInterval);
- $scope.$on('$destroy',function(){
- $interval.cancel(_loadInterval);
- });
- });
-
- // ======================= Profile Detail =======================
- feature.controller('detail', function(PageConfig, Site, $scope, $wrapState, Entities) {
- PageConfig.pageTitle = "User Profile";
- PageConfig.pageSubTitle = Site.current().tags.site;
- PageConfig
- .addNavPath("User Profile", "/userProfile/list")
- .addNavPath("Detail");
-
- $scope.user = $wrapState.param.filter;
-
- // User profile
- $scope.profiles = {};
- $scope.profileList = Entities.queryEntities("MLModelService", {site: Site.current().tags.site, user: $scope.user});
- $scope.profileList._promise.then(function() {
- $.each($scope.profileList, function(i, unit) {
- unit._content = common.parseJSON(unit.content);
- $scope.profiles[unit.tags.algorithm] = unit;
- });
-
- // DE
- if($scope.profiles.DE) {
- console.log($scope.profiles.DE);
-
- $scope.profiles.DE._chart = {};
-
- $scope.profiles.DE.estimates = {};
- $.each($scope.profiles.DE._content, function(key, value) {
- if(key !== "statistics") {
- $scope.profiles.DE.estimates[key] = value;
- }
- });
-
- var _meanList = [];
- var _stddevList = [];
-
- $.each($scope.profiles.DE._content.statistics, function(i, unit) {
- _meanList[i] = {
- x: unit.commandName,
- y: unit.mean
- };
- _stddevList[i] = {
- x: unit.commandName,
- y: unit.stddev
- };
- });
- $scope.profiles.DE._chart.series = [
- {
- key: "mean",
- values: _meanList
- },
- {
- key: "stddev",
- values: _stddevList
- }
- ];
-
- // Percentage table list
- $scope.profiles.DE.meanList = [];
- var _total = common.array.sum($scope.profiles.DE._content.statistics, "mean");
- $.each($scope.profiles.DE._content.statistics, function(i, unit) {
- $scope.profiles.DE.meanList.push({
- command: unit.commandName,
- percentage: unit.mean / _total
- });
- });
- }
-
- // EigenDecomposition
- if($scope.profiles.EigenDecomposition && $scope.profiles.EigenDecomposition._content.principalComponents) {
- $scope.profiles.EigenDecomposition._chart = {
- series: [],
- };
-
- $.each($scope.profiles.EigenDecomposition._content.principalComponents, function(z, grp) {
- var _line = [];
- $.each(grp, function(x, y) {
- _line.push([x,y,z]);
- });
-
- $scope.profiles.EigenDecomposition._chart.series.push({
- data: _line
- });
- });
- }
- });
-
- // UI
- $scope.showRawData = function(content) {
- $.dialog({
- title: "Raw Data",
- content: $("<pre>").text(content)
- });
- };
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/detail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/detail.html b/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/detail.html
deleted file mode 100644
index 0f94e03..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/detail.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-user"> </i>
- <h3 class="box-title">
- {{user}}
- </h3>
- </div>
- <div class="box-body">
- <div>
- <div class="inline-group">
- <dl><dt>User</dt><dd>{{user}}</dd></dl>
- <dl><dt>Site</dt><dd>{{Site.current().tags.site}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Other Info</dt><dd class="text-muted">N/A</dd></dl>
- </div>
- </div>
-
- <div class="overlay" ng-hide="profileList._promise.$$state.status === 1;">
- <span class="fa fa-refresh fa-spin"></span>
- </div>
- </div>
-</div>
-
-<!-- Analysis -->
-<div class="nav-tabs-custom">
- <ul class="nav nav-tabs">
- <li class="active">
- <a href="[data-id='DE']" data-toggle="tab" ng-click=" currentTab='DE'">DE</a>
- </li>
- <li>
- <a href="[data-id='EigenDecomposition']" data-toggle="tab" ng-click=" currentTab='EigenDecomposition'">EigenDecomposition</a>
- </li>
- <li class="pull-right">
- <button class="btn btn-primary" ng-click="showRawData(currentTab === 'EigenDecomposition' ? profiles.EigenDecomposition.content : profiles.DE.content)">Raw Data</button>
- </li>
- </ul>
- <div class="tab-content">
- <div class="tab-pane active" data-id="DE">
- <div class="row">
- <div class="col-md-9">
- <div nvd3="profiles.DE._chart.series" data-config="{chart: 'column', xType: 'text', height: 400}" class="nvd3-chart-cntr" height="400"></div>
- <div class="inline-group text-center">
- <dl ng-repeat="(key, value) in profiles.DE.estimates"><dt>{{key}}</dt><dd>{{value}}</dd></dl>
- </div>
- </div>
-
- <div class="col-md-3">
- <table class="table table-bordered">
- <thead>
- <tr>
- <th>Command</th>
- <th>Percentage</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="unit in profiles.DE.meanList">
- <td>{{unit.command}}</td>
- <td class="text-right">{{(unit.percentage*100).toFixed(2)}}%</td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div><!-- /.tab-pane -->
- <div class="tab-pane" data-id="EigenDecomposition">
- <div line3d-chart height="400" data="profiles.EigenDecomposition._chart.series"> </div>
- </div><!-- /.tab-pane -->
- </div><!-- /.tab-content -->
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/list.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/list.html b/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/list.html
deleted file mode 100644
index 2f14479..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/userProfile/page/list.html
+++ /dev/null
@@ -1,138 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-list-alt"> </i>
- <h3 class="box-title">
- User Profiles
- <small><a data-toggle="collapse" href="[data-id='algorithms']">Detail</a></small>
- </h3>
- <div class="pull-right">
- <a class="label label-primary" ng-class="runningTaskCount() ? 'label-primary' : 'label-default'" data-toggle="modal" data-target="#taskMDL">Update</a>
- </div>
- </div>
- <div class="box-body">
- <!-- Algorithms -->
- <div data-id="algorithms" class="collapse">
- <table class="table table-bordered">
- <thead>
- <tr>
- <th>Name</th>
- <td>Feature</td>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="algorithm in algorithmEntity.policy.algorithms">
- <td>{{algorithm.name}}</td>
- <td>{{algorithm.features}}</td>
- </tr>
- </tbody>
- </table>
- <hr/>
- </div>
-
- <!-- User Profile List -->
- <p ng-show="profileList._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div sorttable source="profileList" ng-show="profileList._promise.$$state.status === 1">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="10%" sortpath="user">User</th>
- <th>Most Predominat Feature</th>
- <th width="10"></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <a href="#/userProfile/detail/{{item.user}}">{{item.user}}</a>
- </td>
- <td>
- {{item.DE.topCommands.slice(0,3).join(", ")}}
- </td>
- <td>
- <a href="#/userProfile/detail/{{item.user}}">Detail</a>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
-</div>
-
-<!-- Modal: User profile Schedule Task -->
-<div class="modal fade" id="taskMDL" tabindex="-1" role="dialog">
- <div class="modal-dialog modal-lg" 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">×</span>
- </button>
- <h4 class="modal-title">Training History</h4>
- </div>
- <div class="modal-body">
- <div sorttable source="tasks">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th sortpath="tags.type">Command</th>
- <th sortpath="timestamp">Start Time</th>
- <th sortpath="updateTime">Update Time</th>
- <th sortpath="duration">Duration</th>
- <th sortpath="status">Status</th>
- <th width="10"> </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>{{item.tags.type}}</td>
- <td>{{common.format.date(item.timestamp) || "--"}}</td>
- <td>{{common.format.date(item.updateTime) || "--"}}</td>
- <td>{{item._duration}}</td>
- <td class="text-nowrap">
- <span class="fa fa-hourglass-start text-muted" ng-show="item.status === 'INITIALIZED'"></span>
- <span class="fa fa-hourglass-half text-info" ng-show="item.status === 'PENDING'"></span>
- <span class="fa fa-circle-o-notch text-primary" ng-show="item.status === 'EXECUTING'"></span>
- <span class="fa fa-check-circle text-success" ng-show="item.status === 'SUCCEEDED'"></span>
- <span class="fa fa-exclamation-circle text-danger" ng-show="item.status === 'FAILED'"></span>
- <span class="fa fa-ban text-muted" ng-show="item.status === 'CANCELED'"></span>
- {{item.status}}
- </td>
- <td>
- <a ng-click="_parent.showTaskDetail(item)">Detail</a>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-primary pull-left" ng-click="updateTask()" ng-show="Auth.isRole('ROLE_ADMIN')">
- Update Now
- </button>
- <button type="button" class="btn btn-default" data-dismiss="modal">
- Close
- </button>
- </div>
- </div>
- </div>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/images/favicon.png
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/images/favicon.png b/eagle-webservice/src/main/webapp/app/public/images/favicon.png
deleted file mode 100644
index 3bede2a..0000000
Binary files a/eagle-webservice/src/main/webapp/app/public/images/favicon.png and /dev/null differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/images/favicon_white.png
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/images/favicon_white.png b/eagle-webservice/src/main/webapp/app/public/images/favicon_white.png
deleted file mode 100644
index 9879e92..0000000
Binary files a/eagle-webservice/src/main/webapp/app/public/images/favicon_white.png and /dev/null differ
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/app.config.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/app.config.js b/eagle-webservice/src/main/webapp/app/public/js/app.config.js
deleted file mode 100644
index d7c4be9..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/app.config.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- app.config = {
- // ============================================================================
- // = URLs =
- // ============================================================================
- urls: {
- HOST: '..',
-
- updateEntity: 'rest/entities?serviceName=${serviceName}',
- queryEntity: 'rest/entities/rowkey?serviceName=${serviceName}&value=${encodedRowkey}',
- queryEntities: 'rest/entities?query=${serviceName}[${condition}]{${values}}&pageSize=100000',
- deleteEntity: 'rest/entities/delete?serviceName=${serviceName}&byId=true',
- deleteEntities: 'rest/entities?query=${serviceName}[${condition}]{*}&pageSize=100000',
-
- queryGroup: 'rest/entities?query=${serviceName}[${condition}]<${groupBy}>{${values}}&pageSize=100000',
- querySeries: 'rest/entities?query=${serviceName}[${condition}]<${groupBy}>{${values}}&pageSize=100000&timeSeries=true&intervalmin=${intervalmin}',
-
- query: 'rest/',
-
- userProfile: 'rest/authentication',
- logout: 'logout',
-
- maprNameResolver: '../rest/maprNameResolver',
-
- DELETE_HOOK: {
- FeatureDescService: 'rest/module/feature?feature=${feature}',
- ApplicationDescService: 'rest/module/application?application=${application}',
- SiteDescService: 'rest/module/site?site=${site}',
- TopologyDescriptionService: 'rest/app/topology?topology=${topology}'
- },
- UPDATE_HOOK: {
- SiteDescService: 'rest/module/siteApplication'
- }
- },
- };
-
- // ============================================================================
- // = URLs =
- // ============================================================================
- app.getURL = function(name, kvs) {
- var _path = app.config.urls[name];
- if(!_path) throw "URL:'" + name + "' not exist!";
- var _url = app.packageURL(_path);
- if(kvs !== undefined) {
- _url = common.template(_url, kvs);
- }
- return _url;
- };
-
- app.getMapRNameResolverURL = function(name,value, site) {
- var key = "maprNameResolver";
- var _path = app.config.urls[key];
- if(!_path) throw "URL:'" + name + "' not exist!";
- var _url = _path;
- if(name == "fNameResolver") {
- _url += "/" + name + "?fName=" + value + "&site=" + site;
- } else if(name == "sNameResolver") {
- _url += "/" + name + "?sName=" + value + "&site=" + site;
- } else if (name == "vNameResolver") {
- _url += "/" + name + "?vName=" + value + "&site=" + site;
- } else{
- throw "resolver:'" + name + "' not exist!";
- }
- return _url;
- };
-
- function getHookURL(hookType, serviceName) {
- var _path = app.config.urls[hookType][serviceName];
- if(!_path) return null;
-
- return app.packageURL(_path);
- }
-
- /***
- * Eagle support delete function to process special entity delete. Which will delete all the relative entity.
- * @param serviceName
- */
- app.getDeleteURL = function(serviceName) {
- return getHookURL('DELETE_HOOK', serviceName);
- };
-
- /***
- * Eagle support update function to process special entity update. Which will update all the relative entity.
- * @param serviceName
- */
- app.getUpdateURL = function(serviceName) {
- return getHookURL('UPDATE_HOOK', serviceName);
- };
-
- app.packageURL = function (path) {
- var _host = localStorage.getItem("HOST") || app.config.urls.HOST;
- return (_host ? _host + "/" : '') + path;
- };
-
- app._Host = function(host) {
- if(host) {
- localStorage.setItem("HOST", host);
- return app;
- }
- return localStorage.getItem("HOST");
- };
- app._Host.clear = function() {
- localStorage.removeItem("HOST");
- };
- app._Host.sample = "http://localhost:9099/eagle-service";
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/js/app.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/js/app.js b/eagle-webservice/src/main/webapp/app/public/js/app.js
deleted file mode 100644
index 70b4afe..0000000
--- a/eagle-webservice/src/main/webapp/app/public/js/app.js
+++ /dev/null
@@ -1,499 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var app = {};
-
-(function() {
- 'use strict';
-
- /* App Module */
- var eagleApp = angular.module('eagleApp', ['ngRoute', 'ngAnimate', 'ui.router', 'eagleControllers', 'featureControllers', 'eagle.service']);
-
- // GRUNT REPLACEMENT: eagleApp.buildTimestamp = TIMESTAMP
- eagleApp._TRS = function() {
- return eagleApp.buildTimestamp || Math.random();
- };
-
- // ======================================================================================
- // = Feature Module =
- // ======================================================================================
- var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
- var FN_ARG_SPLIT = /,/;
- var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
- var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
-
- var featureControllers = angular.module('featureControllers', ['ui.bootstrap', 'eagle.components']);
- var featureControllerCustomizeHtmlTemplate = {};
- var featureControllerProvider;
- var featureProvider;
-
- featureControllers.config(function ($controllerProvider, $provide) {
- featureControllerProvider = $controllerProvider;
- featureProvider = $provide;
- });
-
- featureControllers.service("Feature", function($wrapState, PageConfig, ConfigPageConfig, FeaturePageConfig) {
- var _features = {};
- var _services = {};
-
- var Feature = function(name, config) {
- this.name = name;
- this.config = config || {};
- this.features = {};
- };
-
- /***
- * Inner function. Replace the dependency of constructor.
- * @param constructor
- * @private
- */
- Feature.prototype._replaceDependencies = function(constructor) {
- var i, srvName;
- var _constructor, _$inject;
- var fnText, argDecl;
-
- if($.isArray(constructor)) {
- _constructor = constructor[constructor.length - 1];
- _$inject = constructor.slice(0, -1);
- } else if(constructor.$inject) {
- _constructor = constructor;
- _$inject = constructor.$inject;
- } else {
- _$inject = [];
- _constructor = constructor;
- fnText = constructor.toString().replace(STRIP_COMMENTS, '');
- argDecl = fnText.match(FN_ARGS);
- $.each(argDecl[1].split(FN_ARG_SPLIT), function(i, arg) {
- arg.replace(FN_ARG, function(all, underscore, name) {
- _$inject.push(name);
- });
- });
- }
- _constructor.$inject = _$inject;
-
- for(i = 0 ; i < _$inject.length ; i += 1) {
- srvName = _$inject[i];
- _$inject[i] = this.features[srvName] || _$inject[i];
- }
-
- return _constructor;
- };
-
- /***
- * Register a common service for feature usage. Common service will share between the feature. If you are coding customize feature, use 'Feature.service' is the better way.
- * @param name
- * @param constructor
- */
- Feature.prototype.commonService = function(name, constructor) {
- if(!_services[name]) {
- featureProvider.service(name, constructor);
- _services[name] = this.name;
- } else {
- throw "Service '" + name + "' has already be registered by feature '" + _services[name] + "'";
- }
- };
-
- /***
- * Register a service for feature usage.
- * @param name
- * @param constructor
- */
- Feature.prototype.service = function(name, constructor) {
- var _serviceName;
- if(!this.features[name]) {
- _serviceName = "__FEATURE_" + this.name + "_" + name;
- featureProvider.service(_serviceName, this._replaceDependencies(constructor));
- this.features[name] = _serviceName;
- } else {
- console.warn("Service '" + name + "' has already be registered.");
- }
- };
-
- /***
- * Create an navigation item in left navigation bar
- * @param path
- * @param title
- * @param icon use Font Awesome. Needn't with 'fa fa-'.
- */
- Feature.prototype.navItem = function(path, title, icon) {
- title = title || path;
- icon = icon || "question";
-
- FeaturePageConfig.addNavItem(this.name, {
- icon: icon,
- title: title,
- url: "#/" + this.name + "/" + path
- });
- };
-
- /***
- * Register a controller.
- * @param name
- * @param constructor
- */
- Feature.prototype.controller = function(name, constructor, htmlTemplatePath) {
- var _name = this.name + "_" + name;
-
- // Replace feature registered service
- constructor = this._replaceDependencies(constructor);
-
- // Register controller
- featureControllerProvider.register(_name, constructor);
- if(htmlTemplatePath) {
- featureControllerCustomizeHtmlTemplate[_name] = htmlTemplatePath;
- }
-
- return _name;
- };
-
- /***
- * Register a configuration controller for admin usage.
- * @param name
- * @param constructor
- */
- Feature.prototype.configController = function(name, constructor, htmlTemplatePath) {
- var _name = "config_" + this.name + "_" + name;
-
- // Replace feature registered service
- constructor = this._replaceDependencies(constructor);
-
- // Register controller
- featureControllerProvider.register(_name, constructor);
- if(htmlTemplatePath) {
- featureControllerCustomizeHtmlTemplate[_name] = htmlTemplatePath;
- }
-
- return _name;
- };
-
- /***
- * Create an navigation item in left navigation bar for admin configuraion page
- * @param path
- * @param title
- * @param icon use Font Awesome. Needn't with 'fa fa-'.
- */
- Feature.prototype.configNavItem = function(path, title, icon) {
- title = title || path;
- icon = icon || "question";
-
- ConfigPageConfig.addNavItem(this.name, {
- icon: icon,
- title: title,
- url: "#/config/" + this.name + "/" + path
- });
- };
-
- // Register
- featureControllers.register = Feature.register = function(featureName, config) {
- _features[featureName] = _features[featureName] || new Feature(featureName, config);
- return _features[featureName];
- };
-
- // Page go
- Feature.go = function(feature, page, filter) {
- if(!filter) {
- $wrapState.go("page", {
- feature: feature,
- page: page
- }, 2);
- } else {
- $wrapState.go("pageFilter", {
- feature: feature,
- page: page,
- filter: filter
- }, 2);
- }
- };
-
- // Get feature by name
- Feature.get = function (featureName) {
- return _features[featureName];
- };
-
- return Feature;
- });
-
- // ======================================================================================
- // = Router config =
- // ======================================================================================
- eagleApp.config(function ($stateProvider, $urlRouterProvider, $animateProvider) {
- // Resolve
- function _resolve(config) {
- config = config || {};
-
- var resolve = {
- Site: function (Site) {
- return Site._promise();
- },
- Authorization: function (Authorization) {
- if(!config.roleType) {
- return Authorization._promise();
- } else {
- return Authorization.rolePromise(config.roleType);
- }
- },
- Application: function (Application) {
- return Application._promise();
- }
- };
-
- if(config.featureCheck) {
- resolve._navigationCheck = function($q, $wrapState, Site, Application) {
- var _deferred = $q.defer();
-
- $q.all(Site._promise(), Application._promise()).then(function() {
- var _match, i, tmpApp;
- var _site = Site.current();
- var _app = Application.current();
-
- // Check application
- if(_site && (
- !_app ||
- !_site.applicationList.set[_app.tags.application] ||
- !_site.applicationList.set[_app.tags.application].enabled
- )
- ) {
- _match = false;
-
- for(i = 0 ; i < _site.applicationGroupList.length ; i += 1) {
- tmpApp = _site.applicationGroupList[i].enabledList[0];
- if(tmpApp) {
- _app = Application.current(tmpApp);
- _match = true;
- break;
- }
- }
-
- if(!_match) {
- _app = null;
- Application.current(null);
- }
- }
- }).finally(function() {
- _deferred.resolve();
- });
-
- return _deferred.promise;
- };
- }
-
- return resolve;
- }
-
- // Router
- var _featureBase = {
- templateUrl: function ($stateParams) {
- var _htmlTemplate = featureControllerCustomizeHtmlTemplate[$stateParams.feature + "_" + $stateParams.page];
- return "public/feature/" + $stateParams.feature + "/page/" + (_htmlTemplate || $stateParams.page) + ".html?_=" + eagleApp._TRS();
- },
- controllerProvider: function ($stateParams) {
- return $stateParams.feature + "_" + $stateParams.page;
- },
- resolve: _resolve({featureCheck: true}),
- pageConfig: "FeaturePageConfig"
- };
-
- $urlRouterProvider.otherwise("/landing");
- $stateProvider
- // =================== Landing ===================
- .state('landing', {
- url: "/landing",
- templateUrl: "partials/landing.html?_=" + eagleApp._TRS(),
- controller: "landingCtrl",
- resolve: _resolve({featureCheck: true})
- })
-
- // ================ Authorization ================
- .state('login', {
- url: "/login",
- templateUrl: "partials/login.html?_=" + eagleApp._TRS(),
- controller: "authLoginCtrl",
- access: {skipCheck: true}
- })
-
- // ================ Configuration ================
- // Site
- .state('configSite', {
- url: "/config/site",
- templateUrl: "partials/config/site.html?_=" + eagleApp._TRS(),
- controller: "configSiteCtrl",
- pageConfig: "ConfigPageConfig",
- resolve: _resolve({roleType: 'ROLE_ADMIN'})
- })
-
- // Application
- .state('configApplication', {
- url: "/config/application",
- templateUrl: "partials/config/application.html?_=" + eagleApp._TRS(),
- controller: "configApplicationCtrl",
- pageConfig: "ConfigPageConfig",
- resolve: _resolve({roleType: 'ROLE_ADMIN'})
- })
-
- // Feature
- .state('configFeature', {
- url: "/config/feature",
- templateUrl: "partials/config/feature.html?_=" + eagleApp._TRS(),
- controller: "configFeatureCtrl",
- pageConfig: "ConfigPageConfig",
- resolve: _resolve({roleType: 'ROLE_ADMIN'})
- })
-
- // Feature configuration page
- .state('configFeatureDetail', $.extend({url: "/config/:feature/:page"}, {
- templateUrl: function ($stateParams) {
- var _htmlTemplate = featureControllerCustomizeHtmlTemplate[$stateParams.feature + "_" + $stateParams.page];
- return "public/feature/" + $stateParams.feature + "/page/" + (_htmlTemplate || $stateParams.page) + ".html?_=" + eagleApp._TRS();
- },
- controllerProvider: function ($stateParams) {
- return "config_" + $stateParams.feature + "_" + $stateParams.page;
- },
- pageConfig: "ConfigPageConfig",
- resolve: _resolve({roleType: 'ROLE_ADMIN'})
- }))
-
- // =================== Feature ===================
- // Dynamic feature page
- .state('page', $.extend({url: "/:feature/:page"}, _featureBase))
- .state('pageFilter', $.extend({url: "/:feature/:page/:filter"}, _featureBase))
- ;
-
- // Animation
- $animateProvider.classNameFilter(/^((?!(fa-spin)).)*$/);
- $animateProvider.classNameFilter(/^((?!(tab-pane)).)*$/);
- });
-
- eagleApp.filter('parseJSON', function () {
- return function (input, defaultVal) {
- return common.parseJSON(input, defaultVal);
- };
- });
-
- eagleApp.filter('split', function () {
- return function (input, regex) {
- return input.split(regex);
- };
- });
-
- eagleApp.filter('reverse', function () {
- return function (items) {
- return items.slice().reverse();
- };
- });
-
- // ======================================================================================
- // = Main Controller =
- // ======================================================================================
- eagleApp.controller('MainCtrl', function ($scope, $wrapState, $http, $injector, ServiceError, PageConfig, FeaturePageConfig, Site, Authorization, Entities, nvd3, Application, Feature, UI) {
- window.serviceError = $scope.ServiceError = ServiceError;
- window.site = $scope.Site = Site;
- window.auth = $scope.Auth = Authorization;
- window.entities = $scope.Entities = Entities;
- window.application = $scope.Application = Application;
- window.pageConfig = $scope.PageConfig = PageConfig;
- window.featurePageConfig = $scope.FeaturePageConfig = FeaturePageConfig;
- window.feature = $scope.Feature = Feature;
- window.ui = $scope.UI = UI;
- window.nvd3 = nvd3;
- $scope.app = app;
- $scope.common = common;
-
- Object.defineProperty(window, "scope",{
- get: function() {
- return angular.element("[ui-view]").scope();
- }
- });
-
- // Clean up
- $scope.$on('$stateChangeStart', function (event, next, nextParam, current, currentParam) {
- console.log("[Switch] current ->", current, currentParam);
- console.log("[Switch] next ->", next, nextParam);
- // Page initialization
- PageConfig.reset();
-
- // Dynamic navigation list
- if(next.pageConfig) {
- $scope.PageConfig.navConfig = $injector.get(next.pageConfig);
- } else {
- $scope.PageConfig.navConfig = {};
- }
-
- // Authorization
- // > Login check
- if (!common.getValueByPath(next, "access.skipCheck", false)) {
- if (!Authorization.isLogin) {
- console.log("[Authorization] Need access. Redirect...");
- $wrapState.go("login");
- }
- }
-
- // > Role control
- /*var _roles = common.getValueByPath(next, "access.roles", []);
- if (_roles.length && Authorization.userProfile.roles) {
- var _roleMatch = false;
- $.each(_roles, function (i, roleName) {
- if (Authorization.isRole(roleName)) {
- _roleMatch = true;
- return false;
- }
- });
-
- if (!_roleMatch) {
- $wrapState.path("/dam");
- }
- }*/
- });
-
- $scope.$on('$stateChangeError', function (event, next, nextParam, current, currentParam, error) {
- console.error("[Switch] Error", arguments);
- });
-
- // Get side bar navigation item class
- $scope.getNavClass = function (page) {
- var path = page.url.replace(/^#/, '');
-
- if ($wrapState.path() === path) {
- PageConfig.pageTitle = PageConfig.pageTitle || page.title;
- return "active";
- } else {
- return "";
- }
- };
-
- // Get side bar navigation item class visible
- $scope.getNavVisible = function (page) {
- if (!page.roles) return true;
-
- for (var i = 0; i < page.roles.length; i += 1) {
- var roleName = page.roles[i];
- if (Authorization.isRole(roleName)) {
- return true;
- }
- }
-
- return false;
- };
-
- // Authorization
- $scope.logout = function () {
- console.log("[Authorization] Logout. Redirect...");
- Authorization.logout();
- $wrapState.go("login");
- };
- });
-})();
\ No newline at end of file
[13/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js
new file mode 100644
index 0000000..fafe699
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/index.js
@@ -0,0 +1,489 @@
+/*
+ * 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 () {
+ /**
+ * `register` is global function that for application to set up 'controller', 'service', 'directive', 'route' in Eagle
+ */
+ var jpmApp = register(['ngRoute', 'ngAnimate', 'ui.router', 'eagle.service']);
+
+ jpmApp.route("jpmList", {
+ url: "/jpm/list?startTime&endTime",
+ site: true,
+ templateUrl: "partials/job/list.html",
+ controller: "listCtrl",
+ resolve: { time: true }
+ }).route("jpmOverview", {
+ url: "/jpm/overview?startTime&endTime",
+ site: true,
+ templateUrl: "partials/job/overview.html",
+ controller: "overviewCtrl",
+ resolve: { time: true }
+ }).route("jpmStatistics", {
+ url: "/jpm/statistics",
+ site: true,
+ templateUrl: "partials/job/statistic.html",
+ controller: "statisticCtrl"
+ }).route("jpmDetail", {
+ url: "/jpm/detail/:jobId",
+ site: true,
+ templateUrl: "partials/job/detail.html",
+ controller: "detailCtrl"
+ }).route("jpmJobTask", {
+ url: "/jpm/jobTask/:jobId?startTime&endTime",
+ site: true,
+ templateUrl: "partials/job/task.html",
+ controller: "jobTaskCtrl"
+ }).route("jpmCompare", {
+ url: "/jpm/compare/:jobDefId?from&to",
+ site: true,
+ reloadOnSearch: false,
+ templateUrl: "partials/job/compare.html",
+ controller: "compareCtrl"
+ });
+
+ jpmApp.portal({name: "YARN Jobs", icon: "taxi", list: [
+ {name: "Overview", path: "jpm/overview"},
+ {name: "Job Statistics", path: "jpm/statistics"},
+ {name: "Job List", path: "jpm/list"}
+ ]}, true);
+
+ jpmApp.service("JPM", function ($q, $http, Time, Site, Application) {
+ var JPM = window._JPM = {};
+
+ // TODO: timestamp support
+ JPM.QUERY_LIST = '${baseURL}/rest/entities?query=${query}[${condition}]{${fields}}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+ JPM.QUERY_GROUPS = '${baseURL}/rest/entities?query=${query}[${condition}]<${groups}>{${field}}${order}${top}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+ JPM.QUERY_GROUPS_INTERVAL = '${baseURL}/rest/entities?query=${query}[${condition}]<${groups}>{${field}}${order}${top}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}&intervalmin=${intervalMin}&timeSeries=true';
+ JPM.QUERY_METRICS = '${baseURL}/rest/entities?query=GenericMetricService[${condition}]{*}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+ JPM.QUERY_METRICS_AGG = '${baseURL}/rest/entities?query=GenericMetricService[${condition}]<${groups}>{${field}}${order}${top}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+ JPM.QUERY_METRICS_INTERVAL = '${baseURL}/rest/entities?query=GenericMetricService[${condition}]<${groups}>{${field}}${order}${top}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}&intervalmin=${intervalMin}&timeSeries=true';
+ JPM.QUERY_MR_JOBS = '${baseURL}/rest/mrJobs/search';
+ JPM.QUERY_JOB_LIST = '${baseURL}/rest/mrJobs?query=%s[${condition}]{${fields}}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+ JPM.QUERY_JOB_STATISTIC = '${baseURL}/rest/mrJobs/jobCountsByDuration?site=${site}&timeDistInSecs=${times}&startTime=${startTime}&endTime=${endTime}&jobType=${jobType}';
+ JPM.QUERY_TASK_STATISTIC = '${baseURL}/rest/mrTasks/taskCountsByDuration?jobId=${jobId}&site=${site}&timeDistInSecs=${times}&top=${top}';
+
+ JPM.QUERY_MR_JOB_COUNT = '${baseURL}/rest/mrJobs/runningJobCounts';
+ //JPM.QUERY_MR_JOB_METRIC_TOP = '${baseURL}eagle-service/rest/mrJobs/jobMetrics/entities';
+
+ /**
+ * Fetch query content with current site application configuration
+ * @param {string} queryName
+ */
+ var getQuery = JPM.getQuery = function(queryName, siteId) {
+ var baseURL;
+ siteId = siteId || Site.current().siteId;
+ var app = Application.find("JPM_WEB_APP", siteId)[0];
+ var host = app.configuration["service.host"];
+ var port = app.configuration["service.port"];
+
+ if(!host && !port) {
+ baseURL = "";
+ } else {
+ if(host === "localhost" || !host) {
+ host = location.hostname;
+ }
+ if(!port) {
+ port = location.port;
+ }
+ baseURL = "http://" + host + ":" + port;
+ }
+
+ return common.template(JPM["QUERY_" + queryName], {baseURL: baseURL});
+ };
+
+
+ function wrapList(promise) {
+ var _list = [];
+ _list._done = false;
+
+ _list._promise = promise.then(
+ /**
+ * @param {{}} res
+ * @param {{}} res.data
+ * @param {{}} res.data.obj
+ */
+ function (res) {
+ _list.splice(0);
+ Array.prototype.push.apply(_list, res.data.obj);
+ _list._done = true;
+ return _list;
+ });
+ return _list;
+ }
+
+ function toFields(fields) {
+ return (fields || []).length > 0 ? $.map(fields, function (field) {
+ return "@" + field;
+ }).join(",") : "*";
+ }
+
+ JPM.get = function (url, params) {
+ return $http({
+ url: url,
+ method: "GET",
+ params: params
+ });
+ };
+
+ JPM.condition = function (condition) {
+ return $.map(condition, function (value, key) {
+ return "@" + key + '="' + value + '"';
+ }).join(" AND ");
+ };
+
+ /**
+ * Fetch eagle query list
+ * @param query
+ * @param condition
+ * @param {[]?} groups
+ * @param {string} field
+ * @param {number|null} intervalMin
+ * @param startTime
+ * @param endTime
+ * @param {(number|null)?} top
+ * @param {number?} limit
+ * @return {[]}
+ */
+ JPM.groups = function (query, condition, groups, field, intervalMin, startTime, endTime, top, limit) {
+ var fields = field.split(/\s*,\s*/);
+ var orderId = -1;
+ var fieldStr = $.map(fields, function (field, index) {
+ var matches = field.match(/^([^\s]*)(\s+.*)?$/);
+ if(matches[2]) {
+ orderId = index;
+ }
+ return matches[1];
+ }).join(", ");
+
+ var config = {
+ query: query,
+ condition: JPM.condition(condition),
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime),
+ groups: toFields(groups),
+ field: fieldStr,
+ order: orderId === -1 ? "" : ".{" + fields[orderId] + "}",
+ top: top ? "&top=" + top : "",
+ intervalMin: intervalMin,
+ limit: limit || 100000
+ };
+
+ var metrics_url = common.template(intervalMin ? getQuery("GROUPS_INTERVAL") : getQuery("GROUPS"), config);
+ var _list = wrapList(JPM.get(metrics_url));
+ _list._aggInfo = {
+ groups: groups,
+ startTime: Time(startTime).valueOf(),
+ interval: intervalMin * 60 * 1000
+ };
+ _list._promise.then(function () {
+ if(top) _list.reverse();
+ });
+ return _list;
+ };
+
+ /**
+ * Fetch eagle query list
+ * @param {string} query
+ * @param {{}?} condition
+ * @param {(string|number|{})?} startTime
+ * @param {(string|number|{})?} endTime
+ * @param {[]?} fields
+ * @param {number?} limit
+ * @return {[]}
+ */
+ JPM.list = function (query, condition, startTime, endTime, fields, limit) {
+ var config = {
+ query: query,
+ condition: JPM.condition(condition),
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime),
+ fields: toFields(fields),
+ limit: limit || 10000
+ };
+
+ return wrapList(JPM.get(common.template(getQuery("LIST"), config)));
+ };
+
+ /**
+ * Fetch job list
+ * @param condition
+ * @param startTime
+ * @param endTime
+ * @param {[]?} fields
+ * @param {number?} limit
+ * @return {[]}
+ */
+ JPM.jobList = function (condition, startTime, endTime, fields, limit) {
+ var config = {
+ condition: JPM.condition(condition),
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime),
+ fields: toFields(fields),
+ limit: limit || 10000
+ };
+
+ var jobList_url = common.template(getQuery("JOB_LIST"), config);
+ return wrapList(JPM.get(jobList_url));
+ };
+
+ /**
+ * Fetch job metric list
+ * @param condition
+ * @param metric
+ * @param startTime
+ * @param endTime
+ * @param {number?} limit
+ * @return {[]}
+ */
+ JPM.metrics = function (condition, metric, startTime, endTime, limit) {
+ var config = {
+ condition: JPM.condition(condition),
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime),
+ metric: metric,
+ limit: limit || 10000
+ };
+
+ var metrics_url = common.template(getQuery("METRICS"), config);
+ var _list = wrapList(JPM.get(metrics_url));
+ _list._promise.then(function () {
+ _list.reverse();
+ });
+ return _list;
+ };
+
+ /**
+ * Fetch job metric list
+ * @param {{}} condition
+ * @param {string} metric
+ * @param {[]} groups
+ * @param {string} field
+ * @param {number|null|false} intervalMin
+ * @param startTime
+ * @param endTime
+ * @param {number?} top
+ * @param {number?} limit
+ * @return {[]}
+ */
+ JPM.aggMetrics = function (condition, metric, groups, field, intervalMin, startTime, endTime, top, limit) {
+ var fields = field.split(/\s*,\s*/);
+ var orderId = -1;
+ var fieldStr = $.map(fields, function (field, index) {
+ var matches = field.match(/^([^\s]*)(\s+.*)?$/);
+ if(matches[2]) {
+ orderId = index;
+ }
+ return matches[1];
+ }).join(", ");
+
+ var config = {
+ condition: JPM.condition(condition),
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime),
+ metric: metric,
+ groups: toFields(groups),
+ field: fieldStr,
+ order: orderId === -1 ? "" : ".{" + fields[orderId] + "}",
+ top: top ? "&top=" + top : "",
+ intervalMin: intervalMin,
+ limit: limit || 100000
+ };
+
+ var metrics_url = common.template(intervalMin ? getQuery("METRICS_INTERVAL") : getQuery("METRICS_AGG"), config);
+ var _list = wrapList(JPM.get(metrics_url));
+ _list._aggInfo = {
+ groups: groups,
+ startTime: Time(startTime).valueOf(),
+ interval: intervalMin * 60 * 1000
+ };
+ _list._promise.then(function () {
+ _list.reverse();
+ });
+ return _list;
+ };
+
+ JPM.aggMetricsToEntities = function (list, flatten) {
+ var _list = [];
+ _list.done = false;
+ _list._promise = list._promise.then(function () {
+ var _startTime = list._aggInfo.startTime;
+ var _interval = list._aggInfo.interval;
+
+ $.each(list, function (i, obj) {
+ var tags = {};
+ $.each(list._aggInfo.groups, function (j, group) {
+ tags[group] = obj.key[j];
+ });
+
+ var _subList = $.map(obj.value[0], function (value, index) {
+ return {
+ timestamp: _startTime + index * _interval,
+ value: [value],
+ tags: tags
+ };
+ });
+
+ if(flatten) {
+ _list.push.apply(_list, _subList);
+ } else {
+ _list.push(_subList);
+ }
+ });
+ _list.done = true;
+ return _list;
+ });
+ return _list;
+ };
+
+ /**
+ * Fetch job duration distribution
+ * @param {string} site
+ * @param {string} jobType
+ * @param {string} times
+ * @param {{}} startTime
+ * @param {{}} endTime
+ */
+ JPM.jobDistribution = function (site, jobType, times, startTime, endTime) {
+ var url = common.template(getQuery("JOB_STATISTIC"), {
+ site: site,
+ jobType: jobType,
+ times: times,
+ startTime: Time.format(startTime),
+ endTime: Time.format(endTime)
+ });
+ return JPM.get(url);
+ };
+
+ JPM.taskDistribution = function (site, jobId, times, top) {
+ var url = common.template(getQuery("TASK_STATISTIC"), {
+ site: site,
+ jobId: jobId,
+ times: times,
+ top: top || 10
+ });
+ return JPM.get(url);
+ };
+
+ /**
+ * Get job list by sam jobDefId
+ * @param {string} site
+ * @param {string|undefined?} jobDefId
+ * @param {string|undefined?} jobId
+ * @return {[]}
+ */
+ JPM.findMRJobs = function (site, jobDefId, jobId) {
+ return wrapList(JPM.get(getQuery("MR_JOBS"), {
+ site: site,
+ jobDefId: jobDefId,
+ jobId: jobId
+ }));
+ };
+
+ /**
+ * Convert Entity list data to Chart supported series
+ * @param name
+ * @param metrics
+ * @param {{}|boolean?} rawData
+ * @param {{}?} option
+ * @return {{name: *, symbol: string, type: string, data: *}}
+ */
+ JPM.metricsToSeries = function(name, metrics, rawData, option) {
+ if(arguments.length === 3 && typeof rawData === "object") {
+ option = rawData;
+ rawData = false;
+ }
+
+ var data = $.map(metrics, function (metric) {
+ return rawData ? metric.value[0] : {
+ x: metric.timestamp,
+ y: metric.value[0]
+ };
+ });
+ return $.extend({
+ name: name,
+ symbol: 'none',
+ type: "line",
+ data: data
+ }, option || {});
+ };
+
+ JPM.metricsToInterval = function (metricList, interval) {
+ if(metricList.length === 0) return [];
+
+ var list = $.map(metricList, function (metric) {
+ var timestamp = Math.floor(metric.timestamp / interval) * interval;
+ var remainderPtg = (metric.timestamp % interval) / interval;
+ return {
+ timestamp: remainderPtg < 0.5 ? timestamp : timestamp + interval,
+ value: [metric.value[0]]
+ };
+ });
+
+ var resultList = [list[0]];
+ for(var i = 1 ; i < list.length ; i += 1) {
+ var start = list[i - 1];
+ var end = list[i];
+
+ var distance = (end.timestamp - start.timestamp);
+ if(distance > 0) {
+ var steps = distance / interval;
+ var des = (end.value[0] - start.value[0]) / steps;
+ for (var j = 1; j <= steps; j += 1) {
+ resultList.push({
+ timestamp: start.timestamp + j * interval,
+ value: [start.value[0] + des * j]
+ });
+ }
+ }
+ }
+ return resultList;
+ };
+
+ JPM.getStateClass = function (state) {
+ switch ((state || "").toUpperCase()) {
+ case "NEW":
+ case "NEW_SAVING":
+ case "SUBMITTED":
+ case "ACCEPTED":
+ return "warning";
+ case "RUNNING":
+ return "info";
+ case "SUCCESS":
+ case "SUCCEEDED":
+ return "success";
+ case "FINISHED":
+ return "primary";
+ case "FAILED":
+ return "danger";
+ }
+ return "default";
+ };
+
+ return JPM;
+ });
+
+ jpmApp.requireCSS("style/index.css");
+ jpmApp.require("widget/jobStatistic.js");
+ jpmApp.require("ctrl/overviewCtrl.js");
+ jpmApp.require("ctrl/statisticCtrl.js");
+ jpmApp.require("ctrl/listCtrl.js");
+ jpmApp.require("ctrl/detailCtrl.js");
+ jpmApp.require("ctrl/jobTaskCtrl.js");
+ jpmApp.require("ctrl/compareCtrl.js");
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/compare.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/compare.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/compare.html
new file mode 100644
index 0000000..4ab8140
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/compare.html
@@ -0,0 +1,274 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="row flex">
+ <div class="col-sm-12 col-md-3">
+ <div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Summary
+ </h3>
+ </div>
+ <div class="box-body">
+ <table class="table table-striped">
+ <tbody>
+ <tr>
+ <th>Def Id</th>
+ <td class="text-break">{{jobDefId}}</td>
+ </tr>
+ <tr>
+ <th>Type</th>
+ <td>{{jobList[0].tags.jobType}}</td>
+ </tr>
+ <tr>
+ <th>Site</th>
+ <td>{{jobList[0].tags.site}}</td>
+ </tr>
+ <tr>
+ <th>Owner</th>
+ <td>{{jobList[0].tags.user}}</td>
+ </tr>
+ <tr>
+ <th>Queue</th>
+ <td>{{jobList[0].tags.queue}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-9">
+ <div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Comparison
+ <small>
+ Click to compare job
+ (ctrl + click: set <strong>from Job</strong>, shift + click: set <strong>to Job</strong>)
+ </small>
+ </h3>
+ </div>
+ <div class="box-body">
+ <div class="jpm-chart">
+ <div chart="trendChart" class="jpm-chart-container" series="jobTrendSeries" category="jobTrendCategory"
+ ng-click="compareJobSelect" option="jobTrendOption"></div>
+ <div ng-if="(jobTrendSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="box box-primary" ng-if="fromJob && toJob">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Comparison
+ </h3>
+ <div class="box-tools pull-right">
+ <button type="button" class="btn btn-box-tool" data-widget="collapse">
+ <i class="fa fa-minus"></i>
+ </button>
+ </div>
+ </div>
+ <div class="box-body">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Field</th>
+ <th>From</th>
+ <th>To</th>
+ <th>Field</th>
+ <th>From</th>
+ <th>To</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>
+ Job Id
+ <a class="fa fa-retweet" ng-click="exchangeJobs()"></a>
+ </th>
+ <td><a ui-sref="jpmDetail({siteId: site, jobId: fromJob.tags.jobId})">{{fromJob.tags.jobId}}</a></td>
+ <td><a ui-sref="jpmDetail({siteId: site, jobId: toJob.tags.jobId})">{{toJob.tags.jobId}}</a></td>
+ <th>Duration</th>
+ <td>{{Time.diffStr(fromJob.durationTime)}}</td>
+ <td>
+ {{Time.diffStr(toJob.durationTime)}}
+ <span class="{{jobCompareClass('durationTime')}}">{{jobCompareValue('durationTime')}}</span>
+ </td>
+ </tr>
+ <tr>
+ <th>Total Maps</th>
+ <td>{{common.number.toFixed(fromJob.numTotalMaps)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.numTotalMaps)}}
+ <span class="{{jobCompareClass('numTotalMaps')}}">{{jobCompareValue('numTotalMaps')}}</span>
+ </td>
+ <th>Total Reduces</th>
+ <td>{{common.number.toFixed(fromJob.numTotalReduces)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.numTotalReduces)}}
+ <span class="{{jobCompareClass('numTotalReduces')}}">{{jobCompareValue('numTotalReduces')}}</span>
+ </td>
+ </tr>
+ <tr>
+ <th>HDFS Read Bytes</th>
+ <td>{{common.number.toFixed(fromJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)}}
+ <span class="{{jobCompareClass(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','HDFS_BYTES_READ'])}}">
+ {{jobCompareValue(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','HDFS_BYTES_READ'])}}
+ </span>
+ </td>
+ <th>HDFS Write Bytes</th>
+ <td>{{common.number.toFixed(fromJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)}}
+ <span class="{{jobCompareClass(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','HDFS_BYTES_WRITTEN'])}}">
+ {{jobCompareValue(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','HDFS_BYTES_WRITTEN'])}}
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <th>Local Read Bytes</th>
+ <td>{{common.number.toFixed(fromJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_READ)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_READ)}}
+ <span class="{{jobCompareClass(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','FILE_BYTES_READ'])}}">
+ {{jobCompareValue(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','FILE_BYTES_READ'])}}
+ </span>
+ </td>
+ <th>Local Write Bytes</th>
+ <td>{{common.number.toFixed(fromJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_WRITTEN)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_WRITTEN)}}
+ <span class="{{jobCompareClass(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','FILE_BYTES_WRITTEN'])}}">
+ {{jobCompareValue(['jobCounters','counters','org.apache.hadoop.mapreduce.FileSystemCounter','FILE_BYTES_WRITTEN'])}}
+ </span>
+ </td>
+ </tr>
+ <tr>
+ <th>Last Map Duration</th>
+ <td>{{common.number.toFixed(fromJob.lastMapDuration)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.lastMapDuration)}}
+ <span class="{{jobCompareClass('lastMapDuration')}}">{{jobCompareValue('lastMapDuration')}}</span>
+ </td>
+ <th>Last Reduce Duration</th>
+ <td>{{common.number.toFixed(fromJob.lastReduceDuration)}}</td>
+ <td>
+ {{common.number.toFixed(toJob.lastReduceDuration)}}
+ <span class="{{jobCompareClass('lastReduceDuration')}}">{{jobCompareValue('lastReduceDuration')}}</span>
+ </td>
+ </tr>
+ <tr>
+ <th>Data Local Maps</th>
+ <td>{{common.number.toFixed(fromJob.dataLocalMapsPercentage * 100)}}%</td>
+ <td>
+ {{common.number.toFixed(toJob.dataLocalMapsPercentage * 100)}}%
+ <span class="{{jobCompareClass('dataLocalMapsPercentage')}}">{{jobCompareValue('dataLocalMapsPercentage')}}</span>
+ </td>
+ <th>Rack Local Maps</th>
+ <td>{{common.number.toFixed(fromJob.rackLocalMapsPercentage * 100)}}%</td>
+ <td>
+ {{common.number.toFixed(toJob.rackLocalMapsPercentage * 100)}}%
+ <span class="{{jobCompareClass('rackLocalMapsPercentage')}}">{{jobCompareValue('rackLocalMapsPercentage')}}</span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="row">
+ <div class="col-lg-6 col-md-12">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="comparisonChart_Container.series"
+ category="comparisonChart_Container.categories"></div>
+ <div ng-if="(comparisonChart_Container.series || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-6 col-md-12">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="comparisonChart_allocatedMB.series"
+ category="comparisonChart_allocatedMB.categories"></div>
+ <div ng-if="(comparisonChart_allocatedMB.series || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-6 col-md-12">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="comparisonChart_vCores.series"
+ category="comparisonChart_vCores.categories"></div>
+ <div ng-if="(comparisonChart_vCores.series || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-lg-6 col-md-12">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="comparisonChart_taskDistribution.series"
+ category="comparisonChart_taskDistribution.categories"></div>
+ <div ng-if="(comparisonChart_taskDistribution.series || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="box box-primary" ng-if="jobList.length">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ History Jobs
+ </h3>
+ </div>
+ <div class="box-body">
+ <div sort-table="jobList" sortpath="-startTime">
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th width="10" sortpath="currentState">Status</th>
+ <th sortpath="tags.jobId">Id</th>
+ <th sortpath="tags.jobName">Name</th>
+ <th width="140" sortpath="startTime">Start Time</th>
+ <th width="140" sortpath="durationTime">Duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="label label-{{getStateClass(item.currentState)}}">{{item.currentState}}</span></td>
+ <td class="text-no-break">
+ <span ng-if="item.tags.jobId === fromJob.tags.jobId">[From]</span>
+ <span ng-if="item.tags.jobId === toJob.tags.jobId">[To]</span>
+ <a ng-click="compareJobSelect($event, item)">{{item.tags.jobId}}</a>
+ <a class="fa fa-link" ui-sref="jpmDetail({siteId: site, jobId: item.tags.jobId})" target="_blank"></a>
+ </td>
+ <td class="text-break">{{item.tags.jobName}}</td>
+ <td>{{Time.format(item.startTime)}}</td>
+ <td>{{Time.diffStr(item.durationTime)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/detail.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/detail.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/detail.html
new file mode 100644
index 0000000..57561ba
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/detail.html
@@ -0,0 +1,256 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="row flex">
+ <div class="col-lg-6 col-md-12">
+ <div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Job Info
+ <span class="label label-{{getStateClass(job.currentState)}}">{{job.currentState}}</span>
+ </h3>
+ <div class="pull-right box-tools">
+ <a ui-sref="jpmCompare({siteId: site, jobDefId: job.tags.jobDefId, to: job.tags.jobId})" class="btn btn-primary btn-xs">
+ <span class="fa fa-code-fork"></span>
+ Compare
+ </a>
+ </div>
+ </div>
+ <div class="box-body">
+ <table class="table table-striped">
+ <tbody>
+ <tr>
+ <th>Job Name</th>
+ <td class="text-break">{{job.tags.jobName}}</td>
+ <th>Job Def Id</th>
+ <td class="text-break">{{job.tags.jobDefId}}</td>
+ </tr>
+ <tr>
+ <th>Job Id</th>
+ <td class="text-break">
+ {{job.tags.jobId}}
+ <a class="fa fa-link" href="{{job.trackingUrl}}" target="_blank" ng-if="job.trackingUrl"></a>
+ </td>
+ <th>Job Exec Id</th>
+ <td class="text-break">{{job.tags.jobExecId}}</td>
+ </tr>
+ <tr>
+ <th>User</th>
+ <td>{{job.tags.user}}</td>
+ <th>Queue</th>
+ <td>{{job.tags.queue}}</td>
+ </tr>
+ <tr>
+ <th>Site</th>
+ <td>{{job.tags.site}}</td>
+ <th>Job Type</th>
+ <td>{{job.tags.jobType}}</td>
+ </tr>
+ <tr>
+ <th>Submission Time</th>
+ <td>{{Time.format(job.submissionTime)}}</td>
+ <th>Duration</th>
+ <td class="text-light-blue">{{Time.diffStr(job.durationTime)}}</td>
+ </tr>
+ <tr>
+ <th>Start Time</th>
+ <td>{{Time.format(job.startTime)}}</td>
+ <th>End Time</th>
+ <td>{{Time.format(job.endTime)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div ng-if="!job" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-lg-6 col-md-12">
+ <div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Map Reduce
+ </h3>
+ </div>
+ <div class="box-body">
+ <table class="table table-striped">
+ <tbody>
+ <tr>
+ <th>Finished Maps</th>
+ <td class="text-success">{{common.number.toFixed(job.numFinishedMaps)}}</td>
+ <th>Failed Maps</th>
+ <td class="text-danger">{{common.number.toFixed(job.numFailedMaps)}}</td>
+ <th>Total Maps</th>
+ <td>{{common.number.toFixed(job.numTotalMaps)}}</td>
+ </tr>
+ <tr>
+ <th>Finished Reduces</th>
+ <td class="text-success">{{common.number.toFixed(job.numFinishedReduces)}}</td>
+ <th>Failed Reduces</th>
+ <td class="text-danger">{{common.number.toFixed(job.numFailedReduces)}}</td>
+ <th>Total Reduces</th>
+ <td>{{common.number.toFixed(job.numTotalReduces)}}</td>
+ </tr>
+ <tr>
+ <th>Data Local Maps</th>
+ <td>
+ {{common.number.toFixed(job.dataLocalMaps)}}
+ ({{common.number.toFixed(job.dataLocalMapsPercentage * 100)}}%)
+ </td>
+ <th>Rack Local Maps</th>
+ <td>
+ {{common.number.toFixed(job.rackLocalMaps)}}
+ ({{common.number.toFixed(job.rackLocalMapsPercentage * 100)}}%)
+ </td>
+ <th>Total Launched Maps</th>
+ <td>{{common.number.toFixed(job.totalLaunchedMaps)}}</td>
+ </tr>
+ <tr>
+ <th>Map vCores</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters["org.apache.hadoop.mapreduce.JobCounter"].VCORES_MILLIS_MAPS)}}</td>
+ <th>Map CPU</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters.MapTaskAttemptCounter.CPU_MILLISECONDS)}}</td>
+ <th>HDFS Read Bytes</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)}}</td>
+ </tr>
+ <tr>
+ <th>Reduce vCores</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters["org.apache.hadoop.mapreduce.JobCounter"].VCORES_MILLIS_REDUCES)}}</td>
+ <th>Map CPU</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters.ReduceTaskAttemptCounter.CPU_MILLISECONDS)}}</td>
+ <th>HDFS Write Bytes</th>
+ <td>{{common.number.toFixed(job.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)}}</td>
+ </tr>
+ <tr ng-if="!isRunning">
+ <th>Last Map Duration</th>
+ <td>{{Time.diffStr(job.lastMapDuration)}}</td>
+ <th>Last Reduce Duration</th>
+ <td>{{Time.diffStr(job.lastReduceDuration)}}</td>
+ <th></th>
+ <td></td>
+ </tr>
+ <tr ng-if="isRunning">
+ <th>Map Progress</th>
+ <td>{{common.number.toFixed(job.mapProgress)}}%</td>
+ <th>Reduce Progress</th>
+ <td>{{common.number.toFixed(job.reduceProgress)}}%</td>
+ <th></th>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div ng-if="!job" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Dashboards
+ </h3>
+ <div class="pull-right box-tools">
+ <a ui-sref="jpmJobTask({siteId: site, jobId: job.tags.jobId, startTime: startTimestamp, endTime: endTimestamp})"
+ class="btn btn-primary btn-xs" target="_blank" ng-if="!isRunning">
+ <span class="fa fa-map"></span>
+ Task Statistic
+ </a>
+ </div>
+ </div>
+ <div class="box-body">
+ <div class="row">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="allocatedSeries"></div>
+ <div ng-if="(allocatedSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="vCoresSeries"></div>
+ <div ng-if="(vCoresSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-sm-12 col-md-6" ng-hide="taskBucket">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="taskSeries" category="taskCategory" ng-click="taskSeriesClick"></div>
+ <div ng-if="(taskSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-sm-12 col-md-6" ng-show="taskBucket">
+ <div class="jpm-chart">
+ <div class="jpm-chart-container scroll">
+ <h3>
+ <a class="fa fa-arrow-circle-o-left" ng-click="backToTaskSeries()"></a>
+ Top Tasks
+ </h3>
+
+ <table class="table table-sm table-bordered no-margin">
+ <thead>
+ <tr>
+ <!--th>Task</th-->
+ <th>Host</th>
+ <th>HDFS Read</th>
+ <th>HDFS Write</th>
+ <th>Local Read</th>
+ <th>Local Write</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="task in taskBucket.topEntities track by $index">
+ <!--td>{{task.tags.taskId}}</td-->
+ <td>{{task.host || "[" + task.tags.taskId + "]"}}</td>
+ <td>{{common.number.format(task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_READ)}}</td>
+ <td>{{common.number.format(task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].HDFS_BYTES_WRITTEN)}}</td>
+ <td>{{common.number.format(task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_READ)}}</td>
+ <td>{{common.number.format(task.jobCounters.counters["org.apache.hadoop.mapreduce.FileSystemCounter"].FILE_BYTES_WRITTEN)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="nodeTaskCountSeries" category="nodeTaskCountCategory"
+ ></div>
+ <div ng-if="(nodeTaskCountSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/list.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/list.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/list.html
new file mode 100644
index 0000000..d64afe3
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/list.html
@@ -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.
+ -->
+
+<style>
+ .box .box-header .box-title small a {
+ cursor: pointer;
+ padding: 0 5px;
+ border-right: 1px solid #999;
+ }
+ .box .box-header .box-title small a:last-child {
+ border-right: none;
+ }
+ .box .box-header .box-title small a.text-default {
+ color: #999;
+ }
+
+ .box .box-header .box-title small a.active {
+ font-weight: bolder;
+ text-decoration: underline;
+ }
+</style>
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Job List
+ <small>
+ <a class="no-select text-{{getStateClass(state.key)}}" ng-class="{active: (tableScope.search || '').toUpperCase() === state.key}"
+ ng-repeat="state in jobStateList" ng-click="fillSearch(state.key)">
+ {{state.key}}: {{state.value}}
+ </a>
+ </small>
+ <span ng-show="!jobList._done || isSorting" class="fa fa-refresh fa-spin no-animate"></span>
+ </h3>
+ </div>
+ <div class="box-body">
+ <div id="jobList" sort-table="jobList" is-sorting="isSorting" search-path-list="searchPathList" scope="tableScope">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th sortpath="tags.jobId">Job ID</th>
+ <th sortpath="currentState">Status</th>
+ <th sortpath="tags.user" width="10">User</th>
+ <th sortpath="tags.queue">Queue</th>
+ <th sortpath="submissionTime">Submission Time</th>
+ <th sortpath="startTime">Start Time</th>
+ <th sortpath="endTime">End Time</th>
+ <th sortpath="duration">Duration</th>
+ <th sortpath="numTotalMaps">Map Tasks</th>
+ <th sortpath="numTotalReduces">Reduce Tasks</th>
+ <th sortpath="runningContainers">Containers</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in jobList">
+ <td>
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.tags.jobId})" target="_blank">{{item.tags.jobId}}</a>
+ </td>
+ <td class="text-center">
+ <span class="label label-sm label-{{getStateClass(item.currentState)}}">
+ {{item.currentState}}
+ </span>
+ </td>
+ <td>{{item.tags.user}}</td>
+ <td>{{item.tags.queue}}</td>
+ <td>{{Time.format(item.submissionTime)}}</td>
+ <td>{{Time.format(item.startTime)}}</td>
+ <td>{{Time.format(item.endTime)}}</td>
+ <td>{{Time.diffStr(item.duration)}}</td>
+ <td>{{item.numTotalMaps}}</td>
+ <td>{{item.numTotalReduces}}</td>
+ <td>{{item.runningContainers || "-"}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Running Metrics
+ </h3>
+ </div>
+ <div class="box-body no-padding">
+ <div class="row border-split">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <h3 class="text-center">Number of Running Jobs</h3>
+ <div chart class="jpm-chart-container" series="runningTrendSeries" option="chartLeftOption"></div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <h3 class="text-center">Running Containers</h3>
+ <div chart class="jpm-chart-container" series="runningContainersSeries" option="chartRightOption"></div>
+ </div>
+ </div>
+ </div>
+ <div class="row border-split">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <h3 class="text-center">Allocated vCores</h3>
+ <div chart class="jpm-chart-container" series="allocatedvcoresSeries" option="chartLeftOption"></div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <h3 class="text-center">Allocated Memory (GB)</h3>
+ <div chart class="jpm-chart-container" series="allocatedMBSeries" option="allocatedMBOption"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/overview.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/overview.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/overview.html
new file mode 100644
index 0000000..06e85ea
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/overview.html
@@ -0,0 +1,347 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#hdfsBytes" data-toggle="tab">HDFS IO Bytes</a></li>
+ <li><a href="#hdfsOPs" data-toggle="tab">HDFS IO OPs</a></li>
+ <li><a href="#diskIO" data-toggle="tab">Disk IO</a></li>
+ <li><a href="#cpu" data-toggle="tab">CPU Usage</a></li>
+ <li><a href="#memory" data-toggle="tab">Memory Usage</a></li>
+ <li class="pull-right">
+ <select class="form-control" ng-model="type" ng-change="typeChange()">
+ <option ng-repeat="(type, value) in aggregationMap track by $index" value="{{type}}">By {{common.string.capitalize(type)}}</option>
+ </select>
+ </li>
+ </ul>
+ <div class="tab-content keepContent with-border">
+ <div class="tab-pane active" id="hdfsBytes">
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top HDFS Bytes Read</h3>
+ <div chart class="jpm-chart-container" series="hdfsBtyesReadSeries" option="commonOption"></div>
+ <div ng-if="!hdfsBtyesReadSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in hdfsBtyesReadSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <hr/>
+
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top HDFS Bytes Written</h3>
+ <div chart class="jpm-chart-container" series="hdfsBtyesWrittenSeries"
+ option="commonOption"></div>
+ <div ng-if="!hdfsBtyesWrittenSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in hdfsBtyesWrittenSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="tab-pane" id="hdfsOPs">
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top HDFS Read OPs</h3>
+ <div chart class="jpm-chart-container" series="hdfsReadOpsSeries" option="commonOption"></div>
+ <div ng-if="!hdfsReadOpsSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in hdfsReadOpsSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <hr/>
+
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top HDFS Write OPs</h3>
+ <div chart class="jpm-chart-container" series="hdfsWriteOpsSeries" option="commonOption"></div>
+ <div ng-if="!hdfsWriteOpsSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in hdfsWriteOpsSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="tab-pane" id="diskIO">
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top File Bytes Read</h3>
+ <div chart class="jpm-chart-container" series="fileBytesReadSeries" option="commonOption"></div>
+ <div ng-if="!fileBytesReadSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in fileBytesReadSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <hr/>
+
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top File Bytes Written</h3>
+ <div chart class="jpm-chart-container" series="fileBytesWrittenSeries"
+ option="commonOption"></div>
+ <div ng-if="!fileBytesWrittenSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in fileBytesWrittenSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="tab-pane" id="cpu">
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top CPU Usage</h3>
+ <div chart class="jpm-chart-container" series="cpuUsageSeries" option="commonOption"></div>
+ <div ng-if="!cpuUsageSeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in cpuUsageSeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="tab-pane" id="memory">
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top Physical Memory Usage</h3>
+ <div chart class="jpm-chart-container" series="physicalMemorySeries"
+ option="commonOption"></div>
+ <div ng-if="!physicalMemorySeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in physicalMemorySeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <hr/>
+
+ <div class="row">
+ <div class="col-sm-6 col-md-8 col-lg-9">
+ <div class="jpm-chart chart-lg overlay-wrapper">
+ <h3 class="text-center">Top Virtual Memory Usage</h3>
+ <div chart class="jpm-chart-container" series="virtualMemorySeries" option="commonOption"></div>
+ <div ng-if="!virtualMemorySeries._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6 col-md-4 col-lg-3">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Total</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="item in virtualMemorySeriesList track by $index">
+ <td class="text-break">
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.name})" ng-if="type === 'job'" target="_blank">
+ {{item.name}}
+ </a>
+ <span ng-if="type !== 'job'">{{item.name}}</span>
+ </td>
+ <td title="{{item.total}}">{{common.number.abbr(item.total, true)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/statistic.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/statistic.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/statistic.html
new file mode 100644
index 0000000..9ce721a
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/statistic.html
@@ -0,0 +1,120 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li ng-class="{active: type === 'hourly'}"><a ng-click="switchType('hourly')">Hourly</a></li>
+ <li ng-class="{active: type === 'daily'}"><a ng-click="switchType('daily')">Daily</a></li>
+ <li ng-class="{active: type === 'weekly'}"><a ng-click="switchType('weekly')">Weekly</a></li>
+ <li ng-class="{active: type === 'monthly'}"><a ng-click="switchType('monthly')">Monthly</a></li>
+ </ul>
+ <div class="tab-content">
+ <div class="jpm-chart">
+ <h3 class="text-center">Number of Submitted Jobs</h3>
+ <div chart class="jpm-chart-container"
+ series="jobDistributionSeries"
+ category-func="jobDistributionCategoryFunc"
+ option="jobDistributionSeriesOption"
+ ng-click="distributionClick"></div>
+ <div ng-if="(jobDistributionSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="box-body no-padding" ng-show="distributionSelectedIndex !== -1">
+ <div class="row border-split">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart overlay-wrapper">
+ <h3 class="text-center">[{{distributionSelectedType}}] Top Job Count By User</h3>
+ <div chart class="jpm-chart-container" series="topUserJobCountSeries" category="topUserJobCountSeriesCategory" option="commonChartOption"></div>
+ <div ng-if="topUserJobCountSeries.length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart overlay-wrapper">
+ <h3 class="text-center">[{{distributionSelectedType}}] Top Job Count By Type</h3>
+ <div chart class="jpm-chart-container" series="topTypeJobCountSeries" category="topTypeJobCountSeriesCategory" option="commonChartOption"></div>
+ <div ng-if="topUserJobCountSeries.length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart overlay-wrapper">
+ <h3 class="text-center">[{{distributionSelectedType}}] Top Job Count Trend By User</h3>
+ <div chart class="jpm-chart-container" series="topUserJobCountTrendSeries" category-func="drillDownCategoryFunc" option="commonTrendChartOption"></div>
+ <div ng-if="topUserJobCountTrendSeries.length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart overlay-wrapper">
+ <h3 class="text-center">[{{distributionSelectedType}}] Top Job Count Trend By Type</h3>
+ <div chart class="jpm-chart-container" series="topTypeJobCountTrendSeries" category-func="drillDownCategoryFunc" option="commonTrendChartOption"></div>
+ <div ng-if="topUserJobCountTrendSeries.length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6" ng-show="!jobList">
+ <div class="jpm-chart overlay-wrapper">
+ <h3 class="text-center">[{{distributionSelectedType}}] Job Duration Distribution</h3>
+ <div chart class="jpm-chart-container" series="jobDurationDistributionSeries" category="bucketDurationCategory" option="commonChartOption"></div>
+ <div ng-if="jobDurationDistributionSeries.length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-12" ng-show="jobList">
+ <div class="overlay-wrapper">
+ <div sort-table="jobList" style="margin-top: 10px;">
+ <table class="table table-bordered table-striped">
+ <thead>
+ <tr>
+ <th>Job Id</th>
+ <th>Job Name</th>
+ <th>Type</th>
+ <th>User</th>
+ <th width="135">Start Time</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <a ui-sref="jpmDetail({siteId: site, jobId: item.tags.jobId})" target="_blank">{{item.tags.jobId}}</a>
+ </td>
+ <td>{{item.tags.jobName}}</td>
+ <td>{{item.tags.jobType}}</td>
+ <td>{{item.tags.user}}</td>
+ <td>{{Time.format(item.startTime)}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div ng-if="!jobList._done" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/task.html
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/task.html b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/task.html
new file mode 100644
index 0000000..9460db6
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/partials/job/task.html
@@ -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.
+ -->
+
+<div class="box box-primary">
+ <div class="box-header with-border">
+ <h3 class="box-title">
+ Task Schedule Trend
+ </h3>
+ </div>
+ <div class="box-body">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="scheduleSeries" category="scheduleCategory"></div>
+ <div ng-if="(scheduleSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="nav-tabs-custom">
+ <ul class="nav nav-tabs">
+ <li class="active"><a href="#scheduleDistribution" data-toggle="tab">Schedule Distribution</a></li>
+ <li><a href="#durationDistribution" data-toggle="tab">Duration Distribution</a></li>
+ </ul>
+ <div class="tab-content keepContent">
+ <!-- By Schedule Distribution -->
+ <div class="tab-pane fade in active" id="scheduleDistribution">
+ <div class="row">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="statusSeries" category="bucketScheduleCategory" option="statusOption"></div>
+ <div ng-if="(statusSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationSeries" category="bucketScheduleCategory" option="durationOption"></div>
+ <div ng-if="(durationSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="hdfsReadSeries" category="bucketScheduleCategory" option="hdfsReadOption"></div>
+ <div ng-if="(hdfsReadSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="hdfsWriteSeries" category="bucketScheduleCategory" option="hdfsWriteOption"></div>
+ <div ng-if="(hdfsWriteSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="localReadSeries" category="bucketScheduleCategory" option="localReadOption"></div>
+ <div ng-if="(localReadSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="localWriteSeries" category="bucketScheduleCategory" option="localWriteOption"></div>
+ <div ng-if="(localWriteSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- By Duration Distribution -->
+ <div class="tab-pane fade" id="durationDistribution">
+ <div class="row">
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationStatusSeries" category="bucketDurationCategory" option="durationStatusOption"></div>
+ <div ng-if="(durationStatusSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationMapReduceSeries" category="bucketDurationCategory" option="durationMapReduceOption"></div>
+ <div ng-if="(durationMapReduceSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationHdfsReadSeries" category="bucketDurationCategory" option="durationHdfsReadOption"></div>
+ <div ng-if="(durationHdfsReadSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationHdfsWriteSeries" category="bucketDurationCategory" option="durationHdfsWriteOption"></div>
+ <div ng-if="(durationHdfsWriteSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationLocalReadSeries" category="bucketDurationCategory" option="durationLocalReadOption"></div>
+ <div ng-if="(durationLocalReadSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-12 col-md-6">
+ <div class="jpm-chart">
+ <div chart class="jpm-chart-container" series="durationLocalWriteSeries" category="bucketDurationCategory" option="durationLocalWriteOption"></div>
+ <div ng-if="(durationLocalWriteSeries || []).length === 0" class="overlay">
+ <i class="fa fa-refresh fa-spin"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css
new file mode 100644
index 0000000..fbe238f
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/style/index.css
@@ -0,0 +1,76 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+.jpm-chart {
+ position: relative;
+ margin-bottom: 15px;
+}
+
+.jpm-chart h3 {
+ margin: 10px 0 10px 0;
+}
+
+.jpm-chart .jpm-chart-container {
+ height: 300px;
+ position: relative;
+}
+
+.jpm-chart .jpm-chart-container.scroll {
+ overflow-y: auto;
+}
+
+.jpm-chart.chart-lg .jpm-chart-container {
+ height: 350px;
+}
+
+.with-border .jpm-chart {
+ padding-bottom: 15px;
+ margin-bottom: 15px;
+ border-bottom: 1px solid #f4f4f4;
+}
+
+.with-border .jpm-chart:last-child {
+ padding-bottom: 0;
+ margin-bottom: 0;
+ border-bottom: 0;
+}
+
+.jpm-chart .overlay {
+ top: 0;
+ bottom: 0;
+ position: absolute;
+ width: 100%;
+ background: rgba(255,255,255,0.7);
+}
+
+.jpm-chart .overlay > .fa {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-left: -15px;
+ margin-top: -15px;
+ color: #000;
+ font-size: 30px;
+}
+
+.small-box.jpm {
+ margin: 0;
+ height: 100%;
+ min-height: 110px;
+}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/widget/jobStatistic.js
----------------------------------------------------------------------
diff --git a/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/widget/jobStatistic.js b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/widget/jobStatistic.js
new file mode 100644
index 0000000..1572d5e
--- /dev/null
+++ b/eagle-jpm/eagle-jpm-web/src/main/webapp/app/apps/jpm/widget/jobStatistic.js
@@ -0,0 +1,108 @@
+/*
+ * 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 () {
+ /**
+ * `register` without params will load the module which using require
+ */
+ register(function (jpmApp) {
+ jpmApp.directive("jpmWidget", function () {
+ return {
+ restrict: 'AE',
+ controller: function($scope, $interval, Application, JPM, Time) {
+ var site = $scope.site;
+ var refreshInterval;
+
+ if(!site) {
+ $scope.list = $.map(Application.find("JPM_WEB_APP"), function (app) {
+ return {
+ siteId: app.site.siteId,
+ siteName: app.site.siteName || app.site.siteId,
+ count: -1
+ };
+ });
+ } else {
+ $scope.list = [{
+ siteId: site.siteId,
+ siteName: site.siteName || site.siteId,
+ count: -1
+ }];
+ }
+
+ function refresh() {
+ $.each($scope.list, function (i, site) {
+ var query = JPM.getQuery("GROUPS", site.siteId);
+ var url = common.template(query, {
+ query: "RunningJobExecutionService",
+ condition: '@site="' + site.siteId + '" AND @internalState="RUNNING"',
+ groups: "@site",
+ field: "count",
+ order: "",
+ top: "",
+ limit: 100000,
+ startTime: Time.format(Time().subtract(3, "d")),
+ endTime: Time.format(Time().add(1, "d"))
+ });
+ JPM.get(url).then(function (res) {
+ site.count = common.getValueByPath(res, ["data", "obj", 0, "value", 0]);
+ });
+ });
+ }
+
+ refresh();
+ refreshInterval = $interval(refresh, 30 * 1000);
+
+ $scope.$on('$destroy', function() {
+ $interval.cancel(refreshInterval);
+ });
+ },
+ template:
+ '<div class="small-box bg-aqua jpm">' +
+ '<div class="inner">' +
+ '<h3>JPM</h3>' +
+ '<p ng-repeat="site in list track by $index">' +
+ '<a ui-sref="jpmList({siteId: site.siteId})">' +
+ '<strong>{{site.siteName}}</strong>: ' +
+ '<span ng-show="site.count === -1" class="fa fa-refresh fa-spin no-animate"></span>' +
+ '<span ng-show="site.count !== -1">{{site.count}}</span> Running Jobs' +
+ '</a>' +
+ '</p>' +
+ '</div>' +
+ '<div class="icon">' +
+ '<i class="fa fa-taxi"></i>' +
+ '</div>' +
+ '</div>',
+ replace: true
+ };
+ });
+
+ /**
+ * Customize the widget content. Return false will prevent auto compile.
+ * @param {{}} $element
+ * @param {function} $element.append
+ */
+ function registerWidget($element) {
+ $element.append(
+ $("<div jpm-widget data-site='site'>")
+ );
+ }
+
+ jpmApp.widget("jobStatistic", registerWidget);
+ jpmApp.widget("jobStatistic", registerWidget, true);
+ });
+})();
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/.gitignore
----------------------------------------------------------------------
diff --git a/eagle-server/.gitignore b/eagle-server/.gitignore
new file mode 100644
index 0000000..f3d085a
--- /dev/null
+++ b/eagle-server/.gitignore
@@ -0,0 +1,7 @@
+/bin/
+/target/
+/src/main/webapp/app/dev/apps
+grunt.json
+node_modules
+ui
+tmp
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-server/pom.xml b/eagle-server/pom.xml
index 31b219d..13cdff6 100644
--- a/eagle-server/pom.xml
+++ b/eagle-server/pom.xml
@@ -223,9 +223,30 @@
</profile>
</profiles>
<build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>exec-ui-install</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>bash</executable>
+ <arguments>
+ <argument>${basedir}/ui-build.sh</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
<resources>
<resource>
- <directory>src/main/webapp/app</directory>
+ <directory>src/main/webapp/app/ui</directory>
<targetPath>assets</targetPath>
</resource>
<resource>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/.editorconfig
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/.editorconfig b/eagle-server/src/main/webapp/app/.editorconfig
new file mode 100644
index 0000000..42a9b69
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/.editorconfig
@@ -0,0 +1,27 @@
+# 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.
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = tab
+indent_size = 4
+
+[*.md]
+trim_trailing_whitespace = false
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/Gruntfile.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/Gruntfile.js b/eagle-server/src/main/webapp/app/Gruntfile.js
new file mode 100644
index 0000000..3606d84
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/Gruntfile.js
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+'use strict';
+
+module.exports = function (grunt) {
+ // ==========================================================
+ // = Parse Resource =
+ // ==========================================================
+ /*console.log('Generating resource tree...');
+
+ var env = require('jsdom').env;
+ var fs = require('fs');
+
+ var html = fs.readFileSync('dev/index.html', 'utf8');
+
+ console.log("1111", env);
+ env(html, function (err, window) {
+ console.log(">>>!!!");
+ if (err) console.log(err);
+
+ var $ = require('jquery')(window);
+ var $cssList = $('link[href][rel="stylesheet"]');
+ var cssList = $.map($cssList, function (ele) {
+ return $(ele).attr("href");
+ });
+
+ console.log(">>>", cssList);
+ });
+ console.log(">>>222");*/
+
+ // ==========================================================
+ // = Grunt Config =
+ // ==========================================================
+ grunt.initConfig({
+ config: grunt.file.readJSON('grunt.json'),
+
+ jshint: {
+ options: {
+ browser: true,
+ globals: {
+ $: true,
+ jQuery: true,
+ moment: true
+ }
+ },
+ all: [
+ 'dev/**/*.js'
+ ]
+ },
+
+ clean: {
+ build: ['ui/', 'tmp/'],
+ tmp: ['tmp/'],
+ ui: ['ui/']
+ },
+
+ copy: {
+ worker: {
+ files: [
+ {expand: true, cwd: 'dev/', src: '<%= config.copy.js.worker %>', dest: 'tmp'}
+ ]
+ },
+ ui: {
+ files: [
+ {expand: true, cwd: 'tmp/', src: ['**'], dest: 'ui'},
+ {expand: true, cwd: 'dev/', src: ['public/images/**', 'partials/**'], dest: 'ui'},
+ {expand: true, cwd: 'node_modules/font-awesome/', src: ['fonts/**'], dest: 'ui/public'},
+ {expand: true, cwd: 'node_modules/bootstrap/', src: ['fonts/**'], dest: 'ui/public'}
+ ]
+ }
+ },
+
+ concat: {
+ js_project: '<%= config.concat.js.project %>',
+ js_require: '<%= config.concat.js.require %>',
+ css_require: {
+ options: {
+ separator: '\n',
+ process: function(src) {
+ return "@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);" +
+ src.replace('@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);', '');
+ }
+ },
+ src: '<%= config.concat.css.require.src %>',
+ dest: '<%= config.concat.css.require.dest %>'
+ }
+ },
+
+ 'regex-replace': {
+ strict: {
+ src: ['tmp/public/js/project.js'],
+ actions: [
+ {
+ name: 'use strict',
+ search: '\\\'use strict\\\';?',
+ replace: '',
+ flags: 'gmi'
+ },
+ {
+ name: 'build timestamp',
+ search: '\\/\\/ GRUNT REPLACEMENT\\: Module\\.buildTimestamp \\= TIMESTAMP',
+ replace: 'Module.buildTimestamp = ' + (+new Date()) + ';',
+ flags: 'gmi'
+ }
+ ]
+ }
+ },
+
+ uglify: {
+ project: {
+ options: {
+ mangle: false,
+ sourceMap: true,
+ sourceMapIncludeSources: true
+ },
+ files: [
+ {
+ src: 'tmp/public/js/doc.js',
+ dest: 'tmp/public/js/doc.min.js'
+ }
+ ]
+ }
+ },
+
+ cssmin: {
+ project: {
+ files: {
+ 'tmp/public/css/project.min.css': '<%= config.concat.css.project.src %>',
+ }
+ }
+ },
+
+ htmlrefs: {
+ project: {
+ src: 'dev/index.html',
+ dest: "tmp/index.html"
+ }
+ },
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-cssmin');
+ grunt.loadNpmTasks('grunt-htmlrefs');
+ grunt.loadNpmTasks('grunt-regex-replace');
+ grunt.loadNpmTasks('grunt-contrib-copy');
+
+ grunt.registerTask('default', [
+ // jshint
+ 'jshint:all',
+
+ // Clean Env
+ 'clean:build',
+
+ // Compress JS
+ 'concat:js_require',
+ 'copy:worker',
+ 'concat:js_project',
+ 'regex-replace:strict',
+ 'uglify',
+
+ // Compress CSS
+ 'cssmin:project',
+ 'concat:css_require',
+
+ // Pass HTML Resources
+ 'htmlrefs',
+ 'copy:ui',
+
+ // Clean Env
+ 'clean:tmp'
+ ]);
+};
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/README.md
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/README.md b/eagle-server/src/main/webapp/app/README.md
new file mode 100644
index 0000000..b4168d5
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/README.md
@@ -0,0 +1,4 @@
+Apache Eagle Web APP
+==
+
+Web client for Apache Eagle
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/src/main/webapp/app/build/index.js
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/webapp/app/build/index.js b/eagle-server/src/main/webapp/app/build/index.js
new file mode 100644
index 0000000..bacbf53
--- /dev/null
+++ b/eagle-server/src/main/webapp/app/build/index.js
@@ -0,0 +1,144 @@
+/*
+ * 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';
+ console.log('Generating resource tree...');
+
+ var env = require('jsdom').env;
+ var fs = require('fs');
+
+ // Parse tree
+ fs.readFile('dev/index.html', 'utf8', function (err, html) {
+ if (err) return console.log(err);
+
+ env(html, function (err, window) {
+ if (err) console.log(err);
+
+ // Get js / css resource
+ var $ = require('jquery')(window);
+ function getResList(match, attr) {
+ var $eleList = $(match);
+ var requireList = [];
+ var projectList = [];
+ var list = [];
+
+ $.each($eleList, function (i, ele) {
+ var path = $(ele).attr(attr);
+
+ if(path.match(/^apps/)) return;
+
+ if(path.match(/node_modules/)) {
+ requireList.push(path.replace(/\.\.\//, ""));
+ list.push(path.replace(/\.\.\//, ""));
+ } else {
+ projectList.push("dev/" + path);
+ list.push("dev/" + path);
+ }
+ });
+
+ return {
+ list: list,
+ requireList: requireList,
+ projectList: projectList
+ };
+ }
+
+ var cssList = getResList('link[href][rel="stylesheet"]', 'href');
+ var jsList = getResList('script[src]', 'src');
+
+ // JS Worker process
+ var workerFolderPath = 'dev/public/js/worker/';
+ var workerList = fs.readdirSync(workerFolderPath);
+ var workerRequireList = [];
+
+ workerList = workerList.map(function (path) {
+ if(!/\w+Worker\.js/.test(path)) return;
+
+ var workerPath = workerFolderPath + path;
+ var content = fs.readFileSync(workerPath, 'utf8');
+ var regex = /self\.importScripts\(["']([^"']*)["']\)/g;
+ var match;
+ while ((match = regex.exec(content)) !== null) {
+ var modulePath = match[1];
+ workerRequireList.push((workerFolderPath + modulePath).replace(/^dev\//, ""));
+ }
+
+ return workerPath.replace(/^dev\//, "");
+ }).filter(function (path) {
+ return !!path;
+ });
+
+ // Parse grunt config
+ var resJson = {
+ concat: {
+ js: {
+ require: {
+ options: {
+ separator: '\n'
+ },
+ src: jsList.requireList,
+ dest: 'tmp/public/js/modules.js'
+ },
+ project: {
+ options: {
+ separator: '\n',
+ sourceMap :true
+ },
+ src: jsList.projectList,
+ dest: 'tmp/public/js/doc.js'
+ }
+ },
+ css: {
+ require: {
+ src: cssList.requireList.concat('tmp/public/css/project.min.css'),
+ dest: 'tmp/public/css/doc.css'
+ },
+ project: {
+ options: {
+ separator: '\n'
+ },
+ src: cssList.projectList,
+ dest: 'tmp/public/js/project.min.css'
+ }
+ }
+ },
+ copy: {
+ js: {
+ worker: workerList.concat(workerRequireList)
+ }
+ }
+ };
+
+ // Save tree & call grunt
+ fs.writeFile('grunt.json', JSON.stringify(resJson, null, '\t'), 'utf8', function (err) {
+ if(err) return console.log(err);
+
+ console.log("Grunt packaging...");
+ var exec = require('child_process').exec;
+ var grunt = exec('npm run grunt');
+
+ grunt.stdout.pipe(process.stdout);
+ grunt.stderr.pipe(process.stdout);
+ grunt.on('exit', function() {
+ process.exit()
+ })
+ });
+ });
+ });
+})();
[04/14] incubator-eagle git commit: [EAGLE-574] UI refactor for
support 0.5 api
Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
deleted file mode 100644
index 207c8df..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
+++ /dev/null
@@ -1,1224 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module("featureControllers");
- var feature = featureControllers.register("common");
-
- // ==============================================================
- // = Function =
- // ==============================================================
- feature.service("Policy", function(Entities) {
- var Policy = function () {};
-
- Policy.updatePolicyStatus = function(policy, status) {
- $.dialog({
- title: "Confirm",
- content: "Do you want to " + (status ? "enable" : "disable") + " policy[" + policy.tags.policyId + "]?",
- confirm: true
- }, function(ret) {
- if(ret) {
- policy.enabled = status;
- Entities.updateEntity("AlertDefinitionService", policy);
- }
- });
- };
- Policy.deletePolicy = function(policy, callback) {
- $.dialog({
- title: "Confirm",
- content: "Do you want to delete policy[" + policy.tags.policyId + "]?",
- confirm: true
- }, function(ret) {
- if(ret) {
- policy.enabled = status;
- Entities.deleteEntity("AlertDefinitionService", policy)._promise.finally(function() {
- if(callback) {
- callback(policy);
- }
- });
- }
- });
- };
- return Policy;
- });
-
- feature.service("Notification", function(Entities) {
- var Notification = function () {};
- Notification.map = {};
-
- Notification.list = Entities.queryEntities("AlertNotificationService");
- Notification.list._promise.then(function () {
- $.each(Notification.list, function (i, notification) {
- // Parse fields
- notification.fieldList = common.parseJSON(notification.fields, []);
-
- // Fill map
- Notification.map[notification.tags.notificationType] = notification;
- });
- });
-
- Notification.promise = Notification.list._promise;
-
- return Notification;
- });
-
- // ==============================================================
- // = Policies =
- // ==============================================================
-
- // ========================= Policy List ========================
- feature.navItem("policyList", "Policies", "list");
- feature.controller('policyList', function(PageConfig, Site, $scope, Application, Entities, Policy) {
- PageConfig.pageTitle = "Policy List";
- PageConfig.pageSubTitle = Site.current().tags.site;
-
- // Initial load
- $scope.policyList = [];
- $scope.application = Application.current();
-
- // List policies
- var _policyList = Entities.queryEntities("AlertDefinitionService", {site: Site.current().tags.site, application: $scope.application.tags.application});
- _policyList._promise.then(function() {
- $.each(_policyList, function(i, policy) {
- policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
-
- $scope.policyList.push(policy);
- });
- });
- $scope.policyList._promise = _policyList._promise;
-
- // Function
- $scope.searchFunc = function(item) {
- var key = $scope.search;
- if(!key) return true;
-
- var _key = key.toLowerCase();
- function _hasKey(item, path) {
- var _value = common.getValueByPath(item, path, "").toLowerCase();
- return _value.indexOf(_key) !== -1;
- }
- return _hasKey(item, "tags.policyId") || _hasKey(item, "__expression") || _hasKey(item, "description") || _hasKey(item, "owner");
- };
-
- $scope.updatePolicyStatus = Policy.updatePolicyStatus;
- $scope.deletePolicy = function(policy) {
- Policy.deletePolicy(policy, function(policy) {
- var _index = $scope.policyList.indexOf(policy);
- $scope.policyList.splice(_index, 1);
- });
- };
- });
-
- // ======================= Policy Detail ========================
- feature.controller('policyDetail', function(PageConfig, Site, $scope, $wrapState, $interval, Entities, Policy, nvd3) {
- var MAX_PAGESIZE = 10000;
- var seriesRefreshInterval;
-
- PageConfig.pageTitle = "Policy Detail";
- PageConfig.lockSite = true;
- PageConfig
- .addNavPath("Policy List", "/common/policyList")
- .addNavPath("Policy Detail");
-
- $scope.chartConfig = {
- chart: "line",
- xType: "time"
- };
-
- // Query policy
- if($wrapState.param.filter) {
- $scope.policyList = Entities.queryEntity("AlertDefinitionService", $wrapState.param.filter);
- } else {
- $scope.policyList = Entities.queryEntities("AlertDefinitionService", {
- policyId: $wrapState.param.policy,
- site: $wrapState.param.site,
- alertExecutorId: $wrapState.param.executor
- });
- }
-
- $scope.policyList._promise.then(function() {
- var policy = null;
-
- if($scope.policyList.length === 0) {
- $.dialog({
- title: "OPS!",
- content: "Policy not found!"
- }, function() {
- location.href = "#/common/policyList";
- });
- return;
- } else {
- policy = $scope.policyList[0];
-
- policy.__notificationList = common.parseJSON(policy.notificationDef, []);
-
- policy.__expression = common.parseJSON(policy.policyDef, {}).expression;
-
- $scope.policy = policy;
- Site.current(Site.find($scope.policy.tags.site));
- console.log($scope.policy);
- }
-
- // Visualization
- var _intervalType = 0;
- var _intervalList = [
- ["Daily", 1440, function() {
- var _endTime = app.time.now().hour(23).minute(59).second(59).millisecond(0);
- var _startTime = _endTime.clone().subtract(1, "month").hour(0).minute(0).second(0).millisecond(0);
- return [_startTime, _endTime];
- }],
- ["Hourly", 60, function() {
- var _endTime = app.time.now().minute(59).second(59).millisecond(0);
- var _startTime = _endTime.clone().subtract(48, "hour").minute(0).second(0).millisecond(0);
- return [_startTime, _endTime];
- }],
- ["Every 5 minutes", 5, function() {
- var _endTime = app.time.now().second(59).millisecond(0);
- var _minute = Math.floor(_endTime.minute() / 5) * 5;
- var _startTime = _endTime.clone().minute(_minute).subtract(5 * 30, "minute").second(0).millisecond(0);
- _endTime.minute(_minute + 4);
- return [_startTime, _endTime];
- }]
- ];
-
- function _loadSeries(seriesName, metricName, condition) {
- var list = Entities.querySeries("GenericMetricService", $.extend({_metricName: metricName}, condition), "@site", "sum(value)", _intervalList[_intervalType][1]);
- var seriesList = nvd3.convert.eagle([list]);
- if(!$scope[seriesName]) $scope[seriesName] = seriesList;
- list._promise.then(function() {
- $scope[seriesName] = seriesList;
- });
- }
-
- function refreshSeries() {
- var _timeRange = _intervalList[_intervalType][2]();
- var _startTime = _timeRange[0];
- var _endTime = _timeRange[1];
- var _cond = {
- application: policy.tags.application,
- policyId: policy.tags.policyId,
- _startTime: _startTime,
- _endTime: _endTime
- };
- console.log("Range:", common.format.date(_startTime, "datetime"), common.format.date(_endTime, "datetime"));
-
- // > eagle.policy.eval.count
- _loadSeries("policyEvalSeries", "eagle.policy.eval.count", _cond);
-
- // > eagle.policy.eval.fail.count
- _loadSeries("policyEvalFailSeries", "eagle.policy.eval.fail.count", _cond);
-
- // > eagle.alert.count
- _loadSeries("alertSeries", "eagle.alert.count", _cond);
-
- // > eagle.alert.fail.count
- _loadSeries("alertFailSeries", "eagle.alert.fail.count", _cond);
- }
-
- // Alert list
- $scope.alertList = Entities.queryEntities("AlertService", {
- site: Site.current().tags.site,
- application: policy.tags.application,
- policyId: policy.tags.policyId,
- _pageSize: MAX_PAGESIZE,
- _duration: 1000 * 60 * 60 * 24 * 30,
- __ETD: 1000 * 60 * 60 * 24
- });
-
- refreshSeries();
- seriesRefreshInterval = $interval(refreshSeries, 60000);
-
- // Menu
- $scope.visualizationMenu = [
- {icon: "clock-o", title: "Interval", list:
- $.map(_intervalList, function(item, index) {
- var _item = {icon: "clock-o", title: item[0], func: function() {
- _intervalType = index;
- refreshSeries();
- }};
- Object.defineProperty(_item, 'strong', {
- get: function() {return _intervalType === index;}
- });
- return _item;
- })
- }
- ];
- });
-
- // Function
- $scope.updatePolicyStatus = Policy.updatePolicyStatus;
- $scope.deletePolicy = function(policy) {
- Policy.deletePolicy(policy, function() {
- location.href = "#/common/policyList";
- });
- };
-
- // Clean up
- $scope.$on('$destroy', function() {
- $interval.cancel(seriesRefreshInterval);
- });
- });
-
- // ======================== Policy Edit =========================
- function policyCtrl(create, PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
- PageConfig.pageTitle = create ? "Policy Create" : "Policy Edit";
- PageConfig.pageSubTitle = Site.current().tags.site;
- PageConfig
- .addNavPath("Policy List", "/common/policyList")
- .addNavPath("Policy Edit");
-
- var _winTimeDesc = "Number unit[millisecond, sec, min, hour, day, month, year]. e.g. 23 sec";
- var _winTimeRegex = /^\d+\s+(millisecond|milliseconds|second|seconds|sec|minute|minutes|min|hour|hours|day|days|week|weeks|month|months|year|years)$/;
- var _winTimeDefaultValue = '10 min';
- $scope.config = {
- window: [
- // Display name, window type, required columns[Title, Description || "LONG_FIELD", Regex check, default value]
- {
- title: "Message Time Slide",
- description: "Using timestamp filed from input is used as event's timestamp",
- type: "externalTime",
- fields:[
- {title: "Field", defaultValue: "timestamp", hide: true},
- {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
- ]
- },
- {
- title: "System Time Slide",
- description: "Using System time is used as timestamp for event's timestamp",
- type: "time",
- fields:[
- {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
- ]
- },
- {
- title: "System Time Batch",
- description: "Same as System Time Window except the policy is evaluated at fixed duration",
- type: "timeBatch",
- fields:[
- {title: "Time", description: _winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
- ]
- },
- {
- title: "Length Slide",
- description: "The slide window has a fixed length",
- type: "length",
- fields:[
- {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/}
- ]
- },
- {
- title: "Length Batch",
- description: "Same as Length window except the policy is evaluated in batch mode when fixed event count reached",
- type: "lengthBatch",
- fields:[
- {title: "Number", description: "Number only. e.g. 1023", regex: /^\d+$/}
- ]
- }
- ]
- };
-
- $scope.Notification = Notification;
-
- $scope.create = create;
- $scope.encodedRowkey = $wrapState.param.filter;
-
- $scope.step = 0;
- $scope.applications = {};
- $scope.policy = {};
-
- // ==========================================
- // = Notification =
- // ==========================================
- $scope.notificationTabHolder = null;
-
- $scope.newNotification = function (notificationType) {
- var __notification = {
- notificationType: notificationType
- };
-
- $.each(Notification.map[notificationType].fieldList, function (i, field) {
- __notification[field.name] = field.value || "";
- });
-
- $scope.policy.__.notification.push(__notification);
- };
-
- Notification.promise.then(function () {
- $scope.menu = Authorization.isRole('ROLE_ADMIN') ? [
- {icon: "cog", title: "Configuration", list: [
- {icon: "trash", title: "Delete", danger: true, func: function () {
- var notification = $scope.notificationTabHolder.selectedPane.data;
- UI.deleteConfirm(notification.notificationType).then(null, null, function(holder) {
- common.array.remove(notification, $scope.policy.__.notification);
- holder.closeFunc();
- });
- }}
- ]},
- {icon: "plus", title: "New", list: $.map(Notification.list, function(notification) {
- return {
- icon: "plus",
- title: notification.tags.notificationType,
- func: function () {
- $scope.newNotification(notification.tags.notificationType);
- setTimeout(function() {
- $scope.notificationTabHolder.setSelect(-1);
- $scope.$apply();
- }, 0);
- }
- };
- })}
- ] : [];
- });
-
- // ==========================================
- // = Data Preparation =
- // ==========================================
- // Steam list
- var _streamList = Entities.queryEntities("AlertStreamService", {application: Application.current().tags.application});
- var _executorList = Entities.queryEntities("AlertExecutorService", {application: Application.current().tags.application});
- $scope.streamList = _streamList;
- $scope.executorList = _executorList;
- $scope.streamReady = false;
-
- $q.all([_streamList._promise, _executorList._promise]).then(function() {
- // Map executor with stream
- $.each(_executorList, function(i, executor) {
- $.each(_streamList, function(j, stream) {
- if(stream.tags.application === executor.tags.application && stream.tags.streamName === executor.tags.streamName) {
- stream.alertExecutor = executor;
- return false;
- }
- });
- });
-
- // Fill stream list
- $.each(_streamList, function(i, unit) {
- var _srcStreamList = $scope.applications[unit.tags.application] = $scope.applications[unit.tags.application] || [];
- _srcStreamList.push(unit);
- });
-
- $scope.streamReady = true;
-
- // ==========================================
- // = Function =
- // ==========================================
- function _findStream(application, streamName) {
- var _streamList = $scope.applications[application];
- if(!_streamList) return null;
-
- for(var i = 0 ; i < _streamList.length ; i += 1) {
- if(_streamList[i].tags.streamName === streamName) {
- return _streamList[i];
- }
- }
- return null;
- }
-
- // ==========================================
- // = Step control =
- // ==========================================
- $scope.steps = [
- // >> Select stream
- {
- title: "Select Stream",
- ready: function() {
- return $scope.streamReady;
- },
- init: function() {
- $scope.policy.__.streamName = $scope.policy.__.streamName ||
- common.array.find($scope.policy.tags.application, _streamList, "tags.application").tags.streamName;
- },
- nextable: function() {
- var _streamName = common.getValueByPath($scope.policy, "__.streamName");
- if(!_streamName) return false;
-
- // Detect stream in current data source list
- return !!common.array.find(_streamName, $scope.applications[$scope.policy.tags.application], "tags.streamName");
- }
- },
-
- // >> Define Alert Policy
- {
- title: "Define Alert Policy",
- init: function() {
- // Normal mode will fetch meta list
- if(!$scope.policy.__.advanced) {
- var _stream = _findStream($scope.policy.tags.application, $scope.policy.__.streamName);
- $scope._stream = _stream;
-
- if(!_stream) {
- $.dialog({
- title: "OPS",
- content: "Stream not found! Current application don't match any stream."
- });
- return;
- }
-
- if(!_stream.metas) {
- _stream.metas = Entities.queryEntities("AlertStreamSchemaService", {application: $scope.policy.tags.application, streamName: $scope.policy.__.streamName});
- _stream.metas._promise.then(function() {
- _stream.metas.sort(function(a, b) {
- if(a.tags.attrName < b.tags.attrName) {
- return -1;
- } else if(a.tags.attrName > b.tags.attrName) {
- return 1;
- }
- return 0;
- });
- });
- }
- }
- },
- ready: function() {
- if(!$scope.policy.__.advanced) {
- return $scope._stream.metas._promise.$$state.status === 1;
- }
- return true;
- },
- nextable: function() {
- if($scope.policy.__.advanced) {
- // Check stream source
- $scope._stream = null;
- $.each($scope.applications[$scope.policy.tags.application], function(i, stream) {
- if(($scope.policy.__._expression || "").indexOf(stream.tags.streamName) !== -1) {
- $scope._stream = stream;
- return false;
- }
- });
- return $scope._stream;
- } else {
- // Window
- if($scope.policy.__.windowConfig) {
- var _winMatch = true;
- var _winConds = $scope.getWindow().fields;
- $.each(_winConds, function(i, cond) {
- if(!(cond.val || "").match(cond.regex)) {
- _winMatch = false;
- return false;
- }
- });
- if(!_winMatch) return false;
-
- // Aggregation
- if($scope.policy.__.groupAgg) {
- if(!$scope.policy.__.groupAggPath ||
- !$scope.policy.__.groupCondOp ||
- !$scope.policy.__.groupCondVal) {
- return false;
- }
- }
- }
- }
- return true;
- }
- },
-
- // >> Configuration & Notification
- {
- title: "Configuration & Notification",
- nextable: function() {
- return !!$scope.policy.tags.policyId;
- }
- }
- ];
-
- // ==========================================
- // = Policy Logic =
- // ==========================================
- _streamList._promise.then(function() {
- // Initial policy entity
- if(create) {
- $scope.policy = {
- __: {
- toJSON: jQuery.noop,
- conditions: {},
- notification: [],
- dedupe: {
- alertDedupIntervalMin: 10,
- fields: []
- },
- _dedupTags: {},
- policy: {},
- window: "externalTime",
- group: "",
- groupAgg: "count",
- groupAggPath: "timestamp",
- groupCondOp: ">=",
- groupCondVal: "2"
- },
- description: "",
- enabled: true,
- prefix: "alertdef",
- remediationDef: "",
- tags: {
- application: Application.current().tags.application,
- policyType: "siddhiCEPEngine"
- }
- };
-
- // If configured data source
- if($wrapState.param.app) {
- $scope.policy.tags.application = $wrapState.param.app;
- if(common.array.find($wrapState.param.app, Site.current().applicationList, "tags.application")) {
- setTimeout(function() {
- $scope.changeStep(0, 2, false);
- $scope.$apply();
- }, 1);
- }
- }
-
- // Start step
- $scope.changeStep(0, 1, false);
- console.log($scope.policy);
- } else {
- var _policy = Entities.queryEntity("AlertDefinitionService", $scope.encodedRowkey);
- _policy._promise.then(function() {
- if(_policy.length) {
- $scope.policy = _policy[0];
- $scope.policy.__ = {
- toJSON: jQuery.noop
- };
-
- Site.current(Site.find($scope.policy.tags.site));
- } else {
- $.dialog({
- title: "OPS",
- content: "Policy not found!"
- }, function() {
- $wrapState.path("/common/policyList");
- $scope.$apply();
- });
- return;
- }
-
- var _application = Application.current();
- if(_application.tags.application !== $scope.policy.tags.application) {
- _application = Application.find($scope.policy.tags.application);
- if(_application) {
- Application.current(_application, false);
- console.log("Application not match. Do reload...");
- $wrapState.reload();
- } else {
- $.dialog({
- title: "OPS",
- content: "Application not found! Current policy don't match any application."
- }, function() {
- $location.path("/common/policyList");
- $scope.$apply();
- });
- }
- return;
- }
-
- // === Revert inner data ===
- // >> De-dupe
- $scope.policy.__._dedupTags = {};
- $scope.policy.__.dedupe = common.parseJSON($scope.policy.dedupeDef, {});
- $.each($scope.policy.__.dedupe.fields || [], function (i, field) {
- $scope.policy.__._dedupTags[field] = true;
- });
-
- // >> Notification
- $scope.policy.__.notification = common.parseJSON($scope.policy.notificationDef, []);
-
- // >> Policy
- var _policyUnit = $scope.policy.__.policy = common.parseJSON($scope.policy.policyDef);
-
- // >> Parse expression
- $scope.policy.__.conditions = {};
- var _condition = _policyUnit.expression.match(/from\s+(\w+)(\[(.*)])?(#window[^\)]*\))?\s+(select (\w+, )?(\w+)\((\w+)\) as [\w\d_]+ (group by (\w+) )?having ([\w\d_]+) ([<>=]+) ([^\s]+))?/);
- var _cond_stream = _condition[1];
- var _cond_query = _condition[3] || "";
- var _cond_window = _condition[4];
- var _cond_group = _condition[5];
- var _cond_groupUnit = _condition.slice(7,14);
-
- if(!_condition) {
- $scope.policy.__.advanced = true;
- } else {
- // > StreamName
- var _streamName = _cond_stream;
- var _cond = _cond_query;
-
- $scope.policy.__.streamName = _streamName;
-
- // > Conditions
- // Loop condition groups
- if(_cond.trim() !== "" && /^\(.*\)$/.test(_cond)) {
- var _condGrps = _cond.substring(1, _cond.length - 1).split(/\)\s+and\s+\(/);
- $.each(_condGrps, function(i, line) {
- // Loop condition cells
- var _condCells = line.split(/\s+or\s+/);
- $.each(_condCells, function(i, cell) {
- var _opMatch = cell.match(/(\S*)\s*(==|!=|>|<|>=|<=|contains)\s*('(?:[^'\\]|\\.)*'|[\w\d]+)/);
- if(!common.isEmpty(_opMatch)) {
- var _key = _opMatch[1];
- var _op = _opMatch[2];
- var _val = _opMatch[3];
- var _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
- var _type = "";
- if(_val.match(/'.*'/)) {
- _val = _val.slice(1, -1);
- _type = "string";
- } else if(_val === "true" || _val === "false") {
- var _regexMatch = _key.match(/^str:regexp\((\w+),'(.*)'\)/);
- var _containsMatch = _key.match(/^str:contains\((\w+),'(.*)'\)/);
- var _mathes = _regexMatch || _containsMatch;
- if(_mathes) {
- _key = _mathes[1];
- _val = _mathes[2];
- _type = "string";
- _op = _regexMatch ? "regex" : "contains";
- _conds = $scope.policy.__.conditions[_key] = $scope.policy.__.conditions[_key] || [];
- } else {
- _type = "bool";
- }
- } else {
- _type = "number";
- }
- _conds.push($scope._CondUnit(_key, _op, _val, _type));
- }
- });
- });
- } else if(_cond_query !== "") {
- $scope.policy.__.advanced = true;
- }
- }
-
- if($scope.policy.__.advanced) {
- $scope.policy.__._expression = _policyUnit.expression;
- } else {
- // > window
- if(!_cond_window) {
- $scope.policy.__.window = "externalTime";
- $scope.policy.__.group = "";
- $scope.policy.__.groupAgg = "count";
- $scope.policy.__.groupAggPath = "timestamp";
- $scope.policy.__.groupCondOp = ">=";
- $scope.policy.__.groupCondVal = "2";
- } else {
- try {
- $scope.policy.__.windowConfig = true;
-
- var _winCells = _cond_window.match(/\.(\w+)\((.*)\)/);
- $scope.policy.__.window = _winCells[1];
- var _winConds = $scope.getWindow().fields;
- $.each(_winCells[2].split(","), function(i, val) {
- _winConds[i].val = val;
- });
-
- // Group
- if(_cond_group) {
- $scope.policy.__.group = _cond_groupUnit[3];
- $scope.policy.__.groupAgg = _cond_groupUnit[0];
- $scope.policy.__.groupAggPath = _cond_groupUnit[1];
- $scope.policy.__.groupAggAlias = _cond_groupUnit[4] === "aggValue" ? "" : _cond_groupUnit[4];
- $scope.policy.__.groupCondOp = _cond_groupUnit[5];
- $scope.policy.__.groupCondVal = _cond_groupUnit[6];
- } else {
- $scope.policy.__.group = "";
- $scope.policy.__.groupAgg = "count";
- $scope.policy.__.groupAggPath = "timestamp";
- $scope.policy.__.groupCondOp = ">=";
- $scope.policy.__.groupCondVal = "2";
- }
- } catch(err) {
- $scope.policy.__.window = "externalTime";
- }
- }
- }
-
- $scope.changeStep(0, 2, false);
- console.log($scope.policy);
- });
- }
- });
-
- // ==========================================
- // = Function =
- // ==========================================
- // UI: Highlight select step
- $scope.stepSelect = function(step) {
- return step === $scope.step ? "active" : "";
- };
-
- // UI: Collapse all
- $scope.collapse = function(cntr) {
- var _list = $(cntr).find(".collapse").css("height", "auto");
- if(_list.hasClass("in")) {
- _list.removeClass("in");
- } else {
- _list.addClass("in");
- }
- };
-
- // Step process. Will fetch target step attribute and return boolean
- function _check(key, step) {
- var _step = $scope.steps[step - 1];
- if(!_step) return;
-
- var _value = _step[key];
- if(typeof _value === "function") {
- return _value();
- } else if(typeof _value === "boolean") {
- return _value;
- }
- return true;
- }
- // Check step is ready. Otherwise will display load animation
- $scope.stepReady = function(step) {
- return _check("ready", step);
- };
- // Check whether process next step. Otherwise will disable next button
- $scope.checkNextable = function(step) {
- return !_check("nextable", step);
- };
- // Switch step
- $scope.changeStep = function(step, targetStep, check) {
- if(check === false || _check("checkStep", step)) {
- $scope.step = targetStep;
-
- _check("init", targetStep);
- }
- };
-
- // Window
- $scope.getWindow = function() {
- if(!$scope.policy || !$scope.policy.__) return null;
- return common.array.find($scope.policy.__.window, $scope.config.window, "type");
- };
-
- // Aggregation
- $scope.groupAggPathList = function() {
- return $.grep(common.getValueByPath($scope, "_stream.metas", []), function(meta) {
- return $.inArray(meta.attrType, ['long','integer','number', 'double', 'float']) !== -1;
- });
- };
-
- $scope.updateGroupAgg = function() {
- $scope.policy.__.groupAggPath = $scope.policy.__.groupAggPath || common.getValueByPath($scope.groupAggPathList()[0], "tags.attrName");
-
- if($scope.policy.__.groupAgg === 'count') {
- $scope.policy.__.groupAggPath = 'timestamp';
- }
- };
-
- // Resolver
- $scope.resolverTypeahead = function(value, resolver) {
- var _resolverList = Entities.query("stream/attributeresolve", {
- site: Site.current().tags.site,
- resolver: resolver,
- query: value
- });
- return _resolverList._promise.then(function() {
- return _resolverList;
- });
- };
-
- // Used for input box when pressing enter
- $scope.conditionPress = function(event) {
- if(event.which == 13) {
- setTimeout(function() {
- $(event.currentTarget).closest(".input-group").find("button").click();
- }, 1);
- }
- };
- // Check whether has condition
- $scope.hasCondition = function(key, type) {
- var _list = common.getValueByPath($scope.policy.__.conditions, key, []);
- if(_list.length === 0) return false;
-
- if(type === "bool") {
- return !_list[0].ignored();
- }
- return true;
- };
- // Condition unit definition
- $scope._CondUnit = function(key, op, value, type) {
- return {
- key: key,
- op: op,
- val: value,
- type: type,
- ignored: function() {
- return this.type === "bool" && this.val === "none";
- },
- getVal: function() {
- return this.type === "string" ? "'" + this.val + "'" : this.val;
- },
- toString: function() {
- return this.op + " " + this.getVal();
- },
- toCondString: function() {
- var _op = this.op === "=" ? "==" : this.op;
- if(_op === "regex") {
- return "str:regexp(" + this.key + "," + this.getVal() + ")==true";
- } else if(_op === "contains") {
- return "str:contains(" + this.key + "," + this.getVal() + ")==true";
- } else {
- return this.key + " " + _op + " " + this.getVal();
- }
- }
- };
- };
-
- //for maprfs, if key is status or volume or src/dst, convert these names to id.
- $scope.convertToID = function(_condList, key, op, name, type, site){
- if(key == "dst" || key == "src") {
- Entities.maprfsNameToID("fNameResolver", name, site)._promise.then(
- function(response){
- console.log("success");
- console.log(response);
- _condList.push($scope._CondUnit(key, op, response.data, type));
- },
- function(error, status){
- console.log("error: " + status);
- }
- );
- }
- else if (key == "status"){
- Entities.maprfsNameToID("sNameResolver", name, site)._promise.then(
- function(response){
- console.log("success");
- console.log(response);
- _condList.push($scope._CondUnit(key, op, response.data, type));
- },
- function(error, status){
- console.log("error: " + status);
- }
- );
- }
- else if (key == "volume") {
- Entities.maprfsNameToID("vNameResolver", name, site)._promise.then(
- function(response){
- console.log("success");
- console.log(response);
- _condList.push($scope._CondUnit(key, op, response.data, type));
- },
- function(error, status){
- console.log("error: " + status);
- }
- );
- }
- };
-
- // Add condition for policy
- $scope.addCondition = function(key, op, value, type) {
- if(value === "" || value === undefined) return false;
-
- var _condList = $scope.policy.__.conditions[key] = $scope.policy.__.conditions[key] || [];
- _condList.push($scope._CondUnit(key, op, value, type));
-
- //if it is mapr application, covert human readable name to ids
- if(Application.current().tags.application == "maprFSAuditLog") {
- if ( key == "dst" || key == "src" || key == "volume" || key == "status") {
- $scope.convertToID(_condList, key, op, value , type, Site.current().tags.site);
- }
- }
-
- return true;
- };
- // Convert condition list to description string
- $scope.parseConditionDesc = function(key) {
- return $.map($scope.policy.__.conditions[key] || [], function(cond) {
- if(!cond.ignored()) return "[" + cond.toString() + "]";
- }).join(" or ");
- };
-
- // To query
- $scope.toQuery = function() {
- if(!$scope.policy.__) return "";
-
- if($scope.policy.__.advanced) return $scope.policy.__._expression;
-
- // > Query
- var _query = $.map(common.getValueByPath($scope.policy, "__.conditions", {}), function(list) {
- var _conds = $.map(list, function(cond) {
- if(!cond.ignored()) return cond.toCondString();
- });
- if(_conds.length) {
- return "(" + _conds.join(" or ") + ")";
- }
- }).join(" and ");
- if(_query) {
- _query = "[" + _query + "]";
- }
-
- // > Window
- var _window = $scope.getWindow();
- var _windowStr = "";
- if($scope.policy.__.windowConfig) {
- _windowStr = $.map(_window.fields, function(field) {
- return field.val;
- }).join(",");
- _windowStr = "#window." + _window.type + "(" + _windowStr + ")";
-
- // > Group
- if($scope.policy.__.group) {
- _windowStr += common.template(" select ${group}, ${groupAgg}(${groupAggPath}) as ${groupAggAlias} group by ${group} having ${groupAggAlias} ${groupCondOp} ${groupCondVal}", {
- group: $scope.policy.__.group,
- groupAgg: $scope.policy.__.groupAgg,
- groupAggPath: $scope.policy.__.groupAggPath,
- groupCondOp: $scope.policy.__.groupCondOp,
- groupCondVal: $scope.policy.__.groupCondVal,
- groupAggAlias: $scope.policy.__.groupAggAlias || "aggValue"
- });
- } else {
- _windowStr += common.template(" select ${groupAgg}(${groupAggPath}) as ${groupAggAlias} having ${groupAggAlias} ${groupCondOp} ${groupCondVal}", {
- groupAgg: $scope.policy.__.groupAgg,
- groupAggPath: $scope.policy.__.groupAggPath,
- groupCondOp: $scope.policy.__.groupCondOp,
- groupCondVal: $scope.policy.__.groupCondVal,
- groupAggAlias: $scope.policy.__.groupAggAlias || "aggValue"
- });
- }
- } else {
- _windowStr = " select *";
- }
-
- return common.template("from ${stream}${query}${window} insert into outputStream;", {
- stream: $scope.policy.__.streamName,
- query: _query,
- window: _windowStr
- });
- };
-
- // ==========================================
- // = Update Policy =
- // ==========================================
- // dedupeDef notificationDef policyDef
- $scope.finishPolicy = function() {
- $scope.lock = true;
-
- // dedupeDef
- $scope.policy.__.dedupe.fields = $.map($scope.policy.__._dedupTags, function (value, key) {
- if(value) return key;
- });
- $scope.policy.dedupeDef = JSON.stringify($scope.policy.__.dedupe);
-
- // notificationDef
- $scope.policy.__.notification = $scope.policy.__.notification || [];
-
- $scope.policy.notificationDef = JSON.stringify($scope.policy.__.notification);
-
- // policyDef
- $scope.policy.__.policy = {
- expression: $scope.toQuery(),
- type: "siddhiCEPEngine"
- };
- $scope.policy.policyDef = JSON.stringify($scope.policy.__.policy);
-
- // alertExecutorId
- if($scope._stream.alertExecutor) {
- $scope.policy.tags.alertExecutorId = $scope._stream.alertExecutor.tags.alertExecutorId;
- } else {
- $scope.lock = false;
- $.dialog({
- title: "OPS!",
- content: "Alert Executor not defined! Please check 'AlertExecutorService'!"
- });
- return;
- }
-
- // site
- $scope.policy.tags.site = $scope.policy.tags.site || Site.current().tags.site;
-
- // owner
- $scope.policy.owner = Authorization.userProfile.username;
-
- // Update function
- function _updatePolicy() {
- Entities.updateEntity("AlertDefinitionService", $scope.policy)._promise.success(function(data) {
- $.dialog({
- title: "Success",
- content: (create ? "Create" : "Update") + " success!"
- }, function() {
- if(data.success) {
- location.href = "#/common/policyList";
- } else {
- $.dialog({
- title: "OPS",
- content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data)
- });
- }
- });
-
- $scope.create = create = false;
- $scope.encodedRowkey = data.obj[0];
- }).error(function(data) {
- $.dialog({
- title: "OPS",
- content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data)
- });
- }).then(function() {
- $scope.lock = false;
- });
- }
-
- // Check if already exist
- if($scope.create) {
- var _checkList = Entities.queryEntities("AlertDefinitionService", {
- alertExecutorId: $scope.policy.tags.alertExecutorId,
- policyId: $scope.policy.tags.policyId,
- policyType: "siddhiCEPEngine",
- application: $scope.policy.tags.application
- });
- _checkList._promise.then(function() {
- if(_checkList.length) {
- $.dialog({
- title: "Override Confirm",
- content: "Already exists PolicyID '" + $scope.policy.tags.policyId + "'. Do you want to override?",
- confirm: true
- }, function(ret) {
- if(ret) {
- _updatePolicy();
- } else {
- $scope.lock = false;
- $scope.$apply();
- }
- });
- } else {
- _updatePolicy();
- }
- });
- } else {
- _updatePolicy();
- }
- };
- });
- }
-
- feature.controller('policyCreate', function(PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
- var _args = [true];
- _args.push.apply(_args, arguments);
- policyCtrl.apply(this, _args);
- }, "policyEdit");
- feature.controller('policyEdit', function(PageConfig, Site, Policy, $scope, $wrapState, $q, UI, Entities, Application, Authorization, Notification) {
- PageConfig.lockSite = true;
- var _args = [false];
- _args.push.apply(_args, arguments);
- policyCtrl.apply(this, _args);
- });
-
- // ==============================================================
- // = Alerts =
- // ==============================================================
-
- // ========================= Alert List =========================
- feature.navItem("alertList", "Alerts", "exclamation-triangle");
- feature.controller('alertList', function(PageConfig, Site, $scope, $wrapState, $interval, $timeout, Entities, Application) {
- PageConfig.pageSubTitle = Site.current().tags.site;
-
- var MAX_PAGESIZE = 10000;
-
- // Initial load
- $scope.application = Application.current();
-
- $scope.alertList = [];
- $scope.alertList.ready = false;
-
- // Load data
- function _loadAlerts() {
- if($scope.alertList._promise) {
- $scope.alertList._promise.abort();
- }
-
- var _list = Entities.queryEntities("AlertService", {
- site: Site.current().tags.site,
- application: $scope.application.tags.application,
- _pageSize: MAX_PAGESIZE,
- _duration: 1000 * 60 * 60 * 24 * 30,
- __ETD: 1000 * 60 * 60 * 24
- });
- $scope.alertList._promise = _list._promise;
- _list._promise.then(function() {
- var index;
-
- if($scope.alertList[0]) {
- // List new alerts
- for(index = 0 ; index < _list.length ; index += 1) {
- var _alert = _list[index];
- _alert.__new = true;
- if(_alert.encodedRowkey === $scope.alertList[0].encodedRowkey) {
- break;
- }
- }
-
- if(index > 0) {
- $scope.alertList.unshift.apply($scope.alertList, _list.slice(0, index));
-
- // Clean up UI highlight
- $timeout(function() {
- $.each(_list, function(i, alert) {
- delete alert.__new;
- });
- }, 100);
- }
- } else {
- // List all alerts
- $scope.alertList.push.apply($scope.alertList, _list);
- }
-
- $scope.alertList.ready = true;
- });
- }
-
- _loadAlerts();
- var _loadInterval = $interval(_loadAlerts, app.time.refreshInterval);
- $scope.$on('$destroy',function(){
- $interval.cancel(_loadInterval);
- });
- });
-
- // ======================== Alert Detail ========================
- feature.controller('alertDetail', function(PageConfig, Site, $scope, $wrapState, Entities) {
- PageConfig.pageTitle = "Alert Detail";
- PageConfig.lockSite = true;
- PageConfig
- .addNavPath("Alert List", "/common/alertList")
- .addNavPath("Alert Detail");
-
- $scope.common = common;
-
- // Query policy
- $scope.alertList = Entities.queryEntity("AlertService", $wrapState.param.filter);
- $scope.alertList._promise.then(function() {
- if($scope.alertList.length === 0) {
- $.dialog({
- title: "OPS!",
- content: "Alert not found!"
- }, function() {
- location.href = "#/common/alertList";
- });
- } else {
- $scope.alert = $scope.alertList[0];
- $scope.alert.rawAlertContext = JSON.stringify($scope.alert.alertContext, null, "\t");
- Site.current(Site.find($scope.alert.tags.site));
- console.log($scope.alert);
- }
- });
-
- // UI
- $scope.getMessageTime = function(alert) {
- var _time = common.getValueByPath(alert, "alertContext.timestamp");
- return Number(_time);
- };
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
deleted file mode 100644
index 309fac3..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title" id="policyId">
- {{alert.tags.policyId}}
- <small>{{common.format.date(alert.timestamp)}}</small>
- </h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <a class="btn btn-primary pull-right" href="#/common/policyDetail/?policy={{alert.tags.policyId}}&site={{alert.tags.site}}&executor={{alert.tags.alertExecutorId}}">View Policy</a>
-
- <div class="inline-group">
- <dl><dt>Site</dt><dd>{{alert.tags.site}}</dd></dl>
- <dl><dt>Data Source</dt><dd>{{alert.tags.application}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Alert Time</dt><dd>{{common.format.date(alert.timestamp)}}</dd></dl>
- <dl><dt>Message Time</dt><dd>{{common.format.date(alert.alertContext.properties.timestamp)}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Stream Name</dt><dd>{{alert.tags.sourceStreams}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Alert Source</dt><dd>{{alert.tags.alertSource}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>User</dt><dd>{{alert.alertContext.properties.user}}</dd></dl>
- <dl><dt>Host</dt><dd>{{alert.alertContext.properties.host}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Event</dt><dd>{{alert.alertContext.properties.alertEvent}}</dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Message</dt><dd>{{alert.alertContext.properties.alertMessage}}</dd></dl>
- </div>
- </div><!-- /.box-body -->
-
- <div class="overlay" ng-hide="alertList._promise.$$state.status === 1;">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-
- <div class="box-footer clearfix">
- <a data-toggle="collapse" href="[data-id='rawAlertContext']">
- Raw Alert Context
- </a>
- <div data-id="rawAlertContext" class="collapse">
- <pre>{{alert.rawAlertContext}}</pre>
- </div>
- </div>
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
deleted file mode 100644
index 0415cc0..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-list-alt"> </i>
- <h3 class="box-title">
- {{application.displayName}}
- </h3>
- </div>
- <div class="box-body">
- <p ng-show="!alertList.ready">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div sorttable source="alertList" sort="-timestamp">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="170" sortpath="timestamp">Alert Time</th>
- <th width="170" sortpath="alertContext.properties.timestamp">Message Time</th>
- <th width="105" sortpath="tags.application">Application</th>
- <th width="150" sortpath="tags.policyId">Policy Name</th>
- <th width="60" sortpath="alertContext.properties.user">User</th>
- <th width="150" sortpath="tags.alertSource">Source</th>
- <th sortpath="alertContext.properties.emailMessage">Description</th>
- <th width="50"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-class="{info : item.__new}">
- <td>{{common.format.date(item.timestamp)}}</td>
- <td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
- <td>{{item.tags.application}}</td>
- <td class="text-nowrap">
- <a class="fa fa-share-square-o" ng-show="item.tags.policyId"
- href="#/common/policyDetail/?policy={{item.tags.policyId}}&site={{item.tags.site}}&executor={{item.tags.alertExecutorId}}"> </a>
- {{item.tags.policyId}}
- </td>
- <td>{{item.alertContext.properties.user}}</td>
- <td>{{item.tags.alertSource}}</td>
- <td>{{item.alertContext.properties.alertMessage}}</td>
- <td><a href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
- </tr>
- </tbody>
- </table>
- </div>
-
- </div>
- <!--div class="box-footer clearfix">
- </div-->
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
deleted file mode 100644
index cdddc43..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
+++ /dev/null
@@ -1,173 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title">
- {{policy.tags.policyId}}
- <small>{{policy.tags.site}}</small>
- </h3>
- </div>
-
- <div class="box-body">
- <div class="row">
- <div class="col-xs-8">
- <div class="inline-group">
- <dl><dt>Data Source</dt><dd>{{policy.tags.application}}</dd></dl>
- <dl><dt>Status</dt><dd>
- <span ng-show="policy.enabled" class="text-muted"><i class="fa fa-square text-green"></i> Enabled</span>
- <span ng-show="!policy.enabled" class="text-muted"><i class="fa fa-square text-muted"></i> Disabled</span>
- </dd></dl>
- </div>
- <div class="inline-group">
- <dl><dt>Description</dt><dd>{{policy.description}}</dd></dl>
- </div>
- <!--div class="inline-group">
- <dl><dt>Alert</dt><dd>
- <a href="mailto:{{mail}}" ng-repeat="mail in policy.__mailList track by $index" style="margin-right: 10px;">{{mail}}</a>
- <div tabs>
- <pane ng-repeat="notification in policy.__notificationList track by $index" data-title="{{notification.notificationType}}">
- </pane>
- </div>
- </dd></dl>
- </div-->
- <label>Notification</label>
- <div tabs>
- <pane ng-repeat="notification in policy.__notificationList track by $index" data-title="{{notification.notificationType}}">
- <table class="table table-bordered">
- <tbody>
- <tr ng-repeat="(key, value) in notification track by $index">
- <th width="30%">{{key}}</th>
- <td>{{value}}</td>
- </tr>
- </tbody>
- </table>
- </pane>
- </div>
- </div>
- <div class="col-xs-4 text-right" ng-show="Auth.isRole('ROLE_ADMIN')">
- <a class="btn btn-primary" href="#/common/policyEdit/{{policy.encodedRowkey}}">Edit</a>
- <button class="btn btn-warning" ng-show="!policy.enabled" ng-click="updatePolicyStatus(policy, true)">Enable</button>
- <button class="btn btn-warning" ng-show="policy.enabled" ng-click="updatePolicyStatus(policy, false)">Disable</button>
- <button class="btn btn-danger" ng-click="deletePolicy(policy)">Delete</button>
- </div>
- </div>
- </div>
-
- <div class="overlay" ng-hide="policyList._promise.$$state.status === 1;">
- <i class="fa fa-refresh fa-spin"></i>
- </div>
-
- <div class="box-footer clearfix">
- <a data-toggle="collapse" href="[data-id='query']">
- View Query
- </a>
- <div data-id="query" class="collapse in">
- <pre>{{policy.__expression}}</pre>
- </div>
- </div>
-</div>
-
-<div tabs>
- <pane data-title="Visualization" menu="visualizationMenu">
- <div class="row">
- <div class="col-xs-6">
- <div nvd3="policyEvalSeries" data-title="Policy Eval Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
- </div>
- <div class="col-xs-6">
- <div nvd3="policyEvalFailSeries" data-title="Policy Eval Fail Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
- </div>
- <div class="col-xs-6">
- <div nvd3="alertSeries" data-title="Alert Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
- </div>
- <div class="col-xs-6">
- <div nvd3="alertFailSeries" data-title="Alert Fail Count" data-config="chartConfig" class="nvd3-chart-cntr"></div>
- </div>
- </div>
- </pane>
- <pane data-title="Statistics">
- <div class="row">
- <div class="col-xs-3">
- <div class="info-box bg-aqua">
- <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
- <div class="info-box-content">
- <span class="info-box-text">Policy Eval Count</span>
- <span class="info-box-number">{{common.array.sum(policyEvalSeries, "1")}} <small>(Monthly)</small></span>
- <span class="info-box-number">{{policyEvalSeries[policyEvalSeries.length - 1][1]}} <small>(Daily)</small></span>
- </div>
- </div>
- </div>
- <div class="col-xs-3">
- <div class="info-box bg-red">
- <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
- <div class="info-box-content">
- <span class="info-box-text">Policy Eval Fail Count</span>
- <span class="info-box-number">{{common.array.sum(policyEvalFailSeries, "1")}} <small>(Monthly)</small></span>
- <span class="info-box-number">{{policyEvalFailSeries[policyEvalFailSeries.length - 1][1]}} <small>(Daily)</small></span>
- </div>
- </div>
- </div>
- <div class="col-xs-3">
- <div class="info-box bg-aqua">
- <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
- <div class="info-box-content">
- <span class="info-box-text">Alert Count</span>
- <span class="info-box-number">{{common.array.sum(alertSeries, "1")}} <small>(Monthly)</small></span>
- <span class="info-box-number">{{alertSeries[alertSeries.length - 1][1]}} <small>(Daily)</small></span>
- </div>
- </div>
- </div>
- <div class="col-xs-3">
- <div class="info-box bg-red">
- <span class="info-box-icon"><i class="fa fa-bookmark-o"></i></span>
- <div class="info-box-content">
- <span class="info-box-text">Alert Fail Count</span>
- <span class="info-box-number">{{common.array.sum(alertFailSeries, "1")}} <small>(Monthly)</small></span>
- <span class="info-box-number">{{alertFailSeries[alertFailSeries.length - 1][1]}} <small>(Daily)</small></span>
- </div>
- </div>
- </div>
- </div>
- </pane>
- <pane data-title="Alerts">
- <div sorttable source="alertList" sort="-timestamp">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="170" sortpath="timestamp">Alert Time</th>
- <th width="170" sortpath="alertContext.properties.timestamp">Message Time</th>
- <th width="60" sortpath="alertContext.properties.user">User</th>
- <th width="150" sortpath="alertContext.properties.host">Host</th>
- <th sortpath="alertContext.properties.emailMessage">Description</th>
- <th width="50"> </th>
- </tr>
- </thead>
- <tbody>
- <tr ng-class="{info : item.__new}">
- <td>{{common.format.date(item.timestamp)}}</td>
- <td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
- <td>{{item.alertContext.properties.user}}</td>
- <td>{{item.alertContext.properties.host}}</td>
- <td>{{item.alertContext.properties.alertMessage}}</td>
- <td><a href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
- </tr>
- </tbody>
- </table>
- </div>
- </pane>
-</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
deleted file mode 100644
index 33d4cde..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
+++ /dev/null
@@ -1,346 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="progress active" ng-show="!streamReady">
- <div class="progress-bar progress-bar-primary progress-bar-striped" style="width: 100%">
- </div>
-</div>
-
-<!-- Step navigation -->
-<div ng-show="streamReady">
- <div class="row step-cntr">
- <div class="col-sm-4" ng-repeat="step in steps">
- <div class="step" ng-class="stepSelect($index + 1)">
- <h1>{{$index + 1}}</h1>
- <h2>Step {{$index + 1}}</h2>
- <p title="{{step.title}}">{{step.title}}</p>
- </div>
- </div>
- </div>
-
- <!-- Step container -->
- <div class="box box-info">
- <div class="box-header with-border">
- <h3 class="box-title">Step {{step}} - {{steps[step - 1].title}}</h3>
- </div><!-- /.box-header -->
-
- <div class="box-body">
- <!-- ---------------------- Step Body Start ---------------------- -->
-
- <!-- Step 1: Stream -->
- <div ng-show="step === 1">
- <div class="pull-right" ng-show="policy.__.advanced === undefined">
- <span class="text-muted">or</span>
- <a ng-click="policy.__.advanced = true;">Advanced</a>
- </div>
-
- <div class="form-group">
- <label>Select Stream</label>
- <select class="form-control" ng-model="policy.__.streamName" ng-show="!policy.__.advanced">
- <option ng-repeat="stream in applications[policy.tags.application]">{{stream.tags.streamName}}</option>
- </select>
- <select class="form-control" ng-show="policy.__.advanced" disabled="disabled">
- <option>[Advanced Mode]</option>
- </select>
- </div>
-
- <div class="checkbox" ng-show="policy.__.advanced !== undefined">
- <label>
- <input type="checkbox" ng-model="policy.__.advanced">
- Advanced Mode
- </label>
- </div>
- </div>
-
- <!-- Step 2: Define Alert Policy -->
- <div ng-show="step === 2 && !policy.__.advanced">
- <!-- Criteria -->
- <div>
- <label>Match Criteria</label>
- <a ng-click="collapse('.panel-group')">expand / collapse all</a>
-
- <div class="panel-group panel-group-sm" role="tablist">
- <div class="panel panel-default"
- ng-repeat="meta in _stream.metas"
- ng-init="op = '=='; val = null; type = (meta.attrType || '').toLowerCase();">
- <div class="panel-heading" role="tab">
- <h4 class="panel-title">
- <span class="bg-navy disabled color-palette pull-right">
- {{parseConditionDesc(meta.tags.attrName)}}
- </span>
-
- <a role="button" data-toggle="collapse" href="[data-name='{{meta.tags.attrName}}']" class="collapsed">
- <span class="fa fa-square" ng-class="hasCondition(meta.tags.attrName, type) ? 'text-green' : 'text-muted'"> </span>
- {{meta.attrDisplayName || meta.tags.attrName}}
- <span class="fa fa-question-circle" ng-show="meta.attrDescription"
- uib-tooltip="{{meta.attrDescription}}" tooltip-placement="right" tooltip-animation="false"> </span>
- </a>
- </h4>
- </div>
- <div data-name="{{meta.tags.attrName}}" data-type="{{meta.attrType}}" role="tabpanel" class="collapse">
- <div class="panel-body">
- <ul ng-show="type !== 'bool'">
- <li ng-repeat="cond in policy.__.conditions[meta.tags.attrName]">
- [<a ng-click="policy.__.conditions[meta.tags.attrName].splice($index, 1)">X</a>]
- {{cond.toString()}}
- </li>
- </ul>
-
- <!-- String -->
- <div ng-if="type == 'string'">
- <div class="input-group" style="max-width: 450px;">
- <div class="input-group-btn">
- <select class="form-control" ng-model="op">
- <option ng-repeat="mark in ['==','!=','contains','regex']">{{mark}}</option>
- </select>
- </div>
-
- <!-- With resolver -->
- <input type="text" class="form-control" autocomplete="off" ng-model="val" ng-show="meta.attrValueResolver"
- ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)"
- uib-typeahead="item for item in resolverTypeahead($viewValue, meta.attrValueResolver)">
- <!-- Without resolver -->
- <input type="text" class="form-control" autocomplete="off" ng-model="val" ng-show="!meta.attrValueResolver"
- ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)">
-
- <span class="input-group-btn">
- <button class="btn btn-info btn-flat" type="button" ng-click="addCondition(meta.tags.attrName, op, val, type);val=null;">Add</button>
- </span>
- </div>
- </div>
-
- <!-- Number -->
- <div ng-if="type == 'long' || type == 'integer' || type == 'number' || type == 'double' || type == 'float'">
- <div class="input-group" style="max-width: 450px;">
- <div class="input-group-btn">
- <select class="form-control" ng-model="op">
- <option ng-repeat="mark in ['==','!=','>','>=','<','<=']">{{mark}}</option>
- </select>
- </div>
-
- <input type="number" class="form-control" autocomplete="off" placeholder="Number Only..." ng-model="val" ng-keypress="conditionPress($event, meta.tags.attrName, op, val, type)">
- <span class="input-group-btn">
- <button class="btn btn-info btn-flat" type="button" ng-click="addCondition(meta.tags.attrName, op, val, type) ? val=null : void(0);">Add</button>
- </span>
- </div>
- </div>
-
- <!-- Boolean -->
- <div ng-if="type == 'bool'" ng-init="policy.__.conditions[meta.tags.attrName] = policy.__.conditions[meta.tags.attrName] || [_CondUnit(meta.tags.attrName, '==', 'none', 'bool')]">
- <select class="form-control" ng-model="policy.__.conditions[meta.tags.attrName][0].val" style="max-width: 100px;">
- <option ng-repeat="bool in ['none','true','false']">{{bool}}</option>
- </select>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- Window -->
- <div class="checkbox">
- <label>
- <input type="checkbox" ng-checked="policy.__.windowConfig" ng-click="policy.__.windowConfig = !policy.__.windowConfig"> Slide Window
- </label>
- </div>
- <div ng-show="policy.__.windowConfig">
- <div class="row">
- <div class="col-md-4">
- <div class="form-group">
- <label>Window</label>
- <select class="form-control" ng-model="policy.__.window"
- uib-tooltip="{{getWindow().description}}" tooltip-animation="false">
- <option ng-repeat="item in config.window" value="{{item.type}}">{{item.title}}</option>
- </select>
- </div>
- </div>
-
- <!-- fields -->
- <div class="col-md-4" ng-repeat="field in getWindow().fields" ng-init="field.val = field.val || (field.defaultValue || '');" ng-hide="field.hide">
- <div class="form-group" ng-class="{'has-warning' : !field.val || !field.val.match(field.regex)}">
- <label>Window - {{field.title}}</label>
- <input type="text" class="form-control" autocomplete="off" placeholder="{{field.description}}" ng-model="field.val" title="{{field.description}}">
- </div>
- </div>
- </div>
-
- <!-- Aggregation -->
- <div class="row">
- <div class="col-md-4">
- <div class="form-group" ng-class="{'text-yellow' : (policy.__.groupAgg && !policy.__.groupAggPath)}">
- <label>Aggregation</label>
- <div class="input-group">
- <div class="input-group-btn">
- <select class="form-control" ng-model="policy.__.groupAgg" ng-change="updateGroupAgg()">
- <option ng-repeat="op in ['max','min','avg','count', 'sum']">{{op}}</option>
- </select>
- </div>
- <select class="form-control" ng-model="policy.__.groupAggPath" ng-class="{'has-warning' : !policy.__.groupAggPath}" id="groupAggPath"
- ng-show="policy.__.groupAgg" ng-disabled="policy.__.groupAgg === 'count'">
- <option ng-repeat="meta in groupAggPathList()">{{meta.tags.attrName}}</option>
- </select>
- </div>
- </div>
- </div>
-
- <div class="col-md-4">
- <div class="form-group" ng-class="{'text-yellow' : (!policy.__.groupCondOp || !policy.__.groupCondVal)}">
- <label>Condition</label>
- <div class="input-group">
- <div class="input-group-btn">
- <select class="form-control" ng-model="policy.__.groupCondOp" ng-class="{'has-warning' : !policy.__.groupCondOp}">
- <option ng-repeat="op in ['>','<','>=','<=','==']">{{op}}</option>
- </select>
- </div>
- <input type="text" class="form-control" ng-model="policy.__.groupCondVal" ng-class="{'has-warning' : !policy.__.groupCondVal}" />
- </div>
- </div>
- </div>
-
- <div class="col-md-4">
- <div class="form-group">
- <label>Alias (Optional)</label>
- <input type="text" class="form-control" ng-model="policy.__.groupAggAlias" placeholder="Default: aggValue" />
- </div>
- </div>
- </div>
-
- <!-- Group -->
- <div class="row">
- <div class="col-md-4">
- <div class="form-group">
- <label>Group By</label>
- <select class="form-control" ng-model="policy.__.group">
- <option value="">None</option>
- <option ng-repeat="meta in _stream.metas">{{meta.tags.attrName}}</option>
- </select>
- </div>
- </div>
- </div>
- </div>
- </div>
-
- <!-- Step 2: Define Alert Policy -->
- <div ng-show="step === 2 && policy.__.advanced">
- <div class="form-group">
- <label>Query Expression</label>
- <textarea class="form-control" ng-model="policy.__._expression"
- placeholder="Query expression. e.g. from hdfsAuditLogEventStream[(cmd=='open') and (host=='localhost' or host=='127.0.0.1')]#window.time(2 sec) select * insert into outputStream;" rows="5"></textarea>
- </div>
- </div>
-
- <!-- Step 3: Email Notification -->
- <div ng-show="step === 3">
- <div class="row">
- <div class="col-xs-4">
- <div class="form-group" ng-class="{'has-warning' : !policy.tags.policyId}">
- <label>Policy Name</label>
- <input type="text" class="form-control" placeholder="" ng-model="policy.tags.policyId" ng-disabled="!create">
- </div>
- </div>
- <div class="col-xs-3">
- <div class="form-group">
- <label>
- Alert De-Dup Interval(min)
- <span class="fa fa-question-circle" uib-tooltip="Same type alert will be De-dup in configured interval"> </span>
- </label>
- <input type="number" class="form-control" ng-model="policy.__.dedupe.alertDedupIntervalMin" placeholder="[Minute] Number only. Suggestion: 720">
- </div>
- </div>
- <div class="col-xs-2">
- <div class="form-group">
- <label>
- Enabled
- </label>
- <p>
- <input type="checkbox" checked="checked" ng-model="policy.enabled">
- </p>
- </div>
- </div>
- </div>
-
- <div>
- <a data-toggle="collapse" href="[data-id='advancedDeDup']">Advanced De-Dup</a>
- <div data-id='advancedDeDup' class="collapse">
- <label>
- De-Dup Key
- <span class="fa fa-question-circle" uib-tooltip="Type of grouping alerts. If you don't know how to config, leave it default."> </span>
- </label>
- <div class="form-group">
- <div class="checkbox" ng-repeat="meta in _stream.metas" ng-init="create ? policy.__._dedupTags[meta.tags.attrName] = !!meta.usedAsTag : void(0);">
- <label>
- <input type="checkbox" ng-model="policy.__._dedupTags[meta.tags.attrName]">
- {{meta.tags.attrName}}
- </label>
- </div>
- </div>
- </div>
- </div>
-
- <hr/>
-
- <label>Notification</label>
- <div tabs menu="menu" holder="notificationTabHolder" ng-show="policy.__.notification.length">
- <pane ng-repeat="notification in policy.__.notification track by $index" data-data="notification" data-title="{{notification.notificationType}}">
- <div class="form-group" ng-repeat="field in Notification.map[notification.notificationType].fieldList track by $index">
- <label>{{field.name}}</label>
- <input type="text" class="form-control" ng-model="notification[field.name]">
- </div>
- <p class="text-muted" ng-if="Notification.map[notification.notificationType].fieldList.length === 0">No configuration required</p>
- </pane>
- </div>
- <ul ng-show="policy.__.notification.length === 0">
- <li ng-repeat="notification in Notification.list track by $index">
- <a ng-click="newNotification(notification.tags.notificationType)">+ New {{notification.tags.notificationType}} Notification</a>
- </li>
- </ul>
-
- <hr/>
-
- <div class="form-group">
- <label>Description</label>
- <textarea class="form-control" placeholder="Policy description" ng-model="policy.description"></textarea>
- </div>
-
- <a data-toggle="collapse" href="[data-id='policyQuery']">
- View Query
- </a>
- <div class="collapse in" data-id="policyQuery">
- <pre>{{toQuery()}}</pre>
- </div>
- </div>
-
- <!-- ----------------------- Step Body End ----------------------- -->
- </div><!-- /.box-body -->
-
- <div class="overlay" ng-hide="stepReady(step)">
- <span class="fa fa-refresh fa-spin"> </span>
- </div>
-
- <div class="box-footer text-right">
- <button class="btn btn-info" ng-show="step > 1" ng-click="changeStep(step, step - 1, false)" ng-disabled="lock">
- Prev <span class="fa fa-arrow-circle-o-left"> </span>
- </button>
- <button class="btn btn-info" ng-show="step < steps.length" ng-click="changeStep(step, step + 1)" ng-disabled="checkNextable(step) || lock">
- Next <span class="fa fa-arrow-circle-o-right"> </span>
- </button>
- <button class="btn btn-info" ng-show="step === steps.length" ng-click="finishPolicy()" ng-disabled="checkNextable(step) || lock">
- Done <span class="fa fa-check-circle-o"> </span>
- </button>
- </div>
- </div>
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
deleted file mode 100644
index 20a38dd..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<div class="box box-primary">
- <div class="box-header with-border">
- <i class="fa fa-list-alt"> </i>
- <h3 class="box-title">
- {{application.displayName}}
- </h3>
- </div>
- <div class="box-body">
- <div class="row">
- <div class="col-xs-3">
- <div class="search-box">
- <input type="search" class="form-control input-sm" placeholder="Search" ng-model="search" />
- <span class="fa fa-search"> </span>
- </div>
- </div>
- <div class="col-xs-6">
- <div class="inline-group form-inline text-muted">
- <dl><dt><i class="fa fa-square text-green"> </i></dt><dd>Enabled</dd></dl>
- <dl><dt><i class="fa fa-square text-muted"> </i></dt><dd>Disabled</dd></dl>
- </div>
- </div>
- <div class="col-xs-3 text-right">
- <a class="btn btn-primary" href="#/common/policyCreate/{{!application ? '' : '?app=' + application.tags.application}}" ng-show="Auth.isRole('ROLE_ADMIN')">
- New Policy
- <i class="fa fa-plus-circle"> </i>
- </a>
- </div>
- </div>
-
- <p ng-show="policyList._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div sorttable source="policyList" ng-show="policyList._promise.$$state.status === 1" search="false" searchfunc="searchFunc">
- <table class="table table-bordered" ng-non-bindable>
- <thead>
- <tr>
- <th width="30" sortpath="enabled"> </th>
- <th width="200" sortpath="tags.policyId">Policy Name</th>
- <th sortpath="description">Description</th>
- <th width="150" sortpath="owner">Owner</th>
- <th width="170" sortpath="lastModifiedDate">Last Modified</th>
- <th width="95" ng-show="_parent.Auth.isRole('ROLE_ADMIN')">Action</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><span class='fa fa-square' ng-class="item.enabled ? 'text-green' : 'text-muted'"> </span></td>
- <td><a href="#/common/policyDetail/{{item.encodedRowkey}}" style="width: 200px;" class="text-breakall">{{item.tags.policyId}}</a></td>
- <td>{{item.description}}</td>
- <td>{{item.owner}}</td>
- <td>{{common.format.date(item.lastModifiedDate) || "-"}}</td>
- <td ng-show="_parent.Auth.isRole('ROLE_ADMIN')">
- <a class="fa fa-pencil btn btn-default btn-xs" uib-tooltip="Edit" tooltip-animation="false" href="#/common/policyEdit/{{item.encodedRowkey}}"> </a>
- <button class="fa fa-play sm btn btn-default btn-xs" uib-tooltip="Enable" tooltip-animation="false" ng-show="!item.enabled" ng-click="_parent.updatePolicyStatus(item, true)"> </button>
- <button class="fa fa-pause sm btn btn-default btn-xs" uib-tooltip="Disable" tooltip-animation="false" ng-show="item.enabled" ng-click="_parent.updatePolicyStatus(item, false)"> </button>
- <button class="rm fa fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" tooltip-animation="false" ng-click="_parent.deletePolicy(item)"> </button>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <!--div class="box-footer clearfix">
- </div-->
-</div>
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js b/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
deleted file mode 100644
index c17d612..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function() {
- 'use strict';
-
- var featureControllers = angular.module('featureControllers');
- var feature = featureControllers.register("metadata");
-
- // ==============================================================
- // = Function =
- // ==============================================================
-
- // ==============================================================
- // = Metadata =
- // ==============================================================
-
- // ======================= Metadata List ========================
- feature.navItem("streamList", "Metadata", "bullseye");
- feature.controller('streamList', function(PageConfig, Site, $scope, $q, Application, Entities) {
- PageConfig.hideSite = true;
-
- $scope.streams = {};
- $scope._streamEntity = null;
- $scope._streamEntityLock = false;
-
- // =========================================== List ===========================================
- var _streamList = Entities.queryEntities("AlertStreamService", {application: Application.current().tags.application});
- var _streamSchemaList = Entities.queryEntities("AlertStreamSchemaService", {application: Application.current().tags.application});
- $scope.streamList = _streamList;
- $scope.streamSchemaList = _streamSchemaList;
-
- _streamList._promise.then(function() {
- $.each(_streamList, function(i, stream) {
- stream.metaList = [];
- $scope.streams[stream.tags.application + "_" + stream.tags.streamName] = stream;
- });
- });
-
- $q.all([_streamList._promise, _streamSchemaList._promise]).then(function() {
- $.each(_streamSchemaList, function(i, meta) {
- var _stream = $scope.streams[meta.tags.application + "_" + meta.tags.streamName];
- if(_stream) {
- _stream.metaList.push(meta);
- } else {
- console.warn("[Meta] Stream not match:", meta.tags.application + "_" + meta.tags.streamName);
- }
- });
- });
- });
-})();
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html b/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
deleted file mode 100644
index 2b73300..0000000
--- a/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<p ng-show="streamList._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
-</p>
-
-<div class="box box-primary" ng-repeat="stream in streams">
- <div class="box-header with-border">
- <h3 class="box-title">{{stream.tags.streamName}}</h3>
- </div>
- <div class="box-body">
- <div class="inline-group">
- <dl>
- <dt>
- Data Source
- </dt>
- <dd>
- {{stream.tags.application}}
- </dd>
- </dl>
- <dl>
- <dt>
- Stream Name
- </dt>
- <dd>
- {{stream.tags.streamName}}
- </dd>
- </dl>
- </div>
- <div>
- <dl>
- <dt>
- Description
- </dt>
- <dd>
- {{stream.description}}
- </dd>
- </dl>
- </div>
-
- <p ng-show="streamSchemaList._promise.$$state.status !== 1">
- <span class="fa fa-refresh fa-spin"> </span>
- Loading...
- </p>
-
- <div class="list" ng-show="streamSchemaList._promise.$$state.status === 1">
- <table class="table table-bordered">
- <thead>
- <tr>
- <th width="10%">Attribute Name</th>
- <th width="12%">Display Name</th>
- <th width="8%">Type</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr ng-repeat="meta in stream.metaList">
- <td>{{meta.tags.attrName}}</td>
- <td>{{meta.attrDisplayName}}</td>
- <td><span class="label label-warning">{{meta.attrType}}</span></td>
- <td>{{meta.attrDescription}}</td>
- </tr>
- </tbody>
- </table>
- </div>
- </div><!-- /.box-body -->
- <!-- Loading (remove the following to stop the loading)-->
-</div>
\ No newline at end of file