You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zipkin.apache.org by ad...@apache.org on 2019/05/20 09:52:47 UTC
[incubator-zipkin] branch master updated: Deleting Zipkin UI (#2605)
This is an automated email from the ASF dual-hosted git repository.
adriancole pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-zipkin.git
The following commit(s) were added to refs/heads/master by this push:
new 5e82715 Deleting Zipkin UI (#2605)
5e82715 is described below
commit 5e827151dc59e5c847bb5b7f245f89c0d48acf7a
Author: Raja Sundaram <47...@users.noreply.github.com>
AuthorDate: Mon May 20 17:52:38 2019 +0800
Deleting Zipkin UI (#2605)
---
LICENSE | 10 -
pom.xml | 10 -
{zipkin-ui => zipkin-lens}/testdata/README.md | 0
{zipkin-ui => zipkin-lens}/testdata/ascend.json | 0
.../testdata/messaging-kafka.json | 0
{zipkin-ui => zipkin-lens}/testdata/messaging.json | 0
.../testdata/messaging2.json | 0
{zipkin-ui => zipkin-lens}/testdata/netflix.json | 0
.../testdata/simple-db-p6.json | 0
{zipkin-ui => zipkin-lens}/testdata/skew.json | 0
.../testdata/smartthings-mobile-web-install.json | 0
.../testdata/smartthings-oauth-authorization.json | 0
zipkin-ui/.babelrc | 4 -
zipkin-ui/.bowerrc | 3 -
zipkin-ui/.eslintrc.js | 17 -
zipkin-ui/.gitignore | 4 -
zipkin-ui/README.md | 166 -
zipkin-ui/bower.json | 8 -
zipkin-ui/css/dependency.css | 79 -
zipkin-ui/css/main.scss | 74 -
zipkin-ui/css/style-loader.js | 26 -
zipkin-ui/css/summary.css | 180 -
zipkin-ui/css/trace.css | 232 -
zipkin-ui/css/traces.css | 216 -
zipkin-ui/index.ejs | 49 -
zipkin-ui/js/component_data/default.js | 127 -
zipkin-ui/js/component_data/dependency.js | 97 -
zipkin-ui/js/component_data/remoteServiceNames.js | 45 -
zipkin-ui/js/component_data/serviceNames.js | 36 -
zipkin-ui/js/component_data/skew.js | 178 -
zipkin-ui/js/component_data/spanCleaner.js | 269 -
zipkin-ui/js/component_data/spanNames.js | 41 -
zipkin-ui/js/component_data/spanNode.js | 293 -
zipkin-ui/js/component_data/trace.js | 50 -
zipkin-ui/js/component_ui/backToTop.js | 39 -
zipkin-ui/js/component_ui/dependencyGraph.js | 211 -
zipkin-ui/js/component_ui/environment.js | 23 -
zipkin-ui/js/component_ui/error.js | 41 -
zipkin-ui/js/component_ui/filterAllServices.js | 66 -
zipkin-ui/js/component_ui/fullPageSpinner.js | 38 -
zipkin-ui/js/component_ui/goToDependency.js | 31 -
zipkin-ui/js/component_ui/goToLens.js | 30 -
zipkin-ui/js/component_ui/goToTrace.js | 30 -
zipkin-ui/js/component_ui/i18n.js | 43 -
zipkin-ui/js/component_ui/infoButton.js | 27 -
zipkin-ui/js/component_ui/infoPanel.js | 30 -
zipkin-ui/js/component_ui/jsonPanel.js | 41 -
zipkin-ui/js/component_ui/lookback.js | 47 -
zipkin-ui/js/component_ui/navbar.js | 39 -
zipkin-ui/js/component_ui/remoteServiceName.js | 53 -
zipkin-ui/js/component_ui/serviceDataModal.js | 111 -
zipkin-ui/js/component_ui/serviceFilterSearch.js | 33 -
zipkin-ui/js/component_ui/serviceName.js | 76 -
zipkin-ui/js/component_ui/spanName.js | 53 -
zipkin-ui/js/component_ui/spanPanel.js | 156 -
zipkin-ui/js/component_ui/spanRow.js | 352 -
zipkin-ui/js/component_ui/timeStamp.js | 57 -
zipkin-ui/js/component_ui/trace.js | 483 -
zipkin-ui/js/component_ui/traceConstants.js | 69 -
zipkin-ui/js/component_ui/traceFilters.js | 64 -
zipkin-ui/js/component_ui/traceSummary.js | 212 -
zipkin-ui/js/component_ui/traceToMustache.js | 148 -
zipkin-ui/js/component_ui/traces.js | 51 -
zipkin-ui/js/component_ui/uploadTrace.js | 78 -
zipkin-ui/js/component_ui/zoomOutSpans.js | 27 -
zipkin-ui/js/config.js | 42 -
zipkin-ui/js/main.js | 48 -
zipkin-ui/js/page/common.js | 36 -
zipkin-ui/js/page/default.js | 133 -
zipkin-ui/js/page/dependency.js | 53 -
zipkin-ui/js/page/trace.js | 73 -
zipkin-ui/js/page/traceViewer.js | 85 -
zipkin-ui/js/publicPath.js | 32 -
zipkin-ui/js/templates.js | 22 -
zipkin-ui/karma.conf.js | 84 -
zipkin-ui/libs/chosen/chosen-sprite.png | Bin 538 -> 0 bytes
zipkin-ui/libs/chosen/chosen-sprite@2x.png | Bin 738 -> 0 bytes
zipkin-ui/libs/chosen/chosen.css | 450 -
zipkin-ui/libs/dagre-d3/.bower.json | 25 -
zipkin-ui/libs/dagre-d3/bower.json | 14 -
zipkin-ui/libs/dagre-d3/js/dagre-d3.js | 5003 -------
zipkin-ui/libs/dagre-d3/js/dagre-d3.min.js | 2 -
zipkin-ui/npm.sh | 27 -
zipkin-ui/package-lock.json | 13974 -------------------
zipkin-ui/package.json | 72 -
zipkin-ui/pom.xml | 102 -
zipkin-ui/static/dep.properties | 22 -
zipkin-ui/static/dep_it_IT.properties | 20 -
zipkin-ui/static/dep_zh_CN.properties | 22 -
zipkin-ui/static/dep_zh_TW.properties | 20 -
zipkin-ui/static/favicon.ico | Bin 25407 -> 0 bytes
zipkin-ui/static/nav.properties | 23 -
zipkin-ui/static/nav_it_IT.properties | 21 -
zipkin-ui/static/nav_zh_CN.properties | 23 -
zipkin-ui/static/nav_zh_TW.properties | 21 -
zipkin-ui/static/trace.properties | 24 -
zipkin-ui/static/trace_it_IT.properties | 24 -
zipkin-ui/static/trace_zh_CN.properties | 24 -
zipkin-ui/static/trace_zh_TW.properties | 24 -
zipkin-ui/static/traces.properties | 57 -
zipkin-ui/static/traces_it_IT.properties | 46 -
zipkin-ui/static/traces_zh_CN.properties | 57 -
zipkin-ui/static/traces_zh_TW.properties | 46 -
zipkin-ui/templates/dependency.mustache | 108 -
zipkin-ui/templates/index.mustache | 268 -
zipkin-ui/templates/layout.mustache | 63 -
zipkin-ui/templates/searchDisabled.mustache | 19 -
zipkin-ui/templates/trace.mustache | 272 -
zipkin-ui/templates/traceViewer.mustache | 277 -
zipkin-ui/test/.eslintrc | 10 -
zipkin-ui/test/component_data/default.test.js | 231 -
zipkin-ui/test/component_data/skew.test.js | 328 -
zipkin-ui/test/component_data/spanCleaner.test.js | 1014 --
zipkin-ui/test/component_data/spanNode.test.js | 242 -
zipkin-ui/test/component_data/trace.test.js | 520 -
zipkin-ui/test/component_ui/serviceName.test.js | 29 -
zipkin-ui/test/component_ui/spanPanel.test.js | 153 -
zipkin-ui/test/component_ui/spanRow.test.js | 1229 --
zipkin-ui/test/component_ui/trace.test.js | 268 -
zipkin-ui/test/component_ui/traceSummary.test.js | 274 -
zipkin-ui/test/component_ui/traceTestHelpers.js | 126 -
.../test/component_ui/traceToMustache.test.js | 326 -
zipkin-ui/test/component_ui/uploadTrace.test.js | 74 -
zipkin-ui/test/config.test.js | 54 -
zipkin-ui/test/page/common.test.js | 26 -
zipkin-ui/webpack.config.js | 85 -
126 files changed, 31720 deletions(-)
diff --git a/LICENSE b/LICENSE
index 930beea..0c1111b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -214,13 +214,3 @@ This product contains a modified part of Okio, distributed by Square:
* License: Apache License v2.0
* Homepage: https://github.com/square/okio
-
-This product contains a modified part of Chosen, distributed by Harvest:
-
- * License: MIT
- * Homepage: https://github.com/harvesthq/chosen
-
-This product contains a modified part of Dager-D3, distributed by Dagre:
-
- * License: MIT
- * Homepage: https://github.com/dagrejs/dagre-d3
diff --git a/pom.xml b/pom.xml
index 3dcf546..9e80a9e 100755
--- a/pom.xml
+++ b/pom.xml
@@ -631,11 +631,6 @@
<exclude>**/.eslintrc</exclude>
<exclude>**/.eslintrc</exclude>
<exclude>**/.eslintrc.js</exclude>
-
- <!-- remove when deleting zipkin-ui #2569 -->
- <exclude>**/chosen.*</exclude>
- <exclude>**/dagre-d3.*</exclude>
-
<exclude>**/testdata/**/*.json</exclude>
<exclude>**/test/data/**/*.json</exclude>
<exclude>LICENSE</exclude>
@@ -742,11 +737,6 @@
<exclude>**/.editorconfig</exclude>
<exclude>**/.eslintrc</exclude>
<exclude>**/.eslintrc.js</exclude>
-
- <!-- remove when deleting zipkin-ui #2569 -->
- <exclude>**/chosen.*</exclude>
- <exclude>**/dagre-d3.*</exclude>
-
<!-- Maven Wrapper generated files -->
<exclude>.mvn/wrapper/maven-wrapper.properties</exclude>
diff --git a/zipkin-ui/testdata/README.md b/zipkin-lens/testdata/README.md
similarity index 100%
rename from zipkin-ui/testdata/README.md
rename to zipkin-lens/testdata/README.md
diff --git a/zipkin-ui/testdata/ascend.json b/zipkin-lens/testdata/ascend.json
similarity index 100%
rename from zipkin-ui/testdata/ascend.json
rename to zipkin-lens/testdata/ascend.json
diff --git a/zipkin-ui/testdata/messaging-kafka.json b/zipkin-lens/testdata/messaging-kafka.json
similarity index 100%
rename from zipkin-ui/testdata/messaging-kafka.json
rename to zipkin-lens/testdata/messaging-kafka.json
diff --git a/zipkin-ui/testdata/messaging.json b/zipkin-lens/testdata/messaging.json
similarity index 100%
rename from zipkin-ui/testdata/messaging.json
rename to zipkin-lens/testdata/messaging.json
diff --git a/zipkin-ui/testdata/messaging2.json b/zipkin-lens/testdata/messaging2.json
similarity index 100%
rename from zipkin-ui/testdata/messaging2.json
rename to zipkin-lens/testdata/messaging2.json
diff --git a/zipkin-ui/testdata/netflix.json b/zipkin-lens/testdata/netflix.json
similarity index 100%
rename from zipkin-ui/testdata/netflix.json
rename to zipkin-lens/testdata/netflix.json
diff --git a/zipkin-ui/testdata/simple-db-p6.json b/zipkin-lens/testdata/simple-db-p6.json
similarity index 100%
rename from zipkin-ui/testdata/simple-db-p6.json
rename to zipkin-lens/testdata/simple-db-p6.json
diff --git a/zipkin-ui/testdata/skew.json b/zipkin-lens/testdata/skew.json
similarity index 100%
rename from zipkin-ui/testdata/skew.json
rename to zipkin-lens/testdata/skew.json
diff --git a/zipkin-ui/testdata/smartthings-mobile-web-install.json b/zipkin-lens/testdata/smartthings-mobile-web-install.json
similarity index 100%
rename from zipkin-ui/testdata/smartthings-mobile-web-install.json
rename to zipkin-lens/testdata/smartthings-mobile-web-install.json
diff --git a/zipkin-ui/testdata/smartthings-oauth-authorization.json b/zipkin-lens/testdata/smartthings-oauth-authorization.json
similarity index 100%
rename from zipkin-ui/testdata/smartthings-oauth-authorization.json
rename to zipkin-lens/testdata/smartthings-oauth-authorization.json
diff --git a/zipkin-ui/.babelrc b/zipkin-ui/.babelrc
deleted file mode 100644
index 831f20a..0000000
--- a/zipkin-ui/.babelrc
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "presets": ["es2015"],
- "plugins": ["transform-object-rest-spread"]
-}
diff --git a/zipkin-ui/.bowerrc b/zipkin-ui/.bowerrc
deleted file mode 100644
index b4b9cc0..0000000
--- a/zipkin-ui/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "app/libs"
-}
diff --git a/zipkin-ui/.eslintrc.js b/zipkin-ui/.eslintrc.js
deleted file mode 100644
index 13f4f29..0000000
--- a/zipkin-ui/.eslintrc.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = {
- extends: 'airbnb',
- plugins: [],
- parserOptions: {
- ecmaFeatures: {
- experimentalObjectRestSpread: true
- }
- },
- rules: {
- 'comma-dangle': 'off',
- 'func-names': 'off',
- 'object-curly-spacing': [2, 'never'],
- 'space-before-function-paren': [2, 'never'],
- eqeqeq: [2, 'allow-null'],
- 'no-else-return': 'off'
- }
-};
diff --git a/zipkin-ui/.gitignore b/zipkin-ui/.gitignore
deleted file mode 100644
index 8f36902..0000000
--- a/zipkin-ui/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignores are placed here to allow node.js-idiomatic builds
-*.log
-node_modules
-dist
diff --git a/zipkin-ui/README.md b/zipkin-ui/README.md
deleted file mode 100644
index 337a6bd..0000000
--- a/zipkin-ui/README.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# zipkin-ui
-
-Zipkin-UI is a single-page application mounted at /zipkin. For simplicity,
-assume paths mentioned below are relative to that. For example, the UI
-reads config.json, from the absolute path /zipkin/config.json
-
-When looking at a trace, the browser is sent to the path "/traces/{id}".
-For the single-page app to serve that route, the server needs to forward
-the request to "/index.html". The same forwarding applies to "/dependencies"
-and any other routes the UI controls.
-
-Under the scenes the JavaScript code looks at window.location to figure
-out what the UI should do. This is handled by a route api defined in the
-crossroads library.
-
-The suggested logic for serving the assets of Zipkin-UI is as follows:
-
- 1. If the browser is requesting a file with an extension (that is, the last path segment has a `.` in it), then
- serve that file. If it doesn't exist, then return 404.
- 1. Otherwise, serve `index.html`.
-
-For an example implementation using Finatra, see [zipkin-query](https://github.com/apache/incubator-zipkin/blob/5dec252e4c562b21bac5ac2f9d0b437d90988f79/zipkin-query/src/main/scala/com/twitter/zipkin/query/ZipkinQueryController.scala).
-
-Note that in cases where a non-existent resource is requested, this logic returns the contents of `index.html`. When
-loaded as a web-page, the client application will handle the problem of telling the user about this. When not,
-it'll take an extra step to find where the problem is - you won't see a 404 in your network tab.
-
-## Why is it packaged as a `.jar` and not a `.zip`?
-
-Since many Zipkin servers are Java-based, it's convenient to distribute the UI as a jar, which can be imported by the
-Gradle build tool. A `.jar` file is really only a `.zip` file, and can be treated as such. It can be opened by any
-program that can extract zip files.
-
-## How do I run against a proxy zipkin-backend?
-
-By specifying the `proxy` environment variable, you can point the zipkin-ui to a different backend, allowing you to access real data while developing locally.
-An example to run with npm would be `proxy=http://myzipkininstance.com:9411/zipkin/ npm run dev`. (note that prefixing with http:// and suffixing the port is mandatory)
-
-## What's the easiest way to develop against this locally?
-
-There is one requirement not currently filled in our build. You need a recent
-version of Chrome (>= 59) installed. If the build doesn't detect this, you can
-explicitly assign a location with the variable [CHROME_BIN](https://github.com/karma-runner/karma-chrome-launcher).
-
-The maven install process downloads everything else needed to do development,
-so you don't need to install node/npm or whatever. Instead, you can use the
-`./npm.sh` shell script to perform npm operations. Here's how you launch zipkin
-server and webapp to work together:
-
-* In one terminal, go to the root of the zipkin repo and run this to build zipkin:
-
-```bash
-# In one terminal, build the server and also make its dependencies (run from the root of the zipkin repo)
-$ mvn -DskipTests --also-make -pl zipkin-server clean install
-# Run it!
-$ java -jar ./zipkin-server/target/zipkin-server-*exec.jar
-```
-
-* In another terminal, launch the zipkin UI server:
-
-```bash
-# Do this in another terminal!
-$ cd zipkin-ui
-$ proxy=http://localhost:9411/zipkin/ ./npm.sh run dev
-```
-
-This runs an NPM development server, which will automatically rebuild the webapp
-when you change the source code. You should now be able to access your local
-copy of zipkin-ui at http://localhost:9090.
-
-## What's an easy way to create new spans for testing?
-
-Using this setup, if you open the web UI and find a trace from zipkin-server,
-you can download a JSON blob by right clicking on the JSON button on the trace
-page (top right) and doing a "Save As". Modify the span to your heart's content,
-and then you can submit the span(s) via curl:
-
-```bash
-$ curl -H "Content-Type: application/json" --data @span.json http://localhost:9411/api/v1/spans
-```
-
-## How do I find logs associated with a particular trace
-
-Since zipkin provides a correlation id (the trace id), it's a good pattern to add it in your logs.
-If you use zipkin, it's very likely that you have distributed logging sytem also and a way to query your logs (e.g. ELK stack).
-
-One convenient way to switch from a trace to its logs is to get a button which directly links a trace to its logs.
-
-This feature can be activated by setting the property `zipkin.ui.logs-url` or its corresponding environment variable:
-
-* Kibana 3: `ZIPKIN_UI_LOGS_URL=http://kibana.company.com/query={traceId}`
-* Kibana 4 or newer: `ZIPKIN_UI_LOGS_URL=http://kibana.company.com/app/kibana#/discover?_a=(index:'filebeat-*',query:(language:lucene,query:'{traceId}'))`
- This assumes that the log data is stored under the `filebeat-*` index pattern (replace this with the correct pattern if needed) and it uses the default timerange of 15 minutes (change it to 24 hours by adding `&_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:now-24h,mode:quick,to:now))` to the URL for example).
-
-where `{traceId}` will be contextually replaced by the trace id.
-
-If this feature is activated, you'll see on the trace detail page an additional button named `logs`.
-
-
-
-## How do I make errors visible in yellow or red?
-The UI interprets an "error" tag as a failed span, coloring it red. It interprets an annotation containing the substring
-"error" as a transient failure. To ensure the UI displays errors, please use the [error key](https://zipkin.apache.org/public/thrift/v1/zipkinCore.html#Const_ERROR) appropriately.
-
-## How do I adjust the error rates in the dependency graph
-
-By default, the /dependency endpoint colors a link yellow when the error
-rate is 50% or higher, or red when it 75% or higher. You can control
-these rates via the `dependency.low-error-rate` and `dependency.high-error-rate`
-properties:
-
-Ex. To make lines yellow when there's a 10% error rate, set:
-`ZIPKIN_UI_DEPENDENCY_LOW_ERROR_RATE=0.1`
-
-To disable coloring of lines, set both rates to a number higher than 1.
-
-## Running behind a reverse proxy
-Starting with Zipkin `1.31.2`, Zipkin UI supports running under an arbitrary _context root_. As a result, it can be proxied
-under a different path than `/zipkin/` such as `/proxy/foo/bar/zipkin/`.
-
-> Note that Zipkin requires the last path segment to be `zipkin`.
-
-> Also note that due to `html-webpack-plugin` limitations, Zipkin UI relies on a
-[`base` tag](https://www.w3schools.com/TAgs/tag_base.asp) and its `href` attribute to be set in the `index.html` file.
-By default its value is `/zipkin/` and as such the reverse proxy must rewrite the value to an alternate _context root_.
-
-### Apache HTTP as a Zipkin reverse proxy
-To configure Apache HTTP as a reverse proxy, following minimal configuration is required.
-
-```
-LoadModule proxy_module libexec/apache2/mod_proxy.so
-LoadModule proxy_html_module libexec/apache2/mod_proxy_html.so
-LoadModule proxy_http_module libexec/apache2/mod_proxy_http.so
-
-ProxyPass /proxy/foo/bar/ http://localhost:9411/
-SetOutputFilter proxy-html
-ProxyHTMLURLMap /zipkin/ /proxy/foo/bar/zipkin/
-ProxyHTMLLinks base href
-```
-
-To access Zipkin UI behind the reverse proxy, execute:
-```bash
-$ curl http://localhost/proxy/foo/bar/zipkin/
-<html><head><!--
- add 'base' tag to work around the fact that 'html-webpack-plugin' does not work
- with '__webpack_public_path__' being set as reported at https://github.com/jantimon/html-webpack-plugin/issues/119
- --><base href="/proxy/foo/bar/zipkin/"><link rel="icon" type="image/x-icon" href="favicon.ico"><meta charset="UTF-8"><title>Webpack App</title><link href="app-94a6ee84dc608c5f9e66.min.css" rel="stylesheet"></head><body>
- <script type="text/javascript" src="app-94a6ee84dc608c5f9e66.min.js"></script></body></html>
-```
-As you would see, the attribute `href` of the `base` tag is rewritten which is the way to get around the
-`html-webpack-plugin` limitations.
-
-Uploading the span is easy as
-```bash
-$ curl -H "Content-Type: application/json" --data-binary "[$(cat ../benchmarks/src/main/resources/span-local.json)]" http://localhost/proxy/foo/bar/api/v1/spans
-```
-
-And then it's observable in the UI:
-```bash
-$ open http://localhost/proxy/foo/bar/zipkin/?serviceName=zipkin-server&startTs=1378193040000&endTs=1505463856013
-```
-### How do I configure security (authentication, authorization)?
-
-Zipkin UI can be secured by running it behind an authenticating proxy like [Apache HTTPD](https://httpd.apache.org/docs/current/howto/auth.html), [Nginx](https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html) or similar. Make sure to also consult the [notes](#apache-http-as-a-zipkin-reverse-proxy) on running apache http as a reverse proxy for the UI, as it can be a bit tricky.
-
-Note that by default, a Zipkin server runs both the UI ('/zipkin') and the span collector ('/api') endpoint. Your configuration to secure the UI should only target the UI endpoint in order to not prevent clients from ingesting span data.
diff --git a/zipkin-ui/bower.json b/zipkin-ui/bower.json
deleted file mode 100644
index a87cb47..0000000
--- a/zipkin-ui/bower.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "name": "zipkin",
- "version": "0.0.0",
- "dependencies": {
- "chosen": "https://github.com/harvesthq/chosen/releases/download/v1.1.0/chosen_v1.1.0.zip",
- "dagre-d3": "~0.2.9"
- }
-}
diff --git a/zipkin-ui/css/dependency.css b/zipkin-ui/css/dependency.css
deleted file mode 100644
index c1efac7..0000000
--- a/zipkin-ui/css/dependency.css
+++ /dev/null
@@ -1,79 +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.
- */
-svg {
- overflow: hidden;
- font-size: 12px;
-}
-
-.node rect {
- stroke: #333;
- stroke-width: 1px;
-}
-
-.node text {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- cursor: default;
-}
-
-.edgeLabel rect {
- fill: #fff;
-}
-
-.edgePath {
- stroke: #333;
- stroke-width: 1px;
- fill: none;
-}
-
-.edgePath path {
- padding: 20px;
-}
-
-svg {
- transition: background-color 800ms;
-}
-
-svg.dark {
- background-color: #7a7a7a;
-}
-
-svg.dark .node:not(.hover):not(.hover-light) rect {
- opacity: 0.3;
-}
-
-svg.dark g.node {
- fill: #fff;
-}
-
-g.hover {
- fill: #fff;
-}
-
-g.hover rect {
- fill: #4058ff;
-}
-
-g.hover-edge {
- stroke: #4058ff;
-}
-
-g.hover-light rect {
- fill: #8a9eff;
-}
diff --git a/zipkin-ui/css/main.scss b/zipkin-ui/css/main.scss
deleted file mode 100644
index 4f79ea0..0000000
--- a/zipkin-ui/css/main.scss
+++ /dev/null
@@ -1,74 +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.
-//
-
-@import "~bootstrap/scss/bootstrap.scss";
-@import "~bootstrap4c-chosen/dist/css/component-chosen.min.css";
-@import "~bootstrap-datepicker/dist/css/bootstrap-datepicker3.css";
-$fa-font-path: "~@fortawesome/fontawesome-free/webfonts" !default;
-@import "~@fortawesome/fontawesome-free/scss/fontawesome.scss";
-@import "~@fortawesome/fontawesome-free/scss/regular.scss";
-@import "~@fortawesome/fontawesome-free/scss/solid.scss";
-
-@import "dependency";
-@import "summary";
-@import "trace";
-@import "traces";
-
-.content {
- padding: 2%;
-}
-
-.chosen-container .chosen-single {
- height: 38px;
-}
-
-#errorPanel {
- display: none;
-}
-
-#fullPageSpinner {
- display: none;
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0px;
- padding-top: 10%;
- background: rgba(256, 256, 256, 0.5);
-}
-
-#fullPageSpinner .progress {
- margin-left: 25%;
- width: 50%;
-}
-
-/*for page up button*/
-#backToTop {
- background-color: #0084B4;
-}
-
-/*for zoomout button*/
-#zoomOutSpans {
- background-color: #d9534f;
-}
-
-/*container for zoomout, pageup buttons*/
-#extraButtonsContainer {
- position: fixed;
- bottom: 0;
- right: 0;
- z-index: 100;
-}
diff --git a/zipkin-ui/css/style-loader.js b/zipkin-ui/css/style-loader.js
deleted file mode 100644
index 7b0a481..0000000
--- a/zipkin-ui/css/style-loader.js
+++ /dev/null
@@ -1,26 +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.
- */
-// We use this file to let webpack generate
-// a css bundle from our stylesheets.
-
-// The import of 'publicPath' module has to be the first statement in this entry point file
-// so that '__webpack_public_path__' (see https://webpack.github.io/docs/configuration.html#output-publicpath)
-// is set soon enough.
-// In the same time, 'contextRoot' is made available as the context root path reference.
-import {contextRoot} from '../js/publicPath';
-
-require('./main.scss');
diff --git a/zipkin-ui/css/summary.css b/zipkin-ui/css/summary.css
deleted file mode 100644
index 27b7f05..0000000
--- a/zipkin-ui/css/summary.css
+++ /dev/null
@@ -1,180 +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.
- */
-#summary {
- list-style: none;
- margin: 10px 0 0 0;
- padding: 0;
-}
-
-#summary a {
- text-decoration: none;
-}
-
-#summary li {
- display: block;
- padding: 5px;
- padding-bottom: 20px;
- margin: 20px 0px 20px;
- list-style: none;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- background-color: rgba(255,255,255,1);
-
- -webkit-transition: background-color .3s;
-}
-
-#summary li:after {
- content: '';
- display: block;
- width: 75%;
- margin: 15px auto -35px;
- border-bottom: 1px solid #EEE;
-}
-
-#summary li:first-child {
- margin-top: 0px;
-}
-
-#summary li:hover {
- background-color: rgba(221,238,246,1);
-}
-
-
-#summary li div {
- display: block;
- padding: 2px 0px;
- margin-bottom: 0px;
- color: #999;
-
- -webkit-transition: .3s;
-}
-
-#summary li .bar-block {
- position: relative;
- line-height: 20px;
- padding: 0px;
- margin: 2px 0;
- background-color: rgba(255, 255, 255, .75);
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-#summary li .bar-graphic {
- position: absolute;
- top: 0px;
- left: 0px;
- display: block;
- height: 20px;
- z-index: 0;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- background-color: #c0deed;
-
- -webkit-transition: .3s;
-}
-
-#summary li:hover .bar-graphic {
- background-color: #0084B4;
-}
-
-#summary li .bar-label {
- position: relative;
- margin-left: 6px;
- padding: 2px 4px;
- color: #0084B4;
- font-weight: bold;
- z-index: 1;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- -webkit-transition: .3s;
-}
-
-#summary li:hover .bar-label {
- color: #FFF;
- background-color: rgba(0,132,180,.35);
-}
-
-#summary .trace-id {
- font-size: 85%;
- color: #0084B4;
- background-color: rgba(0,132,180,.35);
- border-radius: 4px;
-}
-
-#summary .trace-id:hover {
- font-size: 85%;
- color: #FFF;
- background-color: #0084B4;
-}
-
-#summary li div span.label {
- -webkit-transition: .3s;
-}
-
-#summary li div p {
- margin-bottom: 0;
- font-size: 11px;
- font-weight: bold;
- line-height: 11px;
-}
-
-#summary li .summary-details {
- display: inline-block;
-}
-
-#summary li .durations {
- width: 80%;
-}
-
-#summary li .durations span {
- cursor: pointer;
-}
-
-#summary li .timestamp {
- width: 19%;
- text-align: right;
-}
-
-#summary li:hover .durations .label {
- background-color: #F89406;
-}
-
-#summary li .durations .label:hover,
-#summary li .durations .label:hover span {
- background-color: red;
-}
-
-#summary li .timestamp .label {
- background-color: #FAFAFA;
- color: #AAA;
-}
-
-#summary li:hover .timestamp .label {
- background-color: #FFF;
- color: #666;
-}
diff --git a/zipkin-ui/css/trace.css b/zipkin-ui/css/trace.css
deleted file mode 100644
index c1eed1a..0000000
--- a/zipkin-ui/css/trace.css
+++ /dev/null
@@ -1,232 +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.
- */
-#trace-container {
- font-size: 12px;
- margin-bottom: 50px;
-}
-
-#trace-container .tag {
- display: none;
-}
-
-#trace-container .rect-element {
- border-style: dashed;
- border-width: medium;
- position: absolute;
- z-index: 1;
- border-color: #d9534f;
-}
-
-#trace-container .span {
- cursor: pointer;
- display: none;
- position: relative;
- border-bottom: solid 1px #fff;
- border-top: solid 1px #fff;
-}
-
-#trace-container .service-span:hover {
- background: #eee;
- border-color: #ccc;
-}
-
-#trace-container .span:after {
- content: ".";
- display: block;
- clear: both;
- visibility: hidden;
- line-height: 0;
- height: 0;
-}
-
-#trace-container .span .handle {
- overflow: hidden;
- white-space: nowrap;
- float: left;
- opacity: 0.25;
-}
-
-/* ensure spans are shown even if the don't have a name */
-#trace-container .span .handle .service-name:after {
- content: ".";
- visibility: hidden;
-}
-
-#trace-container .span.highlight .handle,
-#trace-container .service-span:hover .handle {
- opacity: 1;
-}
-
-#trace-container .span .handle .expander {
- margin-left: -1.1em;
- color: #000;
- padding: 2px;
- margin-right: 0.25em;
- cursor: pointer;
- font-family: monospace;
-}
-
-#trace-container .span .handle .service-name {
- background: #0084b4;
- color: #fff;
- padding: 2px;
- max-width: 12em;
-}
-
-#trace-container .span .duration-container {
- position: relative;
- margin-left: 155px;
-}
-
-#trace-container #timeLabel.span {
- overflow: visible;
- display: block;
- white-space: nowrap;
-}
-
-#trace-container #timeLabel.span .handle {
- opacity: 1;
-}
-
-#trace-container #timeLabel.span .time-marker {
- margin-left: -1%;
-}
-
-#trace-container .span .time-marker {
- position: absolute;
- color: #333;
-}
-
-#trace-container .span .time-marker-0 { left: 0; width: 1px;}
-#trace-container .span .time-marker-1 { left: 20%; width: 1px;}
-#trace-container .span .time-marker-2 { left: 40%; width: 1px;}
-#trace-container .span .time-marker-3 { left: 60%; width: 1px;}
-#trace-container .span .time-marker-4 { left: 80%; width: 1px;}
-#trace-container .span .time-marker-5 { left: 99%; width: 1px;}
-
-#trace-container .span .duration {
- position: absolute;
- background: #ccc;
- overflow: visible;
- white-space: nowrap;
- padding: 2px;
-}
-
-#trace-container .span .duration .annotation {
- position: absolute;
- top: 5px;
- height: 10px;
- width: 10px;
- background: #fff;
- border: solid 1px #000;
- border-radius: 1em;
- margin-left: -6px;
-}
-
-/*
- * Derived annotations like "Server Start" "Server Finish" are hidden to make
- * the user-defined ones more obvious.
- */
-#trace-container .span .duration .annotation.derived {
- display: none;
-}
-
-#trace-container .span .duration .annotation[data-value="error"] {
- border: solid 1px #ff0000;
- background: #ff0000;
-}
-
-#trace-container .span .duration .tooltip {
- white-space: normal;
-}
-
-#trace-container .span.depth-0 .duration { background: rgba(66, 146, 198, 0.1); }
-#trace-container .span.depth-1 .duration { background: rgba(66, 146, 198, 0.20); }
-#trace-container .span.depth-2 .duration { background: rgba(66, 146, 198, 0.40); }
-#trace-container .span.depth-3 .duration { background: rgba(66, 146, 198, 0.60); }
-#trace-container .span.depth-4 .duration { background: rgba(66, 146, 198, 0.80); }
-#trace-container .span.depth-5 .duration { background: rgba(66, 146, 198, 1); }
-
-#trace-container .span[data-error-type="transient"] .handle .service-name {
- background: #8a6d3b;
-}
-
-#trace-container .span[data-error-type="transient"].depth-0 .duration { background: rgba(230, 215, 140, 0.1); }
-#trace-container .span[data-error-type="transient"].depth-1 .duration { background: rgba(230, 215, 140, 0.20); }
-#trace-container .span[data-error-type="transient"].depth-2 .duration { background: rgba(230, 215, 140, 0.40); }
-#trace-container .span[data-error-type="transient"].depth-3 .duration { background: rgba(230, 215, 140, 0.60); }
-#trace-container .span[data-error-type="transient"].depth-4 .duration { background: rgba(230, 215, 140, 0.80); }
-#trace-container .span[data-error-type="transient"].depth-5 .duration { background: rgba(230, 215, 140, 1); }
-
-#trace-container .span[data-error-type="critical"] .handle .service-name {
- background: #a94442;
-}
-
-#trace-container .span[data-error-type="critical"].depth-0 .duration { background: rgba(230, 162, 161, 0.1); }
-#trace-container .span[data-error-type="critical"].depth-1 .duration { background: rgba(230, 162, 161, 0.20); }
-#trace-container .span[data-error-type="critical"].depth-2 .duration { background: rgba(230, 162, 161, 0.40); }
-#trace-container .span[data-error-type="critical"].depth-3 .duration { background: rgba(230, 162, 161, 0.60); }
-#trace-container .span[data-error-type="critical"].depth-4 .duration { background: rgba(230, 162, 161, 0.80); }
-#trace-container .span[data-error-type="critical"].depth-5 .duration { background: rgba(230, 162, 161, 1); }
-
-#trace-controls form {
- margin-bottom: 10px;
-}
-
-/* hide the fact that these are links */
-#trace-controls .nav-pills a {
- color: #000;
- cursor: default;
- background: rgba(0, 0, 0, 0);
-}
-
-#trace-controls .nav-pills .badge {
- background: #428bca;
-}
-
-#spanPanel tr.anno-error-transient {
- color: #8a6d3b;
- background: #fcf8e3;
-}
-
-#spanPanel tr.anno-error-critical {
- color: #a94442;
- background: #f2dede;
-}
-
-#spanPanel #tags td.value {
- word-break: break-all;
- max-width: 75%;
-}
-#spanPanel #annotations td.value {
- word-break: break-all;
- max-width: 75%;
-}
-#spanPanel #debugInfo td.value {
- word-break: break-all;
- max-width: 75%;
-}
-
-.modal-header .save {
- float: right;
- margin-right: 5px;
-}
-
-/* .save {
- opacity: 0.2;
-} */
-
diff --git a/zipkin-ui/css/traces.css b/zipkin-ui/css/traces.css
deleted file mode 100644
index f4ad956..0000000
--- a/zipkin-ui/css/traces.css
+++ /dev/null
@@ -1,216 +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.
- */
-#trace-filters .label {
- margin-right: 5px;
-}
-
-.date-input, .time-input {
- width: 49%;
- display: inline-block;
-}
-
-.service-filter-remove {
- cursor: pointer;
- border-radius: 0;
- margin-left: 5px;
-}
-
-.service-filter-remove:hover {
- background: red;
-}
-
-#traces {
- list-style: none;
- margin: 10px 0 0 0;
- padding: 0;
-}
-
-#traces a {
- text-decoration: none;
-}
-
-#traces li {
- display: block;
- padding: 5px 0;
- padding-bottom: 20px;
- margin: 20px 0px 20px;
- list-style: none;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- background-color: rgba(255,255,255,1);
-
- -webkit-transition: background-color .3s;
-}
-
-#traces li:after {
- content: '';
- display: block;
- width: 75%;
- margin: 15px auto -35px;
- border-bottom: 1px solid #EEE;
-}
-
-#traces li:first-child {
- margin-top: 0px;
-}
-
-#traces li:hover {
- background-color: rgba(221,238,246,1);
-}
-
-
-#traces li div {
- display: block;
- padding: 2px 0px;
- margin-bottom: 0px;
- color: #999;
-
- -webkit-transition: .3s;
-}
-
-#traces li .bar-block {
- position: relative;
- line-height: 20px;
- padding: 0px;
- margin: 2px 0;
- background-color: rgba(255, 255, 255, .75);
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-
-#traces li .bar-graphic {
- position: absolute;
- top: 0px;
- left: 0px;
- display: block;
- height: 20px;
- z-index: 0;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- background-color: #c0deed;
-
- -webkit-transition: .3s;
-}
-
-#traces li:hover .bar-graphic {
- background-color: #0084B4;
-}
-
-#traces li.trace-error-transient .bar-graphic {
- background-color: #fcf8e3;;
-}
-
-#traces li.trace-error-transient:hover .bar-graphic {
- background-color: #8a6d3b;
-}
-
-#traces li.trace-error-critical .bar-graphic {
- background-color: #f2dede;
-}
-
-#traces li.trace-error-critical:hover .bar-graphic {
- background-color: #a94442;
-}
-
-#traces li .bar-label {
- position: relative;
- margin-left: 6px;
- padding: 2px 4px;
- color: #0084B4;
- font-weight: bold;
- z-index: 1;
-
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-
- -webkit-transition: .3s;
-}
-
-#traces li:hover .bar-label {
- color: #FFF;
- background-color: rgba(0,132,180,.35);
-}
-
-#traces li.trace-error-transient .bar-label {
- color: #8a6d3b;
-}
-
-#traces li.trace-error-transient:hover .bar-label {
- color: #FFF;
- background-color: rgba(138,109,59,.35);
-}
-
-#traces li.trace-error-critical .bar-label {
- color: #a94442;
-}
-
-#traces li.trace-error-critical:hover .bar-label {
- color: #FFF;
- background-color: rgba(169,68,66,.35);
-}
-
-#traces li div span.label {
- -webkit-transition: .3s;
-}
-
-#traces li div p {
- margin-bottom: 0;
- font-size: 11px;
- font-weight: bold;
- line-height: 11px;
-}
-
-#traces li .trace-details {
- display: inline-block;
-}
-
-#traces li .services {
- width: 80%;
-}
-
-#traces li .timestamp {
- width: 19%;
- text-align: right;
-}
-
-#traces li:hover .services .label {
- background-color: #F89406;
-}
-
-#traces li .services .label:hover,
-#traces li .services .label:hover span {
- background-color: red;
-}
-
-#traces li .timestamp .label {
- background-color: #FAFAFA;
- color: #AAA;
-}
-
-#traces li:hover .timestamp .label {
- background-color: #FFF;
- color: #666;
-}
diff --git a/zipkin-ui/index.ejs b/zipkin-ui/index.ejs
deleted file mode 100644
index da4fa9d..0000000
--- a/zipkin-ui/index.ejs
+++ /dev/null
@@ -1,49 +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.
-
--->
-<!DOCTYPE html>
-<html>
- <head>
- <!--
- add 'base' tag to work around the fact that 'html-webpack-plugin' does not work
- with '__webpack_public_path__' being set as reported at https://github.com/jantimon/html-webpack-plugin/issues/119
- -->
- <base href="<%= htmlWebpackPlugin.options.contextRoot %>">
-
- <link rel="icon" type="image/x-icon" href="favicon.ico">
-
- <meta charset="UTF-8">
- <title><%= htmlWebpackPlugin.options.title %></title>
- </head>
- <body>
-
- <!-- we include a reduced header on page load, as this makes the page load feel much smoother -->
-
- <div class='navbar navbar-inverse' role='navigation'>
- <div class='container'>
- <div class='navbar-header'>
- <a class='navbar-brand' href='#'>
- Zipkin<span class='muted' style='font-size: .75em; padding-left: 10px;' data-i18n="nav.inves">Investigate system behavior</span>
- </a>
- </div>
- </div><!--/.nav-collapse -->
- </div>
- </div>
-
- </body>
-</html>
diff --git a/zipkin-ui/js/component_data/default.js b/zipkin-ui/js/component_data/default.js
deleted file mode 100644
index 10bc2f8..0000000
--- a/zipkin-ui/js/component_data/default.js
+++ /dev/null
@@ -1,127 +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.
- */
-import {component} from 'flightjs';
-import {errToStr} from '../../js/component_ui/error';
-import $ from 'jquery';
-import queryString from 'query-string';
-import {traceSummary, traceSummariesToMustache} from '../component_ui/traceSummary';
-import {treeCorrectedForClockSkew} from '../component_data/skew';
-
-const debug = false;
-
-export function convertDurationToMicrosecond(duration) {
- const match = duration.match(/^(\d+)(us|μs|ms|s)$/i);
- if (match) {
- const unit = match[2];
- switch (unit) {
- case 'us':
- case 'μs':
- return match[1];
- case 'ms':
- return String((Number(match[1]) * 1000));
- case 's':
- return String((Number(match[1]) * Math.pow(1000, 2)));
- default:
- // Do nothing
- }
- }
- return duration;
-}
-
-export function convertToApiQuery(source) {
- const query = Object.assign({}, source);
-
- if (query.minDuration) {
- query.minDuration = convertDurationToMicrosecond(query.minDuration);
- }
-
- // zipkin's api looks back from endTs
- if (query.lookback !== 'custom') {
- delete query.startTs;
- delete query.endTs;
- }
- if (query.startTs) {
- if (query.endTs > query.startTs) {
- query.lookback = String(query.endTs - query.startTs);
- }
- delete query.startTs;
- }
- if (query.lookback === 'custom') {
- delete query.lookback;
- }
- if (query.serviceName === 'all') {
- delete query.serviceName;
- }
- if (query.remoteServiceName === 'all') {
- delete query.remoteServiceName;
- }
- if (query.spanName === 'all') {
- delete query.spanName;
- }
- // delete any parameters unused on the server
- Object.keys(query).forEach(key => {
- if (query[key] === '') {
- delete query[key];
- }
- });
- delete query.sortOrder;
- return query;
-}
-
-// Converts the response into data for index.mustache. Traces missing required data are skipped.
-export function convertSuccessResponse(rawResponse, serviceName, apiURL, utc = false) {
- const summaries = [];
- rawResponse.forEach((raw) => {
- if (raw.length === 0) return;
-
- const corrected = treeCorrectedForClockSkew(raw);
- try {
- summaries.push(traceSummary(corrected));
- } catch (e) {
- /* eslint-disable no-console */
- if (debug) console.log(e.toString());
- }
- });
-
- // Take the summaries and convert them to template parameters for index.mustache
- let traces = [];
- if (summaries.length > 0) {
- traces = traceSummariesToMustache(serviceName, summaries, utc);
- }
- return {traces, apiURL, rawResponse};
-}
-
-export default component(function DefaultData() {
- this.after('initialize', function() {
- const query = queryString.parse(window.location.search);
- const serviceName = query.serviceName;
- if (!serviceName) {
- this.trigger('defaultPageModelView', {traces: []});
- return;
- }
- const apiQuery = convertToApiQuery(query);
- const apiURL = `api/v2/traces?${queryString.stringify(apiQuery)}`;
- $.ajax(apiURL, {
- type: 'GET',
- dataType: 'json'
- }).done(rawTraces => {
- this.trigger('defaultPageModelView', convertSuccessResponse(rawTraces, serviceName, apiURL));
- }).fail(e => {
- this.trigger('defaultPageModelView', {traces: [], queryError: errToStr(e)});
- });
- });
-});
diff --git a/zipkin-ui/js/component_data/dependency.js b/zipkin-ui/js/component_data/dependency.js
deleted file mode 100644
index 930b8d6..0000000
--- a/zipkin-ui/js/component_data/dependency.js
+++ /dev/null
@@ -1,97 +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.
- */
-import {component} from 'flightjs';
-import moment from 'moment';
-import $ from 'jquery';
-
-export default component(function dependency() {
- let services = {};
- let dependencies = {};
-
- this.getDependency = function(endTs, lookback) {
- let url = `api/v2/dependencies?endTs=${endTs}`;
- if (lookback) {
- url += `&lookback=${lookback}`;
- }
- $.ajax(url, {
- type: 'GET',
- dataType: 'json',
- success: links => {
- this.links = links.sort((a, b) => a.parent - b.parent || a.child - b.child);
- this.buildServiceData(links);
- this.trigger('dependencyDataReceived', links);
- },
- failure: (jqXHR, status, err) => {
- const error = {
- message: `Couldn't get dependency data from backend: ${err}`
- };
- this.trigger('dependencyDataFailed', error);
- }
- });
- };
-
- this.buildServiceData = function(links) {
- services = {};
- dependencies = {};
- links.forEach(link => {
- const {parent, child} = link;
-
- dependencies[parent] = dependencies[parent] || {};
- dependencies[parent][child] = link;
-
- services[parent] = services[parent] || {serviceName: parent, uses: [], usedBy: []};
- services[child] = services[child] || {serviceName: child, uses: [], usedBy: []};
-
- services[parent].uses.push(child);
- services[child].usedBy.push(parent);
- });
- };
-
- this.after('initialize', function() {
- this.on(document, 'dependencyDataRequested', function(event, {endTs, lookback}) {
- this.getDependency(endTs, lookback);
- });
-
- this.on(document, 'serviceDataRequested', function(event, {serviceName}) {
- this.getServiceData(serviceName, data => {
- this.trigger(document, 'serviceDataReceived', data);
- });
- });
-
- this.on(document, 'parentChildDataRequested', function(event, {parent, child}) {
- this.getDependencyData(parent, child, data => {
- this.trigger(document, 'parentChildDataReceived', data);
- });
- });
-
- const endTs = document.getElementById('endTs').value || moment().valueOf();
- const startTs = document.getElementById('startTs').value;
- let lookback;
- if (startTs && endTs > startTs) {
- lookback = endTs - startTs;
- }
- this.getDependency(endTs, lookback);
- });
-
- this.getServiceData = function(serviceName, callback) {
- callback(services[serviceName]);
- };
-
- this.getDependencyData = function(parent, child, callback) {
- callback(dependencies[parent][child]);
- };
-});
diff --git a/zipkin-ui/js/component_data/remoteServiceNames.js b/zipkin-ui/js/component_data/remoteServiceNames.js
deleted file mode 100644
index 72d2489..0000000
--- a/zipkin-ui/js/component_data/remoteServiceNames.js
+++ /dev/null
@@ -1,45 +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.
- */
-import {component} from 'flightjs';
-import {getError} from '../../js/component_ui/error';
-import $ from 'jquery';
-
-export default component(function remoteServiceNames() {
- this.updateRemoteServiceNames = function(ev, serviceName) {
- if (!serviceName) {
- this.trigger('dataRemoteServiceNames', {remoteServices: []});
- return;
- }
- $.ajax(`api/v2/remoteServices?serviceName=${serviceName}`, {
- type: 'GET',
- dataType: 'json'
- }).done(remoteServices => {
- this.trigger('dataRemoteServiceNames', {remoteServices: remoteServices.sort()});
- }).fail(e => {
- if (e.status && e.status === 404) { // remote service names is a new endpoint
- this.trigger('dataRemoteServiceNames', {remoteServices: []});
- return;
- }
- this.trigger('uiServerError', getError('cannot load remote service names', e));
- });
- };
-
- this.after('initialize', function() {
- this.on('uiChangeServiceName', this.updateRemoteServiceNames);
- this.on('uiFirstLoadRemoteServiceNames', this.updateRemoteServiceNames);
- });
-});
diff --git a/zipkin-ui/js/component_data/serviceNames.js b/zipkin-ui/js/component_data/serviceNames.js
deleted file mode 100644
index 8d0a31b..0000000
--- a/zipkin-ui/js/component_data/serviceNames.js
+++ /dev/null
@@ -1,36 +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.
- */
-import {component} from 'flightjs';
-import {getError} from '../../js/component_ui/error';
-import $ from 'jquery';
-
-export default component(function serviceNames() {
- this.updateServiceNames = function(ev, lastServiceName) {
- $.ajax('api/v2/services', {
- type: 'GET',
- dataType: 'json'
- }).done(names => {
- this.trigger('dataServiceNames', {names: names.sort(), lastServiceName});
- }).fail(e => {
- this.trigger('uiServerError', getError('cannot load service names', e));
- });
- };
-
- this.after('initialize', function() {
- this.on('uiChangeServiceName', this.updateServiceNames);
- });
-});
diff --git a/zipkin-ui/js/component_data/skew.js b/zipkin-ui/js/component_data/skew.js
deleted file mode 100644
index 8eae704..0000000
--- a/zipkin-ui/js/component_data/skew.js
+++ /dev/null
@@ -1,178 +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.
- */
-import {SpanNode, SpanNodeBuilder} from './spanNode';
-
-class ClockSkew {
- constructor(params) {
- const {endpoint, skew} = params;
- this._endpoint = endpoint;
- this._skew = skew;
- }
-
- get endpoint() {
- return this._endpoint;
- }
-
- get skew() {
- return this._skew;
- }
-}
-
-function ipsMatch(a, b) {
- if (!a || !b) return false;
- if (a.ipv6 && b.ipv6 && a.ipv6 === b.ipv6) {
- return true;
- }
- if (!a.ipv4 && !b.ipv4) return false;
- return a.ipv4 === b.ipv4;
-}
-
-// If any annotation has an IP with skew associated, adjust accordingly.
-function adjustTimestamps(span, skew) {
- if (!ipsMatch(skew.endpoint, span.localEndpoint)) return span;
-
- const result = Object.assign({}, span);
- if (span.timestamp) result.timestamp = span.timestamp - skew.skew;
- const annotationLength = span.annotations.length;
- if (annotationLength > 0) result.annotations = [];
- for (let i = 0; i < annotationLength; i++) {
- const a = span.annotations[i];
- result.annotations[i] = {timestamp: a.timestamp - skew.skew, value: a.value};
- }
- return result;
-}
-
-/* Uses span kind to determine if there's clock skew. */
-function getClockSkew(node) {
- const parent = node.parent ? node.parent.span : undefined;
- const child = node.span;
- if (!parent) return undefined;
-
- // skew is only detectable client to server
- if (parent.kind !== 'CLIENT' || child.kind !== 'SERVER') return undefined;
-
- let oneWay = false;
- const clientTimestamp = parent.timestamp;
- const serverTimestamp = child.timestamp;
- if (!clientTimestamp || !serverTimestamp) return undefined;
-
- // skew is when the server happens before the client
- if (serverTimestamp > clientTimestamp) return undefined;
-
- const clientDuration = parent.duration;
- const serverDuration = child.duration;
- if (!clientDuration || !serverDuration) oneWay = true;
-
- const server = child.localEndpoint;
- if (!server) return undefined;
- const client = parent.localEndpoint;
- if (!client) return undefined;
-
- // There's no skew if the RPC is going to itself
- if (ipsMatch(server, client)) return undefined;
-
- if (oneWay) {
- const latency = serverTimestamp - clientTimestamp;
-
- // the only way there is skew is when the client appears to be after the server
- if (latency > 0) return undefined;
- // We can't currently do better than push the client and server apart by minimum duration (1)
- return new ClockSkew({endpoint: server, skew: latency - 1});
- } else {
- // If the client finished before the server (async), we still know the server must have happened
- // after the client. So, push 1us.
- if (clientDuration < serverDuration) {
- const skew = serverTimestamp - clientTimestamp - 1;
- return new ClockSkew({endpoint: server, skew});
- }
-
- // We assume latency is half the difference between the client and server duration.
- const latency = (clientDuration - serverDuration) / 2;
-
- // We can't see skew when send happens before receive
- if (latency < 0) return undefined;
-
- const skew = serverTimestamp - latency - clientTimestamp;
- if (skew !== 0) return new ClockSkew({endpoint: server, skew});
- }
- return undefined;
-}
-
-/*
- * Recursively adjust the timestamps on the span trace. Root span is the reference point, all
- * children's timestamps gets adjusted based on that span's timestamps.
- */
-function adjust(node, skewFromParent) {
- // adjust skew for the endpoint brought over from the parent span
- if (skewFromParent) {
- node.setSpan(adjustTimestamps(node.span, skewFromParent));
- }
-
- // Is there any skew in the current span?
- let skew = getClockSkew(node);
- if (skew) {
- // the current span's skew may be a different endpoint than its parent, so adjust again.
- node.setSpan(adjustTimestamps(node.span, skew));
- } else if (skewFromParent) {
- // Assumes we are on the same host: propagate skew from our parent
- skew = skewFromParent;
- }
- // propagate skew to any children
- node.children.forEach(child => adjust(child, skew));
-}
-
-function treeCorrectedForClockSkew(spans, debug = false) {
- if (spans.length === 0) return new SpanNode();
-
- const trace = new SpanNodeBuilder({debug}).build(spans);
-
- if (!trace.span) {
- if (debug) {
- /* eslint-disable no-console */
- console.log(
- `skipping clock skew adjustment due to missing root span: traceId=${spans[0].traceId}`
- );
- }
- return trace;
- }
-
- const childrenOfRoot = trace.children;
- for (let i = 0; i < childrenOfRoot.length; i++) {
- const next = childrenOfRoot[i].span;
- if (next.parentId || next.shared) continue;
-
- const traceId = next.traceId;
- const spanId = next.id;
- const rootSpanId = trace.span.id;
- if (debug) {
- /* eslint-disable no-console */
- const prefix = 'skipping redundant root span';
- console.log(`${prefix}: traceId=${traceId}, rootSpanId=${rootSpanId}, spanId=${spanId}`);
- }
- return trace;
- }
-
- adjust(trace);
- return trace;
-}
-
-
-module.exports = {
- ipsMatch, // for testing
- getClockSkew, // for testing
- treeCorrectedForClockSkew
-};
diff --git a/zipkin-ui/js/component_data/spanCleaner.js b/zipkin-ui/js/component_data/spanCleaner.js
deleted file mode 100644
index c119dcc..0000000
--- a/zipkin-ui/js/component_data/spanCleaner.js
+++ /dev/null
@@ -1,269 +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.
- */
-import _ from 'lodash';
-
-export function normalizeTraceId(traceId) {
- if (traceId.length > 16) {
- const result = traceId.padStart(32, '0');
- // undo prefix if it will result in a 64-bit trace ID
- if (result.startsWith('0000000000000000')) return result.substring(16);
- return result;
- }
- return traceId.padStart(16, '0');
-}
-
-function isEndpoint(endpoint) {
- return endpoint && Object.keys(endpoint).length > 0;
-}
-
-// This cleans potential dirty v2 inputs, like normalizing IDs etc. It does not affect the input
-export function clean(span) {
- const res = {
- traceId: normalizeTraceId(span.traceId)
- };
-
- // take care not to create self-referencing spans even if the input data is incorrect
- const id = span.id.padStart(16, '0');
- if (span.parentId) {
- const parentId = span.parentId.padStart(16, '0');
- if (parentId !== id) res.parentId = parentId;
- }
- res.id = id;
-
- if (span.name && span.name !== '' && span.name !== 'unknown') res.name = span.name;
- if (span.kind) res.kind = span.kind;
-
- if (span.timestamp) res.timestamp = span.timestamp;
- if (span.duration) res.duration = span.duration;
-
- if (isEndpoint(span.localEndpoint)) res.localEndpoint = Object.assign({}, span.localEndpoint);
- if (isEndpoint(span.remoteEndpoint)) res.remoteEndpoint = Object.assign({}, span.remoteEndpoint);
-
- res.annotations = span.annotations ? span.annotations.slice(0) : [];
- if (res.annotations.length > 1) {
- res.annotations = _(_.unionWith(res.annotations, _.isEqual))
- .sortBy('timestamp', 'value').value();
- }
-
- res.tags = span.tags || {};
-
- if (span.debug) res.debug = true;
- // shared is for the server side, unset it if accidentally set on the client side
- if (span.shared && span.kind !== 'CLIENT') res.shared = true;
-
- return res;
-}
-
-// exposed for testing. assumes spans are already clean
-export function merge(left, right) {
- const res = {
- traceId: right.traceId.length > 16 ? right.traceId : left.traceId
- };
-
- const parentId = left.parentId || right.parentId;
- if (parentId) res.parentId = parentId;
-
- res.id = left.id;
- const name = left.name || right.name;
- if (name) res.name = name;
-
- const kind = left.kind || right.kind;
- if (kind) res.kind = kind;
-
- const timestamp = left.timestamp || right.timestamp;
- if (timestamp) res.timestamp = timestamp;
- const duration = left.duration || right.duration;
- if (duration) res.duration = duration;
-
- const localEndpoint = Object.assign({}, left.localEndpoint, right.localEndpoint);
- if (isEndpoint(localEndpoint)) res.localEndpoint = localEndpoint;
- const remoteEndpoint = Object.assign({}, left.remoteEndpoint, right.remoteEndpoint);
- if (isEndpoint(remoteEndpoint)) res.remoteEndpoint = remoteEndpoint;
-
- if (left.annotations.length === 0) {
- res.annotations = right.annotations;
- } else if (right.annotations.length === 0) {
- res.annotations = left.annotations;
- } else {
- res.annotations = _(_.unionWith(left.annotations, right.annotations, _.isEqual))
- .sortBy('timestamp', 'value').value();
- }
-
- res.tags = Object.assign({}, left.tags, right.tags);
-
- if (left.debug || right.debug) res.debug = true;
- if (left.shared || right.shared) res.shared = true;
- return res;
-}
-
-// compares potentially undefined input
-export function compare(a, b) {
- if (!a && !b) return 0;
- if (!a) return -1;
- if (!b) return 1;
- return (a > b) - (a < b);
-}
-
-function isUndefined(ref) {
- return typeof(ref) === 'undefined';
-}
-
-/*
- * Put spans with null endpoints first, so that their data can be attached to the first span with
- * the same ID and endpoint. It is possible that a server can get the same request on a different
- * port. Not addressing this.
- */
-function compareEndpoint(left, right) {
- // handle nulls first
- if (isUndefined(left)) return -1;
- if (isUndefined(right)) return 1;
-
- const byService = compare(left.serviceName, right.serviceName);
- if (byService !== 0) return byService;
- const byIpV4 = compare(left.ipv4, right.ipv4);
- if (byIpV4 !== 0) return byIpV4;
- return compare(left.ipv6, right.ipv6);
-}
-
-// false or null first (client first)
-function compareShared(left, right) {
- const leftNotShared = isUndefined(left.shared) || !left.shared;
- const rightNotShared = isUndefined(right.shared) || !right.shared;
-
- if (leftNotShared && rightNotShared) {
- return left.kind === 'CLIENT' ? -1 : 1;
- }
- if (leftNotShared) return -1;
- if (rightNotShared) return 1;
- return 0;
-}
-
-export function cleanupComparator(left, right) { // exported for testing
- const bySpanId = compare(left.id, right.id);
- if (bySpanId !== 0) return bySpanId;
- const byShared = compareShared(left, right);
- if (byShared !== 0) return byShared;
- return compareEndpoint(left.localEndpoint, right.localEndpoint);
-}
-
-function tryMerge(current, endpoint) {
- if (!endpoint) return true;
- if (current.serviceName && endpoint.serviceName && current.serviceName !== endpoint.serviceName) {
- return false;
- }
- if (current.ipv4 && endpoint.ipv4 && current.ipv4 !== endpoint.ipv4) {
- return false;
- }
- if (current.ipv6 && endpoint.ipv6 && current.ipv6 !== endpoint.ipv6) {
- return false;
- }
- if (current.port && endpoint.port && current.port !== endpoint.port) {
- return false;
- }
- if (!current.serviceName) {
- current.serviceName = endpoint.serviceName; // eslint-disable-line no-param-reassign
- }
- if (!current.ipv4) current.ipv4 = endpoint.ipv4; // eslint-disable-line no-param-reassign
- if (!current.ipv6) current.ipv6 = endpoint.ipv6; // eslint-disable-line no-param-reassign
- if (!current.port) current.port = endpoint.portAsInt; // eslint-disable-line no-param-reassign
- return true;
-}
-
-// sort by timestamp, then name, root/shared first in case of skew
-export function spanComparator(a, b) { // exported for testing
- if (!a.parentId && b.parentId) { // a is root
- return -1;
- } else if (a.parentId && !b.parentId) { // b is root
- return 1;
- }
-
- // order client first in case of shared spans (shared is always server)
- if (a.id === b.id) return compareShared(a, b);
-
- // Either a and b are root or neither are. sort by shared timestamp, then name
- return compare(a.timestamp, b.timestamp) || compare(a.name, b.name);
-}
-
-/*
- * Spans can be sent in multiple parts. Also client and server spans can share the same ID. This
- * merges both scenarios.
- */
-export function mergeV2ById(spans) {
- let length = spans.length;
- if (length === 0) return spans;
-
- const result = [];
-
- // Let's cleanup any spans and pick the longest ID
- let traceId;
- spans.forEach(span => {
- const cleaned = clean(span);
- if (!traceId || traceId.length !== 32) traceId = cleaned.traceId;
- result.push(cleaned);
- });
-
- if (length <= 1) return result;
- result.sort(cleanupComparator);
-
- // Now start any fixes or merging
- let last;
- for (let i = 0; i < length; i++) {
- let span = result[i];
-
- // Choose the longest trace ID
- if (span.traceId.length !== traceId.length) {
- span.traceId = traceId;
- }
-
- const localEndpoint = span.localEndpoint ? Object.assign({}, span.localEndpoint) : {};
- while (i + 1 < length) {
- const next = result[i + 1];
- if (next.id !== span.id) break;
-
- // This cautiously merges with the next span, if we think it was sent in multiple pieces.
- if (span.shared === next.shared && tryMerge(localEndpoint, next.localEndpoint)) {
- span = merge(span, next);
-
- // remove the merged element
- length--;
- result.splice(i + 1, 1);
- continue;
- }
- break;
- }
-
- // Zipkin and B3 originally used the same span ID between client and server. Some
- // instrumentation are inconsistent about adding the shared flag on the server side. Since we
- // have the entire trace, and it is ordered client-first, we can correct a missing shared flag.
- if (last && last.id === span.id) {
- // Backfill missing shared flag as some instrumentation doesn't add it
- if (last.kind === 'CLIENT' && span.kind === 'SERVER' && !span.shared) {
- span.shared = true;
- }
-
- // handle a shared RPC server span that wasn't propagated its parent span ID
- if (span.shared && !span.parentId && last.parentId) {
- span.parentId = last.parentId;
- }
- }
-
- last = span;
- result[i] = span;
- }
-
- return result.sort(spanComparator);
-}
diff --git a/zipkin-ui/js/component_data/spanNames.js b/zipkin-ui/js/component_data/spanNames.js
deleted file mode 100644
index b1c58fb..0000000
--- a/zipkin-ui/js/component_data/spanNames.js
+++ /dev/null
@@ -1,41 +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.
- */
-import {component} from 'flightjs';
-import {getError} from '../../js/component_ui/error';
-import $ from 'jquery';
-
-export default component(function spanNames() {
- this.updateSpanNames = function(ev, serviceName) {
- if (!serviceName) {
- this.trigger('dataSpanNames', {spans: []});
- return;
- }
- $.ajax(`api/v2/spans?serviceName=${serviceName}`, {
- type: 'GET',
- dataType: 'json'
- }).done(spans => {
- this.trigger('dataSpanNames', {spans: spans.sort()});
- }).fail(e => {
- this.trigger('uiServerError', getError('cannot load span names', e));
- });
- };
-
- this.after('initialize', function() {
- this.on('uiChangeServiceName', this.updateSpanNames);
- this.on('uiFirstLoadSpanNames', this.updateSpanNames);
- });
-});
diff --git a/zipkin-ui/js/component_data/spanNode.js b/zipkin-ui/js/component_data/spanNode.js
deleted file mode 100644
index 2cb1a58..0000000
--- a/zipkin-ui/js/component_data/spanNode.js
+++ /dev/null
@@ -1,293 +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.
- */
-import {compare, mergeV2ById} from './spanCleaner';
-
-/*
- * Convenience type representing a trace tree. Multiple Zipkin features require a trace tree. For
- * example, looking at network boundaries to correct clock skew and aggregating requests paths imply
- * visiting the tree.
- */
-// originally zipkin2.internal.SpanNode.java
-class SpanNode {
- constructor(span) {
- this._parent = undefined; // no default
- this._span = span; // undefined is possible when this is a synthetic root node
- this._children = [];
- }
-
- // Returns the parent, or undefined if root.
- get parent() {
- return this._parent;
- }
-
- _setParent(newParent) {
- this._parent = newParent;
- }
-
- // Returns the span, or undefined if a synthetic root node
- get span() {
- return this._span;
- }
-
- // Returns the children of this node
- get children() {
- return this._children;
- }
-
- // Mutable as some transformations, such as clock skew, adjust the current node in the tree.
- setSpan(span) {
- if (!span) throw new Error('span was undefined');
- this._span = span;
- }
-
- // Adds the child IFF it isn't already a child.
- addChild(child) {
- if (!child) throw new Error('child was undefined');
- if (child === this) throw new Error(`circular dependency on ${this.toString()}`);
- child._setParent(this);
- this._children.push(child);
- }
-
- // throws an error if the trace was empty
- queueRootMostSpans() {
- const queue = [];
- // since the input data could be headless, we first push onto the queue the root-most spans
- if (typeof(this.span) === 'undefined') { // synthetic root
- this.children.forEach(child => queue.push(child));
- } else {
- queue.push(this);
- }
- if (queue.length === 0) throw new Error('Trace was empty');
- return queue;
- }
-
- // Invokes the callback for each span resulting from a breadth-first traversal at this node
- traverse(spanCallback) {
- const queue = this.queueRootMostSpans();
-
- while (queue.length > 0) {
- const current = queue.shift();
-
- spanCallback(current.span);
-
- const children = current.children;
- for (let i = 0; i < children.length; i++) {
- queue.push(children[i]);
- }
- }
- }
-
- toString() {
- if (this._span) return `SpanNode(${JSON.stringify(this._span)})`;
- return 'SpanNode()';
- }
-}
-
-// In javascript, dict keys can't be objects
-function keyString(id, shared = false, endpoint) {
- if (!shared) return id;
- const endpointString = endpoint ? JSON.stringify(endpoint) : 'x';
- return `${id}-${endpointString}`;
-}
-
-function nodeByTimestamp(a, b) {
- return compare(a.span.timestamp, b.span.timestamp);
-}
-
-function sortChildren(node) {
- if (node.children.length > 0) {
- node.children.sort(nodeByTimestamp);
- }
-}
-
-function sortTreeByTimestamp(root) {
- const queue = [];
- queue.push(root);
-
- while (queue.length > 0) {
- const current = queue.shift();
-
- sortChildren(current);
-
- const {children} = current;
- for (let i = 0; i < children.length; i += 1) {
- queue.push(children[i]);
- }
- }
-}
-
-class SpanNodeBuilder {
- constructor(params) {
- const {debug = false} = params;
- this._debug = debug;
- this._rootSpan = undefined;
- this._keyToNode = {};
- this._spanToParent = {};
- }
-
- /*
- * We index spans by (id, shared, localEndpoint) before processing them. This latter fields
- * (shared, endpoint) are important because in zipkin (specifically B3), a server can share
- * (re-use) the same ID as its client. This impacts processing quite a bit when multiple servers
- * share one span ID.
- *
- * In a Zipkin trace, a parent (client) and child (server) can share the same ID if in an
- * RPC. If two different servers respond to the same client, the only way for us to tell which
- * is which is by endpoint. Our goal is to retain full paths across multiple endpoints. Even
- * though instrumentation should be configured in such a way that a client never sends the same
- * span ID to multiple servers, it can happen. Accordingly, we index defensively including any
- * endpoint data that might be available.
- */
- _index(span) {
- let idKey;
- let parentKey;
-
- if (span.shared) {
- // we need to classify a shared span by its endpoint in case multiple servers respond to the
- // same ID sent by the client.
- idKey = keyString(span.id, true, span.localEndpoint);
- // the parent of a server span is a client, which is not ambiguous for a given span ID.
- parentKey = span.id;
- } else {
- idKey = span.id;
- parentKey = span.parentId;
- }
-
- this._spanToParent[idKey] = parentKey;
- }
-
- /**
- * Processing is taking a span and placing it at the most appropriate place in the trace tree.
- * For example, if this is a server span, it would be a different node, and a child of its client
- * even if they share the same span ID.
- *
- * Processing is defensive of typical problems in span reporting, such as depth-first. For
- * example, depth-first reporting implies you can see spans missing their parent. Hence, the
- * result of processing all spans can be a virtual root node.
- */
- _process(span) {
- const endpoint = span.localEndpoint;
- const key = keyString(span.id, span.shared, span.localEndpoint);
- const noEndpointKey = endpoint ? keyString(span.id, span.shared) : key;
-
- let parent;
- if (span.shared) {
- // Shared is a server span. It will very likely be on a different endpoint than the client.
- // Clients are not ambiguous by ID, so we don't need to qualify by endpoint.
- parent = span.id;
- } else if (span.parentId) {
- // We are not a root span, and not a shared server span. Proceed in most specific to least.
-
- // We could be the child of a shared server span (ex a local (intermediate) span on the same
- // endpoint). This is the most specific case, so we try this first.
- parent = keyString(span.parentId, true, endpoint);
- if (this._spanToParent[parent]) {
- this._spanToParent[noEndpointKey] = parent;
- } else {
- // If there's no shared parent, fall back to normal case which is unqualified beyond ID.
- parent = span.parentId;
- }
- } else { // we are root or don't know our parent
- if (this._rootSpan) {
- if (this._debug) {
- const prefix = 'attributing span missing parent to root';
- /* eslint-disable no-console */
- console.log(
- `${prefix}: traceId=${span.traceId}, rootId=${this._rootSpan.span.id}, id=${span.id}`
- );
- }
- }
- }
-
- const node = new SpanNode(span);
- // special-case root, and attribute missing parents to it. In
- // other words, assume that the first root is the "real" root.
- if (!parent && !this._rootSpan) {
- this._rootSpan = node;
- delete this._spanToParent[noEndpointKey];
- } else if (span.shared) {
- // In the case of shared server span, we need to address it both ways, in case intermediate
- // spans are lacking endpoint information.
- this._keyToNode[key] = node;
- this._keyToNode[noEndpointKey] = node;
- } else {
- this._keyToNode[noEndpointKey] = node;
- }
- }
-
- /*
- * Builds a trace tree by merging and processing the input or returns an empty tree.
- *
- * While the input can be incomplete or redundant, they must all be a part of the same trace
- * (e.g. all share the same trace ID).
- */
- build(spans) {
- if (spans.length === 0) throw new Error('Trace was empty');
-
- // In order to make a tree, we need clean data. This will merge any duplicates so that we
- // don't have redundant leaves on the tree.
- const cleaned = mergeV2ById(spans);
- const length = cleaned.length;
- const traceId = cleaned[0].traceId;
-
- if (this._debug) {
- /* eslint-disable no-console */
- console.log(`building trace tree: traceId=${traceId}`);
- }
-
- // Next, index all the spans so that we can understand any relationships.
- for (let i = 0; i < length; i++) {
- this._index(cleaned[i]);
- }
-
- // Now that we've index references to all spans, we can revise any parent-child relationships.
- // Notably, by now, we can tell which is the root-most.
- for (let i = 0; i < length; i++) {
- this._process(cleaned[i]);
- }
-
- if (!this._rootSpan) {
- if (this._debug) {
- /* eslint-disable no-console */
- console.log(`substituting dummy node for missing root span: traceId=${traceId}`);
- }
- this._rootSpan = new SpanNode();
- }
-
- // At this point, we have the most reliable parent-child relationships and can allocate spans
- // corresponding the the best place in the trace tree.
- Object.keys(this._spanToParent).forEach(key => {
- const child = this._keyToNode[key];
- const parent = this._keyToNode[this._spanToParent[key]];
-
- if (!parent) { // Handle headless by attaching spans missing parents to root
- this._rootSpan.addChild(child);
- } else {
- parent.addChild(child);
- }
- });
-
- sortTreeByTimestamp(this._rootSpan);
-
- return this._rootSpan;
- }
-}
-
-module.exports = {
- SpanNode, // for testing
- SpanNodeBuilder
-};
diff --git a/zipkin-ui/js/component_data/trace.js b/zipkin-ui/js/component_data/trace.js
deleted file mode 100644
index 724d7dc..0000000
--- a/zipkin-ui/js/component_data/trace.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import {getError} from '../component_ui/error';
-import {traceToMustache} from '../component_ui/traceToMustache';
-import {treeCorrectedForClockSkew} from '../component_data/skew';
-
-export function toContextualLogsUrl(logsUrl, traceId) {
- if (logsUrl) {
- return logsUrl.replace('{traceId}', traceId);
- }
- return logsUrl;
-}
-
-// Converts the response into data for trace.mustache. Missing required data will raise an error.
-export function convertSuccessResponse(rawResponse, logsUrl) {
- const corrected = treeCorrectedForClockSkew(rawResponse);
- const modelview = traceToMustache(corrected, logsUrl);
- return {modelview, trace: rawResponse};
-}
-
-export default component(function TraceData() {
- this.after('initialize', function() {
- const traceId = this.attr.traceId;
- const logsUrl = toContextualLogsUrl(this.attr.logsUrl, traceId);
- $.ajax(`api/v2/trace/${traceId}`, {
- type: 'GET',
- dataType: 'json'
- }).done(raw => {
- this.trigger('tracePageModelView', convertSuccessResponse(raw, logsUrl));
- }).fail(e => {
- this.trigger('uiServerError', getError(`Cannot load trace ${traceId}`, e));
- });
- });
-});
diff --git a/zipkin-ui/js/component_ui/backToTop.js b/zipkin-ui/js/component_ui/backToTop.js
deleted file mode 100644
index 2fa53f8..0000000
--- a/zipkin-ui/js/component_ui/backToTop.js
+++ /dev/null
@@ -1,39 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-
-export default component(function backToTop() {
- this.toTop = function() {
- event.preventDefault();
- $('html, body').animate({scrollTop: 0}, 300);
- return false;
- };
-
- this.after('initialize', function() {
- /* handle window scroll here*/
- $(window).scroll(function() {
- if ($(this).scrollTop() > 200) {
- $('.back-to-top').fadeIn(300);
- } else {
- $('.back-to-top').fadeOut(300);
- }
- });
-
- this.on('click', this.toTop);
- });
-});
diff --git a/zipkin-ui/js/component_ui/dependencyGraph.js b/zipkin-ui/js/component_ui/dependencyGraph.js
deleted file mode 100644
index 170e368..0000000
--- a/zipkin-ui/js/component_ui/dependencyGraph.js
+++ /dev/null
@@ -1,211 +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.
- */
-import {component} from 'flightjs';
-import d3 from 'd3';
-import $ from 'jquery';
-import dagreD3 from '../../libs/dagre-d3/js/dagre-d3'; // eslint-disable-line no-unused-vars
-
-const dagre = window.dagreD3;
-
-export default component(function dependencyGraph() {
- this.after('initialize', function afterInitialize(container) {
- const lowErrorRate = this.attr.config('dependency').lowErrorRate;
- const highErrorRate = this.attr.config('dependency').highErrorRate;
- this.on(document, 'dependencyDataReceived', function onDependencyDataReceived(ev, ...links) {
- const _this = this;
- const rootSvg = container.querySelector('svg');
- rootSvg.textContent = null;
- const svg = d3.select('svg');
- const svgGroup = svg.append('g');
- const g = new dagre.Digraph();
- const renderer = new dagre.Renderer();
-
- function arrayUnique(array) {
- return array.filter((val, i, arr) => i <= arr.indexOf(val));
- }
-
- function flatten(arrayOfArrays) {
- if (arrayOfArrays.length === 0) {
- return [];
- } else {
- return arrayOfArrays.reduce((a, b) => a.concat(b));
- }
- }
-
- function getIncidentEdgeElements(nodeName) {
- const selectedElements = rootSvg
- .querySelectorAll(`[data-from='${nodeName}'],[data-to='${nodeName}']`);
- return [...selectedElements];
- }
-
- function getIncidentNodeElements(from, to) {
- return [
- rootSvg.querySelector(`[data-node='${from}']`),
- rootSvg.querySelector(`[data-node='${to}']`)
- ];
- }
-
- function getAdjacentNodeElements(centerNode) {
- const edges = g.incidentEdges(centerNode);
- const nodes = flatten(edges.map(edge => g.incidentNodes(edge)));
- const otherNodes = arrayUnique(nodes.filter(node => node !== centerNode));
- const elements = otherNodes.map(name => rootSvg.querySelector(`[data-node='${name}']`));
- return elements;
- }
-
- function scale(i, startRange, endRange, minResult, maxResult) {
- return minResult + (i - startRange) * (maxResult - minResult) / (endRange - startRange);
- }
-
- // Find min/max number of calls for all dependency links
- // to render different arrow widths depending on number of calls
- let minCallCount = 0;
- let maxCallCount = 0;
- links.filter(link => link.parent !== link.child).forEach(link => {
- const numCalls = link.callCount;
- if (minCallCount === 0 || numCalls < minCallCount) {
- minCallCount = numCalls;
- }
- if (numCalls > maxCallCount) {
- maxCallCount = numCalls;
- }
- });
- const minLg = Math.log(minCallCount);
- const maxLg = Math.log(maxCallCount);
-
- function arrowWidth(callCount) {
- const lg = Math.log(callCount);
- return scale(lg, minLg, maxLg, 0.3, 3);
- }
-
- // Get the names of all nodes in the graph
- const parentNames = links.map(link => link.parent);
- const childNames = links.map(link => link.child);
- const allNames = arrayUnique(parentNames.concat(childNames));
-
- // Add nodes/service names to the graph
- allNames.forEach(name => {
- g.addNode(name, {label: name});
- });
-
- // Add edges/dependency links to the graph
- links.filter(link => link.parent !== link.child)
- .forEach(({parent, child, callCount, errorCount}) => {
- g.addEdge(`${parent}->${child}`, parent, child, {
- from: parent,
- to: child,
- callCount,
- errorCount
- });
- });
-
- const layout = dagre.layout()
- .nodeSep(30)
- .rankSep(200)
- .rankDir('LR'); // LR = left-to-right, TB = top-to-bottom.
-
- // Override drawNodes and drawEdgePaths, so we can add
- // hover functionality on top of Dagre.
- const innerDrawNodes = renderer.drawNodes();
- const innerDrawEdgePaths = renderer.drawEdgePaths();
-
- renderer.drawNodes((gInner, svgInner) => {
- const svgNodes = innerDrawNodes(gInner, svgInner);
- // Add mouse hover/click handlers
- svgNodes.attr('data-node', d => d)
- .each(function(d) {
- const $this = $(this);
- const nodeEl = $this[0];
-
- $this.click(() => {
- _this.trigger('showServiceDataModal', {
- serviceName: d
- });
- });
-
- $this.hover(() => {
- nodeEl.classList.add('hover');
- rootSvg.classList.add('dark');
- getIncidentEdgeElements(d).forEach(el => {
- el.classList.add('hover-edge');
- });
- getAdjacentNodeElements(d).forEach(el => {
- el.classList.add('hover-light');
- });
- }, () => {
- nodeEl.classList.remove('hover');
- rootSvg.classList.remove('dark');
- getIncidentEdgeElements(d).forEach(e => {
- e.classList.remove('hover-edge');
- });
- getAdjacentNodeElements(d).forEach(e => {
- e.classList.remove('hover-light');
- });
- });
- });
- return svgNodes;
- });
-
- renderer.drawEdgePaths((gInner, svgInner) => {
- const svgNodes = innerDrawEdgePaths(gInner, svgInner);
- svgNodes.each(function(edge) {
- // Add mouse hover handlers
- const edgeEl = this;
- const $el = $(edgeEl);
-
- const callCount = gInner.edge(edge).callCount;
- const arrowWidthPx = `${arrowWidth(callCount)}px`;
- $el.css('stroke-width', arrowWidthPx);
-
- const errorCount = gInner.edge(edge).errorCount || 0;
- const errorRate = errorCount / callCount;
- if (errorRate >= highErrorRate) {
- $el.css('stroke', 'rgb(230, 162, 161)');
- } else if (errorRate >= lowErrorRate) {
- $el.css('stroke', 'rgb(230, 215, 140)');
- }
-
- $el.hover(() => {
- rootSvg.classList.add('dark');
- const nodes = getIncidentNodeElements(
- edgeEl.getAttribute('data-from'),
- edgeEl.getAttribute('data-to'));
- nodes.forEach(el => { el.classList.add('hover'); });
- edgeEl.classList.add('hover-edge');
- }, () => {
- rootSvg.classList.remove('dark');
- const nodes = getIncidentNodeElements(
- edgeEl.getAttribute('data-from'),
- edgeEl.getAttribute('data-to'));
- nodes.forEach(el => {
- el.classList.remove('hover');
- });
- edgeEl.classList.remove('hover-edge');
- });
- });
-
- svgNodes.attr('data-from', d => gInner.edge(d).from);
- svgNodes.attr('data-to', d => gInner.edge(d).to);
- return svgNodes;
- });
-
- renderer
- .layout(layout)
- .run(g, svgGroup);
- });
- });
-});
diff --git a/zipkin-ui/js/component_ui/environment.js b/zipkin-ui/js/component_ui/environment.js
deleted file mode 100644
index 186d0c1..0000000
--- a/zipkin-ui/js/component_ui/environment.js
+++ /dev/null
@@ -1,23 +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.
- */
-import flight from 'flightjs';
-
-export default flight.component(function environmentUI() {
- this.after('initialize', function() {
- this.$node.text(this.attr.config('environment'));
- });
-});
diff --git a/zipkin-ui/js/component_ui/error.js b/zipkin-ui/js/component_ui/error.js
deleted file mode 100644
index d67f904..0000000
--- a/zipkin-ui/js/component_ui/error.js
+++ /dev/null
@@ -1,41 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-
-export default component(function ErrorUI() {
- this.after('initialize', function() {
- this.on(document, 'uiServerError', function(evt, e) {
- this.$node.append($('<div></div>').text(`ERROR: ${e.desc}: ${e.message}`));
- this.$node.show();
- });
- });
-});
-
-// converts an jqXhr error to a string
-export function errToStr(e) {
- return e.responseJSON ? e.responseJSON.message : `server error (${e.statusText})`;
-}
-
-// transforms an ajax error into something that is passed to
-// trigger('uiServerError')
-export function getError(desc, e) {
- return {
- desc,
- message: errToStr(e)
- };
-}
diff --git a/zipkin-ui/js/component_ui/filterAllServices.js b/zipkin-ui/js/component_ui/filterAllServices.js
deleted file mode 100644
index 5f864fa..0000000
--- a/zipkin-ui/js/component_ui/filterAllServices.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-
-export default component(function filterAllServices() {
- this.$expandAll = $();
- this.$collapseAll = $();
- this.totalServices = 0;
- this.filtered = {};
- this.currentFilterCount = 0;
-
- this.toggleFilter = function(e) {
- this.trigger(document, $(e.target).val());
- };
-
- this.filterAdded = function(e, data) {
- if (this.filtered[data.value]) return;
-
- this.filtered[data.value] = true;
- this.currentFilterCount += 1;
-
- if (this.currentFilterCount === this.totalServices) {
- this.$expandAll.addClass('active');
- } else {
- this.$collapseAll.removeClass('active');
- }
- };
-
- this.filterRemoved = function(e, data) {
- if (!this.filtered[data.value]) return;
-
- this.filtered[data.value] = false;
- this.currentFilterCount -= 1;
-
- if (this.currentFilterCount === 0) {
- this.$collapseAll.addClass('active');
- } else {
- this.$expandAll.removeClass('active');
- }
- };
-
- this.after('initialize', function(node, data) {
- this.totalServices = data.totalServices;
- this.$expandAll = this.$node.find('[value="uiExpandAllSpans"]');
- this.$collapseAll = this.$node.find('[value="uiCollapseAllSpans"]');
-
- this.on('.btn', 'click', this.toggleFilter);
- this.on(document, 'uiAddServiceNameFilter', this.filterAdded);
- this.on(document, 'uiRemoveServiceNameFilter', this.filterRemoved);
- });
-});
diff --git a/zipkin-ui/js/component_ui/fullPageSpinner.js b/zipkin-ui/js/component_ui/fullPageSpinner.js
deleted file mode 100644
index c6e9bf0..0000000
--- a/zipkin-ui/js/component_ui/fullPageSpinner.js
+++ /dev/null
@@ -1,38 +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.
- */
-import {component} from 'flightjs';
-
-export default component(function fullPageSpinner() {
- this.requests = 0;
-
- this.showSpinner = function() {
- this.requests += 1;
- this.$node.show();
- };
-
- this.hideSpinner = function() {
- this.requests -= 1;
- if (this.requests === 0) {
- this.$node.hide();
- }
- };
-
- this.after('initialize', function() {
- this.on(document, 'uiShowFullPageSpinner', this.showSpinner);
- this.on(document, 'uiHideFullPageSpinner', this.hideSpinner);
- });
-});
diff --git a/zipkin-ui/js/component_ui/goToDependency.js b/zipkin-ui/js/component_ui/goToDependency.js
deleted file mode 100644
index 8e436c7..0000000
--- a/zipkin-ui/js/component_ui/goToDependency.js
+++ /dev/null
@@ -1,31 +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.
- */
-import {contextRoot} from '../publicPath';
-import {component} from 'flightjs';
-
-export default component(function goToDependency() {
- this.navigateToDependency = function(evt) {
- evt.preventDefault();
- const endTs = document.getElementById('endTs').value;
- const startTs = document.getElementById('startTs').value;
- window.location.href = `${contextRoot}dependency?endTs=${endTs}&startTs=${startTs}`;
- };
-
- this.after('initialize', function() {
- this.on('submit', this.navigateToDependency);
- });
-});
diff --git a/zipkin-ui/js/component_ui/goToLens.js b/zipkin-ui/js/component_ui/goToLens.js
deleted file mode 100644
index 804500a..0000000
--- a/zipkin-ui/js/component_ui/goToLens.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import Cookies from 'js-cookie';
-
-export default component(function goToLens() {
- this.goToLens = function(evt) {
- evt.preventDefault();
- Cookies.set('lens', 'true');
- window.location.reload(true);
- };
-
- this.after('initialize', function() {
- this.on('submit', this.goToLens);
- });
-});
diff --git a/zipkin-ui/js/component_ui/goToTrace.js b/zipkin-ui/js/component_ui/goToTrace.js
deleted file mode 100644
index 9743665..0000000
--- a/zipkin-ui/js/component_ui/goToTrace.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import {contextRoot} from '../publicPath';
-
-export default component(function goToTrace() {
- this.navigateToTrace = function(evt) {
- evt.preventDefault();
- const traceId = document.getElementById('traceIdQuery').value;
- window.location.href = `${contextRoot}traces/${traceId}`;
- };
-
- this.after('initialize', function() {
- this.on('submit', this.navigateToTrace);
- });
-});
diff --git a/zipkin-ui/js/component_ui/i18n.js b/zipkin-ui/js/component_ui/i18n.js
deleted file mode 100644
index 2935d9e..0000000
--- a/zipkin-ui/js/component_ui/i18n.js
+++ /dev/null
@@ -1,43 +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.
- */
-import 'jquery-i18n-properties';
-import $ from 'jquery';
-import {contextRoot} from '../publicPath';
-
-export function i18nInit(file) {
- // https://github.com/jquery-i18n-properties/jquery-i18n-properties
- $.i18n.properties({
- name: file,
- path: contextRoot,
- mode: 'map',
- // do not append a unix timestamp when requesting the language (.properties) files
- // this allows them to be cached by the browser
- cache: true,
- // do not perform blocking XHR requests, as it blocks the entire browser, including
- // rendering
- async: true,
- callback: () => {
- $('[data-i18n]').each((index, item) => {
- if (item.tagName === 'INPUT' || item.tagName === 'SELECT') {
- $(item).attr('placeholder', $.i18n.prop($(item).attr('data-i18n')));
- } else {
- $(item).html($.i18n.prop($(item).attr('data-i18n')));
- }
- });
- }
- });
-}
diff --git a/zipkin-ui/js/component_ui/infoButton.js b/zipkin-ui/js/component_ui/infoButton.js
deleted file mode 100644
index e65964f..0000000
--- a/zipkin-ui/js/component_ui/infoButton.js
+++ /dev/null
@@ -1,27 +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.
- */
-import {component} from 'flightjs';
-
-export default component(function infoButton() {
- this.requestInfoPanel = function() {
- this.trigger('uiRequestInfoPanel');
- };
-
- this.after('initialize', function() {
- this.on('click', this.requestInfoPanel);
- });
-});
diff --git a/zipkin-ui/js/component_ui/infoPanel.js b/zipkin-ui/js/component_ui/infoPanel.js
deleted file mode 100644
index 67a009e..0000000
--- a/zipkin-ui/js/component_ui/infoPanel.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import bootstrap // eslint-disable-line no-unused-vars
- from 'bootstrap/dist/js/bootstrap.bundle.min.js';
-
-export default component(function infoPanel() {
- this.show = function() {
- this.$node.modal('show');
- };
-
- this.after('initialize', function() {
- this.$node.modal('hide');
- this.on(document, 'uiRequestInfoPanel', this.show);
- });
-});
diff --git a/zipkin-ui/js/component_ui/jsonPanel.js b/zipkin-ui/js/component_ui/jsonPanel.js
deleted file mode 100644
index 663b55e..0000000
--- a/zipkin-ui/js/component_ui/jsonPanel.js
+++ /dev/null
@@ -1,41 +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.
- */
-import {component} from 'flightjs';
-
-export default component(function jsonPanel() {
- this.show = function(e, data) {
- this.$node.find('.modal-title').text(data.title);
- this.$node.find('.save').attr('href', data.link);
- this.$node.find('.modal-body pre code').text(JSON.stringify(data.obj, null, 2));
- this.$node.modal('show');
- };
- this.copyToClipboard = function() {
- const traceJson = document.getElementById('trace-json-content');
- function copyListener(e) {
- e.clipboardData.setData('text/plain', traceJson.textContent);
- e.preventDefault();
- }
- document.addEventListener('copy', copyListener);
- document.execCommand('copy');
- document.removeEventListener('copy', copyListener);
- };
- this.after('initialize', function() {
- this.$node.modal('hide');
- this.on(document, 'uiRequestJsonPanel', this.show);
- this.on('#copy2clipboard', 'click', this.copyToClipboard);
- });
-});
diff --git a/zipkin-ui/js/component_ui/lookback.js b/zipkin-ui/js/component_ui/lookback.js
deleted file mode 100644
index 2ccbba0..0000000
--- a/zipkin-ui/js/component_ui/lookback.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.
- */
-/* eslint-disable prefer-template */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import queryString from 'query-string';
-
-export default component(function lookback() {
- this.refreshCustomFields = function() {
- if (this.$node.val() === 'custom') {
- $('#custom-lookback').show();
- } else {
- $('#custom-lookback').hide();
- }
- };
-
- this.render = function() {
- const selectedLookback = queryString.parse(window.location.search).lookback;
- this.$node.find('option').each((i, option) => {
- const $option = $(option);
- if ($option.val() === selectedLookback) {
- $option.prop('selected', true);
- }
- });
- };
-
- this.after('initialize', function() {
- this.render();
- this.refreshCustomFields();
-
- this.on('change', this.refreshCustomFields);
- });
-});
diff --git a/zipkin-ui/js/component_ui/navbar.js b/zipkin-ui/js/component_ui/navbar.js
deleted file mode 100644
index dda59c9..0000000
--- a/zipkin-ui/js/component_ui/navbar.js
+++ /dev/null
@@ -1,39 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import {i18nInit} from '../component_ui/i18n';
-
-const NavbarUI = component(function navbar() {
- this.onNavigate = function(ev, {route}) {
- this.$node.find('[data-route]').each((i, el) => {
- const $el = $(el);
- if ($el.data('route') === route) {
- $el.addClass('active');
- } else {
- $el.removeClass('active');
- }
- });
- };
-
- this.after('initialize', function() {
- i18nInit('nav');
- this.on(document, 'navigate', this.onNavigate);
- });
-});
-
-export default NavbarUI;
diff --git a/zipkin-ui/js/component_ui/remoteServiceName.js b/zipkin-ui/js/component_ui/remoteServiceName.js
deleted file mode 100644
index 6f3f5eb..0000000
--- a/zipkin-ui/js/component_ui/remoteServiceName.js
+++ /dev/null
@@ -1,53 +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.
- */
-/* eslint-disable prefer-template */
-import {component} from 'flightjs';
-import 'chosen-js';
-import $ from 'jquery';
-import queryString from 'query-string';
-
-export default component(function remoteServiceName() {
- this.updateRemoteServices = function(ev, data) {
- this.render(data.remoteServices);
- this.trigger('chosen:updated');
- };
-
- this.render = function(remoteServices) {
- this.$node.empty();
- this.$node.append($($.parseHTML('<option value="all">all</option>')));
-
- const selectedRemoteServiceName = queryString.parse(window.location.search).remoteServiceName;
- $.each(remoteServices, (i, remoteService) => {
- const option = $($.parseHTML('<option/>'));
- option.val(remoteService);
- option.text(remoteService);
- if (remoteService === selectedRemoteServiceName) {
- option.prop('selected', true);
- }
- this.$node.append(option);
- });
- };
-
- this.after('initialize', function() {
- this.$node.chosen({
- search_contains: true
- });
- this.$node.next('.chosen-container');
-
- this.on(document, 'dataRemoteServiceNames', this.updateRemoteServices);
- });
-});
diff --git a/zipkin-ui/js/component_ui/serviceDataModal.js b/zipkin-ui/js/component_ui/serviceDataModal.js
deleted file mode 100644
index f4f925e..0000000
--- a/zipkin-ui/js/component_ui/serviceDataModal.js
+++ /dev/null
@@ -1,111 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import bootstrap // eslint-disable-line no-unused-vars
- from 'bootstrap/dist/js/bootstrap.bundle.min.js';
-
-function renderDependencyModal(event, data) {
- const $modal = $('#dependencyModal');
- const $parentElement = $(`<a href="">${data.parent}</a>`);
- $parentElement.click(ev => {
- ev.preventDefault();
- this.trigger(document, 'showServiceDataModal', {
- serviceName: data.parent
- });
- });
-
- const $childElement = $(`<a href="">${data.child}</a>`);
- $childElement.click(ev => {
- ev.preventDefault();
- this.trigger(document, 'showServiceDataModal', {
- serviceName: data.child
- });
- });
-
- $modal.find('#dependencyModalParent').html($parentElement);
- $modal.find('#dependencyModalChild').html($childElement);
- $modal.find('#dependencyCallCount').text(data.callCount);
- $modal.find('#dependencyErrorCount').text(data.errorCount || 0);
-
- $('#serviceModal').modal('hide');
- $modal.modal('show');
-}
-
-function renderServiceDataModal(event, data) {
- const $modal = $('#serviceModal');
- $modal.find('#serviceUsedByList').html('');
- data.usedBy.sort((a, b) =>
- a.toLowerCase().localeCompare(b.toLowerCase())
- );
- data.usedBy.forEach(usedBy => {
- const $name = $(`<li><a href="">${usedBy}</a></li>`);
- $name.find('a').click(ev => {
- ev.preventDefault();
- this.trigger(document, 'showDependencyModal', {
- parent: usedBy,
- child: data.serviceName
- });
- });
- $modal.find('#serviceUsedByList').append($name);
- });
-
- $modal.find('#serviceUsesList').html('');
- data.uses.sort((a, b) =>
- a.toLowerCase().localeCompare(b.toLowerCase())
- );
-
- data.uses.forEach(uses => {
- const $name = $(`<li><a href="">${uses}</a></li>`);
- $name.find('a').click(ev => {
- ev.preventDefault();
- this.trigger(document, 'showDependencyModal', {
- parent: data.serviceName,
- child: uses
- });
- });
- $modal.find('#serviceUsesList').append($name);
- });
-
- $modal.find('#serviceModalTitle').text(data.serviceName);
-
- $modal.modal('show');
- $('#dependencyModal').modal('hide');
-}
-
-export default component(function serviceDataModal() {
- this.showServiceDataModal = function(event, data) {
- this.trigger(document, 'serviceDataRequested', {
- serviceName: data.serviceName
- });
- };
-
- this.showDependencyModal = function(event, data) {
- this.trigger(document, 'parentChildDataRequested', {
- parent: data.parent,
- child: data.child,
- callCount: data.callCount
- });
- };
-
- this.after('initialize', function() {
- this.on(document, 'showServiceDataModal', this.showServiceDataModal);
- this.on(document, 'showDependencyModal', this.showDependencyModal);
- this.on(document, 'serviceDataReceived', renderServiceDataModal);
- this.on(document, 'parentChildDataReceived', renderDependencyModal);
- });
-});
diff --git a/zipkin-ui/js/component_ui/serviceFilterSearch.js b/zipkin-ui/js/component_ui/serviceFilterSearch.js
deleted file mode 100644
index a06149c..0000000
--- a/zipkin-ui/js/component_ui/serviceFilterSearch.js
+++ /dev/null
@@ -1,33 +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.
- */
-import {component} from 'flightjs';
-import 'chosen-js';
-
-export default component(function serviceNameFilter() {
- this.onChange = function(e, params) {
- if (params.selected === '') return;
-
- this.trigger(document, 'uiAddServiceNameFilter', {value: params.selected});
- this.$node.val('');
- this.$node.trigger('chosen:updated');
- };
-
- this.after('initialize', function() {
- this.$node.chosen({search_contains: true});
- this.on('change', this.onChange);
- });
-});
diff --git a/zipkin-ui/js/component_ui/serviceName.js b/zipkin-ui/js/component_ui/serviceName.js
deleted file mode 100644
index 935a9e6..0000000
--- a/zipkin-ui/js/component_ui/serviceName.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import Cookies from 'js-cookie';
-import $ from 'jquery';
-import queryString from 'query-string';
-
-import 'chosen-js';
-
- // Sorting based on the localCompare so that sorting can be
- // accomplished for non-ascii (non english) service names.
-export function sortServiceNames(serviceNames) {
- if (serviceNames) {
- serviceNames.sort((a, b) =>
- a.localeCompare(b)
- );
- }
- return serviceNames;
-}
-export default component(function serviceName() {
- this.onChange = function() {
- Cookies.set('last-serviceName', this.$node.val());
- this.triggerChange(this.$node.val());
- };
-
- this.triggerChange = function(name) {
- this.$node.trigger('uiChangeServiceName', name);
- };
-
- this.updateServiceNameDropdown = function(ev, data) {
- $('#serviceName').empty();
- this.$node.append($($.parseHTML('<option value="all">all</option>')));
- const services = sortServiceNames(data.names);
- $.each(services, (i, item) => {
- $('<option>').val(item).text(item).appendTo('#serviceName');
- });
-
- this.$node.find(`[value="${data.lastServiceName}"]`).attr('selected', 'selected');
-
- this.trigger('chosen:updated');
-
- // On the first view there won't be a selected or "last" service
- // name. Instead the first service at the top of the list will be
- // displayed, so load the span names for the top service too.
- if (!data.lastServiceName && services && services.length > 1) {
- this.$node.trigger('uiFirstLoadSpanNames', services[0]);
- this.$node.trigger('uiFirstLoadRemoteServiceNames', services[0]);
- }
- };
-
- this.after('initialize', function() {
- const name = queryString.parse(window.location.search).serviceName
- || Cookies.get('last-serviceName');
- this.triggerChange(name);
-
- this.$node.chosen({search_contains: true});
- this.$node.next('.chosen-container');
-
- this.on('change', this.onChange);
- this.on(document, 'dataServiceNames', this.updateServiceNameDropdown);
- });
-});
diff --git a/zipkin-ui/js/component_ui/spanName.js b/zipkin-ui/js/component_ui/spanName.js
deleted file mode 100644
index 643169c..0000000
--- a/zipkin-ui/js/component_ui/spanName.js
+++ /dev/null
@@ -1,53 +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.
- */
-/* eslint-disable prefer-template */
-import {component} from 'flightjs';
-import 'chosen-js';
-import $ from 'jquery';
-import queryString from 'query-string';
-
-export default component(function spanName() {
- this.updateSpans = function(ev, data) {
- this.render(data.spans);
- this.trigger('chosen:updated');
- };
-
- this.render = function(spans) {
- this.$node.empty();
- this.$node.append($($.parseHTML('<option value="all">all</option>')));
-
- const selectedSpanName = queryString.parse(window.location.search).spanName;
- $.each(spans, (i, span) => {
- const option = $($.parseHTML('<option/>'));
- option.val(span);
- option.text(span);
- if (span === selectedSpanName) {
- option.prop('selected', true);
- }
- this.$node.append(option);
- });
- };
-
- this.after('initialize', function() {
- this.$node.chosen({
- search_contains: true
- });
- this.$node.next('.chosen-container');
-
- this.on(document, 'dataSpanNames', this.updateSpans);
- });
-});
diff --git a/zipkin-ui/js/component_ui/spanPanel.js b/zipkin-ui/js/component_ui/spanPanel.js
deleted file mode 100644
index 4833905..0000000
--- a/zipkin-ui/js/component_ui/spanPanel.js
+++ /dev/null
@@ -1,156 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import {Constants} from './traceConstants';
-
-const entityMap = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- '/': '/',
- '`': '`',
- '=': '='
-};
-
-function escapeHtml(string) {
- return String(string).replace(/[&<>"'`=\/]/g, s => entityMap[s]);
-}
-
-export function isDupeTag(tagMap, anno) {
- if (!tagMap[anno.key]) {
- tagMap[anno.key] = anno.value; // eslint-disable-line no-param-reassign
- } else if (tagMap[anno.key] === anno.value) {
- return true;
- }
- return false;
-}
-
-// Annotation values that contain the word "error" hint of a transient error.
-// This adds a class when that's the case.
-export function maybeMarkTransientError(row, anno) {
- if (/error/i.test(anno.value)) {
- row.addClass('anno-error-transient');
- }
-}
-
-// Normal values are formatted in traceToMustache. However, Quoted json values
-// end up becoming javascript objects later. For this reason, we have to guard
-// and stringify as necessary.
-
-// annotations are named events which shouldn't hold json. If someone passed
-// json, format as a single line. That way the rows corresponding to timestamps
-// aren't disrupted.
-export function formatAnnotationValue(value) {
- const type = $.type(value);
- if (type === 'object' || type === 'array' || value == null) {
- return escapeHtml(JSON.stringify(value));
- } else {
- return escapeHtml(value.toString()); // prevents false from coercing to empty!
- }
-}
-
-// Tags sometimes have large values, for example json representing a query or a
-// stack trace. Format these so that they don't scroll off the side of the screen.
-export function formatTagValue(value) {
- const type = $.type(value);
- if (type === 'object' || type === 'array' || value == null) {
- return `<pre><code>${escapeHtml(JSON.stringify(value, null, 2))}</code></pre>`;
- }
- const result = value.toString();
- // Preformat if the text includes newlines
- return result.indexOf('\n') === -1 ? escapeHtml(result)
- : `<pre><code>${escapeHtml(result)}</code></pre>`;
-}
-
-export default component(function spanPanel() {
- this.$annotationTemplate = null;
- this.$tagTemplate = null;
- this.$showIdsTemplate = null;
-
- this.show = function(e, span) {
- const self = this;
- const tagMap = {};
-
- this.$node.find('.modal-title').text(
- `${span.serviceName}.${span.spanName}: ${span.durationStr}`);
-
- this.$node.find('.service-names').text(span.serviceNames);
-
- const $annoBody = this.$node.find('#annotations tbody').text('');
- $.each(span.annotations, (i, anno) => {
- const $row = self.$annotationTemplate.clone();
- maybeMarkTransientError($row, anno);
- $row.find('td').each(function() {
- const $this = $(this);
- const propertyName = $this.data('key');
- const text = propertyName === 'value'
- ? formatAnnotationValue(anno.value)
- : anno[propertyName];
- $this.append(text);
- });
- $annoBody.append($row);
- });
-
- $annoBody.find('.local-datetime').each(function() {
- const $this = $(this);
- const timestamp = $this.text();
- $this.text((new Date(parseInt(timestamp, 10) / 1000)).toLocaleString());
- });
-
- const $tagBody = this.$node.find('#tags tbody').text('');
- $.each((span.tags || []), (i, anno) => {
- if (isDupeTag(tagMap, anno)) return;
- const $row = self.$tagTemplate.clone();
- if (anno.key === Constants.ERROR) {
- $row.addClass('anno-error-critical');
- }
- $row.find('td').each(function() {
- const $this = $(this);
- const propertyName = $this.data('key');
- const text = propertyName === 'value'
- ? formatTagValue(anno.value)
- : escapeHtml(anno[propertyName]);
- $this.append(text);
- });
- $tagBody.append($row);
- });
-
- const $showIdsBody = this.$node.find('#showIds tbody').text('');
- const showIds = [['traceId', span.traceId],
- ['spanId', span.id],
- ['parentId', span.parentId]];
- $.each(showIds, (i, pair) => {
- const $row = self.$showIdsTemplate.clone();
- $row.find('.key').text(pair[0]);
- $row.find('.value').text(pair[1]);
- $showIdsBody.append($row);
- });
-
- this.$node.modal('show');
- };
-
- this.after('initialize', function() {
- this.$node.modal('hide');
- this.$annotationTemplate = this.$node.find('#annotations tbody tr').remove();
- this.$tagTemplate = this.$node.find('#tags tbody tr').remove();
- this.$showIdsTemplate = this.$node.find('#showIds tbody tr').remove();
- this.on(document, 'uiRequestSpanPanel', this.show);
- });
-});
diff --git a/zipkin-ui/js/component_ui/spanRow.js b/zipkin-ui/js/component_ui/spanRow.js
deleted file mode 100644
index 6c22ef6..0000000
--- a/zipkin-ui/js/component_ui/spanRow.js
+++ /dev/null
@@ -1,352 +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.
- */
-import {ConstantNames} from './traceConstants';
-
-// returns 'critical' if one of the spans has an error tag or currentErrorType was already critical,
-// returns 'transient' if one of the spans has an ERROR annotation, else
-// returns currentErrorType
-export function getErrorType(span, currentErrorType) {
- if (currentErrorType === 'critical') return currentErrorType;
- if (span.tags.error !== undefined) { // empty error tag is ok
- return 'critical';
- } else if (span.annotations.findIndex(ann => ann.value === 'error') !== -1) {
- return 'transient';
- }
- return currentErrorType;
-}
-
-export function formatEndpoint(endpoint) {
- if (!endpoint) return undefined;
- const {ipv4, ipv6, port, serviceName} = endpoint;
- if (ipv4 || ipv6) {
- const ip = ipv6 ? `[${ipv6}]` : ipv4; // arbitrarily prefer ipv6
- const portString = port ? `:${port}` : '';
- const serviceNameString = serviceName ? ` (${serviceName})` : '';
- return ip + portString + serviceNameString;
- } else {
- return serviceName || '';
- }
-}
-
-/*
- * Derived means not annotated directly. Ex 'Server Start' reflects the the timestamp of a
- * kind=SERVER span. 'Server Finish' is timestamp+duration of the same.
- */
-function toAnnotationRow(a, localFormatted, isDerived = false) {
- const res = {
- isDerived,
- value: ConstantNames[a.value] || a.value,
- timestamp: a.timestamp
- };
- if (localFormatted) res.endpoint = localFormatted;
- return res;
-}
-
-function parseAnnotationRows(span) {
- const localFormatted = formatEndpoint(span.localEndpoint) || undefined;
-
- let startTs = span.timestamp || 0;
- let endTs = startTs && span.duration ? startTs + span.duration : 0;
- let msTs = 0;
- let wsTs = 0;
- let wrTs = 0;
- let mrTs = 0;
-
- let begin;
- let end;
-
- let kind = span.kind;
-
- // scan annotations in case there are better timestamps, or inferred kind
- const annotationsToAdd = [];
- span.annotations.forEach(a => {
- switch (a.value) {
- case 'cs':
- kind = 'CLIENT';
- if (a.timestamp <= startTs) {
- startTs = a.timestamp;
- } else {
- annotationsToAdd.push(a);
- }
- break;
- case 'sr':
- kind = 'SERVER';
- if (a.timestamp <= startTs) {
- startTs = a.timestamp;
- } else {
- annotationsToAdd.push(a);
- }
- break;
- case 'ss':
- kind = 'SERVER';
- if (a.timestamp >= endTs) {
- endTs = a.timestamp;
- } else {
- annotationsToAdd.push(a);
- }
- break;
- case 'cr':
- kind = 'CLIENT';
- if (a.timestamp >= endTs) {
- endTs = a.timestamp;
- } else {
- annotationsToAdd.push(a);
- }
- break;
- case 'ms':
- kind = 'PRODUCER';
- msTs = a.timestamp;
- break;
- case 'mr':
- kind = 'CONSUMER';
- mrTs = a.timestamp;
- break;
- case 'ws':
- wsTs = a.timestamp;
- break;
- case 'wr':
- wrTs = a.timestamp;
- break;
- default:
- annotationsToAdd.push(a);
- }
- });
-
- switch (kind) {
- case 'CLIENT':
- begin = 'Client Start';
- end = 'Client Finish';
- break;
- case 'SERVER':
- begin = 'Server Start';
- end = 'Server Finish';
- break;
- case 'PRODUCER':
- begin = 'Producer Start';
- end = 'Producer Finish';
- if (startTs === 0 || (msTs !== 0 && msTs < startTs)) {
- startTs = msTs;
- msTs = 0;
- }
- if (endTs === 0 || (wsTs !== 0 && wsTs > endTs)) {
- endTs = wsTs;
- wsTs = 0;
- }
- break;
- case 'CONSUMER':
- if (startTs === 0 || (wrTs !== 0 && wrTs < startTs)) {
- startTs = wrTs;
- wrTs = 0;
- }
- if (endTs === 0 || (mrTs !== 0 && mrTs > endTs)) {
- endTs = mrTs;
- mrTs = 0;
- }
- if (endTs !== 0 || wrTs !== 0) {
- begin = 'Consumer Start';
- end = 'Consumer Finish';
- } else {
- begin = 'Consumer Start';
- }
- break;
- default:
- }
-
- // restore sometimes special-cased annotations
- if (msTs) annotationsToAdd.push({timestamp: msTs, value: 'ms'});
- if (wsTs) annotationsToAdd.push({timestamp: wsTs, value: 'ws'});
- if (wrTs) annotationsToAdd.push({timestamp: wrTs, value: 'wr'});
- if (mrTs) annotationsToAdd.push({timestamp: mrTs, value: 'mr'});
-
- const beginAnnotation = startTs && begin;
- const endAnnotation = endTs && end;
-
- const annotations = []; // prefer empty to undefined for arrays
-
- let annotationCount = annotationsToAdd.length;
- if (beginAnnotation) {
- annotationCount++;
- annotations.push(toAnnotationRow({
- value: begin,
- timestamp: startTs
- }, localFormatted, true));
- }
-
- annotationsToAdd.forEach((a) => {
- if (beginAnnotation && a.value === begin) return;
- if (endAnnotation && a.value === end) return;
- annotations.push(toAnnotationRow(a, localFormatted));
- });
-
- if (endAnnotation) {
- annotationCount++;
- annotations.push(toAnnotationRow({
- value: end,
- timestamp: endTs
- }, localFormatted, true));
- }
- return annotations;
-}
-
-function parseTagRows(span) {
- const localFormatted = formatEndpoint(span.localEndpoint) || undefined;
-
- const tagRows = []; // prefer empty to undefined for arrays
- const keys = Object.keys(span.tags);
- if (keys.length > 0) {
- keys.forEach(key => {
- const tagRow = {
- key: ConstantNames[key] || key,
- value: span.tags[key]
- };
- if (localFormatted) tagRow.endpoints = [localFormatted];
- tagRows.push(tagRow);
- });
- }
-
- // Ensure there's at least some data that will display the local address
- if (!span.kind && span.annotations.length === 0 && localFormatted && keys.length === 0) {
- tagRows.push({
- key: 'Local Address',
- value: localFormatted
- });
- }
-
- let addr;
- switch (span.kind) {
- case 'CLIENT':
- addr = 'Server Address';
- break;
- case 'SERVER':
- addr = 'Client Address';
- break;
- case 'PRODUCER':
- addr = 'Broker Address';
- break;
- case 'CONSUMER':
- addr = 'Broker Address';
- break;
- default:
- }
-
- if (span.remoteEndpoint) {
- tagRows.push({
- key: addr || 'Server Address', // default when we don't know the endpoint
- value: formatEndpoint(span.remoteEndpoint)
- });
- }
- return tagRows;
-}
-
-// This ensures we don't add duplicate annotations on merge
-function maybePushAnnotation(annotations, a) {
- if (annotations.findIndex(b => a.timestamp === b.timestamp && a.value === b.value) === -1) {
- annotations.push(a);
- }
-}
-
-// This ensures we only add rows for tags that are unique on key and value on merge
-function maybePushTag(tags, a) {
- const sameKeyAndValue = tags.filter(b => a.key === b.key && a.value === b.value);
- if (sameKeyAndValue.length === 0) {
- tags.push(a);
- return;
- }
- if ((a.endpoints || []).length === 0) {
- return; // no endpoints to merge
- }
- // Handle when tags are reported by different endpoints
- sameKeyAndValue.forEach((t) => {
- if (!t.endpoints) t.endpoints = []; // eslint-disable-line no-param-reassign
- a.endpoints.forEach((endpoint) => {
- if (t.endpoints.indexOf(endpoint) === -1) {
- t.endpoints.push(endpoint);
- }
- });
- });
-}
-
-// This guards to ensure we don't add duplicate service names on merge
-function maybePushServiceName(serviceNames, serviceName) {
- if (!serviceName) return;
- if (serviceNames.findIndex(s => s === serviceName) === -1) {
- serviceNames.push(serviceName);
- }
-}
-
-function getServiceName(endpoint) {
- return endpoint ? endpoint.serviceName : undefined;
-}
-
-// Merges the data into a single span row, which is lacking presentation information
-export function newSpanRow(spansToMerge, isLeafSpan) {
- const first = spansToMerge[0];
- const res = {
- spanId: first.id,
- serviceNames: [],
- annotations: [],
- tags: [],
- errorType: 'none'
- };
-
- let sharedTimestamp;
- let sharedDuration;
- spansToMerge.forEach(next => {
- if (next.parentId) res.parentId = next.parentId;
- if (next.name && (!res.spanName || next.kind === 'SERVER')) {
- res.spanName = next.name; // prefer the server's span name
- }
-
- if (next.shared) { // save off any shared timestamp, it is our second choice
- if (!sharedTimestamp) sharedTimestamp = next.timestamp;
- if (!sharedDuration) sharedDuration = next.duration;
- } else {
- if (!res.timestamp && next.timestamp) res.timestamp = next.timestamp;
- if (!res.duration && next.duration) res.duration = next.duration;
- }
-
- const nextLocalServiceName = getServiceName(next.localEndpoint);
- const nextRemoteServiceName = getServiceName(next.remoteEndpoint);
- if (nextLocalServiceName && next.kind === 'SERVER') {
- res.serviceName = nextLocalServiceName; // prefer the server's service name
- } else if (isLeafSpan && nextRemoteServiceName && next.kind === 'CLIENT' && !res.serviceName) {
- // use the client's remote service name only on leaf spans
- res.serviceName = nextRemoteServiceName;
- } else if (nextLocalServiceName && !res.serviceName) {
- res.serviceName = nextLocalServiceName;
- }
-
- maybePushServiceName(res.serviceNames, nextLocalServiceName);
- maybePushServiceName(res.serviceNames, nextRemoteServiceName);
-
- parseAnnotationRows(next).forEach((a) => maybePushAnnotation(res.annotations, a));
- parseTagRows(next).forEach((t) => maybePushTag(res.tags, t));
-
- res.errorType = getErrorType(next, res.errorType);
-
- if (next.debug) res.debug = true;
- });
-
- // timestamp is used to derive positional data later
- if (!res.timestamp && sharedTimestamp) res.timestamp = sharedTimestamp;
- // duration is used for deriving data, and also for the zoom function
- if (!res.duration && sharedDuration) res.duration = sharedDuration;
-
- res.serviceNames.sort();
- res.annotations.sort((a, b) => a.timestamp - b.timestamp);
- return res;
-}
diff --git a/zipkin-ui/js/component_ui/timeStamp.js b/zipkin-ui/js/component_ui/timeStamp.js
deleted file mode 100644
index d27b7ee..0000000
--- a/zipkin-ui/js/component_ui/timeStamp.js
+++ /dev/null
@@ -1,57 +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.
- */
-import {component} from 'flightjs';
-import moment from 'moment';
-import bootstrapDatepicker from 'bootstrap-datepicker'; // eslint-disable-line no-unused-vars
-
-export default component(function timeStamp() {
- this.init = function() {
- this.$timestamp = this.$node.find('.timestamp-value');
- this.$date = this.$node.find('.date-input');
- this.$time = this.$node.find('.time-input');
- const ts = this.$timestamp.val();
- this.setDateTime((ts) ? moment(Number(ts)) : moment());
- };
-
- this.setDateTime = function(time) {
- this.$date.val(time.format('YYYY-MM-DD'));
- this.$time.val(time.format('HH:mm'));
- };
-
- this.setTimestamp = function(time) {
- this.$timestamp.val(time.valueOf());
- };
-
- this.dateChanged = function(e) {
- const time = moment(e.date);
- time.add(moment.duration(this.$time.val()));
- this.setTimestamp(moment.utc(time));
- };
-
- this.timeChanged = function() {
- const time = moment(this.$date.val(), 'YYYY-MM-DD');
- time.add(moment.duration(this.$time.val()));
- this.setTimestamp(moment.utc(time));
- };
-
- this.after('initialize', function() {
- this.init();
- this.on(this.$time, 'change', this.timeChanged);
- this.$date.datepicker({format: 'yyyy-mm-dd'});
- this.on(this.$date, 'changeDate', this.dateChanged.bind(this));
- });
-});
diff --git a/zipkin-ui/js/component_ui/trace.js b/zipkin-ui/js/component_ui/trace.js
deleted file mode 100644
index 01d4388..0000000
--- a/zipkin-ui/js/component_ui/trace.js
+++ /dev/null
@@ -1,483 +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.
- */
-/* eslint-disable no-param-reassign, prefer-template */
-import {component} from 'flightjs';
-import queryString from 'query-string';
-import $ from 'jquery';
-import {i18nInit} from '../component_ui/i18n';
-
-// extracted for testing. this code mutates spans and selectedSpans
-export function showSpans(spans, parents, childIds, selectedSpans) {
- const family = new Set();
- $.each(selectedSpans, (i, $selected) => {
- $selected.show();
- $selected.addClass('highlight');
- $selected.expanded = true;
- $selected.$expander.html('<i class="far fa-minus-square"></i>');
-
- $.each(childIds[$selected.id], (j, cId) => {
- family.add(cId);
- spans[cId].openParents += 1;
- });
- $.each(parents[$selected.id], (j, pId) => {
- /* Parent may not be found for a number of reasons. For example, the
- trace id may not be a span id. Also, it is possible to lose the root
- span data (i.e. a headless trace) */
- if (spans[pId]) {
- family.add(pId);
- spans[pId].openChildren += 1;
- }
- });
- });
- family.forEach(id => spans[id].show());
-}
-
-// extracted for testing. this code mutates spans and selectedSpans
-export function hideSpans(spans, parents, childIds, selectedSpans, childrenOnly) {
- const family = new Set();
- $.each(selectedSpans, (i, $selected) => {
- if (!childrenOnly === 0) {
- $selected.hide();
- }
- $selected.removeClass('highlight');
- $selected.expanded = false;
- $selected.$expander.html('<i class="far fa-plus-square"></i>');
-
- $.each(childIds[$selected.id], (j, cId) => {
- family.add(cId);
- // Decrement only when there is an open parent
- if (spans[cId].openParents >= 1) spans[cId].openParents -= 1;
- });
- if (!childrenOnly) {
- $.each(parents[$selected.id], (j, pId) => {
- /* Parent may not be found for a number of reasons. For example, the
- trace id may not be a span id. Also, it is possible to lose the root
- span data (i.e. a headless trace) */
- if (spans[pId]) {
- family.add(pId);
- spans[pId].openChildren -= 1;
- }
- });
- }
- });
- family.forEach(id => hideSpans(spans, parents, childIds, [spans[id]], true));
- family.forEach(id => spans[id].hide());
-}
-
-function spanChildIds($span) {
- const childIds = ($span.attr('data-child-ids') || '').toString().split(',');
- if (childIds.length === 1 && childIds[0] === '') {
- $span.find('.expander').hide();
- return [];
- } else {
- return childIds;
- }
-}
-
-function initSpan($span) {
- const id = $span.data('id');
- $span.id = id;
- $span.expanded = false;
- $span.$expander = $span.find('.expander');
- $span.openChildren = 0;
- $span.openParents = 0;
-
- const parentId = $span.data('parentId');
- $span.parentId = parentId;
- $span.isRoot = !(parentId !== undefined && parentId !== '');
- return $span;
-}
-
-export function initSpans($node) {
- const spans = {};
- const childIds = {};
- const parents = {};
- // this includes both local and remote service names
- const spansByService = {};
-
- $node.find('.span:not(#timeLabel)').each(function() {
- const span = initSpan($(this));
- const id = span.id;
- const parentId = span.parentId;
-
- spans[id] = span;
- childIds[id] = spanChildIds(span);
- parents[id] = !span.isRoot ? [parentId] : [];
- $.merge(parents[id], parents[parentId] || []);
-
- $.each((span.attr('data-service-names') || '').split(','), (i, sn) => {
- const current = spansByService[sn] || [];
- current.push(id);
- spansByService[sn] = current;
- });
- });
-
- return {
- spans,
- childIds,
- parents,
- spansByService
- };
-}
-
-export default component(function trace() {
- /*
- * Next variables are setting up after initialization.
- * see initSpans
- *
- * this.spans = {};
- * this.parents = {};
- * this.childIds = {};
- * this.spansByService = {};
- */
- this.spansBackup = {};
-
- /* this is for a temporary rectangle which is shown on
- * user's mouse move over span view.*/
- this.rectElement = $('<div>').addClass('rect-element');
-
- /* This method stores original span details for later use.
- * When span view is zoomed in and zoomed out these details help to
- * get back to original span view*/
- this.setupSpansBackup = function($span) {
- const id = $span.data('id');
- $span.id = id;
- this.spansBackup[id] = $span;
- };
-
- /* Returns a jquery object representing the spans in svc */
- this.getSpansByService = function(svc) {
- let spans = this.spansByService[svc];
- if (spans === undefined) {
- this.spansByService[svc] = spans = $();
- } else if (spans.jquery === undefined) {
- this.spansByService[svc] = spans = $(`#${spans.join(',#')}`);
- }
- return spans;
- };
-
- this.expandSpans = function(spans) {
- showSpans(this.spans, this.parents, this.childIds, spans);
- };
-
- this.collapseSpans = function(spans, childrenOnly) {
- hideSpans(this.spans, this.parents, this.childIds, spans, childrenOnly);
- };
-
- this.handleClick = function(e) {
- const $target = $(e.target);
- const $span = this.spans[($target.is('.span') ? $target : $target.parents('.span')).data('id')];
-
- const $expander = $target.is('.expander') ? $target : $target.parents('.expander');
- if ($expander.length > 0) {
- this.toggleExpander($span);
- return;
- }
-
- /* incase mouse moved to another span after mousedown, $span is undefined*/
- if ($span && $span.length > 0) {
- this.showSpanDetails($span);
- }
- };
-
- this.originalDuration = function() {
- const markerText = $('#timeLabel-backup .time-marker-5').text();
- return markerText ? parseFloat(markerText) : 0;
- };
-
- /* On mousedown and mousemove we need to show a selection area and zoomin
- * spans according to width of selected area. During zoomin only the
- * width i.e. x coordinates are considered.*/
- this.handleMouseDown = function(e) {
- const self = this;
-
- const rectTop = e.pageY;
- const rectLeft = e.pageX;
- self.rectElement.appendTo(self.$node);
-
- /* dont draw the rectangle until mouse is moved.
- * this helps in getting the correct parent in case of click
- * event.*/
- self.rectElement.css({top: '0px', left: '0px', width: '0px', height: '0px'});
-
- self.$node.bind('mousemove', ev => {
- /* prevent selection and thus highlighting of spans after mousedown and mousemove*/
- ev.preventDefault();
-
- /* draw a rectangle out of mousedown and mousemove coordinates*/
- const rectWidth = Math.abs(ev.pageX - rectLeft);
- const rectHeight = Math.abs(ev.pageY - rectTop);
-
- const newX = (ev.pageX < rectLeft) ? (rectLeft - rectWidth) : rectLeft;
- const newY = (ev.pageY < rectTop) ? (rectTop - rectHeight) : rectTop;
-
- self.rectElement.css({top: `${newY}px`, left: `${newX}px`,
- width: `${rectWidth}px`, height: `${rectHeight}px`});
- });
-
- self.$node.bind('mouseup', ev => {
- self.$node.unbind('mousemove');
- self.$node.unbind('mouseup');
- /* Add code to calculate mintime and max time from pixel value of
- * mouse down and mouse move*/
- const originalDuration = this.originalDuration();
- const spanClickViewLeftOffsetPx = $($('#trace-container .time-marker-0')[1]).offset().left;
- const spanClickViewWidthPx = $('#trace-container .time-marker-5').position().left;
-
- const rectTopLeft = self.rectElement.position().left;
- /* make sure that redraw mintime starts from 0.0 not less than 0.0.
- * if user starts selecting from servicename adjust the left, width accordingly*/
- const rectElementActualLeft =
- (rectTopLeft < spanClickViewLeftOffsetPx) ? spanClickViewLeftOffsetPx : rectTopLeft;
- const rectElementActualWidth =
- (rectTopLeft < spanClickViewLeftOffsetPx) ?
- (self.rectElement.width() - (spanClickViewLeftOffsetPx - rectTopLeft))
- : self.rectElement.width();
-
- const minTimeOffsetPx = rectElementActualLeft - spanClickViewLeftOffsetPx;
- const maxTimeOffsetPx = (rectElementActualLeft + rectElementActualWidth)
- - spanClickViewLeftOffsetPx;
-
- const minTime = minTimeOffsetPx * (originalDuration / spanClickViewWidthPx);
- const maxTime = maxTimeOffsetPx * (originalDuration / spanClickViewWidthPx);
-
- /* when mousemove doesnt happen mintime is greater than maxtime. since rect-element
- * is created at top left corner of screen, rectElementActualWidth will be negative.
- *We need to invoke mouseclick functionality*/
- if (minTime >= maxTime) {
- /* pass on the target which got clicked. Since we do not draw
- * rectangle on mousedown, we would never endup having
- * rect-element as our target. Target would always be either
- * handle, time-marker, duration which are all children of span class*/
- self.handleClick(ev.target);
- } else {
- /* now that we have min and max time, trigger zoominspans*/
- self.trigger(document, 'uiZoomInSpans', {mintime: minTime, maxtime: maxTime});
- }
- self.rectElement.remove();
- });
- };
-
-
- this.toggleExpander = function($span) {
- if ($span.expanded) {
- this.collapseSpans([$span], true);
- } else {
- this.expandSpans([$span], true);
- }
- };
-
- this.showSpanDetails = function($span) {
- const spanData = {
- annotations: [],
- tags: []
- };
-
- $.each($span.data('keys').split(','), (i, n) => { spanData[n] = $span.data(n); });
-
- $span.find('.annotation').each(function() {
- const $this = $(this);
- const anno = {};
- $.each($this.data('keys').split(','), (e, n) => { anno[n] = $this.data(n); });
- spanData.annotations.push(anno);
- });
-
- $span.find('.tag').each(function() {
- const $this = $(this);
- const anno = {};
- $.each($this.data('keys').split(','), (e, n) => { anno[n] = $this.data(n); });
- spanData.tags.push(anno);
- });
-
- this.trigger('uiRequestSpanPanel', spanData);
- };
-
- this.showSpinnerAround = function(cb, e, data) {
- if (this.actingOnAll) {
- cb(e, data);
- } else {
- this.trigger(document, 'uiShowFullPageSpinner');
- setTimeout(() => {
- cb(e, data);
- this.trigger(document, 'uiHideFullPageSpinner');
- }, 100);
- }
- };
-
-
- this.expandAllSpans = function() {
- const self = this;
- self.actingOnAll = true;
- this.showSpinnerAround(() => {
- showSpans(self.spans, self.parents, self.childIds, self.spans);
- });
- self.actingOnAll = false;
- $('#expandAll').addClass('active');
- $('#collapseAll').removeClass('active');
- };
-
- this.collapseAllSpans = function() {
- const self = this;
- self.actingOnAll = true;
- this.showSpinnerAround(() => {
- $.each(self.spans, (id, $span) => {
- $span.openParents = 0;
- $span.openChildren = 0;
- $span.removeClass('highlight');
- $span.expanded = false;
- $span.$expander.html('<i class="far fa-plus-square"></i>');
- if (!$span.isRoot) $span.hide();
- });
- });
- self.actingOnAll = false;
- $('#expandAll').removeClass('active');
- $('#collapseAll').addClass('active');
- };
-
- /* This method modifies the span container view. It zooms in the span view
- * for selected time duration. Spans starting with in the selected time
- * duration are highlighted with span name in red color.
- * Also unhides zoomout button so that user can click to go
- * back to original span view*/
- this.zoomInSpans = function(node, data) {
- const self = this;
-
- const originalDuration = this.originalDuration();
-
- const mintime = data.mintime;
- const maxtime = data.maxtime;
- const newDuration = maxtime - mintime;
-
- self.$node.find('#timeLabel .time-marker').each(function(i) {
- const v = (mintime + newDuration * (i / 5)).toFixed(2);
- // TODO:show time units according to new duration
- $(this).text(v);
- $(this).css('color', '#d9534f');
- });
-
- const styles = {
- left: '0.0%',
- width: '100.0%',
- color: '#000'
- };
- self.showSpinnerAround(() => {
- $.each(self.spans, (id, $span) => {
- /* corresponding to this id extract span from backupspans list*/
- const origLeftVal = parseFloat((self.spansBackup[id]).find('.duration')[0].style.left);
- const origWidthVal = parseFloat((self.spansBackup[id]).find('.duration')[0].style.width);
- /* left and width are in %, so convert them as time values*/
- const spanStart = (origLeftVal * originalDuration) / 100;
- const spanEnd = spanStart + (origWidthVal * originalDuration) / 100;
- /* reset the color to black. It gets set for inrange spans to red*/
- styles.color = '#000';
-
- /* change style left, width and color of new spans based on mintime and maxtime*/
- if (spanStart < mintime && spanEnd < mintime) {
- styles.left = '0.0%'; styles.width = '0.0%';
- } else if (spanStart < mintime && spanEnd > mintime && spanEnd < maxtime) {
- const w = (((spanEnd - mintime)) / newDuration) * 100 + '%';
- styles.left = '0.0%'; styles.width = w;
- } else if (spanStart < mintime && spanEnd > mintime && spanEnd > maxtime) {
- styles.left = '0.0%'; styles.width = '100.0%';
- } else if (spanStart >= mintime && spanStart < maxtime && spanEnd <= maxtime) {
- const l = (((spanStart - mintime)) / newDuration) * 100 + '%';
- const w = (((spanEnd - spanStart)) / newDuration) * 100 + '%';
- styles.left = l; styles.width = w; styles.color = '#d9534f';
- } else if (spanStart >= mintime && spanStart < maxtime && spanEnd > maxtime) {
- const l = (((spanStart - mintime)) / newDuration) * 100 + '%';
- const w = (((maxtime - spanStart)) / newDuration) * 100 + '%';
- styles.left = l; styles.width = w; styles.color = '#d9534f';
- } else if (spanStart > maxtime) {
- styles.left = '100.0%'; styles.width = '0.0%';
- } else if (spanStart === maxtime) {
- styles.left = '100.0%'; styles.width = '0.0%'; styles.color = '#d9534f';
- } else {
- styles.left = '0.0%'; styles.width = '0.0%';
- }
-
- $span.find('.duration').css('color', styles.color);
- $span.find('.duration').animate({left: styles.left, width: styles.width}, 'slow');
- });
- });
-
- /* make sure that zoom-in on already zoomed spanview is not allowed*/
- self.$node.unbind('mousedown');
-
- /* show zoomOut button now*/
- $('button[value=uiZoomOutSpans]').show('slow');
- };
-
-
- /* This method brings back the original span container in view*/
- this.zoomOutSpans = function() {
- /* re bind mousedown event to enable zoom-in after zoom-out*/
- this.on('mousedown', this.handleMouseDown);
-
- /* get values from the backup trace container*/
- this.$node.find('#timeLabel .time-marker').each(function(i) {
- $(this).css('color', $('#timeLabel-backup .time-marker-' + i).css('color'));
- $(this).text($('#timeLabel-backup .time-marker-' + i).text());
- });
-
- const self = this;
- this.showSpinnerAround(() => {
- $.each(self.spans, (id, $span) => {
- const originalStyleLeft = (self.spansBackup[id]).find('.duration')[0].style.left;
- const originalStyleWidth = (self.spansBackup[id]).find('.duration')[0].style.width;
-
- $span.find('.duration').animate({
- left: originalStyleLeft,
- width: originalStyleWidth
- }, 'slow');
- $span.find('.duration').css('color', '#000');
- });
- });
-
- /* hide zoomOut button now*/
- $('button[value=uiZoomOutSpans]').hide('slow');
- };
-
- this.after('initialize', function() {
- this.on('click', this.handleClick);
- this.on('mousedown', this.handleMouseDown);
- this.on(document, 'uiExpandAllSpans', this.expandAllSpans);
- this.on(document, 'uiCollapseAllSpans', this.collapseAllSpans);
- this.on(document, 'uiZoomInSpans', this.zoomInSpans);
- this.on(document, 'uiZoomOutSpans', this.zoomOutSpans);
-
- const self = this;
- const initData = initSpans(self.$node);
- this.spans = initData.spans;
- this.parents = initData.parents;
- this.childIds = initData.childIds;
- this.spansByService = initData.spansByService;
-
- /* get spans from trace-container-backup*/
- $('#trace-container-backup .span:not(#timeLabel-backup)').each(function() {
- self.setupSpansBackup($(this));
- });
-
- const serviceName = queryString.parse(location.search).serviceName;
- if (serviceName !== undefined) {
- this.trigger(document, 'uiAddServiceNameFilter', {value: serviceName});
- } else {
- this.trigger(document, 'uiExpandAllSpans');
- }
-
- i18nInit('trace');
- });
-});
diff --git a/zipkin-ui/js/component_ui/traceConstants.js b/zipkin-ui/js/component_ui/traceConstants.js
deleted file mode 100644
index b3c2dbd..0000000
--- a/zipkin-ui/js/component_ui/traceConstants.js
+++ /dev/null
@@ -1,69 +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.
- */
-const CLIENT_SEND = 'cs';
-const CLIENT_SEND_FRAGMENT = 'csf';
-const CLIENT_RECEIVE = 'cr';
-const CLIENT_RECEIVE_FRAGMENT = 'crf';
-const MESSAGE_SEND = 'ms';
-const MESSAGE_RECEIVE = 'mr';
-const SERVER_SEND = 'ss';
-const SERVER_SEND_FRAGMENT = 'ssf';
-const SERVER_RECEIVE = 'sr';
-const SERVER_RECEIVE_FRAGMENT = 'srf';
-const SERVER_ADDR = 'sa';
-const CLIENT_ADDR = 'ca';
-const MESSAGE_ADDR = 'ma';
-const WIRE_SEND = 'ws';
-const WIRE_RECEIVE = 'wr';
-const ERROR = 'error';
-const LOCAL_COMPONENT = 'lc';
-export const Constants = {
- CLIENT_SEND,
- CLIENT_SEND_FRAGMENT,
- CLIENT_RECEIVE,
- CLIENT_RECEIVE_FRAGMENT,
- MESSAGE_SEND,
- MESSAGE_RECEIVE,
- SERVER_SEND,
- SERVER_SEND_FRAGMENT,
- SERVER_RECEIVE,
- SERVER_RECEIVE_FRAGMENT,
- SERVER_ADDR,
- CLIENT_ADDR,
- MESSAGE_ADDR,
- ERROR,
- LOCAL_COMPONENT,
-};
-
-export const ConstantNames = {};
-ConstantNames[CLIENT_SEND] = 'Client Send';
-ConstantNames[CLIENT_SEND_FRAGMENT] = 'Client Send Fragment';
-ConstantNames[CLIENT_RECEIVE] = 'Client Receive';
-ConstantNames[CLIENT_RECEIVE_FRAGMENT] = 'Client Receive Fragment';
-ConstantNames[MESSAGE_SEND] = 'Producer Send';
-ConstantNames[MESSAGE_RECEIVE] = 'Consumer Receive';
-ConstantNames[SERVER_SEND] = 'Server Send';
-ConstantNames[SERVER_SEND_FRAGMENT] = 'Server Send Fragment';
-ConstantNames[SERVER_RECEIVE] = 'Server Receive';
-ConstantNames[SERVER_RECEIVE_FRAGMENT] = 'Server Receive Fragment';
-ConstantNames[CLIENT_ADDR] = 'Client Address';
-ConstantNames[MESSAGE_ADDR] = 'Broker Address';
-ConstantNames[SERVER_ADDR] = 'Server Address';
-ConstantNames[WIRE_SEND] = 'Wire Send';
-ConstantNames[WIRE_RECEIVE] = 'Wire Receive';
-ConstantNames[LOCAL_COMPONENT] = 'Local Component';
-// Don't add ERROR to ConstantNames -- css coloring depends on constant name 'error'
diff --git a/zipkin-ui/js/component_ui/traceFilters.js b/zipkin-ui/js/component_ui/traceFilters.js
deleted file mode 100644
index f7ede97..0000000
--- a/zipkin-ui/js/component_ui/traceFilters.js
+++ /dev/null
@@ -1,64 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-
-export default component(function traceFilters() {
- this.idFromService = function(service) {
- return `service-filter-${service.replace(/[^a-z0-9\-_]/gi, '-')}`;
- };
-
- this.addToFilter = function(ev, data) {
- if (this.$node.find(`[data-service-name='${data.value}']`).length) return;
-
- // TODO: should this be mustache instead?
- const $remove =
- $('<span>')
- .attr('class', 'badge service-filter-remove')
- .text('x')
- .on('click', function() { $(this).trigger('uiRemoveServiceNameFilter', data); });
-
- const $html =
- $('<span>')
- .attr('class', 'label service-filter-label')
- .attr('id', this.idFromService(data.value))
- .attr('data-serviceName', data.value)
- .text(data.value)
- .append($remove);
-
- this.$node.find('.service-tags').append($html);
- };
-
- this.removeFromFilter = function(ev, data) {
- this.$node.find(`#${this.idFromService(data.value)}`).remove();
- };
-
- this.updateTraces = function(ev, data) {
- this.$node.find('.filter-current').text(data.traces.length);
- };
-
- this.updateSortOrder = function(ev) {
- this.trigger(document, 'uiUpdateTraceSortOrder', {order: $(ev.target).val()});
- };
-
- this.after('initialize', function() {
- this.on(document, 'uiAddServiceNameFilter', this.addToFilter);
- this.on(document, 'uiRemoveServiceNameFilter', this.removeFromFilter);
- this.on(document, 'uiUpdateTraces', this.updateTraces);
- this.on('.sort-order', 'change', this.updateSortOrder);
- });
-});
diff --git a/zipkin-ui/js/component_ui/traceSummary.js b/zipkin-ui/js/component_ui/traceSummary.js
deleted file mode 100644
index bc8a531..0000000
--- a/zipkin-ui/js/component_ui/traceSummary.js
+++ /dev/null
@@ -1,212 +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.
- */
-// eslint-disable no-nested-ternary
-import _ from 'lodash';
-import moment from 'moment';
-import {getErrorType} from './spanRow';
-
-// To ensure data doesn't scroll off the screen, we need all timestamps, not just
-// client/server ones.
-export function addTimestamps(span, timestamps) {
- if (!span.timestamp) return;
- timestamps.push(span.timestamp);
- if (!span.duration) return;
- timestamps.push(span.timestamp + span.duration);
-}
-
-export function getMaxDuration(timestamps) {
- if (timestamps.length > 1) {
- timestamps.sort();
- return timestamps[timestamps.length - 1] - timestamps[0];
- }
- return 0;
-}
-
-function pushEntry(dict, key, value) {
- if (dict[key]) {
- dict[key].push(value);
- } else {
- dict[key] = [value]; // eslint-disable-line no-param-reassign
- }
-}
-
-function addServiceNameTimestampDuration(span, groupedTimestamps) {
- const value = {
- timestamp: span.timestamp || 0, // only used by totalDuration
- duration: span.duration || 0
- };
-
- if (span.localEndpoint && span.localEndpoint.serviceName) {
- pushEntry(groupedTimestamps, span.localEndpoint.serviceName, value);
- }
- // TODO: only do this if it is a leaf span and a client or producer.
- // If we are at the bottom of the tree, it can be helpful to count also against a remote
- // uninstrumented service
- if (span.remoteEndpoint && span.remoteEndpoint.serviceName) {
- pushEntry(groupedTimestamps, span.remoteEndpoint.serviceName, value);
- }
-}
-
-// Returns null on empty or when missing a timestamp
-export function traceSummary(root) {
- const timestamps = [];
- const groupedTimestamps = {};
-
- let traceId;
- let spanCount = 0;
- let errorType = 'none';
-
- root.traverse(span => {
- spanCount++;
- traceId = span.traceId;
- errorType = getErrorType(span, errorType);
- addTimestamps(span, timestamps);
- addServiceNameTimestampDuration(span, groupedTimestamps);
- });
-
- if (timestamps.length === 0) throw new Error(`Trace ${traceId} is missing a timestamp`);
-
- return {
- traceId,
- timestamp: timestamps[0],
- duration: getMaxDuration(timestamps),
- groupedTimestamps,
- errorType,
- spanCount
- };
-}
-
-// This returns a total duration by merging all overlapping intervals found in the the input.
-//
-// This is used to create servicePercentage for index.mustache when a service is selected
-export function totalDuration(timestampAndDurations) {
- const filtered = _(timestampAndDurations)
- .filter((s) => s.duration) // filter out anything we can't make an interval out of
- .sortBy('timestamp').value(); // to merge intervals, we need the input sorted
-
- if (filtered.length === 0) {
- return 0;
- }
- if (filtered.length === 1) {
- return filtered[0].duration;
- }
-
- let result = filtered[0].duration;
- let currentIntervalEnd = filtered[0].timestamp + filtered[0].duration;
-
- for (let i = 1; i < filtered.length; i++) {
- const next = filtered[i];
- const nextIntervalEnd = next.timestamp + next.duration;
-
- if (nextIntervalEnd <= currentIntervalEnd) { // we are still in the interval
- continue;
- } else if (next.timestamp <= currentIntervalEnd) { // we extending the interval
- result += nextIntervalEnd - currentIntervalEnd;
- currentIntervalEnd = nextIntervalEnd;
- } else { // this is a new interval
- result += next.duration;
- currentIntervalEnd = nextIntervalEnd;
- }
- }
-
- return result;
-}
-
-function formatDate(timestamp, utc) {
- let m = moment(timestamp / 1000);
- if (utc) {
- m = m.utc();
- }
- return m.format('MM-DD-YYYYTHH:mm:ss.SSSZZ');
-}
-
-export function mkDurationStr(duration) {
- if (duration === 0 || typeof duration === 'undefined') {
- return '';
- } else if (duration < 1000) {
- return `${duration.toFixed(0)}μs`;
- } else if (duration < 1000000) {
- if (duration % 1000 === 0) { // Sometimes spans are in milliseconds resolution
- return `${(duration / 1000).toFixed(0)}ms`;
- }
- return `${(duration / 1000).toFixed(3)}ms`;
- } else {
- return `${(duration / 1000000).toFixed(3)}s`;
- }
-}
-
-// maxSpanDurationStr is only used in index.mustache
-export function getServiceSummaries(groupedTimestamps) {
- return _(groupedTimestamps).toPairs()
- .map(([serviceName, sts]) => ({
- serviceName,
- spanCount: sts.length,
- maxSpanDuration: Math.max(...sts.map(t => t.duration))
- }))
- .orderBy(['maxSpanDuration', 'serviceName'], ['desc', 'asc'])
- .map(summary => ({
- serviceName: summary.serviceName,
- spanCount: summary.spanCount,
- maxSpanDurationStr: mkDurationStr(summary.maxSpanDuration)
- })).value();
-}
-
-export function traceSummariesToMustache(serviceName, traceSummaries, utc = false) {
- const maxDuration = Math.max(...traceSummaries.map((s) => s.duration));
-
- return traceSummaries.map((t) => {
- const timestamp = t.timestamp;
-
- const res = {
- traceId: t.traceId, // used to navigate to trace screen
- timestamp, // used only for client-side sort
- startTs: formatDate(timestamp, utc),
- spanCount: t.spanCount
- };
-
- const duration = t.duration || 0;
- if (duration) {
- // used to show the relative duration this trace was compared to others
- res.width = parseInt(parseFloat(duration) / parseFloat(maxDuration) * 100, 10);
- res.duration = duration / 1000; // used only for client-side sort
- res.durationStr = mkDurationStr(duration);
- }
-
- // groupedTimestamps is keyed by service name, if there are no service names in the trace,
- // don't try to add data dependent on service names.
- if (Object.keys(t.groupedTimestamps).length !== 0) {
- res.serviceSummaries = getServiceSummaries(t.groupedTimestamps);
-
- // Only add a service percentage when there is a duration for it
- if (serviceName && duration && t.groupedTimestamps[serviceName]) {
- const serviceTime = totalDuration(t.groupedTimestamps[serviceName]);
- // used for display and also client-side sort by service percentage
- res.servicePercentage = parseInt(parseFloat(serviceTime) / parseFloat(duration) * 100, 10);
- }
- }
-
- if (t.errorType !== 'none') res.infoClass = `trace-error-${t.errorType}`;
- return res;
- }).sort((t1, t2) => {
- const durationComparison = t2.duration - t1.duration;
- if (durationComparison === 0) {
- return t1.traceId.localeCompare(t2.traceId);
- } else {
- return durationComparison;
- }
- });
-}
diff --git a/zipkin-ui/js/component_ui/traceToMustache.js b/zipkin-ui/js/component_ui/traceToMustache.js
deleted file mode 100644
index c97f0ab..0000000
--- a/zipkin-ui/js/component_ui/traceToMustache.js
+++ /dev/null
@@ -1,148 +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.
- */
-import {compare} from '../component_data/spanCleaner';
-import {addTimestamps, getMaxDuration, mkDurationStr} from './traceSummary';
-import {newSpanRow} from './spanRow';
-
-function incrementEntry(dict, key) {
- if (dict[key]) {
- dict[key]++; // eslint-disable-line no-param-reassign
- } else {
- dict[key] = 1; // eslint-disable-line no-param-reassign
- }
-}
-
-// We need to do an initial traversal in order to get the timestamp and duration of the trace,
-// as that is used for positioning spans later.
-function getTraceTimestampAndDuration(root) {
- const timestamps = [];
- root.traverse(span => addTimestamps(span, timestamps));
- return {
- timestamp: timestamps[0] || 0,
- duration: getMaxDuration(timestamps)
- };
-}
-
-function addLayoutDetails(
- spanRow, traceTimestamp, traceDuration, depth, childIds
-) { /* eslint-disable no-param-reassign */
- spanRow.childIds = childIds;
- spanRow.depth = (depth + 1) * 5;
- spanRow.depthClass = (depth - 1) % 6;
-
- // Add the correct width and duration string for the span
- if (spanRow.duration) { // implies traceDuration, as trace duration is derived from spans
- const width = traceDuration ? spanRow.duration / traceDuration * 100 : 0;
- spanRow.width = width < 0.1 ? 0.1 : width;
- spanRow.durationStr = mkDurationStr(spanRow.duration); // bubble over the span in trace view
- } else {
- spanRow.width = 0.1;
- }
-
- if (traceDuration) {
- // position the span at the correct offset in the trace diagram.
- spanRow.left = ((spanRow.timestamp - traceTimestamp) / traceDuration * 100);
-
- // position each annotation at the offset in the trace diagram.
- spanRow.annotations.forEach(a => { /* eslint-disable no-param-reassign */
- // left offset here is from the span
- a.left = spanRow.duration ? ((a.timestamp - spanRow.timestamp) / spanRow.duration * 100) : 0;
- // relative time is for the trace itself
- a.relativeTime = mkDurationStr(a.timestamp - traceTimestamp);
- a.width = 8; // size of the dot
- });
- } else {
- spanRow.left = 0;
- }
-}
-
-function nodeByTimestamp(a, b) {
- return compare(a.span.timestamp, b.span.timestamp);
-}
-
-export function traceToMustache(root, logsUrl) {
- const serviceNameToCount = {};
- let queue = root.queueRootMostSpans();
- const modelview = {
- traceId: queue[0].span.traceId,
- depth: 0,
- spans: []
- };
-
- const {timestamp, duration} = getTraceTimestampAndDuration(root);
- if (!timestamp) throw new Error(`Trace ${modelview.traceId} is missing a timestamp`);
-
- while (queue.length > 0) {
- let current = queue.shift();
-
- // This is more than a normal tree traversal, as we are merging any server spans that share the
- // same ID. When that's the case, we pull up any of their children as if they are our own.
- const spansToMerge = [current.span];
- const children = [];
- current.children.forEach(child => {
- if (current.span.id === child.span.id) {
- spansToMerge.push(child.span);
- child.children.forEach(grandChild => children.push(grandChild));
- } else {
- children.push(child);
- }
- });
-
- // Pulling up children may affect our sort order. We re-sort to ensure rows are added in
- // timestamp order.
- children.sort(nodeByTimestamp);
- queue = children.concat(queue);
- const childIds = children.map(child => child.span.id);
-
- // The mustache template expects one row per span ID. To get the correct depth class, we need to
- // count distinct span IDs above us.
- let depth = 1;
- while (current.parent && current.parent.span) {
- if (current.parent.span.id !== current.span.id) depth++;
- current = current.parent;
- }
- // If we are the deepest span, mark the trace accordingly
- if (depth > modelview.depth) modelview.depth = depth;
-
- const isLeafSpan = children.length === 0;
- const spanRow = newSpanRow(spansToMerge, isLeafSpan);
-
- addLayoutDetails(spanRow, timestamp, duration, depth, childIds);
- // NOTE: This will increment both the local and remote service name
- //
- // TODO: We should only do this if it is a leaf span and a client or producer. If we are at the
- // bottom of the tree, it can be helpful to count also against a remote uninstrumented service.
- spanRow.serviceNames.forEach(serviceName => incrementEntry(serviceNameToCount, serviceName));
-
- modelview.spans.push(spanRow);
- }
-
- modelview.serviceNameAndSpanCounts = Object.keys(serviceNameToCount).sort().map(serviceName =>
- ({serviceName, spanCount: serviceNameToCount[serviceName]})
- );
-
- // the zoom feature needs backups and timeMarkers regardless of if there is a trace duration
- modelview.spansBackup = modelview.spans;
- modelview.timeMarkers = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
- .map((p, index) => ({index, time: mkDurationStr(duration * p)}));
- modelview.timeMarkersBackup = modelview.timeMarkers;
-
- if (duration) modelview.durationStr = mkDurationStr(duration);
- if (logsUrl) modelview.logsUrl = logsUrl;
-
- return modelview;
-}
diff --git a/zipkin-ui/js/component_ui/traces.js b/zipkin-ui/js/component_ui/traces.js
deleted file mode 100644
index 0e3f043..0000000
--- a/zipkin-ui/js/component_ui/traces.js
+++ /dev/null
@@ -1,51 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-
-export default component(function traces() {
- this.$traces = [];
-
- this.sortFunctions = {
- 'service-percentage-desc': (a, b) => b.percentage - a.percentage,
- 'service-percentage-asc': (a, b) => a.percentage - b.percentage,
- 'duration-desc': (a, b) => b.duration - a.duration,
- 'duration-asc': (a, b) => a.duration - b.duration,
- 'timestamp-desc': (a, b) => b.timestamp - a.timestamp,
- 'timestamp-asc': (a, b) => a.timestamp - b.timestamp
- };
-
- this.updateSortOrder = function(ev, data) {
- if (this.sortFunctions.hasOwnProperty(data.order)) {
- this.$traces.sort(this.sortFunctions[data.order]);
- this.$node.html(this.$traces);
- }
- };
-
- this.after('initialize', function() {
- this.$traces = this.$node.find('.trace');
- this.$traces.each(function() {
- const $this = $(this);
- this.duration = parseInt($this.data('duration'), 10);
- this.timestamp = parseInt($this.data('timestamp'), 10);
- this.percentage = parseInt($this.data('servicePercentage'), 10);
- });
- this.on(document, 'uiUpdateTraceSortOrder', this.updateSortOrder);
- const sortOrderSelect = $('.sort-order');
- this.updateSortOrder(null, {order: sortOrderSelect.val()});
- });
-});
diff --git a/zipkin-ui/js/component_ui/uploadTrace.js b/zipkin-ui/js/component_ui/uploadTrace.js
deleted file mode 100644
index 3edf722..0000000
--- a/zipkin-ui/js/component_ui/uploadTrace.js
+++ /dev/null
@@ -1,78 +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.
- */
-import {component} from 'flightjs';
-import FullPageSpinnerUI from '../component_ui/fullPageSpinner';
-import {traceToMustache} from '../component_ui/traceToMustache';
-import {treeCorrectedForClockSkew} from '../component_data/skew';
-
-export function ensureV2(trace) {
- if (!Array.isArray(trace) || trace.length === 0) {
- throw new Error('input is not a list');
- }
- const first = trace[0];
- if (!first.traceId || !first.id) {
- throw new Error('List<Span> implies at least traceId and id fields');
- }
- if (first.binaryAnnotations || (!first.localEndpoint && !first.remoteEndpoint && !first.tags)) {
- throw new Error(
- 'v1 format is not supported. For help, contact https://gitter.im/openzipkin/zipkin');
- }
-}
-
-export default component(function uploadTrace() {
- this.doUpload = function() {
- const files = this.node.files;
- if (files.length === 0) {
- return;
- }
-
- const reader = new FileReader();
- reader.onload = evt => {
- let model;
- try {
- const raw = JSON.parse(evt.target.result);
- ensureV2(raw);
- const corrected = treeCorrectedForClockSkew(raw);
- const modelview = traceToMustache(corrected);
- model = {modelview, trace: raw};
- } catch (e) {
- this.trigger('uiServerError',
- {desc: 'Cannot parse file', message: e});
- throw e;
- } finally {
- this.trigger(document, 'uiHideFullPageSpinner');
- }
-
- this.trigger(document, 'traceViewerPageModelView', model);
- };
-
- reader.onerror = evt => {
- this.trigger(document, 'uiHideFullPageSpinner');
- this.trigger('uiServerError',
- {desc: 'Cannot load file', message: `${evt.target.error.name}`});
- };
-
- this.trigger(document, 'uiShowFullPageSpinner');
- setTimeout(() => reader.readAsText(files[0]), 0);
- };
-
- this.after('initialize', function() {
- this.on('change', this.doUpload);
- FullPageSpinnerUI.teardownAll();
- FullPageSpinnerUI.attachTo('#fullPageSpinner');
- });
-});
diff --git a/zipkin-ui/js/component_ui/zoomOutSpans.js b/zipkin-ui/js/component_ui/zoomOutSpans.js
deleted file mode 100644
index a71c525..0000000
--- a/zipkin-ui/js/component_ui/zoomOutSpans.js
+++ /dev/null
@@ -1,27 +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.
- */
-import {component} from 'flightjs';
-
-export default component(function zoomOutSpans() {
- this.zoomOut = function() {
- this.trigger('uiZoomOutSpans');
- };
-
- this.after('initialize', function() {
- this.on('click', this.zoomOut);
- });
-});
diff --git a/zipkin-ui/js/config.js b/zipkin-ui/js/config.js
deleted file mode 100644
index 6d1e8cf..0000000
--- a/zipkin-ui/js/config.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import $ from 'jquery';
-
-const defaults = {
- environment: '',
- suggestLens: false,
- queryLimit: 10,
- defaultLookback: 60 * 60 * 1000, // 1 hour
- searchEnabled: true,
- dependency: {
- lowErrorRate: 0.5, // 50% of calls in error turns line yellow
- highErrorRate: 0.75 // 75% of calls in error turns line red
- }
-};
-
-export default function loadConfig() {
- return $.ajax('config.json', {
- type: 'GET',
- dataType: 'json'
- }).then(data => function config(key) {
- if (data[key] === false) {
- return false;
- }
-
- return data[key] || defaults[key];
- });
-}
diff --git a/zipkin-ui/js/main.js b/zipkin-ui/js/main.js
deleted file mode 100644
index eacbb0b..0000000
--- a/zipkin-ui/js/main.js
+++ /dev/null
@@ -1,48 +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.
- */
-// The import of 'publicPath' module has to be the first statement in this entry point file
-// so that '__webpack_public_path__' (see https://webpack.github.io/docs/configuration.html#output-publicpath)
-// is set soon enough.
-// In the same time, 'contextRoot' is made available as the context root path reference.
-import {contextRoot} from './publicPath';
-
-import {compose, registry, advice, debug} from 'flightjs';
-import crossroads from 'crossroads';
-import initializeDefault from './page/default';
-import initializeTrace from './page/trace';
-import initializeTraceViewer from './page/traceViewer';
-import initializeDependency from './page/dependency';
-import CommonUI from './page/common';
-import loadConfig from './config';
-import {errToStr} from './component_ui/error';
-
-loadConfig().then(config => {
- debug.enable(true);
- compose.mixin(registry, [advice.withAdvice]);
-
- CommonUI.attachTo(window.document.body, {config});
-
- crossroads.addRoute(contextRoot, () => initializeDefault(config));
- crossroads.addRoute(`${contextRoot}traces/{id}`, traceId => initializeTrace(traceId, config));
- crossroads.addRoute(`${contextRoot}traceViewer`, () => initializeTraceViewer(config));
- crossroads.addRoute(`${contextRoot}dependency`, () => initializeDependency(config));
- crossroads.parse(window.location.pathname);
-}, e => {
- // TODO: better error message, but this is better than a blank screen...
- const err = errToStr(e);
- document.write(`Error loading config.json: ${err}`);
-});
diff --git a/zipkin-ui/js/page/common.js b/zipkin-ui/js/page/common.js
deleted file mode 100644
index e377a59..0000000
--- a/zipkin-ui/js/page/common.js
+++ /dev/null
@@ -1,36 +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.
- */
-import {component} from 'flightjs';
-import EnvironmentUI from '../component_ui/environment';
-import ErrorUI from '../component_ui/error';
-import NavbarUI from '../component_ui/navbar';
-import {layoutTemplate} from '../templates';
-import GoToTraceUI from '../component_ui/goToTrace';
-import GoToLensUI from '../component_ui/goToLens';
-import {contextRoot} from '../publicPath';
-
-export default component(function CommonUI() {
- this.after('initialize', function() {
- const suggestLens = this.attr.config && this.attr.config('suggestLens');
- this.$node.html(layoutTemplate({contextRoot, suggestLens}));
- NavbarUI.attachTo('#navbar');
- ErrorUI.attachTo('#errorPanel');
- EnvironmentUI.attachTo('#environment', {config: this.attr.config});
- GoToTraceUI.attachTo('#traceIdQueryForm');
- GoToLensUI.attachTo('#lensForm');
- });
-});
diff --git a/zipkin-ui/js/page/default.js b/zipkin-ui/js/page/default.js
deleted file mode 100644
index 08620c2..0000000
--- a/zipkin-ui/js/page/default.js
+++ /dev/null
@@ -1,133 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import timeago from 'timeago'; // eslint-disable-line no-unused-vars
-import queryString from 'query-string';
-import DefaultData from '../component_data/default';
-import RemoteServiceNamesData from '../component_data/remoteServiceNames';
-import SpanNamesData from '../component_data/spanNames';
-import ServiceNamesData from '../component_data/serviceNames';
-import ServiceNameUI from '../component_ui/serviceName';
-import RemoteServiceNameUI from '../component_ui/remoteServiceName';
-import SpanNameUI from '../component_ui/spanName';
-import LookbackUI from '../component_ui/lookback';
-import InfoPanelUI from '../component_ui/infoPanel';
-import InfoButtonUI from '../component_ui/infoButton';
-import JsonPanelUI from '../component_ui/jsonPanel';
-import TraceFiltersUI from '../component_ui/traceFilters';
-import TracesUI from '../component_ui/traces';
-import TimeStampUI from '../component_ui/timeStamp';
-import BackToTop from '../component_ui/backToTop';
-import {defaultTemplate} from '../templates';
-import {searchDisabled} from '../templates';
-import {contextRoot} from '../publicPath';
-import {i18nInit} from '../component_ui/i18n';
-import bootstrap // eslint-disable-line no-unused-vars
- from 'bootstrap/dist/js/bootstrap.bundle.min.js';
-
-const DefaultPageComponent = component(function DefaultPage() {
- const sortOptions = [
- {value: 'service-percentage-desc', text: 'Service Percentage: Longest First'},
- {value: 'service-percentage-asc', text: 'Service Percentage: Shortest First'},
- {value: 'duration-desc', text: 'Longest First'},
- {value: 'duration-asc', text: 'Shortest First'},
- {value: 'timestamp-desc', text: 'Newest First'},
- {value: 'timestamp-asc', text: 'Oldest First'}
- ];
-
- const sortSelected = function getSelector(selectedSortValue) {
- return function selected() {
- if (this.value === selectedSortValue) {
- return 'selected';
- }
- return '';
- };
- };
-
- this.after('initialize', function() {
- window.document.title = 'Zipkin - Index';
- if (!this.attr.config('searchEnabled')) {
- this.$node.html(searchDisabled());
- return;
- }
-
- this.trigger(document, 'navigate', {route: 'zipkin/index'});
-
- const query = queryString.parse(window.location.search);
-
- this.on(document, 'defaultPageModelView', function(ev, modelView) {
- const limit = query.limit || this.attr.config('queryLimit');
- const minDuration = query.minDuration;
- const endTs = query.endTs || new Date().getTime();
- const startTs = query.startTs || (endTs - this.attr.config('defaultLookback'));
- const serviceName = query.serviceName || '';
- const annotationQuery = query.annotationQuery || '';
- const sortOrder = query.sortOrder || 'duration-desc';
- const queryWasPerformed = serviceName && serviceName.length > 0;
- this.$node.html(defaultTemplate({
- limit,
- minDuration,
- startTs,
- endTs,
- serviceName,
- annotationQuery,
- queryWasPerformed,
- contextRoot,
- traceCount: modelView.traces.length,
- sortOrderOptions: sortOptions,
- sortOrderSelected: sortSelected(sortOrder),
- apiURL: modelView.apiURL,
- ...modelView
- }));
-
- RemoteServiceNamesData.attachTo(document);
- SpanNamesData.attachTo(document);
- ServiceNamesData.attachTo(document);
- ServiceNameUI.attachTo('#serviceName');
- RemoteServiceNameUI.attachTo('#remoteServiceName');
- SpanNameUI.attachTo('#spanName');
- LookbackUI.attachTo('#lookback');
- InfoPanelUI.attachTo('#infoPanel');
- InfoButtonUI.attachTo('button.info-request');
- JsonPanelUI.attachTo('#jsonPanel');
- TraceFiltersUI.attachTo('#trace-filters');
- TracesUI.attachTo('#traces');
- TimeStampUI.attachTo('#end-ts');
- TimeStampUI.attachTo('#start-ts');
- BackToTop.attachTo('#backToTop');
- i18nInit('traces');
- $('.timeago').timeago();
- // Need to initialize the datepicker when the UI refershes. Can be optimized
- this.$date = this.$node.find('.date-input');
- this.$date.datepicker({format: 'yyyy-mm-dd'});
- this.$node.find('#rawResultsJsonLink').click(e => {
- e.preventDefault();
- this.trigger('uiRequestJsonPanel', {
- title: 'Search Results',
- obj: modelView.rawResponse,
- link: modelView.apiURL
- });
- });
- });
- DefaultData.attachTo(document);
- });
-});
-
-export default function initializeDefault(config) {
- DefaultPageComponent.attachTo('.content', {config});
-}
diff --git a/zipkin-ui/js/page/dependency.js b/zipkin-ui/js/page/dependency.js
deleted file mode 100644
index 0994587..0000000
--- a/zipkin-ui/js/page/dependency.js
+++ /dev/null
@@ -1,53 +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.
- */
-import moment from 'moment';
-import {component} from 'flightjs';
-import $ from 'jquery';
-import queryString from 'query-string';
-import DependencyData from '../component_data/dependency';
-import DependencyGraphUI from '../component_ui/dependencyGraph';
-import ServiceDataModal from '../component_ui/serviceDataModal';
-import TimeStampUI from '../component_ui/timeStamp';
-import GoToDependencyUI from '../component_ui/goToDependency';
-import {dependenciesTemplate} from '../templates';
-import {i18nInit} from '../component_ui/i18n';
-
-const DependencyPageComponent = component(function DependencyPage() {
- this.after('initialize', function() {
- window.document.title = 'Zipkin - Dependency';
- this.trigger(document, 'navigate', {route: 'zipkin/dependency'});
-
- this.$node.html(dependenciesTemplate());
-
- const {startTs, endTs} = queryString.parse(location.search);
- $('#endTs').val(endTs || moment().valueOf());
- // When #1185 is complete, the only visible granularity is day
- $('#startTs').val(startTs || moment().valueOf() - 86400000);
-
- DependencyData.attachTo('#dependency-container');
- DependencyGraphUI.attachTo('#dependency-container', {config: this.attr.config});
- ServiceDataModal.attachTo('#service-data-modal-container');
- TimeStampUI.attachTo('#end-ts');
- TimeStampUI.attachTo('#start-ts');
- GoToDependencyUI.attachTo('#dependency-query-form');
- i18nInit('dep');
- });
-});
-
-export default function initializeDependencies(config) {
- DependencyPageComponent.attachTo('.content', {config});
-}
diff --git a/zipkin-ui/js/page/trace.js b/zipkin-ui/js/page/trace.js
deleted file mode 100644
index 95cbef5..0000000
--- a/zipkin-ui/js/page/trace.js
+++ /dev/null
@@ -1,73 +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.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import TraceData from '../component_data/trace';
-import FilterAllServicesUI from '../component_ui/filterAllServices';
-import FullPageSpinnerUI from '../component_ui/fullPageSpinner';
-import JsonPanelUI from '../component_ui/jsonPanel';
-import SpanPanelUI from '../component_ui/spanPanel';
-import TraceUI from '../component_ui/trace';
-import ZoomOut from '../component_ui/zoomOutSpans';
-import {traceTemplate} from '../templates';
-import {contextRoot} from '../publicPath';
-
-const TracePageComponent = component(function TracePage() {
- this.after('initialize', function() {
- window.document.title = 'Zipkin - Traces';
- $('body').tooltip({
- selector: '[data-toggle="tooltip"]'
- });
- TraceData.attachTo(document, {
- traceId: this.attr.traceId,
- logsUrl: this.attr.config('logsUrl')
- });
- this.on(document, 'tracePageModelView', function(ev, data) {
- this.$node.html(traceTemplate({
- contextRoot,
- ...data.modelview
- }));
-
- FilterAllServicesUI.attachTo('#filterAllServices', {
- totalServices: $('.trace-details.services span').length
- });
- FullPageSpinnerUI.attachTo('#fullPageSpinner');
- JsonPanelUI.attachTo('#jsonPanel');
- SpanPanelUI.attachTo('#spanPanel');
- TraceUI.attachTo('#trace-container');
- ZoomOut.attachTo('#zoomOutSpans');
-
- this.$node.find('#traceJsonLink').click(e => {
- e.preventDefault();
- this.trigger('uiRequestJsonPanel', {
- title: `Trace ${this.attr.traceId}`,
- obj: data.trace,
- link: `${contextRoot}api/v2/trace/${this.attr.traceId}`
- });
- });
-
- $('.annotation:not(.derived)').tooltip({placement: 'left'});
- });
- });
-});
-
-export default function initializeTrace(traceId, config) {
- TracePageComponent.attachTo('.content', {
- traceId,
- config
- });
-}
diff --git a/zipkin-ui/js/page/traceViewer.js b/zipkin-ui/js/page/traceViewer.js
deleted file mode 100644
index 311a071..0000000
--- a/zipkin-ui/js/page/traceViewer.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import {component} from 'flightjs';
-import $ from 'jquery';
-import FilterAllServicesUI from '../component_ui/filterAllServices';
-import JsonPanelUI from '../component_ui/jsonPanel';
-import SpanPanelUI from '../component_ui/spanPanel';
-import TraceUI from '../component_ui/trace';
-import ZoomOut from '../component_ui/zoomOutSpans';
-import UploadTraceUI from '../component_ui/uploadTrace';
-import {traceViewerTemplate} from '../templates';
-import {contextRoot} from '../publicPath';
-
-const TraceViewerPageComponent = component(function TraceViewerPage() {
- this.render = function(model) {
- try {
- this.$node.html(traceViewerTemplate({
- contextRoot,
- ...model
- }));
- } catch (e) {
- this.trigger('uiServerError',
- {desc: 'Failed to render template', message: e.message});
- }
-
- UploadTraceUI.attachTo('#traceFile');
- };
-
- this.attach = function() {
- FilterAllServicesUI.attachTo('#filterAllServices', {
- totalServices: $('.trace-details.services span').length
- });
- JsonPanelUI.attachTo('#jsonPanel');
- SpanPanelUI.attachTo('#spanPanel');
- TraceUI.attachTo('#trace-container');
- ZoomOut.attachTo('#zoomOutSpans');
- };
-
- this.teardown = function() {
- ZoomOut.teardownAll();
- TraceUI.teardownAll();
- SpanPanelUI.teardownAll();
- JsonPanelUI.teardownAll();
- FilterAllServicesUI.teardownAll();
- };
-
- this.after('initialize', function() {
- window.document.title = 'Zipkin - Trace Viewer';
-
- this.render({});
-
- this.on(document, 'traceViewerPageModelView', function(ev, data) {
- this.teardown();
- this.render(data.modelview);
- this.attach();
-
- this.$node.find('#traceJsonLink').click(e => {
- e.preventDefault();
- this.trigger('uiRequestJsonPanel', {title: `Trace ${data.modelview.traceId}`,
- obj: data.trace,
- link: `${contextRoot}traceViewer`});
- });
-
- $('.annotation:not(.core)').tooltip({placement: 'left'});
- });
- });
-});
-
-export default function initializeTrace(config) {
- TraceViewerPageComponent.attachTo('.content', {config});
-}
diff --git a/zipkin-ui/js/publicPath.js b/zipkin-ui/js/publicPath.js
deleted file mode 100644
index ca597d0..0000000
--- a/zipkin-ui/js/publicPath.js
+++ /dev/null
@@ -1,32 +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.
- */
-import $ from 'jquery';
-
-// read the public path from the <base> tag where it has to be set anyway because of
-// html-webpack-plugin limitations: https://github.com/jantimon/html-webpack-plugin/issues/119
-// otherwise it could be: window.location.pathname.replace(/(.*)\/zipkin\/.*/, '$1/zipkin/')
-let contextRoot = $('base').attr('href') || '/zipkin/';
-
-// explicit to avoid having to do a polyfill for String.endsWith
-if (contextRoot.substr(contextRoot.length - 1) !== '/') {
- contextRoot += '/';
-}
-
-// set dynamically 'output.publicPath' as per https://webpack.github.io/docs/configuration.html#output-publicpath
-__webpack_public_path__ = contextRoot; // eslint-disable-line camelcase, no-undef
-
-export {contextRoot};
diff --git a/zipkin-ui/js/templates.js b/zipkin-ui/js/templates.js
deleted file mode 100644
index eec4424..0000000
--- a/zipkin-ui/js/templates.js
+++ /dev/null
@@ -1,22 +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.
- */
-export const defaultTemplate = require('../templates/index.mustache');
-export const searchDisabled = require('../templates/searchDisabled.mustache');
-export const layoutTemplate = require('../templates/layout.mustache');
-export const dependenciesTemplate = require('../templates/dependency.mustache');
-export const traceTemplate = require('../templates/trace.mustache');
-export const traceViewerTemplate = require('../templates/traceViewer.mustache');
diff --git a/zipkin-ui/karma.conf.js b/zipkin-ui/karma.conf.js
deleted file mode 100644
index 4623e78..0000000
--- a/zipkin-ui/karma.conf.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-var webpack = require('webpack');
-
-module.exports = function(config) {
- config.set({
- frameworks: ['mocha', 'chai'],
- files: [
- 'node_modules/babel-polyfill/dist/polyfill.js',
- 'test/*test.js',
- 'test/**/*test.js'
- ],
-
- preprocessors: {
- '*test.js': ['babel', 'webpack', 'sourcemap'],
- '**/*test.js': ['babel', 'webpack', 'sourcemap']
- },
-
- client: {
- captureConsole: true
- },
-
- browsers: ['FirefoxHeadless'],
-
- // see https://github.com/karma-runner/karma-firefox-launcher/issues/76
- customLaunchers: {
- FirefoxHeadless: {
- base: 'Firefox',
- flags: [ '-headless' ],
- },
- },
-
- webpack: {
- devtool: 'inline-source-map',
- module: {
- rules: [{
- test: /\.js$/,
- exclude: /node_modules/,
- use: 'babel-loader'
- }, {
- test: /\.mustache$/,
- use: 'mustache-loader'
- }]
- },
- resolve: {
- modules: ['node_modules']
- },
- plugins: [
- new webpack.ProvidePlugin({
- $: "jquery",
- jQuery: "jquery"
- })
- ]
- },
-
- webpackServer: {
- noInfo: true
- },
-
- plugins: [
- require('karma-babel-preprocessor'),
- require('karma-webpack'),
- require('karma-mocha'),
- require('karma-chai'),
- require('karma-chrome-launcher'),
- require('karma-firefox-launcher'),
- require('karma-sourcemap-loader')
- ]
- });
-};
diff --git a/zipkin-ui/libs/chosen/chosen-sprite.png b/zipkin-ui/libs/chosen/chosen-sprite.png
deleted file mode 100644
index c57da70..0000000
Binary files a/zipkin-ui/libs/chosen/chosen-sprite.png and /dev/null differ
diff --git a/zipkin-ui/libs/chosen/chosen-sprite@2x.png b/zipkin-ui/libs/chosen/chosen-sprite@2x.png
deleted file mode 100644
index 6b50545..0000000
Binary files a/zipkin-ui/libs/chosen/chosen-sprite@2x.png and /dev/null differ
diff --git a/zipkin-ui/libs/chosen/chosen.css b/zipkin-ui/libs/chosen/chosen.css
deleted file mode 100644
index 4599aaa..0000000
--- a/zipkin-ui/libs/chosen/chosen.css
+++ /dev/null
@@ -1,450 +0,0 @@
-/*!
-Chosen, a Select Box Enhancer for jQuery and Prototype
-by Patrick Filler for Harvest, http://getharvest.com
-
-Version 1.4.2
-Full source at https://github.com/harvesthq/chosen
-Copyright (c) 2011-2015 Harvest http://getharvest.com
-
-MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
-This file is generated by `grunt build`, do not edit it by hand.
-*/
-
-/* @group Base */
-.chosen-container {
- position: relative;
- display: inline-block;
- vertical-align: middle;
- font-size: 13px;
- zoom: 1;
- *display: inline;
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
-}
-.chosen-container * {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-.chosen-container .chosen-drop {
- position: absolute;
- top: 100%;
- left: -9999px;
- z-index: 1010;
- width: 100%;
- border: 1px solid #aaa;
- border-top: 0;
- background: #fff;
- box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
-}
-.chosen-container.chosen-with-drop .chosen-drop {
- left: 0;
-}
-.chosen-container a {
- cursor: pointer;
-}
-.chosen-container .search-choice .group-name, .chosen-container .chosen-single .group-name {
- margin-right: 4px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- font-weight: normal;
- color: #999999;
-}
-.chosen-container .search-choice .group-name:after, .chosen-container .chosen-single .group-name:after {
- content: ":";
- padding-left: 2px;
- vertical-align: top;
-}
-
-/* @end */
-/* @group Single Chosen */
-.chosen-container-single .chosen-single {
- position: relative;
- display: block;
- overflow: hidden;
- padding: 0 0 0 8px;
- height: 25px;
- border: 1px solid #aaa;
- border-radius: 5px;
- background-color: #fff;
- background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
- background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
- background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
- background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
- background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
- background-clip: padding-box;
- box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1);
- color: #444;
- text-decoration: none;
- white-space: nowrap;
- line-height: 24px;
-}
-.chosen-container-single .chosen-default {
- color: #999;
-}
-.chosen-container-single .chosen-single span {
- display: block;
- overflow: hidden;
- margin-right: 26px;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.chosen-container-single .chosen-single-with-deselect span {
- margin-right: 38px;
-}
-.chosen-container-single .chosen-single abbr {
- position: absolute;
- top: 6px;
- right: 26px;
- display: block;
- width: 12px;
- height: 12px;
- background: url('chosen-sprite.png') -42px 1px no-repeat;
- font-size: 1px;
-}
-.chosen-container-single .chosen-single abbr:hover {
- background-position: -42px -10px;
-}
-.chosen-container-single.chosen-disabled .chosen-single abbr:hover {
- background-position: -42px -10px;
-}
-.chosen-container-single .chosen-single div {
- position: absolute;
- top: 0;
- right: 0;
- display: block;
- width: 18px;
- height: 100%;
-}
-.chosen-container-single .chosen-single div b {
- display: block;
- width: 100%;
- height: 100%;
- background: url('chosen-sprite.png') no-repeat 0px 2px;
-}
-.chosen-container-single .chosen-search {
- position: relative;
- z-index: 1010;
- margin: 0;
- padding: 3px 4px;
- white-space: nowrap;
-}
-.chosen-container-single .chosen-search input[type="text"] {
- margin: 1px 0;
- padding: 4px 20px 4px 5px;
- width: 100%;
- height: auto;
- outline: 0;
- border: 1px solid #aaa;
- background: white url('chosen-sprite.png') no-repeat 100% -20px;
- background: url('chosen-sprite.png') no-repeat 100% -20px;
- font-size: 1em;
- font-family: sans-serif;
- line-height: normal;
- border-radius: 0;
-}
-.chosen-container-single .chosen-drop {
- margin-top: -1px;
- border-radius: 0 0 4px 4px;
- background-clip: padding-box;
-}
-.chosen-container-single.chosen-container-single-nosearch .chosen-search {
- position: absolute;
- left: -9999px;
-}
-
-/* @end */
-/* @group Results */
-.chosen-container .chosen-results {
- color: #444;
- position: relative;
- overflow-x: hidden;
- overflow-y: auto;
- margin: 0 4px 4px 0;
- padding: 0 0 0 4px;
- max-height: 240px;
- -webkit-overflow-scrolling: touch;
-}
-.chosen-container .chosen-results li {
- display: none;
- margin: 0;
- padding: 5px 6px;
- list-style: none;
- line-height: 15px;
- word-wrap: break-word;
- -webkit-touch-callout: none;
-}
-.chosen-container .chosen-results li.active-result {
- display: list-item;
- cursor: pointer;
-}
-.chosen-container .chosen-results li.disabled-result {
- display: list-item;
- color: #ccc;
- cursor: default;
-}
-.chosen-container .chosen-results li.highlighted {
- background-color: #3875d7;
- background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
- background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%);
- background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%);
- background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%);
- background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
- color: #fff;
-}
-.chosen-container .chosen-results li.no-results {
- color: #777;
- display: list-item;
- background: #f4f4f4;
-}
-.chosen-container .chosen-results li.group-result {
- display: list-item;
- font-weight: bold;
- cursor: default;
-}
-.chosen-container .chosen-results li.group-option {
- padding-left: 15px;
-}
-.chosen-container .chosen-results li em {
- font-style: normal;
- text-decoration: underline;
-}
-
-/* @end */
-/* @group Multi Chosen */
-.chosen-container-multi .chosen-choices {
- position: relative;
- overflow: hidden;
- margin: 0;
- padding: 0 5px;
- width: 100%;
- height: auto !important;
- height: 1%;
- border: 1px solid #aaa;
- background-color: #fff;
- background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
- background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%);
- background-image: -moz-linear-gradient(#eeeeee 1%, #ffffff 15%);
- background-image: -o-linear-gradient(#eeeeee 1%, #ffffff 15%);
- background-image: linear-gradient(#eeeeee 1%, #ffffff 15%);
- cursor: text;
-}
-.chosen-container-multi .chosen-choices li {
- float: left;
- list-style: none;
-}
-.chosen-container-multi .chosen-choices li.search-field {
- margin: 0;
- padding: 0;
- white-space: nowrap;
-}
-.chosen-container-multi .chosen-choices li.search-field input[type="text"] {
- margin: 1px 0;
- padding: 0;
- height: 25px;
- outline: 0;
- border: 0 !important;
- background: transparent !important;
- box-shadow: none;
- color: #999;
- font-size: 100%;
- font-family: sans-serif;
- line-height: normal;
- border-radius: 0;
-}
-.chosen-container-multi .chosen-choices li.search-choice {
- position: relative;
- margin: 3px 5px 3px 0;
- padding: 3px 20px 3px 5px;
- border: 1px solid #aaa;
- max-width: 100%;
- border-radius: 3px;
- background-color: #eeeeee;
- background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
- background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-size: 100% 19px;
- background-repeat: repeat-x;
- background-clip: padding-box;
- box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05);
- color: #333;
- line-height: 13px;
- cursor: default;
-}
-.chosen-container-multi .chosen-choices li.search-choice span {
- word-wrap: break-word;
-}
-.chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
- position: absolute;
- top: 4px;
- right: 3px;
- display: block;
- width: 12px;
- height: 12px;
- background: url('chosen-sprite.png') -42px 1px no-repeat;
- font-size: 1px;
-}
-.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover {
- background-position: -42px -10px;
-}
-.chosen-container-multi .chosen-choices li.search-choice-disabled {
- padding-right: 5px;
- border: 1px solid #ccc;
- background-color: #e4e4e4;
- background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
- background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
- color: #666;
-}
-.chosen-container-multi .chosen-choices li.search-choice-focus {
- background: #d4d4d4;
-}
-.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close {
- background-position: -42px -10px;
-}
-.chosen-container-multi .chosen-results {
- margin: 0;
- padding: 0;
-}
-.chosen-container-multi .chosen-drop .result-selected {
- display: list-item;
- color: #ccc;
- cursor: default;
-}
-
-/* @end */
-/* @group Active */
-.chosen-container-active .chosen-single {
- border: 1px solid #5897fb;
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
-}
-.chosen-container-active.chosen-with-drop .chosen-single {
- border: 1px solid #aaa;
- -moz-border-radius-bottomright: 0;
- border-bottom-right-radius: 0;
- -moz-border-radius-bottomleft: 0;
- border-bottom-left-radius: 0;
- background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
- background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%);
- background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%);
- background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%);
- background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
- box-shadow: 0 1px 0 #fff inset;
-}
-.chosen-container-active.chosen-with-drop .chosen-single div {
- border-left: none;
- background: transparent;
-}
-.chosen-container-active.chosen-with-drop .chosen-single div b {
- background-position: -18px 2px;
-}
-.chosen-container-active .chosen-choices {
- border: 1px solid #5897fb;
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
-}
-.chosen-container-active .chosen-choices li.search-field input[type="text"] {
- color: #222 !important;
-}
-
-/* @end */
-/* @group Disabled Support */
-.chosen-disabled {
- opacity: 0.5 !important;
- cursor: default;
-}
-.chosen-disabled .chosen-single {
- cursor: default;
-}
-.chosen-disabled .chosen-choices .search-choice .search-choice-close {
- cursor: default;
-}
-
-/* @end */
-/* @group Right to Left */
-.chosen-rtl {
- text-align: right;
-}
-.chosen-rtl .chosen-single {
- overflow: visible;
- padding: 0 8px 0 0;
-}
-.chosen-rtl .chosen-single span {
- margin-right: 0;
- margin-left: 26px;
- direction: rtl;
-}
-.chosen-rtl .chosen-single-with-deselect span {
- margin-left: 38px;
-}
-.chosen-rtl .chosen-single div {
- right: auto;
- left: 3px;
-}
-.chosen-rtl .chosen-single abbr {
- right: auto;
- left: 26px;
-}
-.chosen-rtl .chosen-choices li {
- float: right;
-}
-.chosen-rtl .chosen-choices li.search-field input[type="text"] {
- direction: rtl;
-}
-.chosen-rtl .chosen-choices li.search-choice {
- margin: 3px 5px 3px 0;
- padding: 3px 5px 3px 19px;
-}
-.chosen-rtl .chosen-choices li.search-choice .search-choice-close {
- right: auto;
- left: 4px;
-}
-.chosen-rtl.chosen-container-single-nosearch .chosen-search,
-.chosen-rtl .chosen-drop {
- left: 9999px;
-}
-.chosen-rtl.chosen-container-single .chosen-results {
- margin: 0 0 4px 4px;
- padding: 0 4px 0 0;
-}
-.chosen-rtl .chosen-results li.group-option {
- padding-right: 15px;
- padding-left: 0;
-}
-.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div {
- border-right: none;
-}
-.chosen-rtl .chosen-search input[type="text"] {
- padding: 4px 5px 4px 20px;
- background: white url('chosen-sprite.png') no-repeat -30px -20px;
- background: url('chosen-sprite.png') no-repeat -30px -20px;
- direction: rtl;
-}
-.chosen-rtl.chosen-container-single .chosen-single div b {
- background-position: 6px 2px;
-}
-.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b {
- background-position: -12px 2px;
-}
-
-/* @end */
-/* @group Retina compatibility */
-@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) {
- .chosen-rtl .chosen-search input[type="text"],
- .chosen-container-single .chosen-single abbr,
- .chosen-container-single .chosen-single div b,
- .chosen-container-single .chosen-search input[type="text"],
- .chosen-container-multi .chosen-choices .search-choice .search-choice-close,
- .chosen-container .chosen-results-scroll-down span,
- .chosen-container .chosen-results-scroll-up span {
- background-image: url('chosen-sprite@2x.png') !important;
- background-size: 52px 37px !important;
- background-repeat: no-repeat !important;
- }
-}
-/* @end */
diff --git a/zipkin-ui/libs/dagre-d3/.bower.json b/zipkin-ui/libs/dagre-d3/.bower.json
deleted file mode 100644
index 2ba09f8..0000000
--- a/zipkin-ui/libs/dagre-d3/.bower.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "dagre-d3",
- "version": "0.2.9",
- "main": [
- "js/dagre-d3.js",
- "js/dagre-d3.min.js"
- ],
- "ignore": [
- "README.md"
- ],
- "dependencies": {
- "d3": "~3.3.8"
- },
- "homepage": "https://github.com/cpettitt/dagre-d3-bower",
- "_release": "0.2.9",
- "_resolution": {
- "type": "version",
- "tag": "v0.2.9",
- "commit": "536bd5b212e4dd1f2d63a20b4daecb2987a0568f"
- },
- "_source": "git://github.com/cpettitt/dagre-d3-bower.git",
- "_target": "~0.2.9",
- "_originalSource": "dagre-d3",
- "_direct": true
-}
\ No newline at end of file
diff --git a/zipkin-ui/libs/dagre-d3/bower.json b/zipkin-ui/libs/dagre-d3/bower.json
deleted file mode 100644
index 27507b0..0000000
--- a/zipkin-ui/libs/dagre-d3/bower.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "name": "dagre-d3",
- "version": "0.2.9",
- "main": [
- "js/dagre-d3.js",
- "js/dagre-d3.min.js"
- ],
- "ignore": [
- "README.md"
- ],
- "dependencies": {
- "d3": "~3.3.8"
- }
-}
diff --git a/zipkin-ui/libs/dagre-d3/js/dagre-d3.js b/zipkin-ui/libs/dagre-d3/js/dagre-d3.js
deleted file mode 100644
index 8728227..0000000
--- a/zipkin-ui/libs/dagre-d3/js/dagre-d3.js
+++ /dev/null
@@ -1,5003 +0,0 @@
-;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-var global=self;/**
- * @license
- * Copyright (c) 2012-2013 Chris Pettitt
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-global.dagreD3 = require('./index');
-
-},{"./index":2}],2:[function(require,module,exports){
-/**
- * @license
- * Copyright (c) 2012-2013 Chris Pettitt
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-module.exports = {
- Digraph: require('graphlib').Digraph,
- Graph: require('graphlib').Graph,
- Renderer: require('./lib/Renderer'),
- json: require('graphlib').converter.json,
- layout: require('dagre').layout,
- version: require('./lib/version'),
- debug: require('dagre').debug
-};
-
-},{"./lib/Renderer":3,"./lib/version":4,"dagre":11,"graphlib":29}],3:[function(require,module,exports){
-var layout = require('dagre').layout;
-
-var d3;
-try { d3 = require('d3'); } catch (_) { d3 = window.d3; }
-
-module.exports = Renderer;
-
-function Renderer() {
- // Set up defaults...
- this._layout = layout();
-
- this.drawNodes(defaultDrawNodes);
- this.drawEdgeLabels(defaultDrawEdgeLabels);
- this.drawEdgePaths(defaultDrawEdgePaths);
- this.positionNodes(defaultPositionNodes);
- this.positionEdgeLabels(defaultPositionEdgeLabels);
- this.positionEdgePaths(defaultPositionEdgePaths);
- this.zoomSetup(defaultZoomSetup);
- this.zoom(defaultZoom);
- this.transition(defaultTransition);
- this.postLayout(defaultPostLayout);
- this.postRender(defaultPostRender);
-
- this.edgeInterpolate('bundle');
- this.edgeTension(0.95);
-}
-
-Renderer.prototype.layout = function(layout) {
- if (!arguments.length) { return this._layout; }
- this._layout = layout;
- return this;
-};
-
-Renderer.prototype.drawNodes = function(drawNodes) {
- if (!arguments.length) { return this._drawNodes; }
- this._drawNodes = bind(drawNodes, this);
- return this;
-};
-
-Renderer.prototype.drawEdgeLabels = function(drawEdgeLabels) {
- if (!arguments.length) { return this._drawEdgeLabels; }
- this._drawEdgeLabels = bind(drawEdgeLabels, this);
- return this;
-};
-
-Renderer.prototype.drawEdgePaths = function(drawEdgePaths) {
- if (!arguments.length) { return this._drawEdgePaths; }
- this._drawEdgePaths = bind(drawEdgePaths, this);
- return this;
-};
-
-Renderer.prototype.positionNodes = function(positionNodes) {
- if (!arguments.length) { return this._positionNodes; }
- this._positionNodes = bind(positionNodes, this);
- return this;
-};
-
-Renderer.prototype.positionEdgeLabels = function(positionEdgeLabels) {
- if (!arguments.length) { return this._positionEdgeLabels; }
- this._positionEdgeLabels = bind(positionEdgeLabels, this);
- return this;
-};
-
-Renderer.prototype.positionEdgePaths = function(positionEdgePaths) {
- if (!arguments.length) { return this._positionEdgePaths; }
- this._positionEdgePaths = bind(positionEdgePaths, this);
- return this;
-};
-
-Renderer.prototype.transition = function(transition) {
- if (!arguments.length) { return this._transition; }
- this._transition = bind(transition, this);
- return this;
-};
-
-Renderer.prototype.zoomSetup = function(zoomSetup) {
- if (!arguments.length) { return this._zoomSetup; }
- this._zoomSetup = bind(zoomSetup, this);
- return this;
-};
-
-Renderer.prototype.zoom = function(zoom) {
- if (!arguments.length) { return this._zoom; }
- if (zoom) {
- this._zoom = bind(zoom, this);
- } else {
- delete this._zoom;
- }
- return this;
-};
-
-Renderer.prototype.postLayout = function(postLayout) {
- if (!arguments.length) { return this._postLayout; }
- this._postLayout = bind(postLayout, this);
- return this;
-};
-
-Renderer.prototype.postRender = function(postRender) {
- if (!arguments.length) { return this._postRender; }
- this._postRender = bind(postRender, this);
- return this;
-};
-
-Renderer.prototype.edgeInterpolate = function(edgeInterpolate) {
- if (!arguments.length) { return this._edgeInterpolate; }
- this._edgeInterpolate = edgeInterpolate;
- return this;
-};
-
-Renderer.prototype.edgeTension = function(edgeTension) {
- if (!arguments.length) { return this._edgeTension; }
- this._edgeTension = edgeTension;
- return this;
-};
-
-Renderer.prototype.run = function(graph, orgSvg) {
- // First copy the input graph so that it is not changed by the rendering
- // process.
- graph = copyAndInitGraph(graph);
-
- // Create zoom elements
- var svg = this._zoomSetup(graph, orgSvg);
-
- // Create layers
- svg
- .selectAll('g.edgePaths, g.edgeLabels, g.nodes')
- .data(['edgePaths', 'edgeLabels', 'nodes'])
- .enter()
- .append('g')
- .attr('class', function(d) { return d; });
-
- // Create node and edge roots, attach labels, and capture dimension
- // information for use with layout.
- var svgNodes = this._drawNodes(graph, svg.select('g.nodes'));
- var svgEdgeLabels = this._drawEdgeLabels(graph, svg.select('g.edgeLabels'));
-
- svgNodes.each(function(u) { calculateDimensions(this, graph.node(u)); });
- svgEdgeLabels.each(function(e) { calculateDimensions(this, graph.edge(e)); });
-
- // Now apply the layout function
- var result = runLayout(graph, this._layout);
-
- // Copy useDef attribute from input graph to output graph
- graph.eachNode(function(u, a) {
- if (a.useDef) {
- result.node(u).useDef = a.useDef;
- }
- });
-
- // Run any user-specified post layout processing
- this._postLayout(result, svg);
-
- var svgEdgePaths = this._drawEdgePaths(graph, svg.select('g.edgePaths'));
-
- // Apply the layout information to the graph
- this._positionNodes(result, svgNodes);
- this._positionEdgeLabels(result, svgEdgeLabels);
- this._positionEdgePaths(result, svgEdgePaths, orgSvg);
-
- this._postRender(result, svg);
-
- return result;
-};
-
-function copyAndInitGraph(graph) {
- var copy = graph.copy();
-
- if (copy.graph() === undefined) {
- copy.graph({});
- }
-
- if (!('arrowheadFix' in copy.graph())) {
- copy.graph().arrowheadFix = true;
- }
-
- // Init labels if they were not present in the source graph
- copy.nodes().forEach(function(u) {
- var value = copyObject(copy.node(u));
- copy.node(u, value);
- if (!('label' in value)) { value.label = ''; }
- });
-
- copy.edges().forEach(function(e) {
- var value = copyObject(copy.edge(e));
- copy.edge(e, value);
- if (!('label' in value)) { value.label = ''; }
- });
-
- return copy;
-}
-
-function copyObject(obj) {
- var copy = {};
- for (var k in obj) {
- copy[k] = obj[k];
- }
- return copy;
-}
-
-function calculateDimensions(group, value) {
- var bbox = group.getBBox();
- value.width = bbox.width;
- value.height = bbox.height;
-}
-
-function runLayout(graph, layout) {
- var result = layout.run(graph);
-
- // Copy labels to the result graph
- graph.eachNode(function(u, value) { result.node(u).label = value.label; });
- graph.eachEdge(function(e, u, v, value) { result.edge(e).label = value.label; });
-
- return result;
-}
-
-function defaultDrawNodes(g, root) {
- var nodes = g.nodes().filter(function(u) { return !isComposite(g, u); });
-
- var svgNodes = root
- .selectAll('g.node')
- .classed('enter', false)
- .data(nodes, function(u) { return u; });
-
- svgNodes.selectAll('*').remove();
-
- svgNodes
- .enter()
- .append('g')
- .style('opacity', 0)
- .attr('class', 'node enter');
-
- svgNodes.each(function(u) {
- var attrs = g.node(u),
- domNode = d3.select(this);
- addLabel(attrs, domNode, true, 10, 10);
- });
-
- this._transition(svgNodes.exit())
- .style('opacity', 0)
- .remove();
-
- return svgNodes;
-}
-
-function defaultDrawEdgeLabels(g, root) {
- var svgEdgeLabels = root
- .selectAll('g.edgeLabel')
- .classed('enter', false)
- .data(g.edges(), function (e) { return e; });
-
- svgEdgeLabels.selectAll('*').remove();
-
- svgEdgeLabels
- .enter()
- .append('g')
- .style('opacity', 0)
- .attr('class', 'edgeLabel enter');
-
- svgEdgeLabels.each(function(e) { addLabel(g.edge(e), d3.select(this), false, 0, 0); });
-
- this._transition(svgEdgeLabels.exit())
- .style('opacity', 0)
- .remove();
-
- return svgEdgeLabels;
-}
-
-var defaultDrawEdgePaths = function(g, root) {
- var svgEdgePaths = root
- .selectAll('g.edgePath')
- .classed('enter', false)
- .data(g.edges(), function(e) { return e; });
-
- var DEFAULT_ARROWHEAD = 'url(#arrowhead)',
- createArrowhead = DEFAULT_ARROWHEAD;
- if (!g.isDirected()) {
- createArrowhead = null;
- } else if (g.graph().arrowheadFix !== 'false' && g.graph().arrowheadFix !== false) {
- createArrowhead = function() {
- var strokeColor = d3.select(this).style('stroke');
- if (strokeColor) {
- var id = 'arrowhead-' + strokeColor.replace(/[^a-zA-Z0-9]/g, '_');
- getOrMakeArrowhead(root, id).style('fill', strokeColor);
- return 'url(#' + id + ')';
- }
- return DEFAULT_ARROWHEAD;
- };
- }
-
- svgEdgePaths
- .enter()
- .append('g')
- .attr('class', 'edgePath enter')
- .append('path')
- .style('opacity', 0);
-
- svgEdgePaths
- .selectAll('path')
- .each(function(e) { applyStyle(g.edge(e).style, d3.select(this)); })
- .attr('marker-end', createArrowhead);
-
- this._transition(svgEdgePaths.exit())
- .style('opacity', 0)
- .remove();
-
- return svgEdgePaths;
-};
-
-function defaultPositionNodes(g, svgNodes) {
- function transform(u) {
- var value = g.node(u);
- return 'translate(' + value.x + ',' + value.y + ')';
- }
-
- // For entering nodes, position immediately without transition
- svgNodes.filter('.enter').attr('transform', transform);
-
- this._transition(svgNodes)
- .style('opacity', 1)
- .attr('transform', transform);
-}
-
-function defaultPositionEdgeLabels(g, svgEdgeLabels) {
- function transform(e) {
- var value = g.edge(e);
- var point = findMidPoint(value.points);
- return 'translate(' + point.x + ',' + point.y + ')';
- }
-
- // For entering edge labels, position immediately without transition
- svgEdgeLabels.filter('.enter').attr('transform', transform);
-
- this._transition(svgEdgeLabels)
- .style('opacity', 1)
- .attr('transform', transform);
-}
-
-function isEllipse(obj) {
- return Object.prototype.toString.call(obj) === '[object SVGEllipseElement]';
-}
-
-function isCircle(obj) {
- return Object.prototype.toString.call(obj) === '[object SVGCircleElement]';
-}
-
-function isPolygon(obj) {
- return Object.prototype.toString.call(obj) === '[object SVGPolygonElement]';
-}
-
-function intersectNode(nd, p1, root) {
- if (nd.useDef) {
- var definedFig = root.select('defs #' + nd.useDef).node();
- if (definedFig) {
- var outerFig = definedFig.childNodes[0];
- if (isCircle(outerFig) || isEllipse(outerFig)) {
- return intersectEllipse(nd, outerFig, p1);
- } else if (isPolygon(outerFig)) {
- return intersectPolygon(nd, outerFig, p1);
- }
- }
- }
- // TODO: use bpodgursky's shortening algorithm here
- return intersectRect(nd, p1);
-}
-
-function defaultPositionEdgePaths(g, svgEdgePaths, root) {
- var interpolate = this._edgeInterpolate,
- tension = this._edgeTension;
-
- function calcPoints(e) {
- var value = g.edge(e);
- var source = g.node(g.incidentNodes(e)[0]);
- var target = g.node(g.incidentNodes(e)[1]);
- var points = value.points.slice();
-
- var p0 = points.length === 0 ? target : points[0];
- var p1 = points.length === 0 ? source : points[points.length - 1];
-
- points.unshift(intersectNode(source, p0, root));
- points.push(intersectNode(target, p1, root));
-
- return d3.svg.line()
- .x(function(d) { return d.x; })
- .y(function(d) { return d.y; })
- .interpolate(interpolate)
- .tension(tension)
- (points);
- }
-
- svgEdgePaths.filter('.enter').selectAll('path')
- .attr('d', calcPoints);
-
- this._transition(svgEdgePaths.selectAll('path'))
- .attr('d', calcPoints)
- .style('opacity', 1);
-}
-
-// By default we do not use transitions
-function defaultTransition(selection) {
- return selection;
-}
-
-// Setup dom for zooming
-function defaultZoomSetup(graph, svg) {
- var root = svg.property('ownerSVGElement');
- // If the svg node is the root, we get null, so set to svg.
- if (!root) {
- root = svg;
- } else {
- root = d3.select(root);
- }
-
- if (root.select('rect.overlay').empty()) {
- // Create an overlay for capturing mouse events that don't touch foreground
- root.insert('rect', ':first-child')
- .attr('class', 'overlay')
- .attr('width', '100%')
- .attr('height', '100%')
- .style('fill', 'none')
- .style('pointer-events', 'all');
-
- // Capture the zoom behaviour from the svg
- svg = svg.append('g')
- .attr('class', 'zoom');
-
- if (this._zoom) {
- root.call(this._zoom(graph, svg));
- }
- }
-
- return svg;
-}
-
-// By default allow pan and zoom
-function defaultZoom(graph, svg) {
- return d3.behavior.zoom().on('zoom', function() {
- svg.attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')');
- });
-}
-
-function defaultPostLayout() {
- // Do nothing
-}
-
-function defaultPostRender(graph, root) {
- if (graph.isDirected()) {
- // Fill = #333 is for backwards compatibility
- getOrMakeArrowhead(root, 'arrowhead')
- .attr('fill', '#333');
- }
-}
-
-function getOrMakeArrowhead(root, id) {
- var search = root.select('#' + id);
- if (!search.empty()) { return search; }
-
- var defs = root.select('defs');
- if (defs.empty()) {
- defs = root.append('svg:defs');
- }
-
- var marker =
- defs
- .append('svg:marker')
- .attr('id', id)
- .attr('viewBox', '0 0 10 10')
- .attr('refX', 8)
- .attr('refY', 5)
- .attr('markerUnits', 'strokeWidth')
- .attr('markerWidth', 8)
- .attr('markerHeight', 5)
- .attr('orient', 'auto');
-
- marker
- .append('svg:path')
- .attr('d', 'M 0 0 L 10 5 L 0 10 z');
-
- return marker;
-}
-
-function addLabel(node, root, addingNode, marginX, marginY) {
- // If the node has 'useDef' meta data, we rely on that
- if (node.useDef) {
- root.append('use').attr('xlink:href', '#' + node.useDef);
- return;
- }
- // Add the rect first so that it appears behind the label
- var label = node.label;
- var rect = root.append('rect');
- if (node.width) {
- rect.attr('width', node.width);
- }
- if (node.height) {
- rect.attr('height', node.height);
- }
-
- var labelSvg = root.append('g'),
- innerLabelSvg;
-
- if (label[0] === '<') {
- addForeignObjectLabel(label, labelSvg);
- // No margin for HTML elements
- marginX = marginY = 0;
- } else {
- innerLabelSvg = addTextLabel(label,
- labelSvg,
- Math.floor(node.labelCols),
- node.labelCut);
- applyStyle(node.labelStyle, innerLabelSvg);
- }
-
- var labelBBox = labelSvg.node().getBBox();
- labelSvg.attr('transform',
- 'translate(' + (-labelBBox.width / 2) + ',' + (-labelBBox.height / 2) + ')');
-
- var bbox = root.node().getBBox();
-
- rect
- .attr('rx', node.rx ? node.rx : 5)
- .attr('ry', node.ry ? node.ry : 5)
- .attr('x', -(bbox.width / 2 + marginX))
- .attr('y', -(bbox.height / 2 + marginY))
- .attr('width', bbox.width + 2 * marginX)
- .attr('height', bbox.height + 2 * marginY)
- .attr('fill', '#fff');
-
- if (addingNode) {
- applyStyle(node.style, rect);
-
- if (node.fill) {
- rect.style('fill', node.fill);
- }
-
- if (node.stroke) {
- rect.style('stroke', node.stroke);
- }
-
- if (node['stroke-width']) {
- rect.style('stroke-width', node['stroke-width'] + 'px');
- }
-
- if (node['stroke-dasharray']) {
- rect.style('stroke-dasharray', node['stroke-dasharray']);
- }
-
- if (node.href) {
- root
- .attr('class', root.attr('class') + ' clickable')
- .on('click', function() {
- window.open(node.href);
- });
- }
- }
-}
-
-function addForeignObjectLabel(label, root) {
- var fo = root
- .append('foreignObject')
- .attr('width', '100000');
-
- var w, h;
- fo
- .append('xhtml:div')
- .style('float', 'left')
- // TODO find a better way to get dimensions for foreignObjects...
- .html(function() { return label; })
- .each(function() {
- w = this.clientWidth;
- h = this.clientHeight;
- });
-
- fo
- .attr('width', w)
- .attr('height', h);
-}
-
-function addTextLabel(label, root, labelCols, labelCut) {
- if (labelCut === undefined) { labelCut = 'false'; }
- labelCut = (labelCut.toString().toLowerCase() === 'true');
-
- var node = root
- .append('text')
- .attr('text-anchor', 'left');
-
- label = label.replace(/\\n/g, '\n');
-
- var arr = labelCols ? wordwrap(label, labelCols, labelCut) : label;
- arr = arr.split('\n');
- for (var i = 0; i < arr.length; i++) {
- node
- .append('tspan')
- .attr('dy', '1em')
- .attr('x', '1')
- .text(arr[i]);
- }
-
- return node;
-}
-
-// Thanks to
-// http://james.padolsey.com/javascript/wordwrap-for-javascript/
-function wordwrap (str, width, cut, brk) {
- brk = brk || '\n';
- width = width || 75;
- cut = cut || false;
-
- if (!str) { return str; }
-
- var regex = '.{1,' + width + '}(\\s|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s|$)');
-
- return str.match(new RegExp(regex, 'g')).join(brk);
-}
-
-function findMidPoint(points) {
- var midIdx = points.length / 2;
- if (points.length % 2) {
- return points[Math.floor(midIdx)];
- } else {
- var p0 = points[midIdx - 1];
- var p1 = points[midIdx];
- return {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2};
- }
-}
-
-function intersectRect(rect, point) {
- var x = rect.x;
- var y = rect.y;
-
- // Rectangle intersection algorithm from:
- // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
- var dx = point.x - x;
- var dy = point.y - y;
- var w = rect.width / 2;
- var h = rect.height / 2;
-
- var sx, sy;
- if (Math.abs(dy) * w > Math.abs(dx) * h) {
- // Intersection is top or bottom of rect.
- if (dy < 0) {
- h = -h;
- }
- sx = dy === 0 ? 0 : h * dx / dy;
- sy = h;
- } else {
- // Intersection is left or right of rect.
- if (dx < 0) {
- w = -w;
- }
- sx = w;
- sy = dx === 0 ? 0 : w * dy / dx;
- }
-
- return {x: x + sx, y: y + sy};
-}
-
-function intersectEllipse(node, ellipseOrCircle, point) {
- // Formulae from: http://mathworld.wolfram.com/Ellipse-LineIntersection.html
-
- var cx = node.x;
- var cy = node.y;
- var rx, ry;
-
- if (isCircle(ellipseOrCircle)) {
- rx = ry = ellipseOrCircle.r.baseVal.value;
- } else {
- rx = ellipseOrCircle.rx.baseVal.value;
- ry = ellipseOrCircle.ry.baseVal.value;
- }
-
- var px = cx - point.x;
- var py = cy - point.y;
-
- var det = Math.sqrt(rx * rx * py * py + ry * ry * px * px);
-
- var dx = Math.abs(rx * ry * px / det);
- if (point.x < cx) {
- dx = -dx;
- }
- var dy = Math.abs(rx * ry * py / det);
- if (point.y < cy) {
- dy = -dy;
- }
-
- return {x: cx + dx, y: cy + dy};
-}
-
-function sameSign(r1, r2) {
- return r1 * r2 > 0;
-}
-
-// Add point to the found intersections, but check first that it is unique.
-function addPoint(x, y, intersections) {
- if (!intersections.some(function (elm) { return elm[0] === x && elm[1] === y; })) {
- intersections.push([x, y]);
- }
-}
-
-function intersectLine(x1, y1, x2, y2, x3, y3, x4, y4, intersections) {
- // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, p7 and p473.
-
- var a1, a2, b1, b2, c1, c2;
- var r1, r2 , r3, r4;
- var denom, offset, num;
- var x, y;
-
- // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + b1 y + c1 = 0.
- a1 = y2 - y1;
- b1 = x1 - x2;
- c1 = (x2 * y1) - (x1 * y2);
-
- // Compute r3 and r4.
- r3 = ((a1 * x3) + (b1 * y3) + c1);
- r4 = ((a1 * x4) + (b1 * y4) + c1);
-
- // Check signs of r3 and r4. If both point 3 and point 4 lie on
- // same side of line 1, the line segments do not intersect.
- if ((r3 !== 0) && (r4 !== 0) && sameSign(r3, r4)) {
- return /*DONT_INTERSECT*/;
- }
-
- // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0
- a2 = y4 - y3;
- b2 = x3 - x4;
- c2 = (x4 * y3) - (x3 * y4);
-
- // Compute r1 and r2
- r1 = (a2 * x1) + (b2 * y1) + c2;
- r2 = (a2 * x2) + (b2 * y2) + c2;
-
- // Check signs of r1 and r2. If both point 1 and point 2 lie
- // on same side of second line segment, the line segments do
- // not intersect.
- if ((r1 !== 0) && (r2 !== 0) && (sameSign(r1, r2))) {
- return /*DONT_INTERSECT*/;
- }
-
- // Line segments intersect: compute intersection point.
- denom = (a1 * b2) - (a2 * b1);
- if (denom === 0) {
- return /*COLLINEAR*/;
- }
-
- offset = Math.abs(denom / 2);
-
- // The denom/2 is to get rounding instead of truncating. It
- // is added or subtracted to the numerator, depending upon the
- // sign of the numerator.
- num = (b1 * c2) - (b2 * c1);
- x = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);
-
- num = (a2 * c1) - (a1 * c2);
- y = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);
-
- // lines_intersect
- addPoint(x, y, intersections);
-}
-
-function intersectPolygon(node, polygon, point) {
- var x1 = node.x;
- var y1 = node.y;
- var x2 = point.x;
- var y2 = point.y;
-
- var intersections = [];
- var points = polygon.points;
-
- var minx = 100000, miny = 100000;
- for (var j = 0; j < points.numberOfItems; j++) {
- var p = points.getItem(j);
- minx = Math.min(minx, p.x);
- miny = Math.min(miny, p.y);
- }
-
- var left = x1 - node.width / 2 - minx;
- var top = y1 - node.height / 2 - miny;
-
- for (var i = 0; i < points.numberOfItems; i++) {
- var p1 = points.getItem(i);
- var p2 = points.getItem(i < points.numberOfItems - 1 ? i + 1 : 0);
- intersectLine(x1, y1, x2, y2, left + p1.x, top + p1.y, left + p2.x, top + p2.y, intersections);
- }
-
- if (intersections.length === 1) {
- return {x: intersections[0][0], y: intersections[0][1]};
- }
-
- if (intersections.length > 1) {
- // More intersections, find the one nearest to edge end point
- intersections.sort(function(p, q) {
- var pdx = p[0] - point.x,
- pdy = p[1] - point.y,
- distp = Math.sqrt(pdx * pdx + pdy * pdy),
-
- qdx = q[0] - point.x,
- qdy = q[1] - point.y,
- distq = Math.sqrt(qdx * qdx + qdy * qdy);
-
- return (distp < distq) ? -1 : (distp === distq ? 0 : 1);
- });
- return {x: intersections[0][0], y: intersections[0][1]};
- } else {
- console.log('NO INTERSECTION FOUND, RETURN NODE CENTER', node);
- return node;
- }
-}
-
-function isComposite(g, u) {
- return 'children' in g && g.children(u).length;
-}
-
-function bind(func, thisArg) {
- // For some reason PhantomJS occassionally fails when using the builtin bind,
- // so we check if it is available and if not, use a degenerate polyfill.
- if (func.bind) {
- return func.bind(thisArg);
- }
-
- return function() {
- return func.apply(thisArg, arguments);
- };
-}
-
-function applyStyle(style, domNode) {
- if (style) {
- var currStyle = domNode.attr('style') || '';
- domNode.attr('style', currStyle + '; ' + style);
- }
-}
-
-},{"d3":10,"dagre":11}],4:[function(require,module,exports){
-module.exports = '0.2.9';
-
-},{}],5:[function(require,module,exports){
-exports.Set = require('./lib/Set');
-exports.PriorityQueue = require('./lib/PriorityQueue');
-exports.version = require('./lib/version');
-
-},{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(require,module,exports){
-module.exports = PriorityQueue;
-
-/**
- * A min-priority queue data structure. This algorithm is derived from Cormen,
- * et al., "Introduction to Algorithms". The basic idea of a min-priority
- * queue is that you can efficiently (in O(1) time) get the smallest key in
- * the queue. Adding and removing elements takes O(log n) time. A key can
- * have its priority decreased in O(log n) time.
- */
-function PriorityQueue() {
- this._arr = [];
- this._keyIndices = {};
-}
-
-/**
- * Returns the number of elements in the queue. Takes `O(1)` time.
- */
-PriorityQueue.prototype.size = function() {
- return this._arr.length;
-};
-
-/**
- * Returns the keys that are in the queue. Takes `O(n)` time.
- */
-PriorityQueue.prototype.keys = function() {
- return this._arr.map(function(x) { return x.key; });
-};
-
-/**
- * Returns `true` if **key** is in the queue and `false` if not.
- */
-PriorityQueue.prototype.has = function(key) {
- return key in this._keyIndices;
-};
-
-/**
- * Returns the priority for **key**. If **key** is not present in the queue
- * then this function returns `undefined`. Takes `O(1)` time.
- *
- * @param {Object} key
- */
-PriorityQueue.prototype.priority = function(key) {
- var index = this._keyIndices[key];
- if (index !== undefined) {
- return this._arr[index].priority;
- }
-};
-
-/**
- * Returns the key for the minimum element in this queue. If the queue is
- * empty this function throws an Error. Takes `O(1)` time.
- */
-PriorityQueue.prototype.min = function() {
- if (this.size() === 0) {
- throw new Error("Queue underflow");
- }
- return this._arr[0].key;
-};
-
-/**
- * Inserts a new key into the priority queue. If the key already exists in
- * the queue this function returns `false`; otherwise it will return `true`.
- * Takes `O(n)` time.
- *
- * @param {Object} key the key to add
- * @param {Number} priority the initial priority for the key
- */
-PriorityQueue.prototype.add = function(key, priority) {
- var keyIndices = this._keyIndices;
- if (!(key in keyIndices)) {
- var arr = this._arr;
- var index = arr.length;
- keyIndices[key] = index;
- arr.push({key: key, priority: priority});
- this._decrease(index);
- return true;
- }
- return false;
-};
-
-/**
- * Removes and returns the smallest key in the queue. Takes `O(log n)` time.
- */
-PriorityQueue.prototype.removeMin = function() {
- this._swap(0, this._arr.length - 1);
- var min = this._arr.pop();
- delete this._keyIndices[min.key];
- this._heapify(0);
- return min.key;
-};
-
-/**
- * Decreases the priority for **key** to **priority**. If the new priority is
- * greater than the previous priority, this function will throw an Error.
- *
- * @param {Object} key the key for which to raise priority
- * @param {Number} priority the new priority for the key
- */
-PriorityQueue.prototype.decrease = function(key, priority) {
- var index = this._keyIndices[key];
- if (priority > this._arr[index].priority) {
- throw new Error("New priority is greater than current priority. " +
- "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority);
- }
- this._arr[index].priority = priority;
- this._decrease(index);
-};
-
-PriorityQueue.prototype._heapify = function(i) {
- var arr = this._arr;
- var l = 2 * i,
- r = l + 1,
- largest = i;
- if (l < arr.length) {
- largest = arr[l].priority < arr[largest].priority ? l : largest;
- if (r < arr.length) {
- largest = arr[r].priority < arr[largest].priority ? r : largest;
- }
- if (largest !== i) {
- this._swap(i, largest);
- this._heapify(largest);
- }
- }
-};
-
-PriorityQueue.prototype._decrease = function(index) {
- var arr = this._arr;
- var priority = arr[index].priority;
- var parent;
- while (index !== 0) {
- parent = index >> 1;
- if (arr[parent].priority < priority) {
- break;
- }
- this._swap(index, parent);
- index = parent;
- }
-};
-
-PriorityQueue.prototype._swap = function(i, j) {
- var arr = this._arr;
- var keyIndices = this._keyIndices;
- var origArrI = arr[i];
- var origArrJ = arr[j];
- arr[i] = origArrJ;
- arr[j] = origArrI;
- keyIndices[origArrJ.key] = i;
- keyIndices[origArrI.key] = j;
-};
-
-},{}],7:[function(require,module,exports){
-var util = require('./util');
-
-module.exports = Set;
-
-/**
- * Constructs a new Set with an optional set of `initialKeys`.
- *
- * It is important to note that keys are coerced to String for most purposes
- * with this object, similar to the behavior of JavaScript's Object. For
- * example, the following will add only one key:
- *
- * var s = new Set();
- * s.add(1);
- * s.add("1");
- *
- * However, the type of the key is preserved internally so that `keys` returns
- * the original key set uncoerced. For the above example, `keys` would return
- * `[1]`.
- */
-function Set(initialKeys) {
- this._size = 0;
- this._keys = {};
-
- if (initialKeys) {
- for (var i = 0, il = initialKeys.length; i < il; ++i) {
- this.add(initialKeys[i]);
- }
- }
-}
-
-/**
- * Returns a new Set that represents the set intersection of the array of given
- * sets.
- */
-Set.intersect = function(sets) {
- if (sets.length === 0) {
- return new Set();
- }
-
- var result = new Set(!util.isArray(sets[0]) ? sets[0].keys() : sets[0]);
- for (var i = 1, il = sets.length; i < il; ++i) {
- var resultKeys = result.keys(),
- other = !util.isArray(sets[i]) ? sets[i] : new Set(sets[i]);
- for (var j = 0, jl = resultKeys.length; j < jl; ++j) {
- var key = resultKeys[j];
- if (!other.has(key)) {
- result.remove(key);
- }
- }
- }
-
- return result;
-};
-
-/**
- * Returns a new Set that represents the set union of the array of given sets.
- */
-Set.union = function(sets) {
- var totalElems = util.reduce(sets, function(lhs, rhs) {
- return lhs + (rhs.size ? rhs.size() : rhs.length);
- }, 0);
- var arr = new Array(totalElems);
-
- var k = 0;
- for (var i = 0, il = sets.length; i < il; ++i) {
- var cur = sets[i],
- keys = !util.isArray(cur) ? cur.keys() : cur;
- for (var j = 0, jl = keys.length; j < jl; ++j) {
- arr[k++] = keys[j];
- }
- }
-
- return new Set(arr);
-};
-
-/**
- * Returns the size of this set in `O(1)` time.
- */
-Set.prototype.size = function() {
- return this._size;
-};
-
-/**
- * Returns the keys in this set. Takes `O(n)` time.
- */
-Set.prototype.keys = function() {
- return values(this._keys);
-};
-
-/**
- * Tests if a key is present in this Set. Returns `true` if it is and `false`
- * if not. Takes `O(1)` time.
- */
-Set.prototype.has = function(key) {
- return key in this._keys;
-};
-
-/**
- * Adds a new key to this Set if it is not already present. Returns `true` if
- * the key was added and `false` if it was already present. Takes `O(1)` time.
- */
-Set.prototype.add = function(key) {
- if (!(key in this._keys)) {
- this._keys[key] = key;
- ++this._size;
- return true;
- }
- return false;
-};
-
-/**
- * Removes a key from this Set. If the key was removed this function returns
- * `true`. If not, it returns `false`. Takes `O(1)` time.
- */
-Set.prototype.remove = function(key) {
- if (key in this._keys) {
- delete this._keys[key];
- --this._size;
- return true;
- }
- return false;
-};
-
-/*
- * Returns an array of all values for properties of **o**.
- */
-function values(o) {
- var ks = Object.keys(o),
- len = ks.length,
- result = new Array(len),
- i;
- for (i = 0; i < len; ++i) {
- result[i] = o[ks[i]];
- }
- return result;
-}
-
-},{"./util":8}],8:[function(require,module,exports){
-/*
- * This polyfill comes from
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
- */
-if(!Array.isArray) {
- exports.isArray = function (vArg) {
- return Object.prototype.toString.call(vArg) === '[object Array]';
- };
-} else {
- exports.isArray = Array.isArray;
-}
-
-/*
- * Slightly adapted polyfill from
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
- */
-if ('function' !== typeof Array.prototype.reduce) {
- exports.reduce = function(array, callback, opt_initialValue) {
- 'use strict';
- if (null === array || 'undefined' === typeof array) {
- // At the moment all modern browsers, that support strict mode, have
- // native implementation of Array.prototype.reduce. For instance, IE8
- // does not support strict mode, so this check is actually useless.
- throw new TypeError(
- 'Array.prototype.reduce called on null or undefined');
- }
- if ('function' !== typeof callback) {
- throw new TypeError(callback + ' is not a function');
- }
- var index, value,
- length = array.length >>> 0,
- isValueSet = false;
- if (1 < arguments.length) {
- value = opt_initialValue;
- isValueSet = true;
- }
- for (index = 0; length > index; ++index) {
- if (array.hasOwnProperty(index)) {
- if (isValueSet) {
- value = callback(value, array[index], index, array);
- }
- else {
- value = array[index];
- isValueSet = true;
- }
- }
- }
- if (!isValueSet) {
- throw new TypeError('Reduce of empty array with no initial value');
- }
- return value;
- };
-} else {
- exports.reduce = function(array, callback, opt_initialValue) {
- return array.reduce(callback, opt_initialValue);
- };
-}
-
-},{}],9:[function(require,module,exports){
-module.exports = '1.1.3';
-
-},{}],10:[function(require,module,exports){
-require("./d3");
-module.exports = d3;
-(function () { delete this.d3; })(); // unset global
-
-},{}],11:[function(require,module,exports){
-/*
-Copyright (c) 2012-2013 Chris Pettitt
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-exports.Digraph = require("graphlib").Digraph;
-exports.Graph = require("graphlib").Graph;
-exports.layout = require("./lib/layout");
-exports.version = require("./lib/version");
-exports.debug = require("./lib/debug");
-
-},{"./lib/debug":12,"./lib/layout":13,"./lib/version":28,"graphlib":29}],12:[function(require,module,exports){
-'use strict';
-
-var util = require('./util');
-
-/**
- * Renders a graph in a stringified DOT format that indicates the ordering of
- * nodes by layer. Circles represent normal nodes. Diamons represent dummy
- * nodes. While we try to put nodes in clusters, it appears that graphviz
- * does not respect this because we're later using subgraphs for ordering nodes
- * in each layer.
- */
-exports.dotOrdering = function(g) {
- var ordering = util.ordering(g.filterNodes(util.filterNonSubgraphs(g)));
- var result = 'digraph {';
-
- function dfs(u) {
- var children = g.children(u);
- if (children.length) {
- result += 'subgraph cluster_' + u + ' {';
- result += 'label="' + u + '";';
- children.forEach(function(v) {
- dfs(v);
- });
- result += '}';
- } else {
- result += u;
- if (g.node(u).dummy) {
- result += ' [shape=diamond]';
- }
- result += ';';
- }
- }
-
- g.children(null).forEach(dfs);
-
- ordering.forEach(function(layer) {
- result += 'subgraph { rank=same; edge [style="invis"];';
- result += layer.join('->');
- result += '}';
- });
-
- g.eachEdge(function(e, u, v) {
- result += u + '->' + v + ';';
- });
-
- result += '}';
-
- return result;
-};
-
-},{"./util":27}],13:[function(require,module,exports){
-'use strict';
-
-var util = require('./util'),
- rank = require('./rank'),
- order = require('./order'),
- CGraph = require('graphlib').CGraph,
- CDigraph = require('graphlib').CDigraph;
-
-module.exports = function() {
- // External configuration
- var config = {
- // How much debug information to include?
- debugLevel: 0,
- // Max number of sweeps to perform in order phase
- orderMaxSweeps: order.DEFAULT_MAX_SWEEPS,
- // Use network simplex algorithm in ranking
- rankSimplex: false,
- // Rank direction. Valid values are (TB, LR)
- rankDir: 'TB'
- };
-
- // Phase functions
- var position = require('./position')();
-
- // This layout object
- var self = {};
-
- self.orderIters = util.propertyAccessor(self, config, 'orderMaxSweeps');
-
- self.rankSimplex = util.propertyAccessor(self, config, 'rankSimplex');
-
- self.nodeSep = delegateProperty(position.nodeSep);
- self.edgeSep = delegateProperty(position.edgeSep);
- self.universalSep = delegateProperty(position.universalSep);
- self.rankSep = delegateProperty(position.rankSep);
- self.rankDir = util.propertyAccessor(self, config, 'rankDir');
- self.debugAlignment = delegateProperty(position.debugAlignment);
-
- self.debugLevel = util.propertyAccessor(self, config, 'debugLevel', function(x) {
- util.log.level = x;
- position.debugLevel(x);
- });
-
- self.run = util.time('Total layout', run);
-
- self._normalize = normalize;
-
- return self;
-
- /*
- * Constructs an adjacency graph using the nodes and edges specified through
- * config. For each node and edge we add a property `dagre` that contains an
- * object that will hold intermediate and final layout information. Some of
- * the contents include:
- *
- * 1) A generated ID that uniquely identifies the object.
- * 2) Dimension information for nodes (copied from the source node).
- * 3) Optional dimension information for edges.
- *
- * After the adjacency graph is constructed the code no longer needs to use
- * the original nodes and edges passed in via config.
- */
- function initLayoutGraph(inputGraph) {
- var g = new CDigraph();
-
- inputGraph.eachNode(function(u, value) {
- if (value === undefined) value = {};
- g.addNode(u, {
- width: value.width,
- height: value.height
- });
- if (value.hasOwnProperty('rank')) {
- g.node(u).prefRank = value.rank;
- }
- });
-
- // Set up subgraphs
- if (inputGraph.parent) {
- inputGraph.nodes().forEach(function(u) {
- g.parent(u, inputGraph.parent(u));
- });
- }
-
- inputGraph.eachEdge(function(e, u, v, value) {
- if (value === undefined) value = {};
- var newValue = {
- e: e,
- minLen: value.minLen || 1,
- width: value.width || 0,
- height: value.height || 0,
- points: []
- };
-
- g.addEdge(null, u, v, newValue);
- });
-
- // Initial graph attributes
- var graphValue = inputGraph.graph() || {};
- g.graph({
- rankDir: graphValue.rankDir || config.rankDir,
- orderRestarts: graphValue.orderRestarts
- });
-
- return g;
- }
-
- function run(inputGraph) {
- var rankSep = self.rankSep();
- var g;
- try {
- // Build internal graph
- g = util.time('initLayoutGraph', initLayoutGraph)(inputGraph);
-
- if (g.order() === 0) {
- return g;
- }
-
- // Make space for edge labels
- g.eachEdge(function(e, s, t, a) {
- a.minLen *= 2;
- });
- self.rankSep(rankSep / 2);
-
- // Determine the rank for each node. Nodes with a lower rank will appear
- // above nodes of higher rank.
- util.time('rank.run', rank.run)(g, config.rankSimplex);
-
- // Normalize the graph by ensuring that every edge is proper (each edge has
- // a length of 1). We achieve this by adding dummy nodes to long edges,
- // thus shortening them.
- util.time('normalize', normalize)(g);
-
- // Order the nodes so that edge crossings are minimized.
- util.time('order', order)(g, config.orderMaxSweeps);
-
- // Find the x and y coordinates for every node in the graph.
- util.time('position', position.run)(g);
-
- // De-normalize the graph by removing dummy nodes and augmenting the
- // original long edges with coordinate information.
- util.time('undoNormalize', undoNormalize)(g);
-
- // Reverses points for edges that are in a reversed state.
- util.time('fixupEdgePoints', fixupEdgePoints)(g);
-
- // Restore delete edges and reverse edges that were reversed in the rank
- // phase.
- util.time('rank.restoreEdges', rank.restoreEdges)(g);
-
- // Construct final result graph and return it
- return util.time('createFinalGraph', createFinalGraph)(g, inputGraph.isDirected());
- } finally {
- self.rankSep(rankSep);
- }
- }
-
- /*
- * This function is responsible for 'normalizing' the graph. The process of
- * normalization ensures that no edge in the graph has spans more than one
- * rank. To do this it inserts dummy nodes as needed and links them by adding
- * dummy edges. This function keeps enough information in the dummy nodes and
- * edges to ensure that the original graph can be reconstructed later.
- *
- * This method assumes that the input graph is cycle free.
- */
- function normalize(g) {
- var dummyCount = 0;
- g.eachEdge(function(e, s, t, a) {
- var sourceRank = g.node(s).rank;
- var targetRank = g.node(t).rank;
- if (sourceRank + 1 < targetRank) {
- for (var u = s, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) {
- var v = '_D' + (++dummyCount);
- var node = {
- width: a.width,
- height: a.height,
- edge: { id: e, source: s, target: t, attrs: a },
- rank: rank,
- dummy: true
- };
-
- // If this node represents a bend then we will use it as a control
- // point. For edges with 2 segments this will be the center dummy
- // node. For edges with more than two segments, this will be the
- // first and last dummy node.
- if (i === 0) node.index = 0;
- else if (rank + 1 === targetRank) node.index = 1;
-
- g.addNode(v, node);
- g.addEdge(null, u, v, {});
- u = v;
- }
- g.addEdge(null, u, t, {});
- g.delEdge(e);
- }
- });
- }
-
- /*
- * Reconstructs the graph as it was before normalization. The positions of
- * dummy nodes are used to build an array of points for the original 'long'
- * edge. Dummy nodes and edges are removed.
- */
- function undoNormalize(g) {
- g.eachNode(function(u, a) {
- if (a.dummy) {
- if ('index' in a) {
- var edge = a.edge;
- if (!g.hasEdge(edge.id)) {
- g.addEdge(edge.id, edge.source, edge.target, edge.attrs);
- }
- var points = g.edge(edge.id).points;
- points[a.index] = { x: a.x, y: a.y, ul: a.ul, ur: a.ur, dl: a.dl, dr: a.dr };
- }
- g.delNode(u);
- }
- });
- }
-
- /*
- * For each edge that was reversed during the `acyclic` step, reverse its
- * array of points.
- */
- function fixupEdgePoints(g) {
- g.eachEdge(function(e, s, t, a) { if (a.reversed) a.points.reverse(); });
- }
-
- function createFinalGraph(g, isDirected) {
- var out = isDirected ? new CDigraph() : new CGraph();
- out.graph(g.graph());
- g.eachNode(function(u, value) { out.addNode(u, value); });
- g.eachNode(function(u) { out.parent(u, g.parent(u)); });
- g.eachEdge(function(e, u, v, value) {
- out.addEdge(value.e, u, v, value);
- });
-
- // Attach bounding box information
- var maxX = 0, maxY = 0;
- g.eachNode(function(u, value) {
- if (!g.children(u).length) {
- maxX = Math.max(maxX, value.x + value.width / 2);
- maxY = Math.max(maxY, value.y + value.height / 2);
- }
- });
- g.eachEdge(function(e, u, v, value) {
- var maxXPoints = Math.max.apply(Math, value.points.map(function(p) { return p.x; }));
- var maxYPoints = Math.max.apply(Math, value.points.map(function(p) { return p.y; }));
- maxX = Math.max(maxX, maxXPoints + value.width / 2);
- maxY = Math.max(maxY, maxYPoints + value.height / 2);
- });
- out.graph().width = maxX;
- out.graph().height = maxY;
-
- return out;
- }
-
- /*
- * Given a function, a new function is returned that invokes the given
- * function. The return value from the function is always the `self` object.
- */
- function delegateProperty(f) {
- return function() {
- if (!arguments.length) return f();
- f.apply(null, arguments);
- return self;
- };
- }
-};
-
-
-},{"./order":14,"./position":19,"./rank":20,"./util":27,"graphlib":29}],14:[function(require,module,exports){
-'use strict';
-
-var util = require('./util'),
- crossCount = require('./order/crossCount'),
- initLayerGraphs = require('./order/initLayerGraphs'),
- initOrder = require('./order/initOrder'),
- sortLayer = require('./order/sortLayer');
-
-module.exports = order;
-
-// The maximum number of sweeps to perform before finishing the order phase.
-var DEFAULT_MAX_SWEEPS = 24;
-order.DEFAULT_MAX_SWEEPS = DEFAULT_MAX_SWEEPS;
-
-/*
- * Runs the order phase with the specified `graph, `maxSweeps`, and
- * `debugLevel`. If `maxSweeps` is not specified we use `DEFAULT_MAX_SWEEPS`.
- * If `debugLevel` is not set we assume 0.
- */
-function order(g, maxSweeps) {
- if (arguments.length < 2) {
- maxSweeps = DEFAULT_MAX_SWEEPS;
- }
-
- var restarts = g.graph().orderRestarts || 0;
-
- var layerGraphs = initLayerGraphs(g);
- // TODO: remove this when we add back support for ordering clusters
- layerGraphs.forEach(function(lg) {
- lg = lg.filterNodes(function(u) { return !g.children(u).length; });
- });
-
- var iters = 0,
- currentBestCC,
- allTimeBestCC = Number.MAX_VALUE,
- allTimeBest = {};
-
- function saveAllTimeBest() {
- g.eachNode(function(u, value) { allTimeBest[u] = value.order; });
- }
-
- for (var j = 0; j < Number(restarts) + 1 && allTimeBestCC !== 0; ++j) {
- currentBestCC = Number.MAX_VALUE;
- initOrder(g, restarts > 0);
-
- util.log(2, 'Order phase start cross count: ' + g.graph().orderInitCC);
-
- var i, lastBest, cc;
- for (i = 0, lastBest = 0; lastBest < 4 && i < maxSweeps && currentBestCC > 0; ++i, ++lastBest, ++iters) {
- sweep(g, layerGraphs, i);
- cc = crossCount(g);
- if (cc < currentBestCC) {
- lastBest = 0;
- currentBestCC = cc;
- if (cc < allTimeBestCC) {
- saveAllTimeBest();
- allTimeBestCC = cc;
- }
- }
- util.log(3, 'Order phase start ' + j + ' iter ' + i + ' cross count: ' + cc);
- }
- }
-
- Object.keys(allTimeBest).forEach(function(u) {
- if (!g.children || !g.children(u).length) {
- g.node(u).order = allTimeBest[u];
- }
- });
- g.graph().orderCC = allTimeBestCC;
-
- util.log(2, 'Order iterations: ' + iters);
- util.log(2, 'Order phase best cross count: ' + g.graph().orderCC);
-}
-
-function predecessorWeights(g, nodes) {
- var weights = {};
- nodes.forEach(function(u) {
- weights[u] = g.inEdges(u).map(function(e) {
- return g.node(g.source(e)).order;
- });
- });
- return weights;
-}
-
-function successorWeights(g, nodes) {
- var weights = {};
- nodes.forEach(function(u) {
- weights[u] = g.outEdges(u).map(function(e) {
- return g.node(g.target(e)).order;
- });
- });
- return weights;
-}
-
-function sweep(g, layerGraphs, iter) {
- if (iter % 2 === 0) {
- sweepDown(g, layerGraphs, iter);
- } else {
- sweepUp(g, layerGraphs, iter);
- }
-}
-
-function sweepDown(g, layerGraphs) {
- var cg;
- for (var i = 1; i < layerGraphs.length; ++i) {
- cg = sortLayer(layerGraphs[i], cg, predecessorWeights(g, layerGraphs[i].nodes()));
- }
-}
-
-function sweepUp(g, layerGraphs) {
- var cg;
- for (var i = layerGraphs.length - 2; i >= 0; --i) {
- sortLayer(layerGraphs[i], cg, successorWeights(g, layerGraphs[i].nodes()));
- }
-}
-
-},{"./order/crossCount":15,"./order/initLayerGraphs":16,"./order/initOrder":17,"./order/sortLayer":18,"./util":27}],15:[function(require,module,exports){
-'use strict';
-
-var util = require('../util');
-
-module.exports = crossCount;
-
-/*
- * Returns the cross count for the given graph.
- */
-function crossCount(g) {
- var cc = 0;
- var ordering = util.ordering(g);
- for (var i = 1; i < ordering.length; ++i) {
- cc += twoLayerCrossCount(g, ordering[i-1], ordering[i]);
- }
- return cc;
-}
-
-/*
- * This function searches through a ranked and ordered graph and counts the
- * number of edges that cross. This algorithm is derived from:
- *
- * W. Barth et al., Bilayer Cross Counting, JGAA, 8(2) 179–194 (2004)
- */
-function twoLayerCrossCount(g, layer1, layer2) {
- var indices = [];
- layer1.forEach(function(u) {
- var nodeIndices = [];
- g.outEdges(u).forEach(function(e) { nodeIndices.push(g.node(g.target(e)).order); });
- nodeIndices.sort(function(x, y) { return x - y; });
- indices = indices.concat(nodeIndices);
- });
-
- var firstIndex = 1;
- while (firstIndex < layer2.length) firstIndex <<= 1;
-
- var treeSize = 2 * firstIndex - 1;
- firstIndex -= 1;
-
- var tree = [];
- for (var i = 0; i < treeSize; ++i) { tree[i] = 0; }
-
- var cc = 0;
- indices.forEach(function(i) {
- var treeIndex = i + firstIndex;
- ++tree[treeIndex];
- while (treeIndex > 0) {
- if (treeIndex % 2) {
- cc += tree[treeIndex + 1];
- }
- treeIndex = (treeIndex - 1) >> 1;
- ++tree[treeIndex];
- }
- });
-
- return cc;
-}
-
-},{"../util":27}],16:[function(require,module,exports){
-'use strict';
-
-var nodesFromList = require('graphlib').filter.nodesFromList,
- /* jshint -W079 */
- Set = require('cp-data').Set;
-
-module.exports = initLayerGraphs;
-
-/*
- * This function takes a compound layered graph, g, and produces an array of
- * layer graphs. Each entry in the array represents a subgraph of nodes
- * relevant for performing crossing reduction on that layer.
- */
-function initLayerGraphs(g) {
- var ranks = [];
-
- function dfs(u) {
- if (u === null) {
- g.children(u).forEach(function(v) { dfs(v); });
- return;
- }
-
- var value = g.node(u);
- value.minRank = ('rank' in value) ? value.rank : Number.MAX_VALUE;
- value.maxRank = ('rank' in value) ? value.rank : Number.MIN_VALUE;
- var uRanks = new Set();
- g.children(u).forEach(function(v) {
- var rs = dfs(v);
- uRanks = Set.union([uRanks, rs]);
- value.minRank = Math.min(value.minRank, g.node(v).minRank);
- value.maxRank = Math.max(value.maxRank, g.node(v).maxRank);
- });
-
- if ('rank' in value) uRanks.add(value.rank);
-
- uRanks.keys().forEach(function(r) {
- if (!(r in ranks)) ranks[r] = [];
- ranks[r].push(u);
- });
-
- return uRanks;
- }
- dfs(null);
-
- var layerGraphs = [];
- ranks.forEach(function(us, rank) {
- layerGraphs[rank] = g.filterNodes(nodesFromList(us));
- });
-
- return layerGraphs;
-}
-
-},{"cp-data":5,"graphlib":29}],17:[function(require,module,exports){
-'use strict';
-
-var crossCount = require('./crossCount'),
- util = require('../util');
-
-module.exports = initOrder;
-
-/*
- * Given a graph with a set of layered nodes (i.e. nodes that have a `rank`
- * attribute) this function attaches an `order` attribute that uniquely
- * arranges each node of each rank. If no constraint graph is provided the
- * order of the nodes in each rank is entirely arbitrary.
- */
-function initOrder(g, random) {
- var layers = [];
-
- g.eachNode(function(u, value) {
- var layer = layers[value.rank];
- if (g.children && g.children(u).length > 0) return;
- if (!layer) {
- layer = layers[value.rank] = [];
- }
- layer.push(u);
- });
-
- layers.forEach(function(layer) {
- if (random) {
- util.shuffle(layer);
- }
- layer.forEach(function(u, i) {
- g.node(u).order = i;
- });
- });
-
- var cc = crossCount(g);
- g.graph().orderInitCC = cc;
- g.graph().orderCC = Number.MAX_VALUE;
-}
-
-},{"../util":27,"./crossCount":15}],18:[function(require,module,exports){
-'use strict';
-
-var util = require('../util'),
- Digraph = require('graphlib').Digraph,
- topsort = require('graphlib').alg.topsort,
- nodesFromList = require('graphlib').filter.nodesFromList;
-
-module.exports = sortLayer;
-
-function sortLayer(g, cg, weights) {
- weights = adjustWeights(g, weights);
- var result = sortLayerSubgraph(g, null, cg, weights);
-
- result.list.forEach(function(u, i) {
- g.node(u).order = i;
- });
- return result.constraintGraph;
-}
-
-function sortLayerSubgraph(g, sg, cg, weights) {
- cg = cg ? cg.filterNodes(nodesFromList(g.children(sg))) : new Digraph();
-
- var nodeData = {};
- g.children(sg).forEach(function(u) {
- if (g.children(u).length) {
- nodeData[u] = sortLayerSubgraph(g, u, cg, weights);
- nodeData[u].firstSG = u;
- nodeData[u].lastSG = u;
- } else {
- var ws = weights[u];
- nodeData[u] = {
- degree: ws.length,
- barycenter: util.sum(ws) / ws.length,
- order: g.node(u).order,
- orderCount: 1,
- list: [u]
- };
- }
- });
-
- resolveViolatedConstraints(g, cg, nodeData);
-
- var keys = Object.keys(nodeData);
- keys.sort(function(x, y) {
- return nodeData[x].barycenter - nodeData[y].barycenter ||
- nodeData[x].order - nodeData[y].order;
- });
-
- var result = keys.map(function(u) { return nodeData[u]; })
- .reduce(function(lhs, rhs) { return mergeNodeData(g, lhs, rhs); });
- return result;
-}
-
-function mergeNodeData(g, lhs, rhs) {
- var cg = mergeDigraphs(lhs.constraintGraph, rhs.constraintGraph);
-
- if (lhs.lastSG !== undefined && rhs.firstSG !== undefined) {
- if (cg === undefined) {
- cg = new Digraph();
- }
- if (!cg.hasNode(lhs.lastSG)) { cg.addNode(lhs.lastSG); }
- cg.addNode(rhs.firstSG);
- cg.addEdge(null, lhs.lastSG, rhs.firstSG);
- }
-
- return {
- degree: lhs.degree + rhs.degree,
- barycenter: (lhs.barycenter * lhs.degree + rhs.barycenter * rhs.degree) /
- (lhs.degree + rhs.degree),
- order: (lhs.order * lhs.orderCount + rhs.order * rhs.orderCount) /
- (lhs.orderCount + rhs.orderCount),
- orderCount: lhs.orderCount + rhs.orderCount,
- list: lhs.list.concat(rhs.list),
- firstSG: lhs.firstSG !== undefined ? lhs.firstSG : rhs.firstSG,
- lastSG: rhs.lastSG !== undefined ? rhs.lastSG : lhs.lastSG,
- constraintGraph: cg
- };
-}
-
-function mergeDigraphs(lhs, rhs) {
- if (lhs === undefined) return rhs;
- if (rhs === undefined) return lhs;
-
- lhs = lhs.copy();
- rhs.nodes().forEach(function(u) { lhs.addNode(u); });
- rhs.edges().forEach(function(e, u, v) { lhs.addEdge(null, u, v); });
- return lhs;
-}
-
-function resolveViolatedConstraints(g, cg, nodeData) {
- // Removes nodes `u` and `v` from `cg` and makes any edges incident on them
- // incident on `w` instead.
- function collapseNodes(u, v, w) {
- // TODO original paper removes self loops, but it is not obvious when this would happen
- cg.inEdges(u).forEach(function(e) {
- cg.delEdge(e);
- cg.addEdge(null, cg.source(e), w);
- });
-
- cg.outEdges(v).forEach(function(e) {
- cg.delEdge(e);
- cg.addEdge(null, w, cg.target(e));
- });
-
- cg.delNode(u);
- cg.delNode(v);
- }
-
- var violated;
- while ((violated = findViolatedConstraint(cg, nodeData)) !== undefined) {
- var source = cg.source(violated),
- target = cg.target(violated);
-
- var v;
- while ((v = cg.addNode(null)) && g.hasNode(v)) {
- cg.delNode(v);
- }
-
- // Collapse barycenter and list
- nodeData[v] = mergeNodeData(g, nodeData[source], nodeData[target]);
- delete nodeData[source];
- delete nodeData[target];
-
- collapseNodes(source, target, v);
- if (cg.incidentEdges(v).length === 0) { cg.delNode(v); }
- }
-}
-
-function findViolatedConstraint(cg, nodeData) {
- var us = topsort(cg);
- for (var i = 0; i < us.length; ++i) {
- var u = us[i];
- var inEdges = cg.inEdges(u);
- for (var j = 0; j < inEdges.length; ++j) {
- var e = inEdges[j];
- if (nodeData[cg.source(e)].barycenter >= nodeData[u].barycenter) {
- return e;
- }
- }
- }
-}
-
-// Adjust weights so that they fall in the range of 0..|N|-1. If a node has no
-// weight assigned then set its adjusted weight to its current position. This
-// allows us to better retain the origiinal position of nodes without neighbors.
-function adjustWeights(g, weights) {
- var minW = Number.MAX_VALUE,
- maxW = 0,
- adjusted = {};
- g.eachNode(function(u) {
- if (g.children(u).length) return;
-
- var ws = weights[u];
- if (ws.length) {
- minW = Math.min(minW, util.min(ws));
- maxW = Math.max(maxW, util.max(ws));
- }
- });
-
- var rangeW = (maxW - minW);
- g.eachNode(function(u) {
- if (g.children(u).length) return;
-
- var ws = weights[u];
- if (!ws.length) {
- adjusted[u] = [g.node(u).order];
- } else {
- adjusted[u] = ws.map(function(w) {
- if (rangeW) {
- return (w - minW) * (g.order() - 1) / rangeW;
- } else {
- return g.order() - 1 / 2;
- }
- });
- }
- });
-
- return adjusted;
-}
-
-},{"../util":27,"graphlib":29}],19:[function(require,module,exports){
-'use strict';
-
-var util = require('./util');
-
-/*
- * The algorithms here are based on Brandes and Köpf, "Fast and Simple
- * Horizontal Coordinate Assignment".
- */
-module.exports = function() {
- // External configuration
- var config = {
- nodeSep: 50,
- edgeSep: 10,
- universalSep: null,
- rankSep: 30
- };
-
- var self = {};
-
- self.nodeSep = util.propertyAccessor(self, config, 'nodeSep');
- self.edgeSep = util.propertyAccessor(self, config, 'edgeSep');
- // If not null this separation value is used for all nodes and edges
- // regardless of their widths. `nodeSep` and `edgeSep` are ignored with this
- // option.
- self.universalSep = util.propertyAccessor(self, config, 'universalSep');
- self.rankSep = util.propertyAccessor(self, config, 'rankSep');
- self.debugLevel = util.propertyAccessor(self, config, 'debugLevel');
-
- self.run = run;
-
- return self;
-
- function run(g) {
- g = g.filterNodes(util.filterNonSubgraphs(g));
-
- var layering = util.ordering(g);
-
- var conflicts = findConflicts(g, layering);
-
- var xss = {};
- ['u', 'd'].forEach(function(vertDir) {
- if (vertDir === 'd') layering.reverse();
-
- ['l', 'r'].forEach(function(horizDir) {
- if (horizDir === 'r') reverseInnerOrder(layering);
-
- var dir = vertDir + horizDir;
- var align = verticalAlignment(g, layering, conflicts, vertDir === 'u' ? 'predecessors' : 'successors');
- xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align);
-
- if (config.debugLevel >= 3)
- debugPositioning(vertDir + horizDir, g, layering, xss[dir]);
-
- if (horizDir === 'r') flipHorizontally(xss[dir]);
-
- if (horizDir === 'r') reverseInnerOrder(layering);
- });
-
- if (vertDir === 'd') layering.reverse();
- });
-
- balance(g, layering, xss);
-
- g.eachNode(function(v) {
- var xs = [];
- for (var alignment in xss) {
- var alignmentX = xss[alignment][v];
- posXDebug(alignment, g, v, alignmentX);
- xs.push(alignmentX);
- }
- xs.sort(function(x, y) { return x - y; });
- posX(g, v, (xs[1] + xs[2]) / 2);
- });
-
- // Align y coordinates with ranks
- var y = 0, reverseY = g.graph().rankDir === 'BT' || g.graph().rankDir === 'RL';
- layering.forEach(function(layer) {
- var maxHeight = util.max(layer.map(function(u) { return height(g, u); }));
- y += maxHeight / 2;
- layer.forEach(function(u) {
- posY(g, u, reverseY ? -y : y);
- });
- y += maxHeight / 2 + config.rankSep;
- });
-
- // Translate layout so that top left corner of bounding rectangle has
- // coordinate (0, 0).
- var minX = util.min(g.nodes().map(function(u) { return posX(g, u) - width(g, u) / 2; }));
- var minY = util.min(g.nodes().map(function(u) { return posY(g, u) - height(g, u) / 2; }));
- g.eachNode(function(u) {
- posX(g, u, posX(g, u) - minX);
- posY(g, u, posY(g, u) - minY);
- });
- }
-
- /*
- * Generate an ID that can be used to represent any undirected edge that is
- * incident on `u` and `v`.
- */
- function undirEdgeId(u, v) {
- return u < v
- ? u.toString().length + ':' + u + '-' + v
- : v.toString().length + ':' + v + '-' + u;
- }
-
- function findConflicts(g, layering) {
- var conflicts = {}, // Set of conflicting edge ids
- pos = {}, // Position of node in its layer
- prevLayer,
- currLayer,
- k0, // Position of the last inner segment in the previous layer
- l, // Current position in the current layer (for iteration up to `l1`)
- k1; // Position of the next inner segment in the previous layer or
- // the position of the last element in the previous layer
-
- if (layering.length <= 2) return conflicts;
-
- function updateConflicts(v) {
- var k = pos[v];
- if (k < k0 || k > k1) {
- conflicts[undirEdgeId(currLayer[l], v)] = true;
- }
- }
-
- layering[1].forEach(function(u, i) { pos[u] = i; });
- for (var i = 1; i < layering.length - 1; ++i) {
- prevLayer = layering[i];
- currLayer = layering[i+1];
- k0 = 0;
- l = 0;
-
- // Scan current layer for next node that is incident to an inner segement
- // between layering[i+1] and layering[i].
- for (var l1 = 0; l1 < currLayer.length; ++l1) {
- var u = currLayer[l1]; // Next inner segment in the current layer or
- // last node in the current layer
- pos[u] = l1;
- k1 = undefined;
-
- if (g.node(u).dummy) {
- var uPred = g.predecessors(u)[0];
- // Note: In the case of self loops and sideways edges it is possible
- // for a dummy not to have a predecessor.
- if (uPred !== undefined && g.node(uPred).dummy)
- k1 = pos[uPred];
- }
- if (k1 === undefined && l1 === currLayer.length - 1)
- k1 = prevLayer.length - 1;
-
- if (k1 !== undefined) {
- for (; l <= l1; ++l) {
- g.predecessors(currLayer[l]).forEach(updateConflicts);
- }
- k0 = k1;
- }
- }
- }
-
- return conflicts;
- }
-
- function verticalAlignment(g, layering, conflicts, relationship) {
- var pos = {}, // Position for a node in its layer
- root = {}, // Root of the block that the node participates in
- align = {}; // Points to the next node in the block or, if the last
- // element in the block, points to the first block's root
-
- layering.forEach(function(layer) {
- layer.forEach(function(u, i) {
- root[u] = u;
- align[u] = u;
- pos[u] = i;
- });
- });
-
- layering.forEach(function(layer) {
- var prevIdx = -1;
- layer.forEach(function(v) {
- var related = g[relationship](v), // Adjacent nodes from the previous layer
- mid; // The mid point in the related array
-
- if (related.length > 0) {
- related.sort(function(x, y) { return pos[x] - pos[y]; });
- mid = (related.length - 1) / 2;
- related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) {
- if (align[v] === v) {
- if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) {
- align[u] = v;
- align[v] = root[v] = root[u];
- prevIdx = pos[u];
- }
- }
- });
- }
- });
- });
-
- return { pos: pos, root: root, align: align };
- }
-
- // This function deviates from the standard BK algorithm in two ways. First
- // it takes into account the size of the nodes. Second it includes a fix to
- // the original algorithm that is described in Carstens, "Node and Label
- // Placement in a Layered Layout Algorithm".
- function horizontalCompaction(g, layering, pos, root, align) {
- var sink = {}, // Mapping of node id -> sink node id for class
- maybeShift = {}, // Mapping of sink node id -> { class node id, min shift }
- shift = {}, // Mapping of sink node id -> shift
- pred = {}, // Mapping of node id -> predecessor node (or null)
- xs = {}; // Calculated X positions
-
- layering.forEach(function(layer) {
- layer.forEach(function(u, i) {
- sink[u] = u;
- maybeShift[u] = {};
- if (i > 0)
- pred[u] = layer[i - 1];
- });
- });
-
- function updateShift(toShift, neighbor, delta) {
- if (!(neighbor in maybeShift[toShift])) {
- maybeShift[toShift][neighbor] = delta;
- } else {
- maybeShift[toShift][neighbor] = Math.min(maybeShift[toShift][neighbor], delta);
- }
- }
-
- function placeBlock(v) {
- if (!(v in xs)) {
- xs[v] = 0;
- var w = v;
- do {
- if (pos[w] > 0) {
- var u = root[pred[w]];
- placeBlock(u);
- if (sink[v] === v) {
- sink[v] = sink[u];
- }
- var delta = sep(g, pred[w]) + sep(g, w);
- if (sink[v] !== sink[u]) {
- updateShift(sink[u], sink[v], xs[v] - xs[u] - delta);
- } else {
- xs[v] = Math.max(xs[v], xs[u] + delta);
- }
- }
- w = align[w];
- } while (w !== v);
- }
- }
-
- // Root coordinates relative to sink
- util.values(root).forEach(function(v) {
- placeBlock(v);
- });
-
- // Absolute coordinates
- // There is an assumption here that we've resolved shifts for any classes
- // that begin at an earlier layer. We guarantee this by visiting layers in
- // order.
- layering.forEach(function(layer) {
- layer.forEach(function(v) {
- xs[v] = xs[root[v]];
- if (v === root[v] && v === sink[v]) {
- var minShift = 0;
- if (v in maybeShift && Object.keys(maybeShift[v]).length > 0) {
- minShift = util.min(Object.keys(maybeShift[v])
- .map(function(u) {
- return maybeShift[v][u] + (u in shift ? shift[u] : 0);
- }
- ));
- }
- shift[v] = minShift;
- }
- });
- });
-
- layering.forEach(function(layer) {
- layer.forEach(function(v) {
- xs[v] += shift[sink[root[v]]] || 0;
- });
- });
-
- return xs;
- }
-
- function findMinCoord(g, layering, xs) {
- return util.min(layering.map(function(layer) {
- var u = layer[0];
- return xs[u];
- }));
- }
-
- function findMaxCoord(g, layering, xs) {
- return util.max(layering.map(function(layer) {
- var u = layer[layer.length - 1];
- return xs[u];
- }));
- }
-
- function balance(g, layering, xss) {
- var min = {}, // Min coordinate for the alignment
- max = {}, // Max coordinate for the alginment
- smallestAlignment,
- shift = {}; // Amount to shift a given alignment
-
- function updateAlignment(v) {
- xss[alignment][v] += shift[alignment];
- }
-
- var smallest = Number.POSITIVE_INFINITY;
- for (var alignment in xss) {
- var xs = xss[alignment];
- min[alignment] = findMinCoord(g, layering, xs);
- max[alignment] = findMaxCoord(g, layering, xs);
- var w = max[alignment] - min[alignment];
- if (w < smallest) {
- smallest = w;
- smallestAlignment = alignment;
- }
- }
-
- // Determine how much to adjust positioning for each alignment
- ['u', 'd'].forEach(function(vertDir) {
- ['l', 'r'].forEach(function(horizDir) {
- var alignment = vertDir + horizDir;
- shift[alignment] = horizDir === 'l'
- ? min[smallestAlignment] - min[alignment]
- : max[smallestAlignment] - max[alignment];
- });
- });
-
- // Find average of medians for xss array
- for (alignment in xss) {
- g.eachNode(updateAlignment);
- }
- }
-
- function flipHorizontally(xs) {
- for (var u in xs) {
- xs[u] = -xs[u];
- }
- }
-
- function reverseInnerOrder(layering) {
- layering.forEach(function(layer) {
- layer.reverse();
- });
- }
-
- function width(g, u) {
- switch (g.graph().rankDir) {
- case 'LR': return g.node(u).height;
- case 'RL': return g.node(u).height;
- default: return g.node(u).width;
- }
- }
-
- function height(g, u) {
- switch(g.graph().rankDir) {
- case 'LR': return g.node(u).width;
- case 'RL': return g.node(u).width;
- default: return g.node(u).height;
- }
- }
-
- function sep(g, u) {
- if (config.universalSep !== null) {
- return config.universalSep;
- }
- var w = width(g, u);
- var s = g.node(u).dummy ? config.edgeSep : config.nodeSep;
- return (w + s) / 2;
- }
-
- function posX(g, u, x) {
- if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
- if (arguments.length < 3) {
- return g.node(u).y;
- } else {
- g.node(u).y = x;
- }
- } else {
- if (arguments.length < 3) {
- return g.node(u).x;
- } else {
- g.node(u).x = x;
- }
- }
- }
-
- function posXDebug(name, g, u, x) {
- if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
- if (arguments.length < 3) {
- return g.node(u)[name];
- } else {
- g.node(u)[name] = x;
- }
- } else {
- if (arguments.length < 3) {
- return g.node(u)[name];
- } else {
- g.node(u)[name] = x;
- }
- }
- }
-
- function posY(g, u, y) {
- if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
- if (arguments.length < 3) {
- return g.node(u).x;
- } else {
- g.node(u).x = y;
- }
- } else {
- if (arguments.length < 3) {
- return g.node(u).y;
- } else {
- g.node(u).y = y;
- }
- }
- }
-
- function debugPositioning(align, g, layering, xs) {
- layering.forEach(function(l, li) {
- var u, xU;
- l.forEach(function(v) {
- var xV = xs[v];
- if (u) {
- var s = sep(g, u) + sep(g, v);
- if (xV - xU < s)
- console.log('Position phase: sep violation. Align: ' + align + '. Layer: ' + li + '. ' +
- 'U: ' + u + ' V: ' + v + '. Actual sep: ' + (xV - xU) + ' Expected sep: ' + s);
- }
- u = v;
- xU = xV;
- });
- });
- }
-};
-
-},{"./util":27}],20:[function(require,module,exports){
-'use strict';
-
-var util = require('./util'),
- acyclic = require('./rank/acyclic'),
- initRank = require('./rank/initRank'),
- feasibleTree = require('./rank/feasibleTree'),
- constraints = require('./rank/constraints'),
- simplex = require('./rank/simplex'),
- components = require('graphlib').alg.components,
- filter = require('graphlib').filter;
-
-exports.run = run;
-exports.restoreEdges = restoreEdges;
-
-/*
- * Heuristic function that assigns a rank to each node of the input graph with
- * the intent of minimizing edge lengths, while respecting the `minLen`
- * attribute of incident edges.
- *
- * Prerequisites:
- *
- * * Each edge in the input graph must have an assigned 'minLen' attribute
- */
-function run(g, useSimplex) {
- expandSelfLoops(g);
-
- // If there are rank constraints on nodes, then build a new graph that
- // encodes the constraints.
- util.time('constraints.apply', constraints.apply)(g);
-
- expandSidewaysEdges(g);
-
- // Reverse edges to get an acyclic graph, we keep the graph in an acyclic
- // state until the very end.
- util.time('acyclic', acyclic)(g);
-
- // Convert the graph into a flat graph for ranking
- var flatGraph = g.filterNodes(util.filterNonSubgraphs(g));
-
- // Assign an initial ranking using DFS.
- initRank(flatGraph);
-
- // For each component improve the assigned ranks.
- components(flatGraph).forEach(function(cmpt) {
- var subgraph = flatGraph.filterNodes(filter.nodesFromList(cmpt));
- rankComponent(subgraph, useSimplex);
- });
-
- // Relax original constraints
- util.time('constraints.relax', constraints.relax(g));
-
- // When handling nodes with constrained ranks it is possible to end up with
- // edges that point to previous ranks. Most of the subsequent algorithms assume
- // that edges are pointing to successive ranks only. Here we reverse any "back
- // edges" and mark them as such. The acyclic algorithm will reverse them as a
- // post processing step.
- util.time('reorientEdges', reorientEdges)(g);
-}
-
-function restoreEdges(g) {
- acyclic.undo(g);
-}
-
-/*
- * Expand self loops into three dummy nodes. One will sit above the incident
- * node, one will be at the same level, and one below. The result looks like:
- *
- * /--<--x--->--\
- * node y
- * \--<--z--->--/
- *
- * Dummy nodes x, y, z give us the shape of a loop and node y is where we place
- * the label.
- *
- * TODO: consolidate knowledge of dummy node construction.
- * TODO: support minLen = 2
- */
-function expandSelfLoops(g) {
- g.eachEdge(function(e, u, v, a) {
- if (u === v) {
- var x = addDummyNode(g, e, u, v, a, 0, false),
- y = addDummyNode(g, e, u, v, a, 1, true),
- z = addDummyNode(g, e, u, v, a, 2, false);
- g.addEdge(null, x, u, {minLen: 1, selfLoop: true});
- g.addEdge(null, x, y, {minLen: 1, selfLoop: true});
- g.addEdge(null, u, z, {minLen: 1, selfLoop: true});
- g.addEdge(null, y, z, {minLen: 1, selfLoop: true});
- g.delEdge(e);
- }
- });
-}
-
-function expandSidewaysEdges(g) {
- g.eachEdge(function(e, u, v, a) {
- if (u === v) {
- var origEdge = a.originalEdge,
- dummy = addDummyNode(g, origEdge.e, origEdge.u, origEdge.v, origEdge.value, 0, true);
- g.addEdge(null, u, dummy, {minLen: 1});
- g.addEdge(null, dummy, v, {minLen: 1});
- g.delEdge(e);
- }
- });
-}
-
-function addDummyNode(g, e, u, v, a, index, isLabel) {
- return g.addNode(null, {
- width: isLabel ? a.width : 0,
- height: isLabel ? a.height : 0,
- edge: { id: e, source: u, target: v, attrs: a },
- dummy: true,
- index: index
- });
-}
-
-function reorientEdges(g) {
- g.eachEdge(function(e, u, v, value) {
- if (g.node(u).rank > g.node(v).rank) {
- g.delEdge(e);
- value.reversed = true;
- g.addEdge(e, v, u, value);
- }
- });
-}
-
-function rankComponent(subgraph, useSimplex) {
- var spanningTree = feasibleTree(subgraph);
-
- if (useSimplex) {
- util.log(1, 'Using network simplex for ranking');
- simplex(subgraph, spanningTree);
- }
- normalize(subgraph);
-}
-
-function normalize(g) {
- var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; }));
- g.eachNode(function(u, node) { node.rank -= m; });
-}
-
-},{"./rank/acyclic":21,"./rank/constraints":22,"./rank/feasibleTree":23,"./rank/initRank":24,"./rank/simplex":26,"./util":27,"graphlib":29}],21:[function(require,module,exports){
-'use strict';
-
-var util = require('../util');
-
-module.exports = acyclic;
-module.exports.undo = undo;
-
-/*
- * This function takes a directed graph that may have cycles and reverses edges
- * as appropriate to break these cycles. Each reversed edge is assigned a
- * `reversed` attribute with the value `true`.
- *
- * There should be no self loops in the graph.
- */
-function acyclic(g) {
- var onStack = {},
- visited = {},
- reverseCount = 0;
-
- function dfs(u) {
- if (u in visited) return;
- visited[u] = onStack[u] = true;
- g.outEdges(u).forEach(function(e) {
- var t = g.target(e),
- value;
-
- if (u === t) {
- console.error('Warning: found self loop "' + e + '" for node "' + u + '"');
- } else if (t in onStack) {
- value = g.edge(e);
- g.delEdge(e);
- value.reversed = true;
- ++reverseCount;
- g.addEdge(e, t, u, value);
- } else {
- dfs(t);
- }
- });
-
- delete onStack[u];
- }
-
- g.eachNode(function(u) { dfs(u); });
-
- util.log(2, 'Acyclic Phase: reversed ' + reverseCount + ' edge(s)');
-
- return reverseCount;
-}
-
-/*
- * Given a graph that has had the acyclic operation applied, this function
- * undoes that operation. More specifically, any edge with the `reversed`
- * attribute is again reversed to restore the original direction of the edge.
- */
-function undo(g) {
- g.eachEdge(function(e, s, t, a) {
- if (a.reversed) {
- delete a.reversed;
- g.delEdge(e);
- g.addEdge(e, t, s, a);
- }
- });
-}
-
-},{"../util":27}],22:[function(require,module,exports){
-'use strict';
-
-exports.apply = function(g) {
- function dfs(sg) {
- var rankSets = {};
- g.children(sg).forEach(function(u) {
- if (g.children(u).length) {
- dfs(u);
- return;
- }
-
- var value = g.node(u),
- prefRank = value.prefRank;
- if (prefRank !== undefined) {
- if (!checkSupportedPrefRank(prefRank)) { return; }
-
- if (!(prefRank in rankSets)) {
- rankSets.prefRank = [u];
- } else {
- rankSets.prefRank.push(u);
- }
-
- var newU = rankSets[prefRank];
- if (newU === undefined) {
- newU = rankSets[prefRank] = g.addNode(null, { originalNodes: [] });
- g.parent(newU, sg);
- }
-
- redirectInEdges(g, u, newU, prefRank === 'min');
- redirectOutEdges(g, u, newU, prefRank === 'max');
-
- // Save original node and remove it from reduced graph
- g.node(newU).originalNodes.push({ u: u, value: value, parent: sg });
- g.delNode(u);
- }
- });
-
- addLightEdgesFromMinNode(g, sg, rankSets.min);
- addLightEdgesToMaxNode(g, sg, rankSets.max);
- }
-
- dfs(null);
-};
-
-function checkSupportedPrefRank(prefRank) {
- if (prefRank !== 'min' && prefRank !== 'max' && prefRank.indexOf('same_') !== 0) {
- console.error('Unsupported rank type: ' + prefRank);
- return false;
- }
- return true;
-}
-
-function redirectInEdges(g, u, newU, reverse) {
- g.inEdges(u).forEach(function(e) {
- var origValue = g.edge(e),
- value;
- if (origValue.originalEdge) {
- value = origValue;
- } else {
- value = {
- originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
- minLen: g.edge(e).minLen
- };
- }
-
- // Do not reverse edges for self-loops.
- if (origValue.selfLoop) {
- reverse = false;
- }
-
- if (reverse) {
- // Ensure that all edges to min are reversed
- g.addEdge(null, newU, g.source(e), value);
- value.reversed = true;
- } else {
- g.addEdge(null, g.source(e), newU, value);
- }
- });
-}
-
-function redirectOutEdges(g, u, newU, reverse) {
- g.outEdges(u).forEach(function(e) {
- var origValue = g.edge(e),
- value;
- if (origValue.originalEdge) {
- value = origValue;
- } else {
- value = {
- originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
- minLen: g.edge(e).minLen
- };
- }
-
- // Do not reverse edges for self-loops.
- if (origValue.selfLoop) {
- reverse = false;
- }
-
- if (reverse) {
- // Ensure that all edges from max are reversed
- g.addEdge(null, g.target(e), newU, value);
- value.reversed = true;
- } else {
- g.addEdge(null, newU, g.target(e), value);
- }
- });
-}
-
-function addLightEdgesFromMinNode(g, sg, minNode) {
- if (minNode !== undefined) {
- g.children(sg).forEach(function(u) {
- // The dummy check ensures we don't add an edge if the node is involved
- // in a self loop or sideways edge.
- if (u !== minNode && !g.outEdges(minNode, u).length && !g.node(u).dummy) {
- g.addEdge(null, minNode, u, { minLen: 0 });
- }
- });
- }
-}
-
-function addLightEdgesToMaxNode(g, sg, maxNode) {
- if (maxNode !== undefined) {
- g.children(sg).forEach(function(u) {
- // The dummy check ensures we don't add an edge if the node is involved
- // in a self loop or sideways edge.
- if (u !== maxNode && !g.outEdges(u, maxNode).length && !g.node(u).dummy) {
- g.addEdge(null, u, maxNode, { minLen: 0 });
- }
- });
- }
-}
-
-/*
- * This function "relaxes" the constraints applied previously by the "apply"
- * function. It expands any nodes that were collapsed and assigns the rank of
- * the collapsed node to each of the expanded nodes. It also restores the
- * original edges and removes any dummy edges pointing at the collapsed nodes.
- *
- * Note that the process of removing collapsed nodes also removes dummy edges
- * automatically.
- */
-exports.relax = function(g) {
- // Save original edges
- var originalEdges = [];
- g.eachEdge(function(e, u, v, value) {
- var originalEdge = value.originalEdge;
- if (originalEdge) {
- originalEdges.push(originalEdge);
- }
- });
-
- // Expand collapsed nodes
- g.eachNode(function(u, value) {
- var originalNodes = value.originalNodes;
- if (originalNodes) {
- originalNodes.forEach(function(originalNode) {
- originalNode.value.rank = value.rank;
- g.addNode(originalNode.u, originalNode.value);
- g.parent(originalNode.u, originalNode.parent);
- });
- g.delNode(u);
- }
- });
-
- // Restore original edges
- originalEdges.forEach(function(edge) {
- g.addEdge(edge.e, edge.u, edge.v, edge.value);
- });
-};
-
-},{}],23:[function(require,module,exports){
-'use strict';
-
-/* jshint -W079 */
-var Set = require('cp-data').Set,
-/* jshint +W079 */
- Digraph = require('graphlib').Digraph,
- util = require('../util');
-
-module.exports = feasibleTree;
-
-/*
- * Given an acyclic graph with each node assigned a `rank` attribute, this
- * function constructs and returns a spanning tree. This function may reduce
- * the length of some edges from the initial rank assignment while maintaining
- * the `minLen` specified by each edge.
- *
- * Prerequisites:
- *
- * * The input graph is acyclic
- * * Each node in the input graph has an assigned `rank` attribute
- * * Each edge in the input graph has an assigned `minLen` attribute
- *
- * Outputs:
- *
- * A feasible spanning tree for the input graph (i.e. a spanning tree that
- * respects each graph edge's `minLen` attribute) represented as a Digraph with
- * a `root` attribute on graph.
- *
- * Nodes have the same id and value as that in the input graph.
- *
- * Edges in the tree have arbitrarily assigned ids. The attributes for edges
- * include `reversed`. `reversed` indicates that the edge is a
- * back edge in the input graph.
- */
-function feasibleTree(g) {
- var remaining = new Set(g.nodes()),
- tree = new Digraph();
-
- if (remaining.size() === 1) {
- var root = g.nodes()[0];
- tree.addNode(root, {});
- tree.graph({ root: root });
- return tree;
- }
-
- function addTightEdges(v) {
- var continueToScan = true;
- g.predecessors(v).forEach(function(u) {
- if (remaining.has(u) && !slack(g, u, v)) {
- if (remaining.has(v)) {
- tree.addNode(v, {});
- remaining.remove(v);
- tree.graph({ root: v });
- }
-
- tree.addNode(u, {});
- tree.addEdge(null, u, v, { reversed: true });
- remaining.remove(u);
- addTightEdges(u);
- continueToScan = false;
- }
- });
-
- g.successors(v).forEach(function(w) {
- if (remaining.has(w) && !slack(g, v, w)) {
- if (remaining.has(v)) {
- tree.addNode(v, {});
- remaining.remove(v);
- tree.graph({ root: v });
- }
-
- tree.addNode(w, {});
- tree.addEdge(null, v, w, {});
- remaining.remove(w);
- addTightEdges(w);
- continueToScan = false;
- }
- });
- return continueToScan;
- }
-
- function createTightEdge() {
- var minSlack = Number.MAX_VALUE;
- remaining.keys().forEach(function(v) {
- g.predecessors(v).forEach(function(u) {
- if (!remaining.has(u)) {
- var edgeSlack = slack(g, u, v);
- if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
- minSlack = -edgeSlack;
- }
- }
- });
-
- g.successors(v).forEach(function(w) {
- if (!remaining.has(w)) {
- var edgeSlack = slack(g, v, w);
- if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
- minSlack = edgeSlack;
- }
- }
- });
- });
-
- tree.eachNode(function(u) { g.node(u).rank -= minSlack; });
- }
-
- while (remaining.size()) {
- var nodesToSearch = !tree.order() ? remaining.keys() : tree.nodes();
- for (var i = 0, il = nodesToSearch.length;
- i < il && addTightEdges(nodesToSearch[i]);
- ++i);
- if (remaining.size()) {
- createTightEdge();
- }
- }
-
- return tree;
-}
-
-function slack(g, u, v) {
- var rankDiff = g.node(v).rank - g.node(u).rank;
- var maxMinLen = util.max(g.outEdges(u, v)
- .map(function(e) { return g.edge(e).minLen; }));
- return rankDiff - maxMinLen;
-}
-
-},{"../util":27,"cp-data":5,"graphlib":29}],24:[function(require,module,exports){
-'use strict';
-
-var util = require('../util'),
- topsort = require('graphlib').alg.topsort;
-
-module.exports = initRank;
-
-/*
- * Assigns a `rank` attribute to each node in the input graph and ensures that
- * this rank respects the `minLen` attribute of incident edges.
- *
- * Prerequisites:
- *
- * * The input graph must be acyclic
- * * Each edge in the input graph must have an assigned 'minLen' attribute
- */
-function initRank(g) {
- var sorted = topsort(g);
-
- sorted.forEach(function(u) {
- var inEdges = g.inEdges(u);
- if (inEdges.length === 0) {
- g.node(u).rank = 0;
- return;
- }
-
- var minLens = inEdges.map(function(e) {
- return g.node(g.source(e)).rank + g.edge(e).minLen;
- });
- g.node(u).rank = util.max(minLens);
- });
-}
-
-},{"../util":27,"graphlib":29}],25:[function(require,module,exports){
-'use strict';
-
-module.exports = {
- slack: slack
-};
-
-/*
- * A helper to calculate the slack between two nodes (`u` and `v`) given a
- * `minLen` constraint. The slack represents how much the distance between `u`
- * and `v` could shrink while maintaining the `minLen` constraint. If the value
- * is negative then the constraint is currently violated.
- *
- This function requires that `u` and `v` are in `graph` and they both have a
- `rank` attribute.
- */
-function slack(graph, u, v, minLen) {
- return Math.abs(graph.node(u).rank - graph.node(v).rank) - minLen;
-}
-
-},{}],26:[function(require,module,exports){
-'use strict';
-
-var util = require('../util'),
- rankUtil = require('./rankUtil');
-
-module.exports = simplex;
-
-function simplex(graph, spanningTree) {
- // The network simplex algorithm repeatedly replaces edges of
- // the spanning tree with negative cut values until no such
- // edge exists.
- initCutValues(graph, spanningTree);
- while (true) {
- var e = leaveEdge(spanningTree);
- if (e === null) break;
- var f = enterEdge(graph, spanningTree, e);
- exchange(graph, spanningTree, e, f);
- }
-}
-
-/*
- * Set the cut values of edges in the spanning tree by a depth-first
- * postorder traversal. The cut value corresponds to the cost, in
- * terms of a ranking's edge length sum, of lengthening an edge.
- * Negative cut values typically indicate edges that would yield a
- * smaller edge length sum if they were lengthened.
- */
-function initCutValues(graph, spanningTree) {
- computeLowLim(spanningTree);
-
- spanningTree.eachEdge(function(id, u, v, treeValue) {
- treeValue.cutValue = 0;
- });
-
- // Propagate cut values up the tree.
- function dfs(n) {
- var children = spanningTree.successors(n);
- for (var c in children) {
- var child = children[c];
- dfs(child);
- }
- if (n !== spanningTree.graph().root) {
- setCutValue(graph, spanningTree, n);
- }
- }
- dfs(spanningTree.graph().root);
-}
-
-/*
- * Perform a DFS postorder traversal, labeling each node v with
- * its traversal order 'lim(v)' and the minimum traversal number
- * of any of its descendants 'low(v)'. This provides an efficient
- * way to test whether u is an ancestor of v since
- * low(u) <= lim(v) <= lim(u) if and only if u is an ancestor.
- */
-function computeLowLim(tree) {
- var postOrderNum = 0;
-
- function dfs(n) {
- var children = tree.successors(n);
- var low = postOrderNum;
- for (var c in children) {
- var child = children[c];
- dfs(child);
- low = Math.min(low, tree.node(child).low);
- }
- tree.node(n).low = low;
- tree.node(n).lim = postOrderNum++;
- }
-
- dfs(tree.graph().root);
-}
-
-/*
- * To compute the cut value of the edge parent -> child, we consider
- * it and any other graph edges to or from the child.
- * parent
- * |
- * child
- * / \
- * u v
- */
-function setCutValue(graph, tree, child) {
- var parentEdge = tree.inEdges(child)[0];
-
- // List of child's children in the spanning tree.
- var grandchildren = [];
- var grandchildEdges = tree.outEdges(child);
- for (var gce in grandchildEdges) {
- grandchildren.push(tree.target(grandchildEdges[gce]));
- }
-
- var cutValue = 0;
-
- // TODO: Replace unit increment/decrement with edge weights.
- var E = 0; // Edges from child to grandchild's subtree.
- var F = 0; // Edges to child from grandchild's subtree.
- var G = 0; // Edges from child to nodes outside of child's subtree.
- var H = 0; // Edges from nodes outside of child's subtree to child.
-
- // Consider all graph edges from child.
- var outEdges = graph.outEdges(child);
- var gc;
- for (var oe in outEdges) {
- var succ = graph.target(outEdges[oe]);
- for (gc in grandchildren) {
- if (inSubtree(tree, succ, grandchildren[gc])) {
- E++;
- }
- }
- if (!inSubtree(tree, succ, child)) {
- G++;
- }
- }
-
- // Consider all graph edges to child.
- var inEdges = graph.inEdges(child);
- for (var ie in inEdges) {
- var pred = graph.source(inEdges[ie]);
- for (gc in grandchildren) {
- if (inSubtree(tree, pred, grandchildren[gc])) {
- F++;
- }
- }
- if (!inSubtree(tree, pred, child)) {
- H++;
- }
- }
-
- // Contributions depend on the alignment of the parent -> child edge
- // and the child -> u or v edges.
- var grandchildCutSum = 0;
- for (gc in grandchildren) {
- var cv = tree.edge(grandchildEdges[gc]).cutValue;
- if (!tree.edge(grandchildEdges[gc]).reversed) {
- grandchildCutSum += cv;
- } else {
- grandchildCutSum -= cv;
- }
- }
-
- if (!tree.edge(parentEdge).reversed) {
- cutValue += grandchildCutSum - E + F - G + H;
- } else {
- cutValue -= grandchildCutSum - E + F - G + H;
- }
-
- tree.edge(parentEdge).cutValue = cutValue;
-}
-
-/*
- * Return whether n is a node in the subtree with the given
- * root.
- */
-function inSubtree(tree, n, root) {
- return (tree.node(root).low <= tree.node(n).lim &&
- tree.node(n).lim <= tree.node(root).lim);
-}
-
-/*
- * Return an edge from the tree with a negative cut value, or null if there
- * is none.
- */
-function leaveEdge(tree) {
- var edges = tree.edges();
- for (var n in edges) {
- var e = edges[n];
- var treeValue = tree.edge(e);
- if (treeValue.cutValue < 0) {
- return e;
- }
- }
- return null;
-}
-
-/*
- * The edge e should be an edge in the tree, with an underlying edge
- * in the graph, with a negative cut value. Of the two nodes incident
- * on the edge, take the lower one. enterEdge returns an edge with
- * minimum slack going from outside of that node's subtree to inside
- * of that node's subtree.
- */
-function enterEdge(graph, tree, e) {
- var source = tree.source(e);
- var target = tree.target(e);
- var lower = tree.node(target).lim < tree.node(source).lim ? target : source;
-
- // Is the tree edge aligned with the graph edge?
- var aligned = !tree.edge(e).reversed;
-
- var minSlack = Number.POSITIVE_INFINITY;
- var minSlackEdge;
- if (aligned) {
- graph.eachEdge(function(id, u, v, value) {
- if (id !== e && inSubtree(tree, u, lower) && !inSubtree(tree, v, lower)) {
- var slack = rankUtil.slack(graph, u, v, value.minLen);
- if (slack < minSlack) {
- minSlack = slack;
- minSlackEdge = id;
- }
- }
- });
- } else {
- graph.eachEdge(function(id, u, v, value) {
- if (id !== e && !inSubtree(tree, u, lower) && inSubtree(tree, v, lower)) {
- var slack = rankUtil.slack(graph, u, v, value.minLen);
- if (slack < minSlack) {
- minSlack = slack;
- minSlackEdge = id;
- }
- }
- });
- }
-
- if (minSlackEdge === undefined) {
- var outside = [];
- var inside = [];
- graph.eachNode(function(id) {
- if (!inSubtree(tree, id, lower)) {
- outside.push(id);
- } else {
- inside.push(id);
- }
- });
- throw new Error('No edge found from outside of tree to inside');
- }
-
- return minSlackEdge;
-}
-
-/*
- * Replace edge e with edge f in the tree, recalculating the tree root,
- * the nodes' low and lim properties and the edges' cut values.
- */
-function exchange(graph, tree, e, f) {
- tree.delEdge(e);
- var source = graph.source(f);
- var target = graph.target(f);
-
- // Redirect edges so that target is the root of its subtree.
- function redirect(v) {
- var edges = tree.inEdges(v);
- for (var i in edges) {
- var e = edges[i];
- var u = tree.source(e);
- var value = tree.edge(e);
- redirect(u);
- tree.delEdge(e);
- value.reversed = !value.reversed;
- tree.addEdge(e, v, u, value);
- }
- }
-
- redirect(target);
-
- var root = source;
- var edges = tree.inEdges(root);
- while (edges.length > 0) {
- root = tree.source(edges[0]);
- edges = tree.inEdges(root);
- }
-
- tree.graph().root = root;
... 22605 lines suppressed ...