Sanitise JS.
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/99c01191 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/99c01191 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/99c01191 Branch: refs/heads/master Commit: 99c01191fe1aa078838e1e71da5274be6f332d9c Parents: e948978 Author: Alasdair Hodge <[email protected]> Authored: Tue Sep 29 18:05:39 2015 +0100 Committer: Alasdair Hodge <[email protected]> Committed: Wed Oct 7 14:33:57 2015 +0100 ---------------------------------------------------------------------- .../src/main/webapp/assets/js/model/app-tree.js | 62 +++--- .../webapp/assets/js/view/application-tree.js | 213 +++++++++---------- .../webapp/assets/js/view/entity-summary.js | 2 +- .../main/webapp/assets/tpl/apps/summary.html | 6 +- .../main/webapp/assets/tpl/apps/tree-item.html | 14 +- 5 files changed, 153 insertions(+), 144 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/99c01191/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js b/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js index 2cf6c54..67efd91 100644 --- a/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js +++ b/usage/jsgui/src/main/webapp/assets/js/model/app-tree.js @@ -20,27 +20,31 @@ define([ "underscore", "backbone" ], function (_, Backbone) { - var AppTree = {} + var AppTree = {}; AppTree.Model = Backbone.Model.extend({ - defaults:function () { + defaults: function() { return { - id:"", - name:"", - type:"", - iconUrl:"", - serviceUp:"", - serviceState:"", - applicationId:"", - parentId:"", - children:[] - } + id: "", + name: "", + type: "", + iconUrl: "", + serviceUp: "", + serviceState: "", + applicationId: "", + parentId: "", + children: [], + members: [] + }; + }, + getDisplayName: function() { + return this.get("name"); }, - getDisplayName:function () { - return this.get("name") + hasChildren: function() { + return this.get("children").length > 0; }, - hasChildren:function () { - return this.get("children").length > 0 + hasMembers: function() { + return this.get("members").length > 0; } }) @@ -48,7 +52,7 @@ define([ model: AppTree.Model, includedEntities: [], - getApplications: function () { + getApplications: function() { var entities = []; _.each(this.models, function(it) { if (it.get('id') == it.get('applicationId')) @@ -56,7 +60,8 @@ define([ }); return entities; }, - getNonApplications: function () { + + getNonApplications: function() { var entities = []; _.each(this.models, function(it) { if (it.get('id') != it.get('applicationId')) @@ -64,20 +69,22 @@ define([ }); return entities; }, - includeEntities: function (entities) { + + includeEntities: function(entities) { // accepts id as string or object with id field var oldLength = this.includedEntities.length; - var newList = [].concat(this.includedEntities) + var newList = [].concat(this.includedEntities); for (entityId in entities) { - var entity = entities[entityId] + var entity = entities[entityId]; if (typeof entity === 'string') - newList.push(entity) + newList.push(entity); else - newList.push(entity.id) + newList.push(entity.id); } - this.includedEntities = _.uniq(newList) + this.includedEntities = _.uniq(newList); return (this.includedEntities.length > oldLength); }, + /** * Depth-first search of entries in this.models for the first entity whose ID matches the * function's argument. Includes each entity's children. @@ -88,7 +95,7 @@ define([ for (var i = 0, l = this.models.length; i < l; i++) { var model = this.models[i]; if (model.get("id") === id) { - return model.getDisplayName() + return model.getDisplayName(); } else { // slice(0) makes a shallow clone of the array var queue = model.get("children").slice(0); @@ -109,6 +116,7 @@ define([ // a string they'll get "stringundefined", whereas this way they'll just get "string". return ""; }, + url: function() { if (this.includedEntities.length) { var ids = this.includedEntities.join(","); @@ -116,7 +124,7 @@ define([ } else return "/v1/applications/fetch"; } - }) + }); - return AppTree + return AppTree; }) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/99c01191/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js index 6c1c972..8a5175a 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/application-tree.js @@ -28,31 +28,31 @@ define([ AppTree, EntityDetailsView, EntitySummary, Application, TreeItemHtml, TreeEmptyHtml, EntityDetailsEmptyHtml, EntityNotFoundHtml) { - var treeViewTemplate = _.template(TreeItemHtml), - notFoundTemplate = _.template(EntityNotFoundHtml); + var treeViewTemplate = _.template(TreeItemHtml); + var notFoundTemplate = _.template(EntityNotFoundHtml); + - var ApplicationTreeView = Backbone.View.extend({ template: treeViewTemplate, hoverTimer: null, - events:{ + events: { 'click span.entity_tree_node .tree-change':'treeChange', 'click span.entity_tree_node':'displayEntity' }, - - initialize:function () { this.collection.on('all', this.modelEvent, this) this.collection.on('change', this.modelChange, this) this.collection.on('remove', this.modelRemove, this) this.collection.on('add', this.modelAdd, this) this.collection.on('reset', this.renderFull, this) + + initialize: function() { _.bindAll(this); }, - beforeClose:function () { - this.collection.off("reset", this.renderFull) - if (this.detailsView) this.detailsView.close() + beforeClose: function() { + this.collection.off("reset", this.renderFull); + if (this.detailsView) this.detailsView.close(); }, modelChange: function (child) { @@ -64,7 +64,8 @@ define([ modelRemove: function (child) { this.removeNode(child.id) }, - modelEvent: function (eventName, event, x) { + + modelEvent: function(eventName, event, x) { if (/^change/i.test(eventName) || eventName == "remove" || eventName == "add" || eventName == "reset" || // above are handled; below is no-op @@ -76,12 +77,12 @@ define([ // ignore; app-explorer should clear the view return; } - + // don't think we get other events, but just in case: - log("unhandled model event") - log(eventName) - log(event) - log(x) + log("unhandled model event"); + log(eventName); + log(event); + log(x); }, removeNode: function(id) { @@ -89,7 +90,6 @@ define([ // collection seems sometimes to have children nodes; // not sure why, but that's okay for now if (this.collection.getApplications().length==0) -// if (this.collection.isEmpty() || $('lozenge-app-tree-wrapper').length==0) this.renderFull(); }, @@ -111,7 +111,7 @@ define([ parentId = node.closest("entity_tree_node_wrapper").data('parentId'); if (!isApp && !parentId) { log("no parentId yet available for "+id+"; skipping;") - return false; + return false; } var statusIconUrl = nModel @@ -218,16 +218,16 @@ define([ $(node).html($newNode) this.addEventsToNode($(node)) } - return true + return true; }, - - renderFull:function () { - var that = this - this.$el.empty() + + renderFull: function() { + var that = this; + this.$el.empty(); // Display tree and highlight the selected entity. - if (this.collection.getApplications().length==0) { - that.$el.append(_.template(TreeEmptyHtml)) + if (this.collection.getApplications().length == 0) { + that.$el.append(_.template(TreeEmptyHtml)); } else { _.each(this.collection.getApplications(), function(appId) { that.updateNode(appId, null, true); @@ -237,63 +237,59 @@ define([ that.updateNode(id); }); } - + this.highlightEntity(); // Render the details for the selected entity. if (this.detailsView) { - this.detailsView.render() + this.detailsView.render(); } else { // if nothing selected, select the first application if (!this.collection.isEmpty()) { var app0 = this.collection.first().id; _.defer(function () { if (!that.selectedEntityId) - that.displayEntityId(app0, app0) + that.displayEntityId(app0, app0); }); } else { _.defer(function() { $("div#details").html(_.template(EntityDetailsEmptyHtml)); - $("div#details").find("a[href='#summary']").tab('show') - }) + $("div#details").find("a[href='#summary']").tab('show'); + }); } } - return this + return this; }, addEventsToNode: function($node) { var that = this; - - // prevent default click-handling - // don't think this is needed, 18 Sep 2013; but leaving for a few weeks just in case -// $('a', $node).click(function(e) { e.preventDefault(); }) - + // show the "light-popup" (expand / expand all / etc) menu // if user hovers for 500ms. surprising there is no option for this (hover delay). // also, annoyingly, clicks around the time the animation starts don't seem to get handled // if the click is in an overlapping reason; this is why we position relative top: 12px in css $('.light-popup', $node).parent().parent().hover( - function (parent) { + function(parent) { that.cancelHoverTimer(); that.hoverTimer = setTimeout(function() { var menu = $(parent.currentTarget).find('.light-popup') menu.show() }, 500); }, - function (parent) { + function(parent) { that.cancelHoverTimer(); var menu = $(parent.currentTarget).find('.light-popup') menu.hide() // hide all others too $('.light-popup').hide() - }) + }); }, + cancelHoverTimer: function() { - var that = this; - if (that.hoverTimer!=null) { - clearTimeout(that.hoverTimer); - that.hoverTimer = null; - } + if (this.hoverTimer != null) { + clearTimeout(this.hoverTimer); + this.hoverTimer = null; + } }, displayEntity: function(event) { @@ -301,29 +297,30 @@ define([ // trying to open in a new tab, do not act on it here! return; event.preventDefault(); - var nodeSpan = $(event.currentTarget) - var nodeA = $(event.currentTarget).children('a').first() - var entityId = nodeSpan.parent().attr("id"), - href = nodeA.attr('href'), - tab = (this.detailsView) + var $nodeSpan = $(event.currentTarget); + var $nodeA = $nodeSpan.children('a').first(); + var entityId = $nodeSpan.closest('.tree-box').data("entityId"); + var href = $nodeA.attr('href'); + var tab = (this.detailsView) ? this.detailsView.$el.find(".tab-pane.active").attr("id") : undefined; if (href) { if (tab) { - href = href+"/"+tab - stateId = entityId+"/"+tab - this.preselectTab(tab) + href = href+"/"+tab; + stateId = entityId+"/"+tab; + this.preselectTab(tab); } Backbone.history.navigate(href); - this.displayEntityId(entityId, nodeSpan.data("app-id")); + this.displayEntityId(entityId, $nodeSpan.data("app-id")); } else { - log("no a.href in clicked target") - log(nodeSpan) + log("no a.href in clicked target"); + log($nodeSpan); } }, - displayEntityId:function (id, appName, afterLoad) { + + displayEntityId: function (id, appName, afterLoad) { var that = this; - this.highlightEntity(id) + this.highlightEntity(id); var entityLoadFailed = function() { return that.displayEntityNotFound(id); @@ -348,17 +345,17 @@ define([ return; } } - - var app = new Application.Model(), - entitySummary = new EntitySummary.Model; - app.url = "/v1/applications/" + appName + var app = new Application.Model(); + var entitySummary = new EntitySummary.Model; + + app.url = "/v1/applications/" + appName; entitySummary.url = "/v1/applications/" + appName + "/entities/" + id; // in case the server response time is low, fade out while it refreshes // (since we can't show updated details until we've retrieved app + entity details) - ViewUtils.fadeToIndicateInitialLoad($("div#details")) - + ViewUtils.fadeToIndicateInitialLoad($("div#details")); + $.when(app.fetch(), entitySummary.fetch()) .done(function() { that.showDetails(app, entitySummary); @@ -375,42 +372,44 @@ define([ var $target = $(event.currentTarget); var $treeBox = $target.closest('.tree-box'); if ($target.hasClass('tr-expand')) { - this.showChildrenOf($treeBox, false) + this.showChildrenOf($treeBox, false); } else if ($target.hasClass('tr-expand-all')) { - this.showChildrenOf($treeBox, true) + this.showChildrenOf($treeBox, true); } else if ($target.hasClass('tr-collapse')) { - this.hideChildrenOf($treeBox, false) + this.hideChildrenOf($treeBox, false); } else if ($target.hasClass('tr-collapse-all')) { - this.hideChildrenOf($treeBox, true) + this.hideChildrenOf($treeBox, true); } else { // default - toggle if ($treeBox.children('.node-children').is(':visible')) { - this.hideChildrenOf($treeBox, false) + this.hideChildrenOf($treeBox, false); } else { - this.showChildrenOf($treeBox, false) + this.showChildrenOf($treeBox, false); } } // hide the popup menu this.cancelHoverTimer(); - $('.light-popup').hide() + $('.light-popup').hide(); // don't let other events interfere return false }, + hideChildrenOf: function($treeBox, recurse) { var that = this; if (recurse) { $treeBox.children('.node-children').children().each(function (index, childBox) { that.hideChildrenOf($(childBox), recurse) - }) + }); } - $treeBox.children('.node-children').slideUp(300) - $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-down').addClass('icon-chevron-right') + $treeBox.children('.node-children').slideUp(300); + $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-down').addClass('icon-chevron-right'); }, + showChildrenOf: function($treeBox, recurse) { var that = this; var idToExpand = $treeBox.children('.entity_tree_node_wrapper').attr('id'); var model = this.collection.get(idToExpand); - if (model==null) { + if (model == null) { // not yet loaded; parallel thread should load return; } @@ -428,43 +427,43 @@ define([ if (recurse) { $treeBox.children('.node-children').children().each(function (index, childBox) { _.defer( function() { that.showChildrenOf($(childBox), recurse) } ); - }) - } + }); + } } }) } - $treeBox.children('.node-children').slideDown(300) - $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down') + $treeBox.children('.node-children').slideDown(300); + $treeBox.children('.entity_tree_node_wrapper').find('.tree-node-state').removeClass('icon-chevron-right').addClass('icon-chevron-down'); if (recurse) { $treeBox.children('.node-children').children().each(function (index, childBox) { - that.showChildrenOf($(childBox), recurse) + that.showChildrenOf($(childBox), recurse); }) } }, - + /** * Causes the tab with the given name to be selected automatically when * the view is next rendered. */ preselectTab: function(tab, tabDetails) { - this.currentTab = tab + this.currentTab = tab; this.currentTabDetails = tabDetails; }, showDetails: function(app, entitySummary) { - var self = this; - ViewUtils.cancelFadeOnceLoaded($("div#details")) - - var whichTab = this.currentTab + var that = this; + ViewUtils.cancelFadeOnceLoaded($("div#details")); + + var whichTab = this.currentTab; if (whichTab === undefined) { whichTab = "summary"; if (this.detailsView) { whichTab = this.detailsView.$el.find(".tab-pane.active").attr("id"); - this.detailsView.close() + this.detailsView.close(); } } if (this.detailsView) { - this.detailsView.close() + this.detailsView.close(); } this.detailsView = new EntityDetailsView({ model:entitySummary, @@ -473,47 +472,47 @@ define([ preselectTab:whichTab, preselectTabDetails:this.currentTabDetails, }); - + this.detailsView.on("entity.expunged", function() { - self.preselectTab("summary"); - var id = self.selectedEntityId; - var model = self.collection.get(id); + that.preselectTab("summary"); + var id = that.selectedEntityId; + var model = that.collection.get(id); if (model && model.get("parentId")) { - self.displayEntityId(model.get("parentId")); - } else if (self.collection) { - self.displayEntityId(self.collection.first().id); + that.displayEntityId(model.get("parentId")); + } else if (that.collection) { + that.displayEntityId(that.collection.first().id); } else if (id) { - self.displayEntityNotFound(id); + that.displayEntityNotFound(id); } else { - self.displayEntityNotFound("?"); + that.displayEntityNotFound("?"); } - self.collection.fetch(); + that.collection.fetch(); }); this.detailsView.render( $("div#details") ); }, - highlightEntity:function (id) { - if (id) this.selectedEntityId = id - else id = this.selectedEntityId - - $(".entity_tree_node_wrapper").removeClass("active") + highlightEntity: function(id) { + if (id) this.selectedEntityId = id; + else id = this.selectedEntityId; + + $(".entity_tree_node_wrapper").removeClass("active"); if (id) { var $selectedNode = $(".entity_tree_node_wrapper#"+id); // make this node active - $selectedNode.addClass("active") - + $selectedNode.addClass("active"); + // open the parent nodes if needed var $nodeToOpenInParent = $selectedNode; while ($nodeToOpenInParent.length && !$nodeToOpenInParent.is(':visible')) { $nodeToOpenInParent = $nodeToOpenInParent.closest('.node-children').closest('.tree-box'); - this.showChildrenOf($nodeToOpenInParent) + this.showChildrenOf($nodeToOpenInParent); } - + // if we want to auto-expand the children of the selected node: // this.showChildrenOf($selectedNode.closest('.tree-box'), false) } } - }) + }); - return ApplicationTreeView + return ApplicationTreeView; }) http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/99c01191/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js index c102a67..51a7c33 100644 --- a/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js +++ b/usage/jsgui/src/main/webapp/assets/js/view/entity-summary.js @@ -43,7 +43,7 @@ define([ this.$("div.catalogItemId").show(); else this.$("div.catalogItemId").hide(); - + this.options.tabView.configView = new EntityConfigView({ model:this.options.model, tabView:this.options.tabView, http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/99c01191/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html index 0965068..ed90c75 100644 --- a/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html +++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/summary.html @@ -73,9 +73,9 @@ under the License. <div class="additional-info-on-problem hide" style="margin-top: 12px;"> </div> - - <div id="status-icon" style="display: inline-block; padding-top: 12px; padding-bottom: 18px; padding-right: 8px; position: absolute; right: 0; top: 0;"></div> - + + <div id="status-icon" style="display: inline-block; padding-top: 12px; padding-bottom: 18px; padding-right: 8px; position: absolute; right: 0; top: 0;"></div> + </div> </div> </div> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/99c01191/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html ---------------------------------------------------------------------- diff --git a/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html b/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html index 3109230..88e671a 100644 --- a/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html +++ b/usage/jsgui/src/main/webapp/assets/tpl/apps/tree-item.html @@ -21,32 +21,33 @@ under the License. <% var isLoaded = (model ? true : false); var isApp = (parentId ? false : true); - + if (!isLoaded) { %> <i>Loading... (<%= id %>)</i> <% } else { var hasChildren = model.hasChildren(); var iconUrl = model.get('iconUrl'); - + var entityIconSize = isApp ? 40 : 30; var statusIconSize = isApp ? 24 : 16; - + var chevronLeft = (isApp ? 5.5 : 1.5); var minHeight = hasChildren && statusIconUrl ? entityIconSize : 24; var statusColumnWidth = hasChildren || statusIconUrl || (!isApp && !iconUrl /* for children, insert space so things line up */) ? statusIconSize : 0; %> - + <span class="entity_tree_node name entity" id="span-<%= id %>" data-entity-type="<%= model.get('type') %>" data-parent-id="<%= parentId %>" data-app-id="<%= model.get('applicationId') %>"> <a href="#v1/applications/<%= model.get('applicationId') %>/entities/<%= id %>"> - + <div style="min-width: <%= statusColumnWidth + (iconUrl ? entityIconSize : 6)%>px; min-height: <%= minHeight %>px; max-height: 40px; display: inline-block; margin-right: 4px; vertical-align: middle;"> <% if (statusIconUrl) { %> <div style="position: absolute; left: 0px; margin: auto; top: <%= isApp && hasChildren ? 3 : 2 %>px;<% if (!hasChildren) { %> bottom: 0px;<% } %>"> <img src="<%= statusIconUrl %>" style="max-width: <%= statusIconSize %>px; max-height: <%= statusIconSize %>px; margin: auto; position: absolute; top: -1px;<% if (!hasChildren) { %> bottom: 0px;<% } %>"> </div> <% } %> + <% if (hasChildren) { %> <div style="position: absolute; left: <%= chevronLeft %>px; margin: auto; <%= statusIconUrl ? "bottom: -1px;" : isApp ? "top: 6px;" : "top: 6px;" %>"> <div class="toggler-icon icon-chevron-right tree-node-state tree-change"> @@ -62,13 +63,14 @@ under the License. </div> </div> <% } %> + <% if (iconUrl) { %> <img src="<%= iconUrl %>" style="max-width: <%= entityIconSize %>px; max-height: <%= entityIconSize %>px; position: absolute; left: <%= statusColumnWidth %>px; top: 0; bottom: 0; margin: auto;"> <% } %> </div> <span style="max-height: 18px; padding-right: 6px; position: relative; margin: auto; top: 2px; bottom: 0;"><%= model.get('name') %></span> - + </a> </span>
