diff --git a/LICENSE.txt b/LICENSE.txt index 0d1d6da..fa6051c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -264,3 +264,15 @@ https://github.com/moment/moment/blob/develop/LICENSE rome.js is a customizable date (and time) picker. It is Copyright © 2014 Nicolas Bevacqua https://github.com/bevacqua/rome + +Backbone.Wreqr is a message passing system for Backbone.js. +It is (c) 2012 Derick Bailey, Muted Solutions, LLC and MIT licensed: +https://github.com/marionettejs/backbone.wreqr/blob/v1.3.1/LICENSE.md + +Backbone.Babysitter manages child views for Backbone.js. +It is (c) 2013 Derick Bailey, Muted Solutions, LLC and MIT licensed: +https://github.com/marionettejs/backbone.babysitter/blob/v0.1.6/LICENSE.md + +Backbone.Marionette is a composite application library for Backbone.js. +It is MIT licensed: +https://github.com/marionettejs/backbone.marionette/blob/v2.4.1/license.txt diff --git a/htrace-core/src/web/app/app.js b/htrace-core/src/web/app/app.js index aafad1f..0bc7100 100644 --- a/htrace-core/src/web/app/app.js +++ b/htrace-core/src/web/app/app.js @@ -16,5 +16,5 @@ * specific language governing permissions and limitations * under the License. */ - -window.App = {}; \ No newline at end of file + +window.app = new Marionette.Application(); diff --git a/htrace-core/src/web/app/models/span.js b/htrace-core/src/web/app/models/span.js index 8f6ef12..306533d 100644 --- a/htrace-core/src/web/app/models/span.js +++ b/htrace-core/src/web/app/models/span.js @@ -18,7 +18,7 @@ */ // Span model -App.Span = Backbone.Model.extend({ +app.Span = Backbone.Model.extend({ "defaults": { "spanId": null, "traceId": null, @@ -53,8 +53,8 @@ App.Span = Backbone.Model.extend({ } }); -App.Spans = Backbone.PageableCollection.extend({ - model: App.Span, +app.Spans = Backbone.PageableCollection.extend({ + model: app.Span, mode: "infinite", url: "/query", state: { diff --git a/htrace-core/src/web/app/setup.js b/htrace-core/src/web/app/setup.js index 106d959..ddeec5e 100644 --- a/htrace-core/src/web/app/setup.js +++ b/htrace-core/src/web/app/setup.js @@ -17,69 +17,81 @@ * under the License. */ -var Router = Backbone.Router.extend({ +var BaseView = Backbone.Marionette.LayoutView.extend({ + "el": "body", + "regions": { + "header": "#header", + "app": "#app" + } +}); - routes: { - "": "search", - "spans/:span": "span" +var Router = Backbone.Marionette.AppRouter.extend({ + "routes": { + "": "init", + "!/search(/:query)": "search", + "!/spans/:id": "span" }, - initialize: function() { - this.spansCollection = new App.Spans(); + "initialize": function() { + // Collection + this.spansCollection = new app.Spans(); this.spansCollection.fetch(); - - this.spanViews = {}; - - this.listSpansView = new App.ListSpansView({ - "collection": this.spansCollection - }).render(); - $("#list *[role='main']").append(this.listSpansView.$el); - - this.searchView = new App.SearchView({ - "collection": this.spansCollection, - "el": $("#list").find("[role='form']") - }).render(); }, - search: function() { - $("*[role='application']").css('display', 'none'); - $("#list").show(); + "init": function() { + Backbone.history.navigate("!/search", {"trigger": true}); }, - span: function(span) { - var root = $("#span"); - - // Cache views to avoid leaks - if (!(span in this.spanViews)) { - var model = this.spansCollection.findWhere({ - "spanId": span - }); + "search": function(query) { + var top = new app.SearchView(); + app.root.app.show(top); - if (!model) { - urlconf.navigate("/", true); - return; - } - - this.spanViews[span] = new App.SpanView({ - "model": model, - "id": "span-details" - }); - } - - var view = this.spanViews[span]; - - $("*[role='application']").css('display', 'none'); - - view.render(); - root.find("*[role='main']").empty(); - root.find("*[role='main']").append(view.$el); + top.controls.show(new app.SearchControlsView({ + "collection": this.spansCollection + })); + top.main.show(new Backgrid.Grid({ + "collection": this.spansCollection, + "columns": [{ + "name": "spanId", + "label": "ID", + "cell": "string", + "editable": false + }, { + "name": "description", + "label": "Description", + "cell": "string", + "editable": false + }], + "row": Backgrid.Row.extend({ + "events": { + "click": "details" + }, + "details": function() { + Backbone.history.navigate("!/spans/" + this.model.get("spanId"), {"trigger": true}); + } + }) + })); + top.pagination.show(new Backgrid.Extension.Paginator({ + collection: this.spansCollection, + })); + }, - root.show(); + "span": function(id) { + var top = new app.DetailsView(); + app.root.app.show(top); + top.span.show(new app.SpanDetailsView({ + "model": this.spansCollection.findWhere({ + "spanId": id + }) + })); } }); -window.urlconf = new Router(); +app.on("start", function(options) { + app.root = new BaseView(); + app.routes = new Router(); -$(function() { Backbone.history.start(); }); + +app.start(); diff --git a/htrace-core/src/web/app/views/details/details.js b/htrace-core/src/web/app/views/details/details.js new file mode 100644 index 0000000..2a2b9f4 --- /dev/null +++ b/htrace-core/src/web/app/views/details/details.js @@ -0,0 +1,39 @@ +/* + * 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. + */ + +app.DetailsView = Backbone.Marionette.LayoutView.extend({ + "template": "#details-layout-template", + "regions": { + "span": "div[role='complementary']", + "content": "div[role='main']" + } +}); + +app.SpanDetailsView = Backbone.Marionette.ItemView.extend({ + "className": "span", + "template": "#span-details-template", + + "serializeData": function() { + var context = { + "span": this.model.toJSON() + }; + context["span"]["duration"] = this.model.duration(); + return context; + } +}); diff --git a/htrace-core/src/web/app/views/search/field.js b/htrace-core/src/web/app/views/search/field.js index 6dea522..34b0110 100644 --- a/htrace-core/src/web/app/views/search/field.js +++ b/htrace-core/src/web/app/views/search/field.js @@ -17,7 +17,7 @@ * under the License. */ -App.SearchFieldView = Backbone.View.extend({ +app.SearchFieldView = Backbone.View.extend({ 'className': 'search-field', 'template': _.template($("#search-field-template").html()), diff --git a/htrace-core/src/web/app/views/search/search.js b/htrace-core/src/web/app/views/search/search.js index 27bdbf4..9d56bdd 100644 --- a/htrace-core/src/web/app/views/search/search.js +++ b/htrace-core/src/web/app/views/search/search.js @@ -17,16 +17,26 @@ * under the License. */ -App.SearchView = Backbone.View.extend({ +app.SearchView = Backbone.Marionette.LayoutView.extend({ + "template": "#search-layout-template", + "regions": { + "controls": "div[role='form']", + "main": "div[role='main']", + "pagination": "div[role='complementary']" + } +}); + +app.SearchControlsView = Backbone.Marionette.View.extend({ + "template": _.template($("#search-controls-template").html()), "events": { "click a.add-field": "addSearchField", "click button.search": "search", }, - 'initialize': function() { + "initialize": function() { this.predicates = []; this.searchFields = []; - this.searchFields.push(new App.SearchFieldView({ + this.searchFields.push(new app.SearchFieldView({ predicates: this.predicates, manager: this, field: 'description' @@ -34,24 +44,25 @@ App.SearchView = Backbone.View.extend({ this.on('removeSearchField', this.removeSearchField, this); }, - 'render': function() { - this.$('.search-fields').append(this.searchFields[0].render().$el); + "render": function() { + this.$el.html(this.template()); + $(this.$el).find('.search-fields').append(this.searchFields[0].render().$el); return this; }, - 'addSearchField': function(e) { + "addSearchField": function(e) { var target = $(e.target); $('button.field').text(target.text()); - var newSearchField = new App.SearchFieldView({ + var newSearchField = new app.SearchFieldView({ predicates: this.predicates, manager: this, field: target.data('field') }); - this.$('.search-fields').append(newSearchField.render().$el); + $(this.$el).find('.search-fields').append(newSearchField.render().$el); this.searchFields.push(newSearchField); }, - 'removeSearchField': function(cid) { + "removeSearchField": function(cid) { var removedFieldIndex = _(this.searchFields).indexOf(_(this.searchFields).findWhere({cid: cid})); this.searchFields.splice(removedFieldIndex, 1); }, @@ -67,6 +78,7 @@ App.SearchView = Backbone.View.extend({ fetch: false, resetState: true }); + this.collection.fullCollection.reset(); this.collection.setPredicates(this.predicates); this.collection.fetch(); diff --git a/htrace-core/src/web/app/views/span.js b/htrace-core/src/web/app/views/span.js deleted file mode 100644 index cbb694d..0000000 --- a/htrace-core/src/web/app/views/span.js +++ /dev/null @@ -1,96 +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. - */ - -App.SpanView = Backbone.View.extend({ - "tagName": "div", - "className": "span", - "template": _.template($("#span-details-template").html()), - - initialize: function() { - _.bindAll(this, "render"); - this.model.bind('change', this.render); - - this.rendered = false; - }, - - "render": function() { - var context = { - "span": this.model.toJSON() - }; - context["span"]["duration"] = this.model.duration(); - - if (this.rendered) { - $(this.el).empty(); - } - - $(this.el).append(this.template(context)); - this.rendered = true; - - return this; - } -}); - -App.ListSpansView = Backbone.View.extend({ - "tagName": "div", - - "initialize": function() { - this.rendered = false; - - this.listSpansView = new Backgrid.Grid({ - collection: this.collection, - columns: [{ - name: "spanId", - label: "ID", - cell: "string", - editable: false - }, { - name: "description", - label: "Description", - cell: "string", - editable: false - }], - row: Backgrid.Row.extend({ - events: { - "click": "details" - }, - details: function() { - urlconf.navigate("/spans/" + this.model.get("spanId"), true); - } - }) - }); - - this.listSpansPaginator = new Backgrid.Extension.Paginator({ - collection: this.collection, - }); - }, - - "render": function() { - $(this.listSpansView.$el).detach(); - $(this.listSpansPaginator.$el).detach(); - - this.listSpansView.render(); - this.listSpansPaginator.render(); - - $(this.$el).append(this.listSpansView.$el); - $(this.$el).append(this.listSpansPaginator.$el); - - return this; - } -}); - diff --git a/htrace-core/src/web/index.html b/htrace-core/src/web/index.html index 4fbc3ac..c57bc4c 100644 --- a/htrace-core/src/web/index.html +++ b/htrace-core/src/web/index.html @@ -36,7 +36,7 @@
-