You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@htrace.apache.org by cm...@apache.org on 2015/06/09 23:59:23 UTC
[18/18] incubator-htrace git commit: HTRACE-174. Refactor GUI
(cmccabe)
HTRACE-174. Refactor GUI (cmccabe)
Project: http://git-wip-us.apache.org/repos/asf/incubator-htrace/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-htrace/commit/baaa3a52
Tree: http://git-wip-us.apache.org/repos/asf/incubator-htrace/tree/baaa3a52
Diff: http://git-wip-us.apache.org/repos/asf/incubator-htrace/diff/baaa3a52
Branch: refs/heads/master
Commit: baaa3a5287ac7f096fbbdfc5cbb135050f22aca4
Parents: 4051054
Author: Colin P. Mccabe <cm...@apache.org>
Authored: Tue Jun 9 14:57:12 2015 -0700
Committer: Colin P. Mccabe <cm...@apache.org>
Committed: Tue Jun 9 14:57:12 2015 -0700
----------------------------------------------------------------------
LICENSE.txt | 30 +-
README.md | 2 +-
htrace-htraced/BUILDING.txt | 2 +-
.../src/go/src/org/apache/htrace/common/rest.go | 2 +-
htrace-htraced/src/web/app/about_view.js | 33 +
htrace-htraced/src/web/app/app.js | 20 -
htrace-htraced/src/web/app/modal.js | 34 +
htrace-htraced/src/web/app/models/span.js | 144 -
htrace-htraced/src/web/app/predicate.js | 117 +
htrace-htraced/src/web/app/predicate_view.js | 68 +
htrace-htraced/src/web/app/query_results.js | 45 +
htrace-htraced/src/web/app/router.js | 74 +
htrace-htraced/src/web/app/search_results.js | 25 +
.../src/web/app/search_results_view.js | 365 +
htrace-htraced/src/web/app/search_view.js | 197 +
htrace-htraced/src/web/app/server_info.js | 31 +
htrace-htraced/src/web/app/setup.js | 192 -
htrace-htraced/src/web/app/span.js | 69 +
htrace-htraced/src/web/app/span_details_view.js | 39 +
htrace-htraced/src/web/app/span_widget.js | 229 +
htrace-htraced/src/web/app/string.js | 66 +
htrace-htraced/src/web/app/time_cursor.js | 74 +
htrace-htraced/src/web/app/triangle_button.js | 103 +
.../src/web/app/views/details/details.js | 47 -
htrace-htraced/src/web/app/views/graph/graph.js | 262 -
.../src/web/app/views/search/field.js | 124 -
.../src/web/app/views/search/search.js | 105 -
.../src/web/app/views/swimlane/swimlane.js | 178 -
htrace-htraced/src/web/app/widget_manager.js | 66 +
htrace-htraced/src/web/custom.css | 101 +
htrace-htraced/src/web/index.html | 306 +-
htrace-htraced/src/web/lib/backbone-1.1.2.js | 1608 +++
.../src/web/lib/css/backgrid-0.3.5.min.css | 1 -
.../lib/css/backgrid-paginator-0.3.5.min.css | 1 -
htrace-htraced/src/web/lib/css/main.css | 45 -
htrace-htraced/src/web/lib/jquery-2.1.4.js | 9210 +++++++++++++++++
htrace-htraced/src/web/lib/js/backbone-1.1.2.js | 1608 ---
.../web/lib/js/backbone.marionette-2.4.1.min.js | 23 -
.../src/web/lib/js/backbone.paginator-2.0.2.js | 1325 ---
htrace-htraced/src/web/lib/js/backgrid-0.3.5.js | 2883 ------
.../src/web/lib/js/backgrid-paginator-0.3.5.js | 433 -
htrace-htraced/src/web/lib/js/d3-3.5.5.js | 9504 ------------------
.../src/web/lib/js/jquery-2.1.3.min.js | 4 -
.../src/web/lib/js/moment-2.9.0.min.js | 7 -
.../src/web/lib/js/underscore-1.7.0.js | 1416 ---
htrace-htraced/src/web/lib/moment-2.10.3.js | 3111 ++++++
htrace-htraced/src/web/lib/rome-2.1.0/rome.css | 94 -
htrace-htraced/src/web/lib/rome-2.1.0/rome.js | 4796 ---------
.../src/web/lib/rome-2.1.0/rome.min.css | 2 -
.../src/web/lib/rome-2.1.0/rome.min.js | 3 -
.../src/web/lib/rome-2.1.0/rome.standalone.js | 1860 ----
.../web/lib/rome-2.1.0/rome.standalone.min.js | 2 -
htrace-htraced/src/web/lib/underscore-1.7.0.js | 1416 +++
pom.xml | 9 +-
54 files changed, 17253 insertions(+), 25258 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/LICENSE.txt
----------------------------------------------------------------------
diff --git a/LICENSE.txt b/LICENSE.txt
index 19dd8f7..3404aff 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -244,43 +244,15 @@ backbone, is a javascript library, that is Copyright (c) 2010-2014
Jeremy Ashkenas, DocumentCloud. It is MIT licensed:
https://github.com/jashkenas/backbone/blob/master/LICENSE
-backbone-paginator, is a javascript library, that is Copyright (c) 2012-2014
-Jimmy Yuen Ho Wong and contributors. It is MIT licensed:
-https://github.com/backbone-paginator/backbone.paginator/blob/master/LICENSE-MIT
-
-backgrid, is a javascript library, that is Copyright (c) 2013
-Jimmy Yuen Ho Wong. It is MIT licensed:
-https://github.com/wyuenho/backgrid/blob/master/LICENSE-MIT
-
-backgrid-paginator, is a javascript library, that is Copyright (c) 2013
-Jimmy Yuen Ho Wong. It is MIT licensed:
-https://github.com/wyuenho/backgrid-paginator/blob/master/LICENSE-MIT
-
moment.js is a front end time conversion project.
It is (c) 2011-2014 Tim Wood, Iskren Chernev, Moment.js contributors
and shared under the MIT license:
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
-
CMP is an implementation of the MessagePack serialization format in
C. It is licensed under the MIT license:
https://github.com/camgunz/cmp/blob/master/LICENSE
go-codec is an implementation of several serialization and deserialization
codecs in Go. It is licensed under the MIT license:
-https://github.com/ugorji/go/blob/master/LICENSE
\ No newline at end of file
+https://github.com/ugorji/go/blob/master/LICENSE
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index b3b486e..d469e22 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,6 @@
HTrace
======
-HTrace is a tracing framework for use with distributed systems written in java.
+HTrace is a tracing framework for use with distributed systems.
See documentation at src/main/site/markdown/index.md or at http://htrace.incubator.apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/BUILDING.txt
----------------------------------------------------------------------
diff --git a/htrace-htraced/BUILDING.txt b/htrace-htraced/BUILDING.txt
index d54d410..abc0113 100644
--- a/htrace-htraced/BUILDING.txt
+++ b/htrace-htraced/BUILDING.txt
@@ -7,7 +7,7 @@ The htrace go code consists of 4 main parts:
* The "htrace" command-line program which can query the server
This is a simple command-line program which can query the htrace server.
-* The htraced Javascript Web UI (not yet implemented)
+* The htraced Javascript Web UI
* The htrace go client library (not yet implemented)
This is the equivalent of the Java HTrace client library, but written in Go.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go b/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go
index eeb9568..b898ca4 100644
--- a/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go
+++ b/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go
@@ -19,7 +19,7 @@
package common
-// Info returned by /serverInfo
+// Info returned by /server/info
type ServerInfo struct {
// The server release version.
ReleaseVersion string
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/about_view.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/about_view.js b/htrace-htraced/src/web/app/about_view.js
new file mode 100644
index 0000000..7dfe868
--- /dev/null
+++ b/htrace-htraced/src/web/app/about_view.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.
+ */
+
+var htrace = htrace || {};
+htrace.AboutView = Backbone.View.extend({
+ render: function() {
+ this.$el.html(_.template($("#about-view-template").html())
+ ({ model : this.model }));
+ console.log("AboutView#render");
+ return this;
+ },
+
+ close: function() {
+ console.log("AboutView#close")
+ this.undelegateEvents();
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/app.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/app.js b/htrace-htraced/src/web/app/app.js
deleted file mode 100644
index 0bc7100..0000000
--- a/htrace-htraced/src/web/app/app.js
+++ /dev/null
@@ -1,20 +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.
- */
-
-window.app = new Marionette.Application();
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/modal.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/modal.js b/htrace-htraced/src/web/app/modal.js
new file mode 100644
index 0000000..91d55fe
--- /dev/null
+++ b/htrace-htraced/src/web/app/modal.js
@@ -0,0 +1,34 @@
+/*
+ * 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 htrace = htrace || {};
+
+// Show a modal dialog box with a warning message.
+htrace.showModalWarning = function(title, body) {
+ var html = _.template($("#modal-warning-template").html())
+ ({ title: title, body: body });
+ htrace.showModal(html);
+}
+
+// Show a modal dialog box.
+htrace.showModal = function(html) {
+ var el = $("#modal");
+ el.html(html);
+ el.modal();
+}
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/models/span.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/models/span.js b/htrace-htraced/src/web/app/models/span.js
deleted file mode 100644
index b8dc114..0000000
--- a/htrace-htraced/src/web/app/models/span.js
+++ /dev/null
@@ -1,144 +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.
- */
-
-// Span model
-app.Span = Backbone.Model.extend({
- "defaults": {
- "spanId": null,
- "traceId": null,
- "processId": null,
- "parents": null,
- "description": null,
- "beginTime": 0,
- "stopTime": 0
- },
-
- shorthand: {
- "s": "spanId",
- "b": "beginTime",
- "e": "stopTime",
- "d": "description",
- "r": "processId",
- "p": "parents",
- "i": "traceId"
- },
-
- parse: function(response, options) {
- var attrs = {};
- var $this = this;
- $.each(response, function(key, value) {
- attrs[(key in $this.shorthand) ? $this.shorthand[key] : key] = value;
- });
- return attrs;
- },
-
- duration: function() {
- return this.get('stopTime') - this.get('beginTime');
- }
-});
-
-app.Spans = Backbone.PageableCollection.extend({
- model: app.Span,
- mode: "infinite",
- url: "/query",
- state: {
- pageSize: 10,
- lastSpanId: null,
- finished: false,
- predicates: []
- },
- queryParams: {
- totalPages: null,
- totalRecords: null,
- firstPage: null,
- lastPage: null,
- currentPage: null,
- pageSize: null,
- sortKey: null,
- order: null,
- directions: null,
-
- /**
- * Query parameter for htraced.
- */
- query: function() {
- var predicates = this.state.predicates.slice(0);
- var lastSpanId = this.state.lastSpanId;
-
- /**
- * Use last pulled span ID to paginate.
- * The htraced API works such that order is defined by the first predicate.
- * Adding a predicate to the end of the predicates list won't change the order.
- * Providing the predicate on spanid will filter all previous spanids.
- */
- if (lastSpanId) {
- predicates.push({
- "op": "gt",
- "field": "spanid",
- "val": lastSpanId
- });
- }
-
- return JSON.stringify({
- lim: this.state.pageSize + 1,
- pred: predicates
- });
- }
- },
-
- initialize: function() {
- this.on("reset", function(collection, response, options) {
- if (response.length == 0) {
- delete this.links[this.state.currentPage];
- this.getPreviousPage();
- }
- }, this);
- },
-
- parseLinks: function(resp, xhr) {
- this.state.finished = resp.length <= this.state.pageSize;
-
- if (this.state.finished) {
- this.state.lastSpanId = null;
- } else {
- this.state.lastSpanId = resp[this.state.pageSize - 1].s;
- }
-
- if (this.state.finished) {
- return {};
- }
-
- return {
- "next": "/query?query=" + this.queryParams.query.call(this)
- };
- },
-
- parseRecords: function(resp) {
- return resp.slice(0, 10);
- },
-
- setPredicates: function(predicates) {
- if (!$.isArray(predicates)) {
- console.error("predicates should be an array");
- return;
- }
-
- this.state.predicates = predicates;
- }
-});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/predicate.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/predicate.js b/htrace-htraced/src/web/app/predicate.js
new file mode 100644
index 0000000..87a5602
--- /dev/null
+++ b/htrace-htraced/src/web/app/predicate.js
@@ -0,0 +1,117 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.checkStringIsPositiveWholeNumber = function(val) {
+ if (!val.match(/^[0-9]([0-9]*)$/)) {
+ if (!val.match(/[^\s]/)) {
+ throw "You entered an empty string into a numeric field.";
+ }
+ throw "Non-numeric characters found.";
+ }
+};
+
+htrace.checkStringIsNotEmpty = function(val) {
+ if (!val.match(/[^\s]/)) {
+ throw "You entered an empty string into a text field.";
+ }
+};
+
+// Predicate type
+htrace.PType = Backbone.Model.extend({
+ initialize: function(options) {
+ this.name = options.name;
+ this.field = options.field;
+ this.op = options.op;
+ },
+
+ // Try to normalize a value of this type into something htraced can accept.
+ // Returns a string containing the normalized value on success. Throws a
+ // string explaining the parse error otherwise.
+ // Dates are represented by milliseconds since the epoch; span ids don't start
+ // with 0x.
+ normalize: function(val) {
+ switch (this.field) {
+ case "begin":
+ return htrace.parseDate(val).valueOf().toString();
+ case "end":
+ return htrace.parseDate(val).valueOf().toString();
+ case "description":
+ htrace.checkStringIsNotEmpty(val);
+ return val;
+ case "duration":
+ htrace.checkStringIsPositiveWholeNumber(val);
+ return val;
+ case "spanid":
+ return htrace.normalizeSpanId(val);
+ default:
+ return "Normalization not implemented for field '" + this.field + "'";
+ }
+ },
+
+ getDefaultValue: function() {
+ switch (this.field) {
+ case "begin":
+ return htrace.dateToString(moment());
+ case "end":
+ return htrace.dateToString(moment());
+ case "description":
+ return "";
+ case "duration":
+ return "0";
+ case "spanid":
+ return "";
+ default:
+ return "(unknown)";
+ }
+ }
+});
+
+htrace.parsePType = function(name) {
+ switch (name) {
+ case "Began after":
+ return new htrace.PType({name: name, field:"begin", op:"gt"});
+ case "Began at or before":
+ return new htrace.PType({name: name, field:"begin", op:"le"});
+ case "Ended after":
+ return new htrace.PType({name: name, field:"end", op:"gt"});
+ case "Ended at or before":
+ return new htrace.PType({name: name, field:"end", op:"le"});
+ case "Description contains":
+ return new htrace.PType({name: name, field:"description", op:"cn"});
+ case "Description is exactly":
+ return new htrace.PType({name: name, field:"description", op:"eq"});
+ case "Duration is longer than":
+ return new htrace.PType({name: name, field:"duration", op:"gt"});
+ case "Duration is at most":
+ return new htrace.PType({name: name, field:"duration", op:"le"});
+ case "Span ID is":
+ return new htrace.PType({name: name, field:"spanid", op:"eq"});
+ default:
+ return null
+ }
+};
+
+htrace.Predicate = function(options) {
+ this.op = options.ptype.op;
+ this.field = options.ptype.field;
+ this.val = options.val;
+ return this;
+};
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/predicate_view.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/predicate_view.js b/htrace-htraced/src/web/app/predicate_view.js
new file mode 100644
index 0000000..aefe896
--- /dev/null
+++ b/htrace-htraced/src/web/app/predicate_view.js
@@ -0,0 +1,68 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.PredicateView = Backbone.View.extend({
+ initialize: function(options) {
+ this.el = options.el;
+ this.index = options.index;
+ this.ptype = options.ptype;
+ this.searchView = options.searchView;
+ },
+
+ events: {
+ "click .closeButton": "remove",
+ },
+
+ render: function() {
+ this.$el.html(_.template($("#predicate-template").html())
+ ({ desc: this.ptype.name, id: this.index }))
+ if (this.getText() === "") {
+ $(this.$el).find(".form-control").val(this.ptype.getDefaultValue());
+ }
+ console.log(this.toString() + "#render");
+ return this;
+ },
+
+ // Handle the user removing this predicate.
+ remove: function() {
+ this.searchView.removePredicateView(this);
+ Backbone.View.prototype.remove.apply(this, arguments);
+ },
+
+ // Get the text which the user has entered in.
+ getText: function() {
+ return $(this.$el).find(".form-control").val().trim();
+ },
+
+ // Get the predicate expressed by this view.
+ // Throw an exception if the predicate can't be parsed.
+ getPredicate: function() {
+ return new htrace.Predicate({
+ ptype: this.ptype,
+ val: this.ptype.normalize(this.getText())
+ });
+ },
+
+ toString: function() {
+ return "PredicateView(this.el=" + this.el + ", this.index=" +
+ this.index + ", this.ptype='" + this.ptype.name + "')";
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/query_results.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/query_results.js b/htrace-htraced/src/web/app/query_results.js
new file mode 100644
index 0000000..6fdde9f
--- /dev/null
+++ b/htrace-htraced/src/web/app/query_results.js
@@ -0,0 +1,45 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.QueryResults = Backbone.Collection.extend({
+ // The query results are spans.
+ model: htrace.Span,
+
+ initialize: function(options) {
+ this.queryJson = options.queryJson;
+ },
+
+ url: function() {
+ return "query?query=" + this.queryString();
+ },
+
+ parse: function(response, xhr) {
+ return response;
+ },
+
+ prettyQueryString: function() {
+ return JSON.stringify(this.queryJson, null, 2);
+ },
+
+ queryString: function() {
+ return JSON.stringify(this.queryJson);
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/router.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/router.js b/htrace-htraced/src/web/app/router.js
new file mode 100644
index 0000000..607da44
--- /dev/null
+++ b/htrace-htraced/src/web/app/router.js
@@ -0,0 +1,74 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.HTraceRouter = Backbone.Router.extend({
+ "routes": {
+ "": "empty",
+ "about": "about",
+ "search": "search",
+ "*unknown": "unknown"
+ },
+
+ empty: function() {
+ console.log("Redirecting to #about.");
+ Backbone.history.navigate("about", {"trigger": true, "replace": true});
+ },
+
+ about: function() {
+ console.log("Visiting #about.");
+ serverInfo = new htrace.ServerInfo();
+ var router = this;
+ serverInfo.fetch({
+ "success": function(model, response, options) {
+ router.switchView(new htrace.AboutView({model: serverInfo, el: "#app"}));
+ router.activateNavBarEntry("about")
+ },
+ "error": function(model, response, options) {
+ window.alert("Failed to fetch htraced server info via GET " +
+ "/server/info: " + JSON.stringify(response));
+ }
+ });
+ },
+
+ search: function() {
+ console.log("Visiting #search.");
+ this.switchView(new htrace.SearchView({el : "#app"}));
+ htrace.router.activateNavBarEntry("search");
+ },
+
+ unknown: function() {
+ console.log("Unknown route " + Backbone.history.getFragment() + ".")
+ },
+
+ "switchView": function(view) {
+ this.view && this.view.close();
+ this.view = view;
+ this.view.render();
+ },
+
+ "activateNavBarEntry": function(id) {
+ $(".nav").find(".active").removeClass("active");
+ $(".nav").find("#" + id).addClass("active");
+ }
+});
+
+htrace.router = new htrace.HTraceRouter();
+Backbone.history.start();
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/search_results.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/search_results.js b/htrace-htraced/src/web/app/search_results.js
new file mode 100644
index 0000000..d214918
--- /dev/null
+++ b/htrace-htraced/src/web/app/search_results.js
@@ -0,0 +1,25 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.SearchResults = Backbone.Collection.extend({
+ // The search results are spans.
+ model: htrace.Span
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/search_results_view.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/search_results_view.js b/htrace-htraced/src/web/app/search_results_view.js
new file mode 100644
index 0000000..b3473c4
--- /dev/null
+++ b/htrace-htraced/src/web/app/search_results_view.js
@@ -0,0 +1,365 @@
+/*
+ * 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 htrace = htrace || {};
+
+htrace.SearchResultsView = Backbone.View.extend({
+ // The minimum time span we will allow between begin and end.
+ MINIMUM_TIME_SPAN: 100,
+
+ begin: 0,
+
+ end: this.MINIMUM_TIME_SPAN,
+
+ focused: false,
+
+ initialize: function(options) {
+ this.model = options.searchResults;
+ this.el = options.el;
+ this.listenTo(this.model, 'add remove change reset', this.render);
+
+ // Re-render the canvas when the window size changes.
+ // Add a debouncer delay to avoid spamming render requests.
+ var view = this;
+ $(window).on("resize", _.debounce(function() {
+ view.render();
+ }, 250));
+ },
+
+ // Get the canvas X coordinate of a mouse click from the absolute event
+ // coordinate.
+ getCanvasX: function(e) {
+ return e.pageX - $("#resultsCanvas").offset().left;
+ },
+
+ // Get the canvas Y coordinate of a mouse click from the absolute event
+ // coordinate.
+ getCanvasY: function(e) {
+ return e.pageY - $("#resultsCanvas").offset().top;
+ },
+
+ handleMouseDown: function(e) {
+ e.preventDefault();
+ var x = this.getCanvasX(e);
+ var y = this.getCanvasY(e);
+ var focused = this.widgetManager.handleMouseDown(x, y);
+ if (focused != this.focused) {
+ this.draw();
+ this.focused = focused;
+ }
+ },
+
+ handleMouseUp: function(e) {
+ e.preventDefault();
+ var x = this.getCanvasX(e);
+ var y = this.getCanvasY(e);
+ this.widgetManager.handleMouseUp(x, y);
+ this.focused = false;
+ this.draw();
+ },
+
+ // When the mouse leaves the canvas, treat it like a mouse up event at -1, -1
+ // if something is focused.
+ handleMouseOut: function(e) {
+ if (this.focused) {
+ this.widgetManager.handleMouseUp(-1, -1);
+ this.focused = false;
+ this.draw();
+ }
+ },
+
+ handleMouseMove: function(e) {
+ e.preventDefault();
+ var x = this.getCanvasX(e);
+ var y = this.getCanvasY(e);
+ if (this.focused) {
+ var mustDraw = false;
+ if (this.widgetManager.handleMouseMove(x, y)) {
+ mustDraw = true;
+ }
+ }
+ if (this.timeCursor.handleMouseMove(x, y)) {
+ mustDraw = true;
+ }
+ if (mustDraw) {
+ this.draw();
+ }
+ },
+
+ render: function() {
+ console.log("SearchResultsView#render.");
+ $(this.el).html(_.template($("#search-results-view-template").html()));
+ $('#selectedTime').attr('readonly', 'readonly');
+ this.canvas = $("#resultsCanvas");
+ this.ctx = this.canvas.get(0).getContext("2d");
+ this.scaleCanvas();
+ this.setupCoordinates();
+ this.setupTimeCursor();
+ this.setupWidgets();
+ this.draw();
+ this.attachEvents();
+ return this;
+ },
+
+ /*
+ * Compute the ratio to use between the size of the canvas (i.e.
+ * canvas.ctx.width, canvas.ctx.height) and the size in "HTML5 pixels." Note
+ * that 'HTML5 pixels" don't actually correspond to screen pixels. A line 1
+ * "HTML5 pixel" wide actually takes up multiple scren pixels, etc.
+ *
+ * TODO: fix this to be sharper
+ */
+ computeScaleFactor: function() {
+ var backingStoreRatio = this.ctx.backingStorePixelRatio ||
+ this.ctx.mozBackingStorePixelRatio ||
+ this.ctx.msBackingStorePixelRatio ||
+ this.ctx.webkitBackingStorePixelRatio ||
+ this.ctx.oBackingStorePixelRatio ||
+ this.ctx.backingStorePixelRatio || 1;
+ return (window.devicePixelRatio || 1) / backingStoreRatio;
+ },
+
+ // Sets up the canvas size and scaling.
+ scaleCanvas: function() {
+ var cssX = this.canvas.parent().innerWidth();
+ var cssY = $(window).innerHeight() - $("#header").innerHeight() - 50;
+ var ratio = this.computeScaleFactor();
+ console.log("scaleCanvas: cssX=" + cssX + ", cssY=" + cssY + ", ratio=" + ratio);
+ this.maxX = cssX;
+ this.maxY = cssY;
+ $('#searchView').css('height', cssY + "px");
+ $('#results').css('width', cssX + "px");
+ $('#results').css('height', cssY + "px");
+ $('#resultsView').css('width', cssX + "px");
+ $('#resultsView').css('height', cssY + "px");
+ $('#resultsDiv').css('width', cssX + "px");
+ $('#resultsDiv').css('height', cssY + "px");
+ $('#resultsCanvas').css('width', cssX + "px");
+ $('#resultsCanvas').css('height', cssY + "px");
+ this.ctx.canvas.width = cssX * ratio;
+ this.ctx.canvas.height = cssY * ratio;
+ this.ctx.scale(ratio, ratio);
+ },
+
+ //
+ // Set up the screen coordinates.
+ //
+ // 0 buttonX descX scrollX maxX
+ // +--------------+----------+--------------------+-----------+
+ // |ProcessId | Buttons | Span Description | Scrollbar |
+ // +--------------+----------+--------------------+-----------+
+ //
+ setupCoordinates: function() {
+ this.buttonX = Math.min(300, Math.floor(this.maxX / 5));
+ this.descX = this.buttonX + Math.min(75, Math.floor(this.maxX / 20));
+ var scrollBarWidth = Math.min(50, Math.floor(this.maxX / 10));
+ this.scrollX = this.maxX - scrollBarWidth;
+ },
+
+ setupTimeCursor: function() {
+ var selectedTime;
+ if (this.timeCursor != null) {
+ selectedTime = this.timeCursor.selectedTime;
+ console.log("setupTimeCursor: selectedTime = (prev) " + selectedTime);
+ } else {
+ selectedTime = this.begin;
+ console.log("setupTimeCursor: selectedTime = (begin) " + selectedTime);
+ }
+ this.timeCursor = new htrace.TimeCursor({
+ ctx: this.ctx,
+ x0: this.descX,
+ xF: this.scrollX,
+ el: "#selectedTime",
+ y0: 0,
+ yF: this.maxY,
+ begin: this.begin,
+ end: this.end,
+ selectedTime: selectedTime
+ });
+ },
+
+ setupWidgets: function() {
+ var widgets = [];
+ var spanWidgetHeight = Math.min(25, Math.floor(this.maxY / 32));
+
+ // Create a SpanWidget for each span we know about
+ var numSpans = this.model.size();
+ for (var i = 0; i < numSpans; i++) {
+ var spanWidget = new htrace.SpanWidget({
+ ctx: this.ctx,
+ span: this.model.at(i),
+ x0: 0,
+ xB: this.buttonX,
+ xD: this.descX,
+ xF: this.scrollX,
+ y0: i * spanWidgetHeight,
+ yF: (i * spanWidgetHeight) + (spanWidgetHeight - 1),
+ begin: this.begin,
+ end: this.end
+ });
+ widgets.push(spanWidget);
+ }
+
+ // Create a new root-leve WidgetManager
+ this.widgetManager = new htrace.WidgetManager({
+ widgets: widgets
+ });
+ },
+
+ draw: function() {
+ if (this.checkCanvasTooSmall()) {
+ return;
+ }
+
+ // Set the background to white.
+ this.ctx.save();
+ this.ctx.fillStyle="#ffffff";
+ this.ctx.strokeStyle="#000000";
+ this.ctx.fillRect(0, 0, this.maxX, this.maxY);
+ this.ctx.restore();
+
+ // Draw all the widgets.
+ this.widgetManager.draw();
+ this.timeCursor.draw();
+ },
+
+ checkCanvasTooSmall: function() {
+ if ((this.maxX < 200) || (this.maxY < 200)) {
+ this.ctx.fillStyle="#cccccc";
+ this.ctx.strokeStyle="#000000";
+ this.ctx.fillRect(0, 0, this.maxX, this.maxY);
+ this.ctx.font = "24px serif";
+ this.ctx.fillStyle="#000000";
+ this.ctx.fillText("Canvas too small!", 0, 24);
+ return true;
+ }
+ return false;
+ },
+
+ attachEvents: function() {
+ // Use jquery to capture mouse events on the canvas.
+ // For some reason using backbone doesn't work for getting these events.
+ var view = this;
+ $("#resultsCanvas").off("mousedown");
+ $("#resultsCanvas").on("mousedown", function(e) {
+ view.handleMouseDown(e);
+ });
+ $("#resultsCanvas").off("mouseup");
+ $("#resultsCanvas").on("mouseup", function(e) {
+ view.handleMouseUp(e);
+ });
+ $("#resultsCanvas").off("mouseout");
+ $("#resultsCanvas").on("mouseout", function(e) {
+ view.handleMouseOut(e);
+ });
+ $(window).off("mouseup");
+ $(window).on("mouseup"), function(e) {
+ view.handleGlobalMouseUp(e);
+ }
+ $("#resultsCanvas").off("mousemove");
+ $("#resultsCanvas").on("mousemove", function(e) {
+ view.handleMouseMove(e);
+ });
+ },
+
+ remove: function() {
+ $(window).off("resize");
+ $("#resultsCanvas").off("mousedown");
+ $("#resultsCanvas").off("mouseup");
+ $("#resultsCanvas").off("mousemove");
+ Backbone.View.prototype.remove.apply(this, arguments);
+ },
+
+ handleBeginOrEndChange: function(e, type) {
+ e.preventDefault();
+ var text = $(e.target).val().trim();
+ var d = null;
+ try {
+ d = htrace.parseDate(text);
+ } catch(err) {
+ $("#begin").val(htrace.dateToString(this.begin));
+ $("#end").val(htrace.dateToString(this.end));
+ htrace.showModalWarning("Timeline " + type + " Format Error",
+ "Please enter a valid time in the timeline " + type + " field.<p/>" +
+ err);
+ return null;
+ }
+ if (type === "begin") {
+ this.setBegin(d.valueOf());
+ } else if (type === "end") {
+ this.setEnd(d.valueOf());
+ } else {
+ throw "invalid type for handleBeginOrEndChange: expected begin or end.";
+ }
+ },
+
+ setBegin: function(val) {
+ if (this.end < val + this.MINIMUM_TIME_SPAN) {
+ this.begin = val;
+ this.end = val + this.MINIMUM_TIME_SPAN;
+ console.log("SearchResultsView#setBegin(begin=" + this.begin +
+ ", end=" + this.end + ")");
+ $("#begin").val(htrace.dateToString(this.begin));
+ $("#end").val(htrace.dateToString(this.end));
+ } else {
+ this.begin = val;
+ console.log("SearchResultsView#setBegin(begin=" + this.begin + ")");
+ $("#begin").val(htrace.dateToString(this.begin));
+ }
+ this.render();
+ },
+
+ setEnd: function(val) {
+ if (this.begin + this.MINIMUM_TIME_SPAN > val) {
+ this.begin = val;
+ this.end = this.begin + this.MINIMUM_TIME_SPAN;
+ console.log("SearchResultsView#setEnd(begin=" + this.begin +
+ ", end=" + this.end + ")");
+ $("#begin").val(htrace.dateToString(this.begin));
+ $("#end").val(htrace.dateToString(this.end));
+ } else {
+ this.end = val;
+ console.log("SearchResultsView#setEnd(end=" + this.end + ")");
+ $("#end").val(htrace.dateToString(this.end));
+ }
+ this.render();
+ },
+
+ zoomFitAll: function() {
+ var numSpans = this.model.size();
+ if (numSpans == 0) {
+ this.setBegin(0);
+ this.setEnd(this.MINIMUM_TIME_SPAN);
+ return;
+ }
+ var minStart = 4503599627370496;
+ var maxEnd = 0;
+ for (var i = 0; i < numSpans; i++) {
+ var span = this.model.at(i);
+ if (span.get('begin') < minStart) {
+ minStart = span.get('begin');
+ }
+ if (span.get('end') > maxEnd) {
+ maxEnd = span.get('end');
+ }
+ }
+ this.setBegin(minStart);
+ this.setEnd(maxEnd);
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/search_view.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/search_view.js b/htrace-htraced/src/web/app/search_view.js
new file mode 100644
index 0000000..52f9101
--- /dev/null
+++ b/htrace-htraced/src/web/app/search_view.js
@@ -0,0 +1,197 @@
+/*
+ * 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 htrace = htrace || {};
+htrace.SearchView = Backbone.View.extend({
+ initialize : function() {
+ this.predicateViews = [];
+ this.highestPredicateIndex = 0;
+ this.searchInProgress = false;
+ this.searchResults = new htrace.SearchResults();
+ this.resultsView = new htrace.SearchResultsView({
+ searchResults: this.searchResults,
+ el: "#results"
+ });
+ },
+
+ events: {
+ "click #searchButton": "searchHandler",
+ "click #clearButton": "clearHandler",
+ "click .add-field": "dropdownHandler",
+ "blur #begin": "blurBeginHandler",
+ "blur #end": "blurEndHandler",
+ "click #zoomButton": "zoomFitAllHandler"
+ },
+
+ searchHandler: function(e){
+ e.preventDefault();
+
+ // Do a new search.
+ this.doSearch(e.ctrlKey);
+ },
+
+ clearHandler: function(e){
+ e.preventDefault();
+
+ // Clear existing search results.
+ this.searchResults.reset();
+ },
+
+ doSearch: function(showDebug){
+ if (this.searchInProgress) {
+ console.log("Can't start a new search while another one is in " +
+ "progress.");
+ return false;
+ }
+
+ // Check if there are no search criteria.
+ if (this.predicateViews.length == 0) {
+ htrace.showModalWarning("No Search Criteria Specified",
+ "You have not specified any search criteria. " +
+ "Use the 'Add Predicate' button to specify what to search for.");
+ return false;
+ }
+
+ // Build the predicate array.
+ predicates = []
+ var predicateViewsLen = this.predicateViews.length;
+ for (var i = 0; i < predicateViewsLen; i++) {
+ var predicateView = this.predicateViews[i];
+ try {
+ predicates.push(predicateView.getPredicate());
+ } catch(err) {
+ htrace.showModalWarning("Search Field Validation Error",
+ "Invalid search string for the '" + predicateView.ptype.name +
+ "' field.<p/>" + err);
+ return false;
+ }
+ }
+ var queryJson = {
+ pred: predicates,
+ lim: 20
+ };
+ // If there are existing search results, we want results which "come after"
+ // those. So pass the last span we saw as a continuation token.
+ if (this.searchResults.size() > 0) {
+ queryJson.prev =
+ this.searchResults.at(this.searchResults.size() - 1).unparse();
+ }
+ var searchView = this;
+ var queryResults = new htrace.QueryResults({queryJson: queryJson});
+ console.log("Starting span query " + queryResults.url());
+ this.searchInProgress = true;
+ queryResults.fetch({
+ success: function(model, response, options){
+ var firstResults = (searchView.searchResults.size() === 0);
+ console.log("Success on span query " + queryResults.url() + ": got " +
+ queryResults.size() + " result(s). firstResults=" + firstResults);
+ searchView.searchResults.add(queryResults.models);
+ if (firstResults) {
+ // After the initial search, zoom to fit everything.
+ // On subsequent searches, we leave the viewport alone.
+ searchView.resultsView.zoomFitAll();
+ }
+ searchView.searchInProgress = false;
+ if (showDebug) {
+ htrace.showModalWarning("Search Debug",
+ "This is the search debug box, accessible by holding down the " +
+ "control key while clicking the search button.<p/>" +
+ "<h3>Query JSON</h3><pre>" + queryResults.prettyQueryString() +
+ "</pre><p/><h3>Response JSON</h3><pre>" +
+ JSON.stringify(queryResults, null, 2) + "</pre><p/>");
+ } else if (queryResults.size() == 0) {
+ if (firstResults) {
+ htrace.showModalWarning("No Results Found",
+ "No results were found for your query.<p/>");
+ } else {
+ htrace.showModalWarning("No Additional Results Found",
+ "No additional results were found for your query.<p/>");
+ }
+ }
+ },
+ error: function(model, response, options){
+ this.searchResults.clear();
+ var err = "Error " + JSON.stringify(response) +
+ " on span query " + query.url();
+ console.log(err);
+ alert(err);
+ searchView.searchInProgress = false;
+ }
+ });
+ return false;
+ },
+
+ dropdownHandler: function(e){
+ e.preventDefault();
+ var text = $(e.target).text();
+ var ptype = htrace.parsePType(text);
+ if (!ptype) {
+ alert("Unable to parse predicate type '" + text + "'");
+ return false;
+ }
+ var index = this.highestPredicateIndex;
+ this.highestPredicateIndex++;
+ var el = "pred" + index;
+ $("#predicates").append('<div id="' + el + '"/></div>');
+ predicateView = new htrace.PredicateView({
+ el: "#" + el,
+ index: index,
+ ptype: ptype,
+ searchView: this
+ });
+ this.predicateViews.push(predicateView);
+ predicateView.render();
+ return true;
+ },
+
+ blurBeginHandler: function(e) {
+ return this.resultsView.handleBeginOrEndChange(e, "begin");
+ },
+
+ blurEndHandler: function(e) {
+ return this.resultsView.handleBeginOrEndChange(e, "end");
+ },
+
+ zoomFitAllHandler: function(e) {
+ e.preventDefault();
+ this.resultsView.zoomFitAll();
+ },
+
+ removePredicateView: function(predicateView) {
+ this.predicateViews = _.without(this.predicateViews, predicateView);
+ },
+
+ render: function() {
+ this.$el.html(_.template($("#search-view-template").html())
+ ({ model : this.model }))
+ this.resultsView.render();
+ console.log("SearchView#render");
+ return this;
+ },
+
+ close: function() {
+ console.log("SearchView#close")
+ while (this.predicateViews.length > 0) {
+ this.predicateViews[0].remove();
+ }
+ this.resultsView.remove();
+ this.resultsView = null;
+ this.undelegateEvents();
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/server_info.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/server_info.js b/htrace-htraced/src/web/app/server_info.js
new file mode 100644
index 0000000..b03f706
--- /dev/null
+++ b/htrace-htraced/src/web/app/server_info.js
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+// htraced ServerInfo sent back from /serverInfo.
+// See rest.go.
+htrace.ServerInfo = Backbone.Model.extend({
+ defaults: {
+ "ReleaseVersion": "unknown",
+ "GitVersion": "unknown",
+ },
+
+ url: function() {
+ return "server/info";
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/setup.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/setup.js b/htrace-htraced/src/web/app/setup.js
deleted file mode 100644
index beb06db..0000000
--- a/htrace-htraced/src/web/app/setup.js
+++ /dev/null
@@ -1,192 +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 BaseView = Backbone.Marionette.LayoutView.extend({
- "el": "body",
- "regions": {
- "header": "#header",
- "app": "#app"
- }
-});
-
-var Router = Backbone.Marionette.AppRouter.extend({
- "routes": {
- "": "init",
- "!/search(?:query)": "search",
- "!/spans/:id": "span",
- "!/swimlane/:id": "swimlane",
- "!/swimlane/:id:?:lim": "swimlane"
- },
-
- "initialize": function() {
- // Collection
- this.spansCollection = new app.Spans();
- },
-
- "init": function() {
- Backbone.history.navigate("!/search", {"trigger": true});
- },
-
- "search": function(query) {
- app.root.app.show(new app.SearchView());
-
- var predicates;
-
- this.spansCollection.switchMode("infinite", {
- fetch: false,
- resetState: true
- });
-
- if (query) {
- predicates = _(query.split(";"))
- .map(function(predicate) {
- return _(predicate.split('&'))
- .reduce(function(mem, op) {
- var op = op.split('=');
- mem[op[0]] = op[1];
- return mem;
- }, {});
- });
- this.spansCollection.fullCollection.reset();
- this.spansCollection.setPredicates(predicates);
- }
- else {
- this.spansCollection.fullCollection.reset();
- this.spansCollection.setPredicates([{"op":"cn","field":"description","val":""}]);
- }
- this.spansCollection.fetch();
-
- app.root.app.currentView.controls.show(
- new app.SearchControlsView({
- "collection": this.spansCollection,
- "predicates": predicates
- }));
- app.root.app.currentView.main.show(
- new Backgrid.Grid({
- "collection": this.spansCollection,
- "columns": [{
- "label": "Begin",
- "cell": Backgrid.Cell.extend({
- className: "begin-cell",
- formatter: {
- fromRaw: function(rawData, model) {
- var beginMs = model.get("beginTime")
- return moment(beginMs).format('YYYY/MM/DD HH:mm:ss,SSS');
- },
- toRaw: function(formattedData, model) {
- return formattedData // data entry not supported for this cell
- }
- }
- }),
- "editable": false,
- "sortable": false
- }, {
- "name": "spanId",
- "label": "ID",
- "cell": "string",
- "editable": false,
- "sortable": false
- }, {
- "name": "processId",
- "label": "processId",
- "cell": "string",
- "editable": false,
- "sortable": false
- }, {
- "label": "Duration",
- "cell": Backgrid.Cell.extend({
- className: "duration-cell",
- formatter: {
- fromRaw: function(rawData, model) {
- return model.duration() + " ms"
- },
- toRaw: function(formattedData, model) {
- return formattedData // data entry not supported for this cell
- }
- }
- }),
- "editable": false,
- "sortable": false
- }, {
- "name": "description",
- "label": "Description",
- "cell": "string",
- "editable": false,
- "sortable": false
- }],
- "row": Backgrid.Row.extend({
- "events": {
- "click": "details"
- },
- "details": function() {
- Backbone.history.navigate("!/spans/" + this.model.get("spanId"), {"trigger": true});
- }
- })
- }));
- app.root.app.currentView.pagination.show(
- new Backgrid.Extension.Paginator({
- collection: this.spansCollection,
- }));
- },
-
- "span": function(id) {
- var span = this.spansCollection.findWhere({
- "spanId": id
- });
-
- if (!span) {
- Backbone.history.navigate("!/search", {"trigger": true});
- return;
- }
-
- var graphView = new app.GraphView({
- "collection": this.spansCollection,
- "id": "span-graph"
- });
-
- graphView.on("update:span", function(d) {
- app.root.app.currentView.span.show(
- new app.SpanDetailsView({
- "model": d.span
- }));
- });
-
- app.root.app.show(new app.DetailsView());
- app.root.app.currentView.content.show(graphView);
- app.root.app.currentView.content.currentView.setSpanId(id);
- },
-
- "swimlane": function(id, lim) {
- var top = new app.SwimlaneView();
- app.root.app.show(top);
- top.swimlane.show(new app.SwimlaneGraphView({
- "spanId": id,
- "lim": lim
- }));
- }
-});
-
-app.on("start", function(options) {
- app.root = new BaseView();
- app.routes = new Router();
-
- Backbone.history.start();
-});
-
-app.start();
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/span.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/span.js b/htrace-htraced/src/web/app/span.js
new file mode 100644
index 0000000..2c06fa0
--- /dev/null
+++ b/htrace-htraced/src/web/app/span.js
@@ -0,0 +1,69 @@
+/*
+ * 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 htrace = htrace || {};
+
+// The invalid span ID, which is all zeroes.
+htrace.INVALID_SPAN_ID = "0000000000000000";
+
+htrace.Span = Backbone.Model.extend({
+ // Parse a span sent from htraced.
+ // We use more verbose names for some attributes.
+ // Missing attributes are treated as zero or empty. Numerical attributes are
+ // forced to be numbers.
+ parse: function(response, options) {
+ var span = {};
+ this.set("spanId", response.s ? response.s : htrace.INVALID_SPAN_ID);
+ this.set("traceId", response.i ? response.i : htrace.INVALID_SPAN_ID);
+ this.set("processId", response.r ? response.r : "");
+ this.set("parents", response.p ? response.p : []);
+ this.set("description", response.d ? response.d : "");
+ this.set("begin", response.b ? parseInt(response.b, 10) : 0);
+ this.set("end", response.e ? parseInt(response.e, 10) : 0);
+ return span;
+ },
+
+ // Transform a span model back into a JSON string suitable for sending over
+ // the wire.
+ unparse: function() {
+ var obj = { };
+ if (!(this.get("spanId") === htrace.INVALID_SPAN_ID)) {
+ obj.s = this.get("spanId");
+ }
+ if (!(this.get("traceId") === htrace.INVALID_SPAN_ID)) {
+ obj.i = this.get("traceId");
+ }
+ if (!(this.get("processId") === "")) {
+ obj.r = this.get("processId");
+ }
+ if (this.get("parents").length > 0) {
+ obj.p = this.get("parents");
+ }
+ if (this.get("description").length > 0) {
+ obj.d = this.get("description");
+ }
+ if (this.get("begin") > 0) {
+ obj.b = this.get("begin");
+ }
+ if (this.get("end") > 0) {
+ obj.e = this.get("end");
+ }
+ return obj;
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/span_details_view.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/span_details_view.js b/htrace-htraced/src/web/app/span_details_view.js
new file mode 100644
index 0000000..9a37055
--- /dev/null
+++ b/htrace-htraced/src/web/app/span_details_view.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.
+ */
+
+var htrace = htrace || {};
+
+htrace.SpanDetailsView = Backbone.View.extend({
+ initialize: function(options) {
+ this.el = options.el;
+ this.model = options.model;
+ }
+
+ render: function() {
+ this.$el.html(_.template($("#about-view-template").html())
+ ({ model : this.model }));
+ console.log("AboutView#render");
+ return this;
+ },
+
+ close: function() {
+ console.log("AboutView#close")
+ this.undelegateEvents();
+ }
+});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/span_widget.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/span_widget.js b/htrace-htraced/src/web/app/span_widget.js
new file mode 100644
index 0000000..f9333d6
--- /dev/null
+++ b/htrace-htraced/src/web/app/span_widget.js
@@ -0,0 +1,229 @@
+/*
+ * 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 htrace = htrace || {};
+
+// Widget containing the trace span displayed on the canvas.
+htrace.SpanWidget = function(params) {
+ for (var k in params) {
+ this[k]=params[k];
+ }
+
+ this.selected = false;
+ this.widgetManagerFocused = false;
+ this.xSize = this.xF - this.x0;
+ this.ySize = this.yF - this.y0;
+ this.xDB = this.xD - this.xB;
+
+ var widgets = [];
+ this.upWidget = new htrace.TriangleButton({
+ ctx: this.ctx,
+ direction: "up",
+ x0: this.xB + 2,
+ xF: this.xB + (this.xDB / 2) - 2,
+ y0: this.y0 + 2,
+ yF: this.yF - 2,
+ });
+ widgets.push(this.upWidget);
+ this.downWidget = new htrace.TriangleButton({
+ ctx: this.ctx,
+ direction: "down",
+ x0: this.xB + (this.xDB / 2) + 2,
+ xF: this.xD - 2,
+ y0: this.y0 + 2,
+ yF: this.yF - 2,
+ });
+ widgets.push(this.downWidget);
+ this.widgetManager = new htrace.WidgetManager({
+ widgets: widgets,
+ });
+
+ this.draw = function() {
+ this.drawBackground();
+ this.drawProcessId();
+ this.drawDescription();
+ this.widgetManager.draw();
+ };
+
+ // Draw the background of this span widget.
+ this.drawBackground = function() {
+ this.ctx.save();
+ if (this.selected) {
+ this.ctx.fillStyle="#ffccff";
+ } else {
+ this.ctx.fillStyle="#ffffff";
+ }
+ this.ctx.fillRect(this.x0, this.y0, this.xSize, this.ySize);
+ this.ctx.restore();
+ }
+
+ // Draw process ID text.
+ this.drawProcessId = function() {
+ this.ctx.save();
+ this.ctx.fillStyle="#000000";
+ this.ctx.font = (this.ySize - 2) + "px sans-serif";
+ this.ctx.beginPath();
+ this.ctx.rect(this.x0, this.y0, this.xB - this.x0, this.ySize);
+ this.ctx.clip();
+ this.ctx.fillText(this.span.get('processId'), this.x0, this.yF - 4);
+ this.ctx.restore();
+ };
+
+ // Draw the span description
+ this.drawDescription = function() {
+ // Draw the light blue bar representing time.
+ this.ctx.save();
+ this.ctx.beginPath();
+ this.ctx.rect(this.xD, this.y0, this.xF - this.xD, this.ySize);
+ this.ctx.clip();
+ this.ctx.strokeStyle="#000000";
+ this.ctx.fillStyle="#a7b7ff";
+ var beginX = this.timeToPosition(this.span.get('begin'));
+ var endX = this.timeToPosition(this.span.get('end'));
+
+ // If the span is completely off the screen, draw a diamond at either the
+ // beginning or the end of the bar to indicate whether it's too early or too
+ // late to be seen.
+ if (endX < this.x0) {
+ beginX = this.xD;
+ endX = this.xD;
+ }
+ if (beginX > this.xF) {
+ beginX = this.xF;
+ endX = this.xF;
+ }
+
+ var gapY = 2;
+ var epsilon = Math.max(2, Math.floor(this.xSize / 1000));
+ if (endX - beginX < epsilon) {
+ // The time interval is too narrow to see. Draw a diamond on the point instead.
+ this.ctx.beginPath();
+ this.ctx.moveTo(beginX, this.y0 + gapY);
+ this.ctx.lineTo(beginX + (Math.floor(this.ySize / 2) - gapY),
+ this.y0 + Math.floor(this.ySize / 2));
+ this.ctx.lineTo(beginX, this.yF - gapY);
+ this.ctx.lineTo(beginX - (Math.floor(this.ySize / 2) - gapY),
+ this.y0 + Math.floor(this.ySize / 2));
+ this.ctx.closePath();
+ this.ctx.fill();
+ } else {
+ // Draw a bar from the start time to the end time.
+// console.log("beginX=" + beginX + ", endX=" + endX +
+// ", begin=" + this.span.get('begin') + ", end=" + this.span.get('end'));
+ this.ctx.fillRect(beginX, this.y0 + gapY, endX - beginX,
+ this.ySize - (gapY * 2));
+ }
+
+ // Draw description text
+ this.ctx.fillStyle="#000000";
+ this.ctx.font = (this.ySize - gapY) + "px sans-serif";
+ this.ctx.fillText(this.span.get('description'), this.xD, this.yF - gapY - 2);
+
+ this.ctx.restore();
+ };
+
+ // Convert a time in milliseconds since the epoch to an x position.
+ this.timeToPosition = function(time) {
+ return this.xD +
+ (((time - this.begin) * (this.xF - this.xD)) /
+ (this.end - this.begin));
+ };
+
+ this.inBoundingBox = function(x, y) {
+ return ((x >= this.x0) && (x <= this.xF) && (y >= this.y0) && (y <= this.yF));
+ };
+
+ this.handleMouseDown = function(x, y) {
+ if (!this.inBoundingBox(x, y)) {
+ return false;
+ }
+ if (this.widgetManager.handleMouseDown(x, y)) {
+ this.widgetManagerFocused = true;
+ return true;
+ }
+ this.selected = !this.selected;
+ this.fillSpanDetailsView();
+ return true;
+ };
+
+ this.handleMouseUp = function(x, y) {
+ if (this.widgetManagerFocused) {
+ this.widgetManager.handleMouseUp(x, y);
+ this.widgetManagerFocused = false;
+ }
+ };
+
+ this.handleMouseMove = function(x, y) {
+ if (!this.widgetManagerFocused) {
+ return false;
+ }
+ return this.widgetManager.handleMouseUp(x, y);
+ };
+
+ this.fillSpanDetailsView = function() {
+ var info = {
+ spanID: this.span.get("spanID"),
+ begin: htrace.dateToString(parseInt(this.span.get("begin"), 10)),
+ end: htrace.dateToString(parseInt(this.span.get("end"), 10))
+ };
+ var explicitOrder = {
+ spanId: -3,
+ begin: -2,
+ end: -1
+ };
+ keys = [];
+ for(k in this.span.attributes) {
+ keys.push(k);
+ if (info[k] == null) {
+ info[k] = this.span.get(k);
+ }
+ }
+ // We sort the keys so that the stuff we want at the top appears at the top,
+ // and everything else is in alphabetical order.
+ keys = keys.sort(function(a, b) {
+ var oa = explicitOrder[a] || 0;
+ var ob = explicitOrder[b] || 0;
+ if (oa < ob) {
+ return -1;
+ } else if (oa > ob) {
+ return 1;
+ } else if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ var len = keys.length;
+ var h = '<table style="table-layout:fixed;width:100%;word-wrap:break-word">';
+ for (i = 0; i < len; i++) {
+ // Make every other row grey to improve visibility.
+ var colorString = ((i%2) == 1) ? "#f1f1f1" : "#ffffff";
+ h += _.template('<tr bgcolor="' + colorString + '">' +
+ '<td style="width:30%;word-wrap:break-word"><%- key %></td>' +
+ '<td style="width:70%;word-wrap:break-word"><%- val %></td>' +
+ "</tr>")({key: keys[i], val: info[keys[i]]});
+ }
+ h += '</table>';
+ $("#spanDetails").html(h);
+ };
+
+ return this;
+};
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/string.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/string.js b/htrace-htraced/src/web/app/string.js
new file mode 100644
index 0000000..b0dfb74
--- /dev/null
+++ b/htrace-htraced/src/web/app/string.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.
+ */
+
+var htrace = htrace || {};
+
+// Parse an ISO8601 date string into a moment.js object.
+htrace.parseDate = function(val) {
+ if (val.match(/^[0-9]([0-9]*)$/)) {
+ // Treat an all-numeric field as UTC milliseconds since the epoch.
+ return moment.utc(parseInt(val, 10));
+ }
+ // Look for approved date formats.
+ var toTry = [
+ "YYYY-MM-DDTHH:mm:ss,SSS",
+ "YYYY-MM-DDTHH:mm:ss",
+ "YYYY-MM-DDTHH:mm",
+ "YYYY-MM-DD"
+ ];
+ for (var i = 0; i < toTry.length; i++) {
+ var m = moment.utc(val, toTry[i], true);
+ if (m.isValid()) {
+ return m;
+ }
+ }
+ throw "Please enter the date either as YYYY-MM-DDTHH:mm:ss,SSS " +
+ "in UTC, or as the number of milliseconds since the epoch.";
+};
+
+// Convert a moment.js moment into an ISO8601-style date string.
+htrace.dateToString = function(val) {
+ return moment.utc(val).format("YYYY-MM-DDTHH:mm:ss,SSS");
+};
+
+// Normalize a span ID into the format the server expects to see
+// (no leading 0x).
+htrace.normalizeSpanId = function(str) {
+ // Strip off the 0x prefix, if there is one.
+ if (str.indexOf("0x") == 0) {
+ str = str.substring(2);
+ }
+ if (str.length != 16) {
+ throw "The length of '" + str + "' was " + str.length +
+ ", but span IDs must be 16 characters long.";
+ }
+ if (str.search(/[^0-9a-fA-F]/) != -1) {
+ throw "Span IDs must contain only hexadecimal digits, but '" + str +
+ "' contained invalid characters.";
+ }
+ return str;
+};
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/time_cursor.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/time_cursor.js b/htrace-htraced/src/web/app/time_cursor.js
new file mode 100644
index 0000000..0060abb
--- /dev/null
+++ b/htrace-htraced/src/web/app/time_cursor.js
@@ -0,0 +1,74 @@
+/*
+ * 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 htrace = htrace || {};
+
+// Draws a vertical bar selecting a time.
+htrace.TimeCursor = function(params) {
+ this.selectedTime = -1;
+ for (var k in params) {
+ this[k]=params[k];
+ }
+
+ this.positionToTime = function(x) {
+ if ((x < this.x0) || (x > this.xF)) {
+ return -1;
+ }
+ return this.begin +
+ (((x - this.x0) * (this.end - this.begin)) / (this.xF - this.x0));
+ };
+
+ this.timeToPosition = function(time) {
+ return this.x0 + (((time - this.begin) *
+ (this.xF - this.x0)) / (this.end - this.begin));
+ };
+
+ this.draw = function() {
+ if (this.selectedTime != -1) {
+ this.ctx.save();
+ this.ctx.beginPath();
+ this.ctx.rect(this.x0, this.y0,
+ this.xF - this.x0, this.yF - this.y0);
+ this.ctx.clip();
+ this.ctx.strokeStyle="#ff0000";
+ var x = this.timeToPosition(this.selectedTime);
+ this.ctx.beginPath();
+ this.ctx.moveTo(x, this.y0);
+ this.ctx.lineTo(x, this.yF);
+ this.ctx.stroke();
+ this.ctx.restore();
+ }
+ };
+
+ this.handleMouseMove = function(x, y) {
+ if ((y >= this.y0) && (y <= this.yF) &&
+ (x >= this.x0) && (x <= this.xF)) {
+ this.selectedTime = this.positionToTime(x);
+ if (this.selectedTime < 0) {
+ $(this.el).val("");
+ } else {
+ $(this.el).val(htrace.dateToString(this.selectedTime));
+ }
+ return true;
+ }
+ return false;
+ };
+
+ return this;
+};
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/triangle_button.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/triangle_button.js b/htrace-htraced/src/web/app/triangle_button.js
new file mode 100644
index 0000000..89f9514
--- /dev/null
+++ b/htrace-htraced/src/web/app/triangle_button.js
@@ -0,0 +1,103 @@
+/*
+ * 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 htrace = htrace || {};
+
+// Triangle button widget.
+htrace.TriangleButton = function(params) {
+ this.fgColor = "#6600ff";
+ this.bgColor = "#ffffff";
+ this.selected = false;
+ this.direction = "down";
+
+ this.draw = function() {
+ this.ctx.save();
+ var fg = this.selected ? this.bgColor : this.fgColor;
+ var bg = this.selected ? this.fgColor : this.bgColor;
+ this.ctx.beginPath();
+ this.ctx.rect(this.x0, this.y0,
+ this.xF - this.x0, this.yF - this.y0);
+ this.ctx.clip();
+ this.ctx.fillStyle = bg;
+ this.ctx.strokeStyle = fg;
+ this.ctx.fillRect(this.x0, this.y0,
+ this.xF - this.x0, this.yF - this.y0);
+ this.ctx.lineWidth = 3;
+ this.ctx.strokeRect(this.x0, this.y0,
+ this.xF - this.x0, this.yF - this.y0);
+ var xPad = (this.xF - this.x0) / 5;
+ var yPad = (this.yF - this.y0) / 5;
+ this.ctx.fillStyle = fg;
+ this.ctx.strokeStyle = fg;
+ this.ctx.beginPath();
+ this.ctx.strokeStyle = fg;
+ if (this.direction === "up") {
+ this.ctx.moveTo(Math.floor(this.x0 + ((this.xF - this.x0) / 2)),
+ this.y0 + yPad);
+ this.ctx.lineTo(this.xF - xPad, this.yF - yPad);
+ this.ctx.lineTo(this.x0 + xPad, this.yF - yPad);
+ } else if (this.direction === "down") {
+ this.ctx.moveTo(this.x0 + xPad, this.y0 + yPad);
+ this.ctx.lineTo(this.xF - xPad, this.y0 + yPad);
+ this.ctx.lineTo(Math.floor(this.x0 + ((this.xF - this.x0) / 2)),
+ this.yF - yPad);
+ } else {
+ console.log("TriangleButton: unknown direction " + this.direction);
+ }
+ this.ctx.closePath();
+ this.ctx.fill();
+ this.ctx.restore();
+ };
+
+ this.inBoundingBox = function(x, y) {
+ return ((x >= this.x0) && (x <= this.xF) && (y >= this.y0) && (y <= this.yF));
+ }
+
+ this.handleMouseDown = function(x, y) {
+// console.log("TriangleButton#handleMouseDown(x=" + x + ", y=" + y +
+// ", x0=" + this.x0 + ", y0="+ this.y0 +
+// ", xF=" + this.xF + ", yF=" + this.yF);
+ if (this.inBoundingBox(x,y)) {
+ this.selected = true;
+ return true;
+ }
+ return false;
+ }
+
+ this.handleMouseUp = function(x, y) {
+ if (this.selected) {
+ console.log("executing callback");
+ }
+ this.selected = false;
+ }
+
+ this.handleMouseMove = function(x, y) {
+ var selected = this.inBoundingBox(x,y);
+ if (this.selected != selected) {
+ this.selected = selected;
+ return true;
+ }
+ return false;
+ }
+
+ for (var k in params) {
+ this[k]=params[k];
+ }
+ return this;
+};
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/views/details/details.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/views/details/details.js b/htrace-htraced/src/web/app/views/details/details.js
deleted file mode 100644
index 2f79e1b..0000000
--- a/htrace-htraced/src/web/app/views/details/details.js
+++ /dev/null
@@ -1,47 +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.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;
- },
-
- "events": {
- "click": "swimlane"
- },
- "swimlane": function() {
- Backbone.history.navigate("!/swimlane/" + this.model.get("spanId"),
- {"trigger": true});
- }
-});
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/baaa3a52/htrace-htraced/src/web/app/views/graph/graph.js
----------------------------------------------------------------------
diff --git a/htrace-htraced/src/web/app/views/graph/graph.js b/htrace-htraced/src/web/app/views/graph/graph.js
deleted file mode 100644
index 7b4f89e..0000000
--- a/htrace-htraced/src/web/app/views/graph/graph.js
+++ /dev/null
@@ -1,262 +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.GraphView = Backbone.View.extend({
- initialize: function(options) {
- options = options || {};
-
- if (!options.id) {
- console.error("GraphView requires argument 'id' to uniquely identify this graph.");
- return;
- }
-
- _.bindAll(this, "render");
- this.collection.bind('change', this.render);
-
- var links = this.links = [];
- var linkTable = this.linkTable = {};
- var nodes = this.nodes = [];
- var nodeTable = this.nodeTable = {};
- var force = this.force
- = d3.layout.force().size([$(window).width(), $(window).height() * 3/4])
- .linkDistance($(window).height() / 5)
- .charge(-120)
- .gravity(0)
- ;
- force.nodes(nodes)
- .links(links);
-
- force.on("tick", function(e) {
- var root = d3.select("#" + options.id);
-
- if (!root.node()) {
- return;
- }
-
- var selectedDatum = root.select(".selected").datum();
-
- // center selected node
- root.select("svg").attr("width", $(root.node()).width());
- selectedDatum.x = root.select("svg").attr("width") / 2;
- selectedDatum.y = root.select("svg").attr("height") / 2;
-
- // Push sources up and targets down to form a weak tree.
- var k = 10 * e.alpha;
- force.links().forEach(function(d, i) {
- d.source.y -= k;
- d.target.y += k;
- });
-
- var nodes = root.selectAll(".node").data(force.nodes());
- nodes.select("circle")
- .attr("cx", function(d) { return d.x; })
- .attr("cy", function(d) { return d.y; });
- nodes.select("text")
- .attr("x", function(d) { return d.x - this.getComputedTextLength() / 2; })
- .attr("y", function(d) { return d.y; });
- root.selectAll(".link").data(force.links())
- .attr("d", function(d) {
- var start = {},
- end = {},
- angle = Math.atan2((d.target.x - d.source.x), (d.target.y - d.source.y));
- start.x = d.source.x + d.source.r * Math.sin(angle);
- end.x = d.target.x - d.source.r * Math.sin(angle);
- start.y = d.source.y + d.source.r * Math.cos(angle);
- end.y = d.target.y - d.source.r * Math.cos(angle);
- return "M" + start.x + " " + start.y
- + " L" + end.x + " " + end.y;
- });
- });
- },
-
- updateLinksAndNodes: function() {
- if (!this.spanId) {
- return;
- }
-
- var $this = this, collection = this.collection;
-
- var selectedSpan = this.collection.findWhere({
- "spanId": this.spanId
- });
-
- var findChildren = function(span) {
- var spanId = span.get("spanId");
- var spans = collection.filter(function(model) {
- return _(model.get("parents")).contains(spanId);
- });
- return _(spans).reject(function(span) {
- return span == null;
- });
- };
- var findParents = function(span) {
- var spans = _(span.get("parents")).map(function(parentSpanId) {
- return collection.findWhere({
- "spanId": parentSpanId
- });
- });
- return _(spans).reject(function(span) {
- return span == null;
- });
- };
- var spanToNode = function(span, level) {
- var table = $this.nodeTable;
- if (!(span.get("spanId") in table)) {
- table[span.get("spanId")] = {
- "name": span.get("spanId"),
- "span": span,
- "level": level,
- "group": 0,
- "x": parseInt($this.svg.attr('width')) / 2,
- "y": 250 + level * 50
- };
- $this.nodes.push(table[span.get("spanId")]);
- }
-
- return table[span.get("spanId")];
- };
- var createLink = function(source, target) {
- var table = $this.linkTable;
- var name = source.span.get("spanId") + "-" + target.span.get("spanId");
- if (!(name in table)) {
- table[name] = {
- "source": source,
- "target": target
- };
- $this.links.push(table[name]);
- }
-
- return table[name];
- };
-
- var parents = [], children = [];
- var selectedSpanNode = spanToNode(selectedSpan, 1);
-
- Array.prototype.push.apply(parents, findParents(selectedSpan));
- _(parents).each(function(span) {
- Array.prototype.push.apply(parents, findParents(span));
- createLink(spanToNode(span, 0), selectedSpanNode)
- });
-
- Array.prototype.push.apply(children, findChildren(selectedSpan));
- _(children).each(function(span) {
- Array.prototype.push.apply(children, findChildren(span));
- createLink(selectedSpanNode, spanToNode(span, 2))
- });
- },
-
- renderLinks: function(selection) {
- var path = selection.enter().append("path")
- .classed("link", true)
- .style("marker-end", "url(#suit)");
- selection.exit().remove();
- return selection;
- },
-
- renderNodes: function(selection) {
- var $this = this;
- var g = selection.enter().append("g").attr("class", "node");
- var circle = g.append("circle")
- .attr("r", function(d) {
- if (!d.radius) {
- d.r = Math.log(d.span.duration());
-
- if (d.r > app.GraphView.MAX_NODE_SIZE) {
- d.r = app.GraphView.MAX_NODE_SIZE;
- }
-
- if (d.r < app.GraphView.MIN_NODE_SIZE) {
- d.r = app.GraphView.MIN_NODE_SIZE;
- }
- }
-
- return d.r;
- });
- var text = g.append("text").text(function(d) {
- return d.span.get("description");
- });
-
- selection.exit().remove();
-
- circle.on("click", function(d) {
- $this.setSpanId(d.name);
- });
-
- selection.classed("selected", null);
- selection.filter(function(d) {
- return d.span.get("spanId") == $this.spanId;
- }).classed("selected", true);
-
- return selection;
- },
-
- setSpanId: function(spanId) {
- var $this = this;
- this.spanId = spanId;
-
- this.updateLinksAndNodes();
-
- this.renderNodes(
- this.svg.selectAll(".node")
- .data(this.force.nodes(), function(d) {
- return d.name;
- }));
-
- this.renderLinks(
- this.svg.selectAll(".link")
- .data(this.force.links(), function(d) {
- return d.source.name + "-" + d.target.name;
- }));
-
- this.force.start();
-
- Backbone.history.navigate("!/spans/" + spanId);
- this.trigger("update:span", {"span": this.collection.findWhere({
- "spanId": spanId
- })});
- },
-
- render: function() {
- this.svg = d3.select(this.$el[0]).append("svg");
- this.svg.attr("height", 500)
- .attr("width", $(window).width())
- .attr("id", this.id);
-
- // Arrows
- this.svg.append("defs").selectAll("marker")
- .data(["suit", "licensing", "resolved"])
- .enter().append("marker")
- .attr("id", function(d) { return d; })
- .attr("viewBox", "0 -5 10 10")
- .attr("refX", 25)
- .attr("refY", 0)
- .attr("markerWidth", 6)
- .attr("markerHeight", 6)
- .attr("orient", "auto")
- .append("path")
- .attr("d", "M0,-5L10,0L0,5 L10,0 L0, -5")
- .style("stroke", "#4679BD")
- .style("opacity", "0.6");
-
- return this;
- }
-});
-
-app.GraphView.MAX_NODE_SIZE = 150;
-app.GraphView.MIN_NODE_SIZE = 50;