http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/utils/TableLayout.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/utils/TableLayout.js b/dashboardv2/public/js/utils/TableLayout.js new file mode 100644 index 0000000..e513241 --- /dev/null +++ b/dashboardv2/public/js/utils/TableLayout.js @@ -0,0 +1,400 @@ +/** + * 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. + */ + + /** + * @file This is the common View file for displaying Table/Grid to be used overall in the application. + */ +define(['require', + 'backbone', + 'hbs!tmpl/common/TableLayout_tmpl', + 'backgrid-filter', + 'backgrid-paginator', + 'backgrid-sizeable', + 'backgrid-orderable' +], function(require, Backbone, FSTablelayoutTmpl) { + 'use strict'; + + var FSTableLayout = Backbone.Marionette.LayoutView.extend( + /** @lends FSTableLayout */ + { + _viewName: 'FSTableLayout', + + template: FSTablelayoutTmpl, + + /** Layout sub regions */ + regions: { + 'rTableList': 'div[data-id="r_tableList"]', + 'rTableSpinner': 'div[data-id="r_tableSpinner"]', + 'rPagination': 'div[data-id="r_pagination"]', + 'rFooterRecords': 'div[data-id="r_footerRecords"]' + }, + + // /** ui selector cache */ + ui: { + selectPageSize: 'select[data-id="pageSize"]' + }, + + gridOpts: { + className: 'table table-bordered table-hover table-condensed backgrid', + emptyText: 'No Records found!' + }, + + /** + * Backgrid.Filter default options + */ + filterOpts: { + placeholder: 'plcHldr.searchByResourcePath', + wait: 150 + }, + + /** + * Paginator default options + */ + paginatorOpts: { + // If you anticipate a large number of pages, you can adjust + // the number of page handles to show. The sliding window + // will automatically show the next set of page handles when + // you click next at the end of a window. + windowSize: 5, // Default is 10 + + // Used to multiple windowSize to yield a number of pages to slide, + // in the case the number is 5 + slideScale: 0.5, // Default is 0.5 + + // Whether sorting should go back to the first page + goBackFirstOnSort: false // Default is true + }, + + /** + page handlers for pagination + */ + controlOpts: { + rewind: { + label: "《", + title: "First" + }, + back: { + label: "〈", + title: "Previous" + }, + forward: { + label: "〉", + title: "Next" + }, + fastForward: { + label: "》", + title: "Last" + } + }, + columnOpts: { + initialColumnsVisible: 4, + // State settings + saveState: false, + loadStateOnInit: true + }, + + includePagination: true, + + includeFilter: false, + + includeHeaderSearch: false, + + includePageSize: false, + + includeFooterRecords: true, + + includeColumnManager: false, + + includeSizeAbleColumns: false, + + includeOrderAbleColumns: false, + + + /** ui events hash */ + events: function() { + var events = {}; + events['change ' + this.ui.selectPageSize] = 'onPageSizeChange'; + return events; + }, + + /** + * intialize a new HDFSTableLayout Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'collection', 'columns', 'includePagination', + 'includeHeaderSearch', 'includeFilter', 'includePageSize', + 'includeFooterRecords', 'includeColumnManager', 'includeSizeAbleColumns', 'includeOrderAbleColumns')); + + _.extend(this.gridOpts, options.gridOpts, { collection: this.collection, columns: this.columns }); + _.extend(this.filterOpts, options.filterOpts); + _.extend(this.paginatorOpts, options.paginatorOpts); + _.extend(this.controlOpts, options.controlOpts); + _.extend(this.columnOpts, options.columnOpts); + + this.bindEvents(); + }, + + /** all events binding here */ + bindEvents: function() { + this.listenTo(this.collection, 'request', function() { + this.$('div[data-id="r_tableSpinner"]').addClass('loading'); + }, this); + this.listenTo(this.collection, 'sync error', function() { + this.$('div[data-id="r_tableSpinner"]').removeClass('loading'); + }, this); + + this.listenTo(this.collection, 'reset', function(collection, response) { + if (this.includePagination) { + this.renderPagination(); + } + if (this.includeFooterRecords) { + this.renderFooterRecords(this.collection.state); + } + }, this); + + /*This "sort" trigger event is fired when clicked on + 'sortable' header cell (backgrid). + Collection.trigger event was fired because backgrid has + removeCellDirection function (backgrid.js - line no: 2088) + which is invoked when "sort" event is triggered + on collection (backgrid.js - line no: 2081). + removeCellDirection function - removes "ascending" and "descending" + which in turn removes chevrons from every 'sortable' header-cells*/ + this.listenTo(this.collection, "backgrid:sort", function() { + this.collection.trigger("sort"); + }); + + /*this.listenTo(this.collection, 'remove', function(model, collection, response){ + if (model.isNew() || !this.includePagination) { + return; + } + if (this.collection.state && this.collection.state.totalRecords>0) { + this.collection.state.totalRecords-=1; + } + if (this.collection.length===0 && this.collection.state && this.collection.state.totalRecords>0) { + + if (this.collection.state.totalRecords>this.collection.state.currentPage*this.collection.state.pageSize) { + this.collection.fetch({reset:true}); + } else { + if (this.collection.state.currentPage>0) { + this.collection.state.currentPage-=1; + this.collection.fetch({reset:true}); + } + } + + } else if (this.collection.length===0 && this.collection.state && this.collection.state.totalRecords===0) { + this.collection.state.currentPage=0; + this.collection.fetch({reset:true}); + } + }, this);*/ + + // It will show tool tip when td has ellipsis Property + this.listenTo(this.collection, "backgrid:refresh", function() { + /*this.$('.table td').bind('mouseenter', function() { + var $this = $(this); + if (this.offsetWidth < this.scrollWidth && !$this.attr('title')) { + $this.attr('title', $this.text()); + } + });*/ + }, this); + + }, + + /** on render callback */ + onRender: function() { + this.renderTable(); + if (this.includePagination) { + this.renderPagination(); + } + if (this.includeFilter) { + this.renderFilter(); + } + if (!this.includePageSize) { + this.ui.selectPageSize.remove(); + } + if (this.includeFooterRecords) { + this.renderFooterRecords(this.collection.state); + } + if (this.includeColumnManager) { + this.renderColumnManager(); + } + if (this.includeSizeAbleColumns) { + this.renderSizeAbleColumns(); + } + if (this.includeOrderAbleColumns) { + this.renderOrderAbleColumns(); + } + this.$('[data-id="pageSize"]').val(this.collection.state.pageSize); + + + }, + /** + * show table + */ + renderTable: function() { + var that = this; + this.rTableList.show(new Backgrid.Grid(this.gridOpts)); + }, + + /** + * show pagination buttons(first, last, next, prev and numbers) + */ + renderPagination: function() { + var options = _.extend({ + collection: this.collection, + controls: this.controlOpts + }, this.paginatorOpts); + + // TODO - Debug this part + if (this.rPagination) { + this.rPagination.show(new Backgrid.Extension.Paginator(options)); + } else if (this.regions.rPagination) { + this.$('div[data-id="r_pagination"]').show(new Backgrid.Extension.Paginator(options)); + } + }, + + /** + * show/hide pagination buttons of the grid + */ + showHidePager: function() { + + if (!this.includePagination) { + return; + } + + if (this.collection.state && this.collection.state.totalRecords > this.collection.state.pageSize) { + this.$('div[data-id="r_pagination"]').show(); + } else { + this.$('div[data-id="r_pagination"]').hide(); + } + }, + + /** + * show/hide filter of the grid + */ + renderFilter: function() { + this.rFilter.show(new Backgrid.Extension.ServerSideFilter({ + collection: this.collection, + name: ['name'], + placeholder: 'plcHldr.searchByResourcePath', + wait: 150 + })); + + setTimeout(function() { + that.$('table').colResizable({ liveDrag: true }); + }, 0); + }, + + /** + * show/hide footer details of the list/collection shown in the grid + */ + renderFooterRecords: function(collectionState) { + var collState = collectionState; + var totalRecords = collState.totalRecords || 0; + var pageStartIndex = totalRecords ? (collState.currentPage * collState.pageSize) : 0; + var pageEndIndex = pageStartIndex + this.collection.length; + + this.$('[data-id="r_footerRecords"]').html('Showing ' + (totalRecords ? pageStartIndex + 1 : 0) + ' to ' + pageEndIndex + ' of ' + totalRecords + ' entries'); + return this; + }, + /** + * ColumnManager for the table + */ + renderColumnManager: function() { + var $el = this.$("[data-id='control']"); + var colManager = new Backgrid.Extension.ColumnManager(this.columns, this.columnOpts); + // Add control + var colVisibilityControl = new Backgrid.Extension.ColumnManagerVisibilityControl({ + columnManager: colManager + }); + + $el.append(colVisibilityControl.render().el); + }, + + renderSizeAbleColumns: function() { + // Add sizeable columns + var sizeAbleCol = new Backgrid.Extension.SizeAbleColumns({ + collection: this.collection, + columns: this.columns, + grid: this.getGridObj() + }); + this.$('thead').before(sizeAbleCol.render().el); + + // Add resize handlers + var sizeHandler = new Backgrid.Extension.SizeAbleColumnsHandlers({ + sizeAbleColumns: sizeAbleCol, + grid: this.getGridObj(), + saveModelWidth: true + }); + this.$('thead').before(sizeHandler.render().el); + + // Listen to resize events + this.columns.on('resize', function(columnModel, newWidth, oldWidth) { + console.log('Resize event on column; name, model, new and old width: ', columnModel.get("name"), columnModel, newWidth, oldWidth); + }); + }, + + renderOrderAbleColumns: function() { + // Add orderable columns + var sizeAbleCol = new Backgrid.Extension.SizeAbleColumns({ + collection: this.collection, + grid: this.getGridObj(), + columns: this.columns + }); + var orderHandler = new Backgrid.Extension.OrderableColumns({ + grid: this.getGridObj(), + sizeAbleColumns: sizeAbleCol + }); + this.$('thead').before(orderHandler.render().el); + }, + + /** on close */ + onClose: function() {}, + + /** + * get the Backgrid object + * @return {null} + */ + getGridObj: function() { + if (this.rTableList.currentView) { + return this.rTableList.currentView; + } + return null; + }, + + /** + * handle change event on page size select box + * @param {Object} e event + */ + onPageSizeChange: function(e) { + var pagesize = $(e.currentTarget).val(); + this.collection.state.pageSize = parseInt(pagesize, 10); + + this.collection.state.currentPage = this.collection.state.firstPage; + + this.collection.fetch({ + sort: false, + reset: true, + cache: false + }); + } + }); + + return FSTableLayout; +});
http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/utils/Utils.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/utils/Utils.js b/dashboardv2/public/js/utils/Utils.js new file mode 100644 index 0000000..7b2db79 --- /dev/null +++ b/dashboardv2/public/js/utils/Utils.js @@ -0,0 +1,187 @@ +/** + * 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. + */ + + define(['require', 'utils/Globals'], function(require, Globals) { + 'use strict'; + + var Utils = {}; + require(['noty'], function() { + $.extend($.noty.defaults, { + timeout: 5000, + layout: "topRight", + theme: "relax", + closeWith: ['click', 'button'], + animation: { + open: 'animated flipInX', + close: 'animated flipOutX', + easing: 'swing', + speed: 500 + } + }); + }); + Utils.generateUUID = function() { + var d = new Date().getTime(); + if (window.performance && typeof window.performance.now === "function") { + d += performance.now(); //use high-precision timer if available + } + var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = (d + Math.random() * 16) % 16 | 0; + d = Math.floor(d / 16); + return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + return uuid; + }; + + Utils.notifyInfo = function(options) { + noty({ + type: "information", + text: "<i class='fa fa-exclamation-circle'></i> " + (options.content || "Info message.") + }); + }; + Utils.notifyWarn = function(options) { + noty({ + type: "warning", + text: "<i class='fa fa-times-circle'></i> " + (options.content || "Info message.") + }); + }; + + Utils.notifyError = function(options) { + noty({ + type: "error", + text: "<i class='fa fa-times-circle'></i> " + (options.content || "Error occurred.") + }); + }; + + Utils.notifySuccess = function(options) { + noty({ + type: "success", + text: "<i class='fa fa-check-circle-o'></i> " + (options.content || "Error occurred.") + }); + }; + Utils.defaultErrorHandler = function(model, error) { + /* + require(['views/common/ErrorView', 'App'], function(vError, App) { + if (error.status == 404) { + App.rContent.show(new vError({ + status: error.status + })); + } else if (error.status == 401) { + App.rContent.show(new vError({ + status: error.status + })); + } else if (error.status == 419) { + window.location = 'login.jsp' + } else if (error.status == "0") { + var diffTime = (new Date().getTime() - prevNetworkErrorTime); + if (diffTime > 3000) { + prevNetworkErrorTime = new Date().getTime(); + Utils.notifyError({ + content: "Network Connection Failure : " + + "It seems you are not connected to the internet. Please check your internet connection and try again" + }) + + } + } + }); + */ + }; + + Utils.localStorage = { + checkLocalStorage: function(key, value) { + if (typeof(Storage) !== "undefined") { + return this.getLocalStorage(key, value); + } else { + console.log('Sorry! No Web Storage support'); + Utils.cookie.checkCookie(key, value); + } + }, + setLocalStorage: function(key, value) { + localStorage.setItem(key, value); + return { found: false, 'value': value }; + }, + getLocalStorage: function(key, value) { + var keyValue = localStorage.getItem(key) + if (!keyValue || keyValue == "undefined") { + return this.setLocalStorage(key, value); + } else { + return { found: true, 'value': keyValue }; + } + } + } + Utils.cookie = { + setCookie: function(cname, cvalue) { + //var d = new Date(); + //d.setTime(d.getTime() + (exdays*24*60*60*1000)); + //var expires = "expires=" + d.toGMTString(); + document.cookie = cname + "=" + cvalue + "; " + return { found: false, 'value': cvalue }; + }, + getCookie: function(findString) { + var search = findString + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1); + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; + }, + checkCookie: function(key, value) { + var findString = getCookie(key); + if (findString != "" || keyValue != "undefined") { + return { found: true, 'value': ((findString == "undefined") ? (undefined) : (findString)) }; + } else { + return setCookie(key, value); + } + } + } + Utils.getQueryParams = function(qs) { + qs = qs.split('+').join(' '); + var params = {}, + tokens, + re = /[?&]?([^=]+)=([^&]*)/g; + while (tokens = re.exec(qs)) { + params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]); + } + return params; + } + + Utils.setUrl = function(options) { + if (options) { + if (options.mergeBrowserUrl) { + var hashUrl = window.location.hash.split("?"); + if (hashUrl.length > 1) { + var param = Utils.getQueryParams(hashUrl[1]); + options.urlParams = _.extend(param, options.urlParams) + } + } + if (options.urlParams) { + var urlParams = "?" + _.each(options.urlParams, function(value, key, obj) { + urlParams += key + "=" + value + "&"; + }); + urlParams = urlParams.slice(0, -1); + options.url += urlParams; + } + Backbone.history.navigate(options.url, { trigger: options.trigger != undefined ? options.trigger : true }); + } + } + return Utils; +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/views/asset/AssetPageLayoutView.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/views/asset/AssetPageLayoutView.js b/dashboardv2/public/js/views/asset/AssetPageLayoutView.js new file mode 100644 index 0000000..c86fef4 --- /dev/null +++ b/dashboardv2/public/js/views/asset/AssetPageLayoutView.js @@ -0,0 +1,416 @@ +/** + * 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/asset/AssetPageLayoutView_tmpl', + 'modules/Modal', + 'models/VEntity', + 'utils/Utils', + 'utils/Globals', +], function(require, Backbone, AssetPageLayoutViewTmpl, Modal, VEntity, Utils, Globals) { + 'use strict'; + + var AssetPageLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends AssetPageLayoutView */ + { + _viewName: 'AssetPageLayoutView', + + template: AssetPageLayoutViewTmpl, + + /** Layout sub regions */ + regions: { + RTagLayoutView: "#r_tagLayoutView", + RSearchLayoutView: "#r_searchLayoutView", + REntityTableLayoutView: "#r_entityTableLayoutView", + }, + + /** ui selector cache */ + ui: { + tagClick: '[data-id="tagClick"]', + addTag: '[data-id="addTag"]', + }, + + /** ui events hash */ + events: function() { + var events = {}; + events["click " + this.ui.tagClick] = function(e) { + if (e.target.nodeName.toLocaleLowerCase() == "i") { + this.onClickTagCross(e); + } else { + Utils.setUrl({ + url: '#!/dashboard/assetPage', + urlParams: { + query: e.currentTarget.text + }, + mergeBrowserUrl: true, + trigger: false + }); + this.vent.trigger("tag:click", { 'query': e.currentTarget.text }); + } + }; + events["click " + this.ui.addTag] = function(e) { + this.addModalView(e); + }; + events["click " + this.ui.tagCrossIcon] = function(e) {}; + return events; + }, + /** + * intialize a new AssetPageLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'globalVent', 'collection', 'vent')); + this.entityModel = new VEntity(); + this.searchCollection = this.collection; + this.fetchList = 0 + this.commonTableOptions = { + collection: this.searchCollection, + includeFilter: false, + includePagination: true, + includePageSize: false, + includeFooterRecords: true, + includeSizeAbleColumns: false, + gridOpts: { + emptyText: 'No Record found!', + className: 'table table-bordered table-hover table-condensed backgrid table-quickMenu' + }, + filterOpts: {}, + paginatorOpts: {} + }; + + }, + bindEvents: function() { + this.listenTo(this.vent, "search:click", function(value) { + this.fetchCollection(value); + this.REntityTableLayoutView.reset(); + }, this); + this.listenTo(this.searchCollection, "reset", function(value) { + this.renderTableLayoutView(); + }, this); + this.listenTo(this.searchCollection, "error", function(value) { + this.$('.fontLoader').hide(); + this.$('.entityTable').show(); + Utils.notifyError({ + content: "Invalid expression" + }); + }, this); + }, + onRender: function() { + this.renderTagLayoutView(); + this.renderSearchLayoutView(); + this.renderTableLayoutView(); + this.bindEvents(); + }, + fetchCollection: function(value) { + this.$('.fontLoader').show(); + this.$('.entityTable').hide(); + if (value) { + if (value.type) { + this.searchCollection.url = "/api/atlas/discovery/search/" + value.type; + } + $.extend(this.searchCollection.queryParams, { 'query': value.query }); + } + this.searchCollection.fetch({ reset: true }); + }, + renderTagLayoutView: function() { + var that = this; + require(['views/tag/TagLayoutView'], function(TagLayoutView) { + that.RTagLayoutView.show(new TagLayoutView({ + globalVent: that.globalVent, + vent: that.vent + })); + }); + }, + renderSearchLayoutView: function() { + var that = this; + require(['views/search/SearchLayoutView'], function(SearchLayoutView) { + that.RSearchLayoutView.show(new SearchLayoutView({ + globalVent: that.globalVent, + vent: that.vent + })); + var hashUrl = window.location.hash.split("?"); + if (hashUrl.length > 1) { + var param = Utils.getQueryParams(hashUrl[1]); + if (param) { + var type = param.searchType; + var query = param.query; + that.vent.trigger("tag:click", { 'query': query, 'searchType': type }); + } + } + }); + }, + renderTableLayoutView: function() { + var that = this, + count = 4; + require(['utils/TableLayout'], function(TableLayout) { + var columnCollection = Backgrid.Columns.extend({ + sortKey: "position", + comparator: function(item) { + return item.get(this.sortKey) || 999; + }, + setPositions: function() { + _.each(this.models, function(model, index) { + if (model.get('name') == "name") { + model.set("position", 1, { silent: true }); + } else if (model.get('name') == "description") { + model.set("position", 2, { silent: true }); + } else if (model.get('name') == "owner") { + model.set("position", 3, { silent: true }); + } else if (model.get('name') == "createTime") { + model.set("position", 4, { silent: true }); + } else { + model.set("position", ++count, { silent: true }); + } + }); + return this; + } + }); + var columns = new columnCollection(that.getEntityTableColumns()); + columns.setPositions().sort(); + that.REntityTableLayoutView.show(new TableLayout(_.extend({}, that.commonTableOptions, { + globalVent: that.globalVent, + columns: columns, + includeOrderAbleColumns: true + }))); + if (that.fetchList <= 0) { + that.$('.fontLoader').hide(); + that.$('.entityTable').show(); + } + }); + + }, + getEntityTableColumns: function() { + var that = this, + col = {}; + var responseData = this.searchCollection.responseData; + if (this.searchCollection.responseData) { + if (responseData.dataType) { + if (responseData.dataType.attributeDefinitions.length == 2 && responseData.dataType.attributeDefinitions[1].name == "instanceInfo") { + return this.getFixedColumn(); + } else { + var modelJSON = this.searchCollection.toJSON()[0]; + _.keys(modelJSON).map(function(key) { + if (key.indexOf("$") == -1 && typeof modelJSON[key] != "object") { + if (typeof modelJSON[key] == "string" || typeof modelJSON[key] == "number") { + if (typeof modelJSON[key] == "number" && key != "createTime") { + return; + } + col[key] = { + cell: (key == "name") ? ("Html") : ("String"), + editable: false, + sortable: false, + orderable: true, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + if (model.get('createTime') == rawValue) { + return new Date(rawValue); + } + if (model.get('name') == rawValue) { + if (model.get('$id$')) { + return '<a href="#!/dashboard/detailPage/' + model.get('$id$').id + '">' + rawValue + '</a>'; + } else { + return '<a>' + rawValue + '</a>'; + } + } else { + return rawValue; + } + } + }) + }; + } + } + }); + col['tag'] = { + label: "Tags", + cell: "Html", + editable: false, + sortable: false, + orderable: true, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var traits = model.get('$traits$'); + var atags = ""; + _.keys(model.get('$traits$')).map(function(key) { + atags += '<a data-id="tagClick">' + traits[key].$typeName$ + '<i class="fa fa-times" data-id="delete" data-name="' + traits[key].$typeName$ + '" data-guid="' + model.get('$id$').id + '" ></i></a>'; + }); + return '<div class="tagList">' + atags + '</div>'; + } + }) + }; + col['addTag'] = { + label: "Tools", + cell: "Html", + editable: false, + sortable: false, + orderable: true, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + if (model.get('$id$')) { + return '<a href="javascript:void(0)" data-id="addTag" class="addTagGuid" data-guid="' + model.get('$id$').id + '" ><i class="fa fa-tag"></i></a>'; + } else { + return '<a href="javascript:void(0)" data-id="addTag"><i class="fa fa-tag"></i></a>'; + + } + } + }) + }; + return this.searchCollection.constructor.getTableCols(col, this.searchCollection); + } + } else { + return this.getFixedColumn(); + } + } + }, + getFixedColumn: function() { + var that = this; + return this.searchCollection.constructor.getTableCols({ + instanceInfo: { + label: "Type Name", + cell: "html", + editable: false, + sortable: false, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var modelObject = model.toJSON(); + if (modelObject.$typeName$ && modelObject.instanceInfo) { + return '<a href="#!/dashboard/detailPage/' + modelObject.instanceInfo.guid + '">' + modelObject.instanceInfo.typeName + '</a>'; + } else if (!modelObject.$typeName$) { + return '<a href="#!/dashboard/detailPage/' + modelObject.guid + '">' + modelObject.typeName + '</a>'; + } + } + }) + }, + name: { + label: "Name", + cell: "html", + editable: false, + sortable: false, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var modelObject = model.toJSON(); + if (modelObject.$typeName$ && modelObject.instanceInfo) { + var guid = model.toJSON().instanceInfo.guid; + ++that.fetchList + model.getEntity(guid, { + beforeSend: function() {}, + success: function(data) { + --that.fetchList + if (that.fetchList <= 0) { + that.$('.fontLoader').hide(); + that.$('.entityTable').show(); + + } + if (data.definition && data.definition.values && data.definition.values.name) { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.values.name); + } else { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.id.id); + } + }, + error: function(error, data, status) { + that.$('.fontLoader').hide(); + that.$('.entityTable').show(); + }, + complete: function() {} + }); + return '<a href="#!/dashboard/detailPage/' + guid + '" data-id="' + guid + '"></a>'; + } else if (!modelObject.$typeName$) { + var guid = model.toJSON().guid; + ++that.fetchList + model.getEntity(guid, { + beforeSend: function() {}, + success: function(data) { + --that.fetchList + if (that.fetchList <= 0) { + that.$('.fontLoader').hide(); + that.$('.entityTable').show(); + } + if (data.definition && data.definition.values && data.definition.values.name) { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.values.name); + } else { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.id.id); + } + }, + error: function(error, data, status) { + that.$('.fontLoader').hide(); + that.$('.entityTable').show(); + }, + complete: function() {} + }); + return '<a href="#!/dashboard/detailPage/' + guid + '" data-id="' + guid + '"></a>'; + } + } + }) + } + }, this.searchCollection); + }, + addModalView: function(e) { + var that = this; + require(['views/tag/addTagModalView'], function(addTagModalView) { + var view = new addTagModalView({ + vent: that.vent, + guid: that.$(e.currentTarget).data("guid"), + modalCollection: that.searchCollection + }); + // view.saveTagData = function() { + //override saveTagData function + // } + }); + }, + onClickTagCross: function(e) { + var tagName = $(e.target).data("name"); + var that = this; + require([ + 'modules/Modal' + ], function(Modal) { + var modal = new Modal({ + title: 'Are you sure you want to delete ?', + okText: 'Delete', + htmlContent: "<b>Tag: " + tagName + "</b>", + cancelText: "Cancel", + allowCancel: true, + okCloses: true, + showFooter: true, + }).open(); + modal.on('ok', function() { + that.deleteTagData(e); + }); + modal.on('closeModal', function() { + modal.trigger('cancel'); + }); + }); + }, + deleteTagData: function(e) { + var that = this, + tagName = $(e.target).data("name"); + var guid = $(e.target).data("guid"); + require(['models/VTag'], function(VTag) { + var tagModel = new VTag(); + tagModel.deleteTag(guid, tagName, { + beforeSend: function() {}, + success: function(data) { + that.searchCollection.fetch({ reset: true }); + }, + error: function(error, data, status) {}, + complete: function() {} + }); + }); + } + }); + return AssetPageLayoutView; +}); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/views/common/BackgridHeader.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/views/common/BackgridHeader.js b/dashboardv2/public/js/views/common/BackgridHeader.js new file mode 100644 index 0000000..b8c5ddb --- /dev/null +++ b/dashboardv2/public/js/views/common/BackgridHeader.js @@ -0,0 +1,267 @@ +/** + * 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. + */ + +define(['require', + 'backbone', + 'underscore', + 'utils/Utils', + 'utils/Globals', + 'backgrid-filter', + 'backgrid-paginator', + 'select2', +], function(require, Backbone, _, Utils) { + 'use strict'; + + var HeaderSearchCell = Backbone.View.extend({ + + tagName: 'td', + + className: 'backgrid-filter', + + template: _.template('<input type="search" <% if (placeholder) { %> placeholder="<%- placeholder %>" <% } %> name="<%- name %>" <% if (style) { %> style="<%- style %>" <% } %> />'), + //<a class='clear' href='#'>×</a>'), + + placeholder: '', + + events: { + 'keyup input': 'evKeyUp', + 'submit': 'search' + }, + + initialize: function(options) { + _.extend(this, _.pick(options, 'column')); + this.name = this.column.get('name'); + if (this.column.get('reName') !== undefined) + this.name = this.column.get('reName'); + + var collection = this.collection, + self = this; + if (Backbone.PageableCollection && collection instanceof Backbone.PageableCollection) { + collection.queryParams[this.name] = function() { + return self.searchBox().val() || null; + }; + } + }, + + render: function() { + this.$el.empty().append(this.template({ + name: this.column.get('name'), + placeholder: this.column.get('placeholder') || 'Search', + style: this.column.get('headerSearchStyle') + })); + this.$el.addClass('renderable'); + this.delegateEvents(); + return this; + + }, + + evKeyUp: function(e) { + var $clearButton = this.clearButton(); + var searchTerms = this.searchBox().val(); + + if (!e.shiftKey) { + this.search(); + } + + if (searchTerms) { + $clearButton.show(); + } else { + $clearButton.hide(); + } + }, + + searchBox: function() { + return this.$el.find('input[type=search]'); + }, + + clearButton: function() { + return this.$el.find('.clear'); + }, + + search: function() { + var data = {}; + // go back to the first page on search + var collection = this.collection; + if (Backbone.PageableCollection && + collection instanceof Backbone.PageableCollection && + collection.mode === 'server') { + collection.state.currentPage = collection.state.firstPage; + } + var query = this.searchBox().val(); + if (query) data[this.name] = query; + if (collection.extraSearchParams) { + _.extend(data, collection.extraSearchParams); + } + if (collection.mode === 'server') { + collection.fetch({ + data: data, + reset: true, + success: function() {}, + error: function(msResponse) { + Utils.notifyError('Error', 'Invalid input data!'); + } + }); + } else if (collection.mode === 'client') { + + } + }, + + clear: function(e) { + if (e) e.preventDefault(); + this.searchBox().val(null); + this.collection.fetch({ + reset: true + }); + } + }); + + var HeaderFilterCell = Backbone.View.extend({ + + tagName: 'td', + + className: 'backgrid-filter', + + template: _.template('<select > <option>ALL</option>' + + '<% _.each(list, function(data) {' + + 'if(_.isObject(data)){ %>' + + '<option value="<%= data.value %>"><%= data.label %></option>' + + '<% }else{ %>' + + '<option value="<%= data %>"><%= data %></option>' + + '<% } %>' + + '<% }); %></select>'), + + placeholder: '', + events: { + 'click': function() {}, + }, + initialize: function(options) { + _.extend(this, _.pick(options, 'column')); + this.name = this.column.get('name'); + this.headerFilterOptions = this.column.get('headerFilterOptions'); + }, + render: function() { + var that = this; + this.$el.empty().append(this.template({ + name: this.column.get('name'), + list: this.headerFilterOptions.filterList, + })); + + this.$el.find('select').select2({ + allowClear: true, + closeOnSelect: false, + width: this.headerFilterOptions.filterWidth || '100%', + height: this.headerFilterOptions.filterHeight || '20px', + }); + + this.$el.addClass('renderable'); + + this.$el.find('select').on('click', function(e) { + that.search(e.currentTarget.value); + }); + return this; + }, + search: function(selectedOptionValue) { + var data = {}, + query; + // go back to the first page on search + var collection = this.collection; + if (Backbone.PageableCollection && + collection instanceof Backbone.PageableCollection && + collection.mode === 'server') { + collection.state.currentPage = collection.state.firstPage; + } + if (selectedOptionValue !== 'ALL') { + query = selectedOptionValue; + } + if (query) { + data[this.name] = query; + } + if (collection.extraSearchParams) { + _.extend(data, collection.extraSearchParams); + } + collection.fetch({ + data: data, + reset: true + }); + }, + /*clear: function (e) { + if (e) e.preventDefault(); + this.searchBox().val(null); + this.collection.fetch({reset: true}); + }*/ + }); + var HeaderRow = Backgrid.Row.extend({ + requiredOptions: ['columns', 'collection'], + + initialize: function() { + Backgrid.Row.prototype.initialize.apply(this, arguments); + }, + makeCell: function(column, options) { + var headerCell; + switch (true) { + case (column.has('canHeaderSearch') && column.get('canHeaderSearch') === true): + headerCell = new HeaderSearchCell({ + column: column, + collection: this.collection, + }); + break; + case (column.has('canHeaderFilter') && column.get('canHeaderFilter') === true): + headerCell = new HeaderFilterCell({ + column: column, + collection: this.collection, + }); + break; + default: + headerCell = new Backbone.View({ + tagName: 'td' + }); + } + return headerCell; + } + }); + var Header = Backgrid.Header.extend({ + + initialize: function(options) { + var args = Array.prototype.slice.apply(arguments); + Backgrid.Header.prototype.initialize.apply(this, args); + + this.searchRow = new HeaderRow({ + columns: this.columns, + collection: this.collection + }); + }, + /** + Renders this table head with a single row of header cells. + */ + render: function() { + var args = Array.prototype.slice.apply(arguments); + Backgrid.Header.prototype.render.apply(this, args); + + this.$el.append(this.searchRow.render().$el); + return this; + }, + remove: function() { + var args = Array.prototype.slice.apply(arguments); + Backgrid.Header.prototype.remove.apply(this, args); + + this.searchRow.remove.apply(this.searchRow, arguments); + return Backbone.View.prototype.remove.apply(this, arguments); + } + }); + return Header; +}); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js new file mode 100644 index 0000000..87adec0 --- /dev/null +++ b/dashboardv2/public/js/views/detail_page/DetailPageLayoutView.js @@ -0,0 +1,195 @@ +/** + * 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/detail_page/DetailPageLayoutView_tmpl' +], function(require, Backbone, DetailPageLayoutViewTmpl) { + 'use strict'; + + var DetailPageLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends DetailPageLayoutView */ + { + _viewName: 'DetailPageLayoutView', + + template: DetailPageLayoutViewTmpl, + + /** Layout sub regions */ + regions: { + REntityDetailTableLayoutView: "#r_entityDetailTableLayoutView", + RSchemaTableLayoutView: "#r_schemaTableLayoutView", + RTagTableLayoutView: "#r_tagTableLayoutView", + RLineageLayoutView: "#r_lineageLayoutView", + }, + /** ui selector cache */ + ui: {}, + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + /** + * intialize a new DetailPageLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'globalVent', 'collection', 'vent')); + this.bindEvents(); + this.commonTableOptions = { + collection: this.collection, + includeFilter: false, + includePagination: false, + includePageSize: false, + includeFooterRecords: true, + gridOpts: { + className: "table table-striped table-condensed backgrid table-quickMenu", + emptyText: 'No records found!' + }, + filterOpts: {}, + paginatorOpts: {} + }; + }, + bindEvents: function() { + this.listenTo(this.collection, 'reset', function() { + var collectionJSON = this.collection.toJSON(); + if (collectionJSON[0].id && collectionJSON[0].id.id) { + var tagGuid = collectionJSON[0].id.id; + } + if (collectionJSON && collectionJSON.length) { + if (collectionJSON[0].values) { + this.name = collectionJSON[0].values.name; + this.description = collectionJSON[0].values.description; + if (this.name) { + this.$('.breadcrumbName').text(this.name); + this.$('.name').show(); + this.$('.name').html('<strong>Name: </strong><span>' + this.name + '</span>'); + } else { + this.$('.name').hide(); + } + if (this.description) { + this.$('.description').show(); + this.$('.description').html('<strong>Description: </strong><span>' + this.description + '</span>'); + } else { + this.$('.description').hide(); + } + } + } + this.renderEntityDetailTableLayoutView(); + this.renderTagTableLayoutView(tagGuid); + this.renderLineageLayoutView(tagGuid); + this.renderSchemaLayoutView(); + }, this); + }, + onRender: function() {}, + renderEntityDetailTableLayoutView: function() { + var that = this; + require(['views/entity/EntityDetailTableLayoutView'], function(EntityDetailTableLayoutView) { + that.REntityDetailTableLayoutView.show(new EntityDetailTableLayoutView({ + globalVent: that.globalVent, + collection: that.collection + })); + }); + }, + renderTagTableLayoutView: function(tagGuid) { + var that = this; + require(['views/tag/TagDetailTableLayoutView'], function(TagDetailTableLayoutView) { + that.RTagTableLayoutView.show(new TagDetailTableLayoutView({ + globalVent: that.globalVent, + collection: that.collection, + guid: tagGuid + })); + }); + }, + renderLineageLayoutView: function(tagGuid) { + var that = this; + require(['views/graph/LineageLayoutView'], function(LineageLayoutView) { + that.RLineageLayoutView.show(new LineageLayoutView({ + globalVent: that.globalVent, + assetName: that.name, + guid: tagGuid + })); + }); + }, + renderSchemaLayoutView: function() { + var that = this; + require(['views/schema/SchemaLayoutView'], function(SchemaLayoutView) { + that.RSchemaTableLayoutView.show(new SchemaLayoutView({ + globalVent: that.globalVent, + name: that.name, + vent: that.vent + })); + }); + }, + getTagTableColumns: function() { + var that = this; + return this.collection.constructor.getTableCols({ + tag: { + label: "Tag", + cell: "html", + editable: false, + sortable: false, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var modelObject = model.toJSON(); + if (modelObject.$typeName$ && modelObject.instanceInfo) { + return '<a href="#!/dashboard/detailPage/' + modelObject.instanceInfo.guid + '">' + modelObject.instanceInfo.typeName + '</a>'; + } else if (!modelObject.$typeName$) { + return '<a href="#!/dashboard/detailPage/' + modelObject.guid + '">' + modelObject.typeName + '</a>'; + } + } + }) + }, + attributes: { + label: "Attributes", + cell: "html", + editable: false, + sortable: false, + formatter: _.extend({}, Backgrid.CellFormatter.prototype, { + fromRaw: function(rawValue, model) { + var modelObject = model.toJSON(); + if (modelObject.$typeName$ && modelObject.instanceInfo) { + var guid = model.toJSON().instanceInfo.guid; + model.getEntity(guid, { + beforeSend: function() {}, + success: function(data) { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.values.name); + }, + error: function(error, data, status) {}, + complete: function() {} + }); + return '<a href="#!/dashboard/detailPage/' + guid + '" data-id="' + guid + '"></a>'; + } else if (!modelObject.$typeName$) { + var guid = model.toJSON().guid; + model.getEntity(guid, { + beforeSend: function() {}, + success: function(data) { + return that.$('td a[data-id="' + guid + '"]').html(data.definition.values.name); + }, + error: function(error, data, status) {}, + complete: function() {} + }); + return '<a href="#!/dashboard/detailPage/' + guid + '" data-id="' + guid + '"></a>'; + } + } + }) + } + }, this.collection); + } + }); + return DetailPageLayoutView; +}); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js b/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.js new file mode 100644 index 0000000..609840e --- /dev/null +++ b/dashboardv2/public/js/views/entity/EntityDetailTableLayoutView.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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/entity/EntityDetailTableLayoutView_tmpl', +], function(require, Backbone, EntityDetailTableLayoutView_tmpl) { + 'use strict'; + + var EntityDetailTableLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends EntityDetailTableLayoutView */ + { + _viewName: 'EntityDetailTableLayoutView', + + template: EntityDetailTableLayoutView_tmpl, + + /** Layout sub regions */ + regions: {}, + + /** ui selector cache */ + ui: { + detailValue: "[data-id='detailValue']", + }, + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + /** + * intialize a new EntityDetailTableLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'globalVent', 'collection')); + this.collectionObject = this.collection.toJSON(); + this.entityModel = new this.collection.model(); + }, + bindEvents: function() {}, + onRender: function() { + this.entityTableGenerate(); + }, + entityTableGenerate: function() { + var that = this, + table = "", + valueObject = this.collectionObject[0].values; + _.keys(valueObject).map(function(key) { + /* if (key == 'columns') + return;*/ + var keyValue = valueObject[key]; + if (_.isArray(keyValue)) { + var subLink = ""; + for (var i = 0; i < keyValue.length; i++) { + var inputOutputField = keyValue[i]; + if (_.isObject(inputOutputField.id)) { + id = inputOutputField.id.id; + } else { + id = inputOutputField.id; + } + that.fetchInputOutputValue(id); + //var coma = (i = 0) ? ('') : (','); + subLink += '<div data-id="' + id + '"></div>'; + } + table += '<tr><td>' + key + '</td><td>' + subLink + '</td></tr>'; + } else if (_.isObject(keyValue)) { + var id = ""; + if (_.isObject(keyValue.id)) { + id = keyValue.id.id; + } else { + id = keyValue.id; + } + that.fetchInputOutputValue(id); + table += '<tr><td>' + key + '</td><td><div data-id="' + id + '"></div></td></tr>'; + } else { + if (key == "createTime" || key == "lastAccessTime" || key == "retention") { + table += '<tr><td>' + key + '</td><td>' + new Date(valueObject[key]) + '</td></tr>'; + } else { + table += '<tr><td>' + key + '</td><td>' + valueObject[key] + '</td></tr>'; + } + + } + }); + that.ui.detailValue.append(table); + }, + fetchInputOutputValue: function(id) { + var that = this; + this.entityModel.getEntity(id, { + beforeSend: function() {}, + success: function(data) { + var value = ""; + if (data.definition.values.name) { + value = data.definition.values.name; + } else { + value = data.GUID; + } + + that.$('td div[data-id="' + data.GUID + '"]').html('<a href="#!/dashboard/detailPage/' + data.GUID + '">' + value + '</a>'); + }, + error: function(error, data, status) {}, + complete: function() {} + }); + } + }); + return EntityDetailTableLayoutView; +}); http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a5f8c5aa/dashboardv2/public/js/views/graph/LineageLayoutView.js ---------------------------------------------------------------------- diff --git a/dashboardv2/public/js/views/graph/LineageLayoutView.js b/dashboardv2/public/js/views/graph/LineageLayoutView.js new file mode 100644 index 0000000..973d091 --- /dev/null +++ b/dashboardv2/public/js/views/graph/LineageLayoutView.js @@ -0,0 +1,285 @@ +/** + * 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. + */ + +define(['require', + 'backbone', + 'hbs!tmpl/graph/LineageLayoutView_tmpl', + 'collection/VLineageList', + 'models/VEntity', + 'dagreD3', + 'd3-tip' +], function(require, Backbone, LineageLayoutViewtmpl, VLineageList, VEntity, dagreD3, d3Tip) { + 'use strict'; + + var LineageLayoutView = Backbone.Marionette.LayoutView.extend( + /** @lends LineageLayoutView */ + { + _viewName: 'LineageLayoutView', + + template: LineageLayoutViewtmpl, + + /** Layout sub regions */ + regions: {}, + + /** ui selector cache */ + ui: { + graph: ".graph" + }, + + /** ui events hash */ + events: function() { + var events = {}; + return events; + }, + + /** + * intialize a new LineageLayoutView Layout + * @constructs + */ + initialize: function(options) { + _.extend(this, _.pick(options, 'globalVent', 'assetName', 'guid')); + this.inputCollection = new VLineageList(); + this.outputCollection = new VLineageList(); + this.entityModel = new VEntity(); + this.inputCollection.url = "/api/atlas/lineage/hive/table/" + this.assetName + "/inputs/graph"; + this.outputCollection.url = "/api/atlas/lineage/hive/table/" + this.assetName + "/outputs/graph"; + this.bindEvents(); + this.fetchGraphData(); + this.data = {}; + this.fetchList = 0 + }, + bindEvents: function() { + this.listenTo(this.inputCollection, 'reset', function() { + $('.lineageLayout').show(); + this.generateData(this.inputCollection, 'input'); + this.outputCollection.fetch({ reset: true }); + }, this); + this.listenTo(this.outputCollection, 'reset', function() { + $('.lineageLayout').show(); + this.generateData(this.outputCollection, 'output'); + this.outputState = true; + }, this); + this.listenTo(this.outputCollection, 'error', function() { + this.$('.fontLoader').hide(); + $('.lineageLayout').hide(); + + }, this); + this.listenTo(this.inputCollection, 'error', function() { + this.$('.fontLoader').hide(); + this.$('.lineageLayout').hide(); + }, this); + }, + onRender: function() { + this.$('.fontLoader').show(); + this.g = new dagreD3.graphlib.Graph() + .setGraph({}) + .setDefaultEdgeLabel(function() { + return {}; + }); + }, + fetchGraphData: function() { + this.inputCollection.fetch({ reset: true }); + }, + generateData: function(collection, type) { + var that = this; + + function addValueInObject(data) { + var obj = {}; + if (data && data.definition && data.definition.values) { + var values = data.definition.values; + obj['label'] = values.name + obj['id'] = data.GUID; + if (values.queryText) { + obj['queryText'] = values.queryText; + } + } else { + obj['label'] = vertices[val].values.name; + } + obj['class'] = "type-TOP"; + that.g.setNode(data.GUID, obj); + --that.fetchList; + if (that.fetchList <= 0) { + if (that.edgesAndvertices) { + that.createGraph(that.edgesAndvertices, that.startingPoint); + } else if (this.outputState && !that.edgesAndvertices) { + that.$('svg').height('100'); + that.$('svg').html('<text x="' + (that.$('svg').width() - 150) / 2 + '" y="' + that.$('svg').height() / 2 + '" fill="black">No lineage data found</text>'); + that.$('.fontLoader').hide(); + } + } + } + + function fetchLoadProcess(id) { + ++that.fetchList + that.entityModel.getEntity(id, { + beforeSend: function() {}, + success: function(data) { + addValueInObject(data); + }, + error: function(error, data, status) {}, + complete: function() {} + }); + } + + function makeNode(c) { + + var edges = c.edges, + vertices = c.vertices, + allKeys = []; + _.each(c.edges, function(val, key, obj) { + allKeys.push(key) + _.each(val, function(val1, key1, obj1) { + allKeys.push(val1) + }); + }); + var uniquNode = _.uniq(allKeys); + _.each(uniquNode, function(val, key) { + var obj = {} + if (vertices[val] && vertices[val].values) { + obj['label'] = vertices[val].values.name; + obj['id'] = val; + obj['class'] = "type-TOP"; + obj['typeName'] = vertices[val].values.vertexId.values.typeName; + that.g.setNode(val, obj); + } else { + fetchLoadProcess(val); + } + + }); + } + _.each(collection.models, function(values) { + var valuObj = values.get('values'); + that.startingPoint = []; + if (!_.isEmpty(valuObj.edges)) { + if (type == "input") { + that.edgesAndvertices = { + edges: {}, + vertices: valuObj.vertices + } + _.each(valuObj.edges, function(val, key, obj) { + _.each(val, function(val1, key1, obj1) { + var chiledParent = {}; + if (!obj[val1]) { + that.startingPoint.push(val1) + } + that.edgesAndvertices.edges[val1] = [key]; + }); + }); + } else { + that.edgesAndvertices = valuObj; + that.startingPoint = [that.guid]; + } + makeNode(that.edgesAndvertices); + } else { + if (type == 'output') { + that.outputState = true; + } + } + }); + if (this.fetchList <= 0) { + if (this.edgesAndvertices) { + this.createGraph(that.edgesAndvertices, this.startingPoint); + } else if (this.outputState && !this.edgesAndvertices) { + this.$('.fontLoader').hide(); + that.$('svg').height('100'); + that.$('svg').html('<text x="' + (that.$('svg').width() - 150) / 2 + '" y="' + that.$('svg').height() / 2 + '" fill="black">No lineage data found</text>'); + } + } + }, + createGraph: function(edgesAndvertices, startingPoint) { + var that = this; + + this.g.nodes().forEach(function(v) { + var node = that.g.node(v); + // Round the corners of the nodes + node.rx = node.ry = 5; + }); + + // Set up edges, no special attributes. + // For input + var lastVal = ""; + _.each(startingPoint, function(val, key, obj) { + that.g.setEdge(val, edgesAndvertices.edges[val][0]); + lastVal = edgesAndvertices.edges[val][0]; + }); + createRemaningEdge(edgesAndvertices.edges, lastVal); + + function createRemaningEdge(obj, starting) { + if (obj[starting] && obj[starting].length) { + that.g.setEdge(starting, obj[starting]); + createRemaningEdge(obj, obj[starting]); + } + } + + if (this.outputState) { + // Create the renderer + var render = new dagreD3.render(); + // Set up an SVG group so that we can translate the final graph. + var svg = d3.select(this.$("svg")[0]), + svgGroup = svg.append("g"); + var zoom = d3.behavior.zoom().on("zoom", function() { + svgGroup.attr("transform", "translate(" + d3.event.translate + ")" + + "scale(" + d3.event.scale + ")"); + }); + var tooltip = d3Tip() + .attr('class', 'd3-tip') + .html(function(d) { + var value = that.g.node(d) + var htmlStr = "<h5>Name: <span style='color:#359f89'>" + value.label + "</span></h5> "; + if (value.queryText) { + htmlStr += "<h5>Query: <span style='color:#359f89'>" + value.queryText + "</span></h5> " + } + return htmlStr; + }); + svg.call(zoom) + .call(tooltip); + + + this.$('.fontLoader').hide(); + // Run the renderer. This is what draws the final graph. + this.g.graph().rankDir = 'LR'; + //render(d3.select(this.$("svg g")[0]), this.g); + render(svgGroup, this.g); + svg.on("dblclick.zoom", function() { + return null; + }) + svgGroup.selectAll("g.nodes g.node") + .on('mouseover', function(d) { + tooltip.show(d); + }) + .on('dblclick', function(d) { + tooltip.hide(d); + Backbone.history.navigate("#!/dashboard/detailPage/" + d, { trigger: true }); + }) + .on('mouseout', function(d) { + tooltip.hide(d); + }); + // Center the graph + var initialScale = 1.5; + zoom.translate([(this.$('svg').width() - this.g.graph().width * initialScale) / 2, (this.$('svg').height() - this.g.graph().height * initialScale) / 2]) + .scale(initialScale) + .event(svg); + //svg.attr('height', this.g.graph().height * initialScale + 40); + + } + + } + }); + return LineageLayoutView; + +});
