├── .gitignore ├── LICENSE ├── README.md ├── dispatch ├── README.md ├── pom.xml └── src │ └── main │ ├── assembly │ └── META-INF │ │ ├── LICENSE │ │ └── NOTICE │ ├── java │ └── org │ │ └── apache │ │ └── qpid │ │ └── dispatch │ │ └── PluginContextListener.java │ ├── resources │ ├── WEB-INF │ │ └── web.xml │ └── log4j.properties │ └── webapp │ ├── index.html │ └── plugin │ ├── css │ ├── brokers.ttf │ ├── dispatch.css │ ├── font-awesome.min.css │ ├── jquery-ui.css │ ├── jquery.tipsy.css │ └── plugin.css │ ├── html │ ├── qdrCharts.html │ ├── qdrConnect.html │ ├── qdrLayout.html │ ├── qdrList.html │ ├── qdrOverview.html │ ├── qdrSchema.html │ ├── qdrTopology.html │ ├── tmplListChart.html │ ├── tmplListTree.html │ └── tmplOverviewTree.html │ ├── js │ ├── dispatchPlugin.js │ ├── navbar.js │ ├── qdrChartService.js │ ├── qdrCharts.js │ ├── qdrList.js │ ├── qdrListChart.js │ ├── qdrNewNode.js │ ├── qdrOverview.js │ ├── qdrOverviewLogsController.js │ ├── qdrSchema.js │ ├── qdrService.js │ ├── qdrSettings.js │ └── qdrTopology.js │ └── lib │ ├── FileSaver.min.js │ ├── d3.min.js │ ├── jquery.tipsy.js │ ├── rhea-min.js │ ├── tooltipsy.min.js │ └── zd3-queue.min.js └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HawtIO plugins for [ActiveMQ Artemis](artemis) and [Qpid dispatch router](dispatch). -------------------------------------------------------------------------------- /dispatch/README.md: -------------------------------------------------------------------------------- 1 | # hawtio dispatch router plugin 2 | 3 | dispatch-hawtio-console.war is a standalone [hawtio](http://hawt.io/) plugin that can be deployed in a server alongside the main hawtio-web application. 4 | 5 | The project creates a war file that can be deployed in various application services and is also OSGi-ified so it deploys nicely into Apache Karaf. 6 | 7 | ## Docker 8 | 9 | The fastest way to use the console is to run the [docker image](https://hub.docker.com/r/ernieallen/dispatch-console/). Follow the installation/run instruction on that page. 10 | 11 | ## Building 12 | The dispatch-hawtio-console.war file is pre-built and can be installed alongside the hawtio.war on any system with a modern java installation. If you want to build the dispatch-hawtio-console.war from source: 13 | 14 | - do a maven build of dispatch 15 | 16 | $ cd console/hawtio 17 | 18 | $ mvn clean install 19 | 20 | The dispatch-hawtio-console--SNAPSHOT.war file should now be in the target directory. 21 | 22 | ## Apache Tomcat installation 23 | 24 | Copy the dispatch-hawtio-console--SNAPSHOT.war file as the following name 25 | 26 | dispatch-hawtio-console.war 27 | 28 | to the deploy directory of Apache Tomcat or similar Java web container. Ensure the hawtio.war file is present in the same directory. Point a browser at http://\/hawtio 29 | Dispatch Router should be available as a tab in the console. 30 | 31 | ## Connecting to a router 32 | 33 | On the Dispatch Router's console page, select the Connect sub tab. Enter the address of a dispatch router. Enter the port of a websockets to tcp proxy and click the Connect button. 34 | 35 | ### Websockts to tcp proxy 36 | 37 | The console communicates to a router using websockets. 38 | The router listens for tcp. Therefore a websockts/tcp proxy is required. 39 | 40 | A popular python based proxy is [websockify](https://github.com/kanaka/websockify). To use it: 41 | 42 | $ yum install python-websockify 43 | 44 | #### Manually running a python websockets/tcp proxy 45 | 46 | $ websockify 5673 0.0.0.0:20009 & 47 | 48 | In the above, websockify is listening for ws traffic on port 5673 and will proxy it to 0.0.0.0:20009. One of the routers will need a listener on the proxied port. An example router .conf file entry is: 49 | 50 | listener { 51 | name: ProxyListener 52 | role: normal 53 | host: 0.0.0.0 54 | port: 20009 55 | saslMechanisms: ANONYMOUS 56 | } 57 | 58 | #### Automatically running a proxy with a router 59 | 60 | You can automatically start the proxy program when a router starts. Add the listener above and the following console section to a router's config file. 61 | 62 | console { 63 | listener: ProxyListener 64 | proxy: wobsockify 65 | args: $host:5673 $host:$port 66 | } 67 | 68 | -------------------------------------------------------------------------------- /dispatch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | org.jboss.rh-messaging 20 | messaging-hawtio-plugins 21 | 1.0.4.CR1 22 | 23 | 4.0.0 24 | 25 | org.jboss.rh-messaging 26 | dispatch-hawtio-console 27 | 1.0.4.CR1 28 | war 29 | 30 | 31 | Qpid Dispatch hawtio console plugin 32 | 33 | 34 | 35 | 37 | 39 | /${artifactId} 40 | 41 | 43 | ${project.artifactId} 44 | 45 | 47 | 48 | 49 | 50 | 1.7 51 | 2.3.7 52 | 53 | ${project.basedir}/target 54 | ${project.artifactId}-${project.version} 55 | ${buildDirectory}/${webapp-dir} 56 | 57 | 59 | 60 | javax.servlet, 61 | *;resolution:=optional 62 | 63 | 64 | 1.7 65 | 1.7 66 | 67 | 68 | 69 | 70 | 73 | 74 | io.hawt 75 | hawtio-plugin-mbean 76 | ${hawtio-version} 77 | 78 | 79 | 80 | 81 | org.slf4j 82 | slf4j-api 83 | ${slf4j-version} 84 | provided 85 | 86 | 87 | org.slf4j 88 | slf4j-log4j12 89 | ${slf4j-version} 90 | provided 91 | 92 | 93 | log4j 94 | log4j 95 | ${log4j-version} 96 | 97 | 98 | 99 | 100 | org.apache.geronimo.specs 101 | geronimo-servlet_2.5_spec 102 | ${geronimo-servlet-2-5-spec-version} 103 | provided 104 | 105 | 106 | 107 | 108 | 109 | 110 | ${buildDirectory} 111 | 113 | 114 | 115 | src/main/resources 116 | true 117 | 118 | **/*.xml 119 | 120 | 121 | 122 | 123 | 124 | 125 | 131 | 132 | 133 | maven-antrun-plugin 134 | ${maven-antrun-plugin-version} 135 | 136 | 137 | 138 | 142 | generate-sources 143 | generate-sources 144 | 145 | run 146 | 147 | 148 | 149 | Building plugin javascript file list 150 | 152 | 153 | 154 | 155 | 158 | 159 | 160 | 161 | Files: ${plugin-scripts} 162 | 163 | Copying log4j.properties 164 | 165 | 166 | 167 | 170 | true 171 | 172 | 173 | 174 | 175 | 176 | 177 | maven-resources-plugin 178 | 179 | 180 | 184 | copy-resources 185 | generate-sources 186 | 187 | resources 188 | 189 | 190 | 191 | 192 | 193 | 196 | 197 | org.apache.felix 198 | maven-bundle-plugin 199 | ${maven-bundle-plugin-version} 200 | 201 | 202 | bundle-manifest 203 | process-classes 204 | 205 | manifest 206 | 207 | 208 | 209 | 210 | ${webapp-outdir}/META-INF 211 | 212 | jar 213 | bundle 214 | war 215 | 216 | 217 | ${plugin-context} 218 | ${plugin-context} 219 | 220 | WEB-INF/lib 221 | *;scope=compile|runtime 222 | true 223 | 224 | ${dispatch.osgi.export} 225 | ${dispatch.osgi.import} 226 | ${dispatch.osgi.dynamic} 227 | ${dispatch.osgi.private.pkg} 228 | 229 | .,WEB-INF/classes 230 | 231 | ${project.description} 232 | ${project.groupId}.${project.artifactId} 233 | HawtIO 234 | ${project.version} 235 | 236 | 237 | 238 | 239 | 243 | 244 | org.apache.maven.plugins 245 | maven-war-plugin 246 | 247 | @{artifactId}@-@{baseVersion}@@{dashClassifier?}@.@{extension}@ 248 | **/classes/OSGI-INF/** 249 | false 250 | 251 | ${webapp-outdir}/META-INF/MANIFEST.MF 252 | 253 | 254 | 255 | true 256 | src/main/resources 257 | 258 | **/*.* 259 | 260 | 261 | 262 | META-INF 263 | ${basedir}/src/main/assembly/META-INF 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 276 | 277 | apache-release 278 | 279 | 280 | 281 | maven-assembly-plugin 282 | 283 | 284 | source-release-assembly 285 | package 286 | 287 | single 288 | 289 | 290 | true 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | -------------------------------------------------------------------------------- /dispatch/src/main/assembly/META-INF/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | 206 | ############################################### 207 | # Third Party Dependency Licensing Information: 208 | ############################################### 209 | 210 | This product bundles SLF4J-API and SLF4J-LOG4J, which are distributed under 211 | the MIT licence: 212 | 213 | Copyright (c) 2004-2013 QOS.ch 214 | All rights reserved. 215 | 216 | Permission is hereby granted, free of charge, to any person obtaining 217 | a copy of this software and associated documentation files (the 218 | "Software"), to deal in the Software without restriction, including 219 | without limitation the rights to use, copy, modify, merge, publish, 220 | distribute, sublicense, and/or sell copies of the Software, and to 221 | permit persons to whom the Software is furnished to do so, subject to 222 | the following conditions: 223 | 224 | The above copyright notice and this permission notice shall be 225 | included in all copies or substantial portions of the Software. 226 | 227 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 228 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 229 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 230 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 231 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 232 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 233 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 234 | 235 | -------------------------------------------------------------------------------- /dispatch/src/main/assembly/META-INF/NOTICE: -------------------------------------------------------------------------------- 1 | Qpid Dispatch hawtio console 2 | Copyright 2016 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | -------------------------------------------------------------------------------- /dispatch/src/main/java/org/apache/qpid/dispatch/PluginContextListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Licensed to the Apache Software Foundation (ASF) under one 4 | * or more contributor license agreements. See the NOTICE file 5 | * distributed with this work for additional information 6 | * regarding copyright ownership. The ASF licenses this file 7 | * to you under the Apache License, Version 2.0 (the 8 | * "License"); you may not use this file except in compliance 9 | * with the License. You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, 14 | * software distributed under the License is distributed on an 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | * KIND, either express or implied. See the License for the 17 | * specific language governing permissions and limitations 18 | * under the License. 19 | * 20 | */ 21 | package org.apache.qpid.dispatch; 22 | 23 | import io.hawt.web.plugin.HawtioPlugin; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import javax.servlet.ServletContext; 28 | import javax.servlet.ServletContextEvent; 29 | import javax.servlet.ServletContextListener; 30 | 31 | public class PluginContextListener implements ServletContextListener { 32 | 33 | private static final Logger LOG = LoggerFactory.getLogger(PluginContextListener.class); 34 | 35 | HawtioPlugin plugin = null; 36 | 37 | @Override 38 | public void contextInitialized(ServletContextEvent servletContextEvent) { 39 | 40 | ServletContext context = servletContextEvent.getServletContext(); 41 | 42 | plugin = new HawtioPlugin(); 43 | plugin.setContext((String)context.getInitParameter("plugin-context")); 44 | plugin.setName(context.getInitParameter("plugin-name")); 45 | plugin.setScripts(context.getInitParameter("plugin-scripts")); 46 | plugin.setDomain(null); 47 | plugin.init(); 48 | 49 | LOG.info("Initialized {} plugin", plugin.getName()); 50 | } 51 | 52 | @Override 53 | public void contextDestroyed(ServletContextEvent servletContextEvent) { 54 | plugin.destroy(); 55 | LOG.info("Destroyed {} plugin", plugin.getName()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /dispatch/src/main/resources/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | Qpid Dispatch Router console hawt.io plugin 23 | Dispatch Router console 24 | 25 | 26 | Plugin's path on the server 27 | plugin-context 28 | ${plugin-context} 29 | 30 | 31 | 32 | Plugin's path on the server 33 | plugin-name 34 | ${project.artifactId} 35 | 36 | 37 | 38 | Plugin's path on the server 39 | plugin-domain 40 | ${plugin-domain} 41 | 42 | 43 | 44 | Plugin's path on the server 45 | plugin-scripts 46 | ${plugin-scripts} 47 | 48 | 49 | 50 | org.apache.qpid.dispatch.PluginContextListener 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /dispatch/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | log4j.rootLogger=INFO, console 21 | 22 | log4j.appender.console=org.apache.log4j.ConsoleAppender 23 | log4j.appender.console.layout=org.apache.log4j.PatternLayout 24 | log4j.appender.console.layout.ConversionPattern=%-5p | %t | %m%n 25 | 26 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | 22 |

Welcome to Qpid Dispatch Router's hawtio console.

23 | 24 |

For more info about this console, please see The Apache Qpid Dispatch Router Book

25 | 26 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/css/brokers.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rh-messaging/artemis-hawtio/ceb1ed0aae1c70db944913ca5fbabd18c53441d1/dispatch/src/main/webapp/plugin/css/brokers.ttf -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/css/dispatch.css: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | 20 | svg { 21 | background-color: transparent; 22 | cursor: default; 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | -ms-user-select: none; 26 | -o-user-select: none; 27 | user-select: none; 28 | } 29 | 30 | svg:not(.active):not(.ctrl) { 31 | cursor: crosshair; 32 | } 33 | #end-arrow-selected, #start-arrow-selected { 34 | stroke: #33F; 35 | fill: #33F; 36 | } 37 | path.link.selected { 38 | stroke-dasharray: 10,2; 39 | stroke: #33F !important; 40 | } 41 | 42 | path.link { 43 | fill: #000; 44 | stroke: #000; 45 | stroke-width: 4px; 46 | cursor: default; 47 | } 48 | 49 | svg:not(.active):not(.ctrl) path.link { 50 | cursor: pointer; 51 | } 52 | 53 | path.link.small { 54 | stroke-width: 2.5; 55 | stroke: darkgray; 56 | } 57 | path.link.highlighted { 58 | stroke: #6F6 !important; 59 | } 60 | marker#start-arrow-highlighted, 61 | marker#end-arrow-highlighted { 62 | fill: #6F6; 63 | } 64 | marker#start-arrow-small, 65 | marker#end-arrow-small { 66 | fill: darkgray; 67 | } 68 | 69 | path.link.dragline { 70 | pointer-events: none; 71 | } 72 | 73 | path.link.hidden { 74 | stroke-width: 0; 75 | } 76 | 77 | circle.node { 78 | stroke-width: 1.5px; 79 | cursor: pointer; 80 | stroke: darkgray; 81 | } 82 | 83 | circle.node.reflexive { 84 | stroke: #F00 !important; 85 | stroke-width: 2.5px; 86 | } 87 | circle.node.selected { 88 | stroke: #6F6 !important; 89 | stroke-width: 2px; 90 | fill: #e0e0ff !important; 91 | } 92 | circle.node.highlighted { 93 | stroke: #6F6; 94 | } 95 | circle.node.inter-router { 96 | fill: #EAEAEA; 97 | } 98 | circle.node.normal.in { 99 | fill: #F0F000; 100 | } 101 | circle.node.normal.out { 102 | fill: #C0F0C0; 103 | } 104 | circle.node.on-demand { 105 | fill: #C0FFC0; 106 | } 107 | circle.node.on-demand.artemis { 108 | fill: #FCC; 109 | /*opacity: 0.2; */ 110 | } 111 | 112 | circle.node.fixed { 113 | stroke-dasharray: 10,2; 114 | } 115 | text { 116 | font: 12px sans-serif; 117 | pointer-events: none; 118 | /*font-family: monospace;*/ 119 | 120 | } 121 | 122 | .tooltipsy 123 | { 124 | padding: 10px; 125 | /* max-width: 320px;*/ 126 | color: #303030; 127 | background-color: #fcfcfe; 128 | border: 1px solid #deca7e; 129 | border-radius: 5px; 130 | } 131 | 132 | .tiptable { 133 | 134 | } 135 | .tiptable tr { 136 | border-bottom: 1px solid #ccc; 137 | } 138 | 139 | .tiptable tr:last-child { 140 | border-bottom: 0px; 141 | } 142 | 143 | .tiptable tr:nth-child(even) { 144 | background: #fcfcfe; 145 | } 146 | .tiptable tr:nth-child(odd) { 147 | background: #FFF 148 | } 149 | 150 | text.id { 151 | text-anchor: middle; 152 | font-weight: bold; 153 | } 154 | 155 | text.label { 156 | text-anchor: start; 157 | font-weight: bold; 158 | } 159 | 160 | .row-fluid.tertiary { 161 | position: relative; 162 | left: 20px; 163 | } 164 | 165 | .row-fluid.tertiary.left { 166 | float: left; 167 | } 168 | 169 | .row-fluid.tertiary.panel { 170 | width: 410px; 171 | /*height: 100%; */ 172 | } 173 | 174 | div#topologyForm .ngViewport, div#topologyForm .gridStyle { 175 | height: auto !important; 176 | min-height: initial !important; 177 | overflow: initial; 178 | } 179 | 180 | div#multiple_details, div#link_details { 181 | height: 300px; 182 | width: 700px; 183 | display: none; 184 | padding: 0.5em; 185 | border: 1px solid; 186 | position: absolute; 187 | background-color: white; 188 | max-height: 330px !important; 189 | overflow: hidden; 190 | } 191 | div#multiple_details div.ngRow.selected { 192 | background-color: #c9dde1 !important; 193 | } 194 | 195 | div.grid-values { 196 | text-align: right; 197 | } 198 | 199 | div.grid-values.ngCellText span { 200 | padding-right: 4px; 201 | } 202 | 203 | .panel-adjacent { 204 | margin-left: 430px; 205 | } 206 | 207 | #topologyForm.selected { 208 | border: 1px solid blue; 209 | } 210 | #topologyForm { 211 | border-right: 1px solid lightgray; 212 | border-bottom: 1px solid lightgray; 213 | /*border: 1px solid white;*/ 214 | padding: 2px; 215 | /* position: relative; */ 216 | /* top: -8px; */ 217 | } 218 | div.qdr-topology.pane.left .ngViewport { 219 | /* border: 1px solid lightgray; */ 220 | } 221 | 222 | #topologyForm > div { 223 | width:396px; 224 | } 225 | 226 | /* globe */ 227 | .land { 228 | fill: #999; 229 | stroke-opacity: 1; 230 | } 231 | 232 | .graticule { 233 | fill: none; 234 | stroke: black; 235 | stroke-width:.5; 236 | opacity:.1; 237 | } 238 | 239 | .labels { 240 | font: 18px sans-serif; 241 | fill: black; 242 | opacity: .85; 243 | text-anchor: middle; 244 | } 245 | 246 | .noclicks { pointer-events:none; } 247 | 248 | .point { opacity:.6; } 249 | 250 | .arcs { 251 | opacity:.7; 252 | stroke: darkgreen; 253 | stroke-width: 3; 254 | } 255 | .flyers { 256 | stroke-width:1; 257 | opacity: 0; 258 | stroke: darkred; 259 | } 260 | .arc, .flyer { 261 | stroke-linejoin: round; 262 | fill:none; 263 | } 264 | .arc { } 265 | .arc:hover { 266 | stroke: darkred; 267 | } 268 | .flyer { } 269 | .flyer:hover { 270 | stroke: darkgreen; 271 | } 272 | .arc.inter-router { 273 | stroke: darkblue; 274 | } 275 | 276 | #addNodeForm { 277 | padding: 1em; 278 | } 279 | 280 | 281 | li.currentStep { 282 | font-weight: bold; 283 | } 284 | 285 | .qdrTopology div.panel { 286 | position: absolute; 287 | } 288 | /* 289 | .ui-dialog-titlebar { 290 | border: 0; 291 | background: transparent; 292 | } 293 | */ 294 | 295 | /* 296 | .ui-tabs.ui-tabs-vertical { 297 | padding: 0; 298 | width: 48em; 299 | } 300 | .ui-tabs.ui-tabs-vertical .ui-widget-header { 301 | border: none; 302 | } 303 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav { 304 | float: left; 305 | width: 10em; 306 | background: #CCC; 307 | border-radius: 4px 0 0 4px; 308 | border-right: 1px solid gray; 309 | } 310 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li { 311 | clear: left; 312 | width: 100%; 313 | margin: 0.1em 0; 314 | border: 1px solid gray; 315 | border-width: 1px 0 1px 1px; 316 | border-radius: 4px 0 0 4px; 317 | overflow: hidden; 318 | position: relative; 319 | right: -2px; 320 | z-index: 2; 321 | } 322 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li a { 323 | display: block; 324 | width: 100%; 325 | padding: 0.1em 1em; 326 | } 327 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li a:hover { 328 | cursor: pointer; 329 | } 330 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active { 331 | margin-bottom: 0.2em; 332 | padding-bottom: 0; 333 | border-right: 1px solid white; 334 | } 335 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li:last-child { 336 | margin-bottom: 10px; 337 | } 338 | .ui-tabs.ui-tabs-vertical .ui-tabs-panel { 339 | float: left; 340 | width: 34em; 341 | border-left: 1px solid gray; 342 | border-radius: 0; 343 | position: relative; 344 | left: -1px; 345 | } 346 | 347 | .ui-tabs .ui-tabs-nav li.ui-tabs-selected { 348 | right: -3px !important; 349 | } 350 | 351 | .ui-tabs li i.ui-icon { 352 | display: inline-block; 353 | } 354 | */ 355 | .ui-tabs .ui-tabs-panel { 356 | /* padding-top: 0 !important; */ 357 | } 358 | 359 | .ui-widget-content fieldset { 360 | float: left; 361 | padding: 0 1em 0 0; 362 | } 363 | 364 | .entity-description { 365 | color: #960; 366 | font-size: 90%; 367 | } 368 | 369 | .attr-description { 370 | padding-top: 1.5em; 371 | float: right; 372 | width: 17em; 373 | } 374 | .attr-annotations { 375 | padding-top: 2.5em; 376 | clear: both; 377 | } 378 | .attr-annotations > span { 379 | padding-top: 0.5em; 380 | border-top: 1px dashed darkgray; 381 | display: block; 382 | } 383 | 384 | .attr-type { 385 | color: #990; 386 | font-size: 85%; 387 | } 388 | .attr-required { 389 | color: red; 390 | font-size: 85%; 391 | } 392 | .attr-unique { 393 | color: green; 394 | font-size: 85%; 395 | } 396 | 397 | #tabs.nodeEntities { 398 | border: 0; 399 | } 400 | 401 | #tabs ul.nodeTabs { 402 | background: #fff; 403 | } 404 | 405 | #tabs #Container { 406 | border-left: 1px solid #aaa; 407 | } 408 | 409 | #tabs.ui-tabs .ui-tabs-nav li { 410 | border-bottom: 1px solid #aaa !important; 411 | } 412 | 413 | .entity-fields { 414 | /* height: 400px; */ 415 | overflow-y: scroll; 416 | overflow-x: hidden; 417 | } 418 | 419 | div.boolean label:first-child { 420 | float: left; 421 | margin-right: 1em; 422 | } 423 | div.boolean { 424 | padding-bottom: 1em; 425 | } 426 | 427 | .entity-fields label { 428 | font-weight: 600; 429 | margin-top: 0.5em; 430 | display: inline; 431 | } 432 | 433 | .aggregate { 434 | text-align: right; 435 | } 436 | 437 | .aggregate i { 438 | float: right; 439 | margin: 3px 3px 3px 8px; 440 | } 441 | 442 | .aggregate .hastip { 443 | padding: 5px; 444 | } 445 | 446 | .subTip .tipsy-inner { 447 | background-color: white; 448 | color: black; 449 | font-size: 1.3em; 450 | border: 1px solid black; 451 | } 452 | 453 | .subTip .tipsy-arrow-n { border-bottom-color: black; } 454 | .subTip .tipsy-arrow-s { border-top-color: black; } 455 | .subTip .tipsy-arrow-e { border-left-color: black; } 456 | .subTip .tipsy-arrow-w { border-right-color: black; } 457 | 458 | 459 | .contextMenu { 460 | display:none; 461 | position:absolute; 462 | left:30px; 463 | top:-30px; 464 | z-index:999; 465 | /* width:300px; */ 466 | } 467 | .contextMenu ul { 468 | width:300px; 469 | margin:0; 470 | padding-left:0; 471 | list-style:none; 472 | background:#fff; 473 | color:#333; 474 | font-weight: 600; 475 | /* -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; */ 476 | -moz-box-shadow:5px 5px 5px #ddd; -webkit-box-shadow:5px 5px 5px #999; box-shadow:5px 5px 5px #ddd; 477 | border: 1px solid #aaa; 478 | } 479 | .contextMenu ul li { 480 | padding:5px 10px; 481 | /* border-bottom: solid 1px #ccc; */ 482 | } 483 | .contextMenu ul li:hover { 484 | background:#4a90d9; color:#fff; 485 | } 486 | .contextMenu ul li:last-child { 487 | border:none; 488 | } 489 | 490 | .na { 491 | display: none; 492 | } 493 | .contextMenu ul li.new { 494 | display: block; 495 | } 496 | .contextMenu ul li.adding, .contextMenu ul li.adding + li { 497 | display: block; 498 | } 499 | .contextMenu ul li.force-display { 500 | display: block; 501 | } 502 | .contextMenu ul li.context-separator { 503 | background-color: lightgray; 504 | height: 1px; 505 | padding: 0; 506 | } 507 | 508 | .ui-tabs.ui-tabs-vertical .ui-tabs-nav li.separated { 509 | margin-top: 1em; 510 | } 511 | 512 | #crosssection { 513 | display: none; 514 | position: absolute; 515 | top: 200px; 516 | left: 600px; 517 | } 518 | 519 | .node circle { 520 | /* fill: rgb(31, 119, 180); 521 | fill-opacity: .25; */ 522 | fill: #cfe2f3; 523 | fill-opacity: .98; 524 | stroke: black; 525 | stroke-width: 3px; 526 | } 527 | 528 | circle.subcircle { 529 | stroke-width: 1px; 530 | /* stroke-dasharray: 2; */ 531 | fill-opacity: 0; 532 | stroke: darkgray; 533 | } 534 | 535 | .leaf circle { 536 | fill: #6fa8dc; 537 | fill-opacity: 0.95; 538 | stroke-width: 3px; 539 | } 540 | 541 | .leaf circle[title] { 542 | font-family: monospace; 543 | 544 | } 545 | 546 | #svg_legend { 547 | position: absolute; 548 | top: 110px; 549 | right: 0; 550 | border: 1px solid #ccc; 551 | border-radius: 5px; 552 | background-color: #fcfcfc; 553 | margin-right: 1.3em; 554 | padding: 1em; 555 | } 556 | 557 | #svg_legend svg { 558 | height: 235px; 559 | width: 180px; 560 | } 561 | 562 | #multiple_details div.gridStyle { 563 | /* height: 50em; */ 564 | min-height: 70px !important; 565 | height: auto !important; 566 | } 567 | 568 | #multiple_details .ngViewport { 569 | height: auto !important; 570 | } 571 | 572 | #multiple_details .gridCellButton button, #link_details .gridCellButton button { 573 | margin: .25em .4em; 574 | font-size: 12px; 575 | height: 2em; 576 | padding-top: .1em; 577 | } 578 | 579 | #linkFilter { 580 | display: none; 581 | padding: 0.5em; 582 | border: 1px solid grey; 583 | background-color: #F0F0F0; 584 | position: absolute; 585 | z-index: 100; 586 | right: 1em; 587 | } 588 | div.formLine label, div.formLine input { 589 | display: inline-block; 590 | padding: 0 8px; 591 | } 592 | 593 | span.filter-icon { 594 | padding-left: 1em; 595 | } 596 | 597 | button.filter-close { 598 | width: 15px; 599 | height: 20px; 600 | padding: 0; 601 | position: absolute; 602 | right: 4px; 603 | top: 4px; 604 | } 605 | 606 | div.filter-title h6 { 607 | margin: 0 0 0.5em 0; 608 | } 609 | 610 | .links button.btn-filter { 611 | padding: 0 1em 0 0; 612 | margin-left: 1em; 613 | font-size: 1em; 614 | } 615 | 616 | button.btn-filter { 617 | float: right; 618 | } 619 | span.dynatree-expanded button.btn-filter, 620 | a.dynatree-title:hover button.btn-filter { 621 | visibility: visible; 622 | } 623 | 624 | div.hdash-button a { 625 | color: white; 626 | } 627 | 628 | .linkDirIn { 629 | color: red; 630 | background-color: #f3f3f3; 631 | } 632 | 633 | .linkDirOut { 634 | color: blue; 635 | background-color: white; 636 | } 637 | 638 | div.topoGrid .ui-grid-viewport { 639 | overflow: hidden !important; 640 | } 641 | 642 | @-moz-document url-prefix() { 643 | .btn {padding: 2px 12px 8px !important;} 644 | #overview-controller .btn {padding: 4px 12px !important;} 645 | #overview-controller .btn.filter-close {padding: 0 !important;} 646 | } 647 | 648 | .ui-fancytree.fancytree-container { 649 | font-size: 14px; 650 | } 651 | 652 | .grid-title { 653 | background-color: #FAFAFA; 654 | background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); 655 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); 656 | background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); 657 | background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); 658 | background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); 659 | background-repeat: repeat-x; 660 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#F2F2F2', GradientType=0); 661 | border-bottom: 1px solid #d4d4d4; 662 | text-shadow: 0 1px 0 #FFFFFF; 663 | border-top-left-radius: 5px; 664 | border-top-right-radius: 5px; 665 | margin: 0 0 10px 0; 666 | padding-bottom: 4px; 667 | } 668 | 669 | .expand-collapse { 670 | float: right; 671 | margin-right: 0.5em; 672 | } 673 | 674 | .pane-viewport { 675 | top: 24px !important; 676 | } 677 | .dynatree-node.loading { 678 | position: initial; 679 | } 680 | 681 | .hideLeft { 682 | position: absolute; 683 | right: 0.5em; 684 | top: 1em; 685 | border: 0; 686 | } 687 | 688 | .showLeft { 689 | position: absolute; 690 | top: 1em; 691 | left: 0.5em; 692 | border: 0; 693 | } 694 | 695 | .newChart { 696 | float: right; 697 | } 698 | 699 | select.unique, input.unique { 700 | border: 2px solid blue; 701 | } 702 | select.required, input.required { 703 | border: 2px solid black; 704 | } 705 | 706 | .required-indicator { 707 | padding-left: 0.5em; 708 | font-size: 0.85em; 709 | vertical-align: super; 710 | } 711 | 712 | .required-indicator::before { 713 | content: '(required)' 714 | } 715 | 716 | .unique-indicator { 717 | padding-left: 0.5em; 718 | font-size: 0.85em; 719 | vertical-align: super; 720 | } 721 | 722 | .unique-indicator::before { 723 | content: '(must be unique if supplied)' 724 | } 725 | 726 | .unique-indicator -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/css/jquery.tipsy.css: -------------------------------------------------------------------------------- 1 | .tipsy { 2 | font-size: 10px; 3 | position: absolute; 4 | padding: 5px; 5 | word-wrap: break-word; 6 | z-index: 100000; 7 | } 8 | 9 | .tipsy-inner { 10 | background-color: #000; 11 | color: #FFF; 12 | max-width: 200px; 13 | padding: 5px 8px 4px 8px; 14 | text-align: center; 15 | } 16 | 17 | /* Rounded corners */ 18 | .tipsy-inner { 19 | border-radius: 3px; 20 | -moz-border-radius: 3px; 21 | -webkit-border-radius: 3px; 22 | } 23 | 24 | /* Uncomment for shadow */ 25 | /*.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }*/ 26 | 27 | .tipsy-arrow { 28 | position: absolute; 29 | width: 0; 30 | height: 0; 31 | line-height: 0; 32 | border: 5px dashed #000; 33 | } 34 | 35 | /* Rules to colour arrows */ 36 | .tipsy-arrow-n { 37 | border-bottom-color: #000; 38 | } 39 | 40 | .tipsy-arrow-s { 41 | border-top-color: #000; 42 | } 43 | 44 | .tipsy-arrow-e { 45 | border-left-color: #000; 46 | } 47 | 48 | .tipsy-arrow-w { 49 | border-right-color: #000; 50 | } 51 | 52 | .tipsy-n .tipsy-arrow { 53 | top: 0px; 54 | left: 50%; 55 | margin-left: -5px; 56 | border-bottom-style: solid; 57 | border-top: none; 58 | border-left-color: transparent; 59 | border-right-color: transparent; 60 | } 61 | 62 | .tipsy-nw .tipsy-arrow { 63 | top: 0; 64 | left: 10px; 65 | border-bottom-style: solid; 66 | border-top: none; 67 | border-left-color: transparent; 68 | border-right-color: transparent; 69 | } 70 | 71 | .tipsy-ne .tipsy-arrow { 72 | top: 0; 73 | right: 10px; 74 | border-bottom-style: solid; 75 | border-top: none; 76 | border-left-color: transparent; 77 | border-right-color: transparent; 78 | } 79 | 80 | .tipsy-s .tipsy-arrow { 81 | bottom: 0; 82 | left: 50%; 83 | margin-left: -5px; 84 | border-top-style: solid; 85 | border-bottom: none; 86 | border-left-color: transparent; 87 | border-right-color: transparent; 88 | } 89 | 90 | .tipsy-sw .tipsy-arrow { 91 | bottom: 0; 92 | left: 10px; 93 | border-top-style: solid; 94 | border-bottom: none; 95 | border-left-color: transparent; 96 | border-right-color: transparent; 97 | } 98 | 99 | .tipsy-se .tipsy-arrow { 100 | bottom: 0; 101 | right: 10px; 102 | border-top-style: solid; 103 | border-bottom: none; 104 | border-left-color: transparent; 105 | border-right-color: transparent; 106 | } 107 | 108 | .tipsy-e .tipsy-arrow { 109 | right: 0; 110 | top: 50%; 111 | margin-top: -5px; 112 | border-left-style: solid; 113 | border-right: none; 114 | border-top-color: transparent; 115 | border-bottom-color: transparent; 116 | } 117 | 118 | .tipsy-w .tipsy-arrow { 119 | left: 0; 120 | top: 50%; 121 | margin-top: -5px; 122 | border-right-style: solid; 123 | border-left: none; 124 | border-top-color: transparent; 125 | border-bottom-color: transparent; 126 | } 127 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/css/plugin.css: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | main-display > .span8 { 20 | height: 100%; 21 | position: relative; 22 | } 23 | 24 | ul.qdrListNodes > li > span { 25 | padding: 6px 20px; 6px; 6px; 26 | display: block; 27 | } 28 | 29 | .qdrList .gridStyle { 30 | width: 20em; 31 | margin-right: 0; 32 | float: left; 33 | } 34 | 35 | 36 | .qdrList div.gridDetails { 37 | width: auto; 38 | } 39 | 40 | div.gridDetails { 41 | margin-left: 1em; 42 | } 43 | 44 | .selectedItems { 45 | /* margin-left: 21em; */ 46 | } 47 | 48 | .qdrListPane { 49 | top: 110px; 50 | } 51 | 52 | .qdrListActions { 53 | width: auto; 54 | margin-left: 1em; 55 | } 56 | 57 | div.listAttrName { 58 | padding-top: 5px; 59 | } 60 | 61 | div.listAttrName i.icon-bar-chart { 62 | float: right; 63 | margin: 3px 5px; 64 | } 65 | 66 | div.listAttrName i.icon-bar-chart.active, div.hastip i.icon-bar-chart.active, li.haschart i { 67 | background-color: #AAFFAA; 68 | } 69 | 70 | div#main div ul.nav li a:not(.btn) { 71 | background: initial !important; 72 | } 73 | 74 | div#main div ul.nav li.active a { 75 | background-color: #f0f0ff !important; 76 | } 77 | 78 | div#main.qdr { 79 | margin-top: 56px !important; 80 | } 81 | 82 | div.charts-header { 83 | font-size: 1.2em; 84 | color: #666666; 85 | margin: 1em 0; 86 | } 87 | 88 | .selectedNode, .selectedAction, .selectedEntity { 89 | font-weight: 600; 90 | color: #606066; 91 | } 92 | 93 | .okButton { 94 | text-align: center; 95 | margin: 1em; 96 | } 97 | 98 | span.showChartsLink { 99 | border: 1px solid blue; 100 | padding: 1px 2px; 101 | } 102 | 103 | div.listGraphs p { 104 | margin: 1em 0 2em 2em; 105 | text-align: center; 106 | } 107 | 108 | div.centered { 109 | text-align: center; 110 | margin: 4em; 111 | } 112 | 113 | .modal-body.centered { 114 | margin: 0; 115 | } 116 | 117 | /* dialog */ 118 | div.aChart { 119 | height: 200px; 120 | width: 400px; 121 | margin: 1em; 122 | } 123 | 124 | /* dashboard */ 125 | div.aChart.hDash { 126 | /* width: 21em; */ 127 | /* height: 17em; */ 128 | width: 100%; 129 | height: 87%; 130 | margin: 0; 131 | padding: 0; 132 | 133 | } 134 | div.chartContainer { 135 | float: left; 136 | width: 100%; 137 | height: 100%; 138 | overflow: hidden; 139 | } 140 | 141 | /* the x and y axis lines */ 142 | .d3Chart g.axis path.domain { 143 | stroke-width: 1; 144 | stroke: black; 145 | } 146 | 147 | /* the line surrounding the area chart */ 148 | div.d3Chart path { 149 | /* stroke: black; */ 150 | stroke-width: 0; 151 | /* opacity: 0.5; */ 152 | } 153 | 154 | /* the line above the area chart */ 155 | /* the color gets overridden */ 156 | div.d3Chart path.line { 157 | stroke: steelblue; 158 | stroke-width: 1.5; 159 | fill: none; 160 | opacity: 1; 161 | } 162 | 163 | .mo-rect { 164 | fill: #ffffdd; 165 | stroke: #f0f0f0; 166 | stroke-width: 1; 167 | } 168 | 169 | .mo-guide { 170 | fill: none; 171 | stroke: #d0d0d0; 172 | stroke-width: 2; 173 | stroke-dasharray: 3,3; 174 | } 175 | 176 | div.d3Chart .title { 177 | text-decoration: underline; 178 | } 179 | 180 | 181 | .axis line, .axis path { 182 | fill: none; 183 | shape-rendering: crispEdges; 184 | stroke-width: 1; 185 | stroke: #000000; 186 | } 187 | 188 | .axis line { 189 | stroke: #C0C0C0; 190 | stroke-dasharray: 1,1; 191 | opacity: 0.5; 192 | } 193 | 194 | .y.axis text, .x.axis text, .focus text, div.d3Chart .title { 195 | font-size: 12px; 196 | } 197 | 198 | .y.axis path { 199 | stroke: #000; 200 | } 201 | 202 | .overlay { 203 | fill: none; 204 | pointer-events: all; 205 | } 206 | 207 | .focus circle { 208 | fill: none; 209 | stroke: steelblue; 210 | } 211 | .focus .fo-table { 212 | /* box-shadow: 2px 2px 3px #EEE; */ 213 | } 214 | 215 | div.d3Chart { 216 | padding: 1em 0; 217 | border: 1px solid #C0C0C0; 218 | } 219 | div.d3Chart.hDash { 220 | border: 0px; 221 | } 222 | 223 | div.d3Chart .axis path { 224 | display: inherit; 225 | } 226 | .c3-circle { 227 | display: none; 228 | } 229 | 230 | .fo-table { 231 | border: 1px solid darkgray; 232 | background-color: white; 233 | font-size: .85em; 234 | } 235 | 236 | .fo-table td { 237 | padding: 4px; 238 | border-left: 1px solid darkgray; 239 | } 240 | .fo-table tr.detail td { 241 | padding: 1px 4px; 242 | } 243 | .fo-title { 244 | color: white; 245 | background-color: darkgray; 246 | } 247 | 248 | .fo-table-legend { 249 | width: 8px; 250 | height: 8px; 251 | border: 1px solid black; 252 | margin: 0 4px; 253 | display: inline-block; 254 | } 255 | 256 | svg .legend { 257 | dominant-baseline: central; 258 | } 259 | 260 | div.chartContainer div.aChart { 261 | margin-top: 0.5em; 262 | } 263 | 264 | #list-controller .tree-header { 265 | position: absolute; 266 | height: auto; 267 | } 268 | 269 | #list-controller select { 270 | height: 25px; 271 | float: left; 272 | padding: 0; 273 | } 274 | 275 | 276 | div#main.qdr div ul.nav li.active a { 277 | background-color: #e0e0ff !important; 278 | color: #000000; 279 | } 280 | 281 | div#main.qdr .selected, .box.selected { 282 | color: #000000; 283 | text-shadow: none; 284 | } 285 | 286 | /* the selected node on the list page */ 287 | div.qdrList li.active, ul.qdrListNodes li.active { 288 | background-color: #e0e0ff; 289 | } 290 | 291 | div.qdr-attributes span.dynatree-selected a { 292 | background-color: #e0e0ff; 293 | } 294 | div.qdr-attributes.pane, div.qdr-topology.pane { 295 | position: absolute; 296 | margin-left: 10px; 297 | } 298 | div.qdr-overview.pane { 299 | position: absolute; 300 | } 301 | div.qdr-topology.pane.left { 302 | width: auto; 303 | /*border-right: 1px solid lightgray; */ 304 | } 305 | 306 | /* the selected row in the name table */ 307 | div#main.qdr div.qdrList div.selected { 308 | background-color: #e0e0ff !important; 309 | } 310 | 311 | #dialogChart, #dialogEditChart { 312 | height: 200px; 313 | } 314 | 315 | .chartOptions .modal-body { 316 | overflow-y: initial; 317 | } 318 | 319 | div.qdrCharts p.chartLabels button { 320 | float: right; 321 | } 322 | 323 | div.qdrCharts p.chartLabels { 324 | padding-right: 1em;; 325 | } 326 | 327 | p.dialogHeader { 328 | text-align: center; 329 | } 330 | 331 | p.dialogHeader input { 332 | margin-top: 10px; 333 | width: 480px; 334 | } 335 | 336 | .ui-slider-tick { 337 | position: absolute; 338 | background-color: #666; 339 | width: 2px; 340 | height: 8px; 341 | top: 12px; 342 | z-index: -1; 343 | } 344 | 345 | label.rateGroup { 346 | float: left; 347 | } 348 | 349 | div.chartOptions div.dlg-slider { 350 | float: left; 351 | width: 28em; 352 | font-size: 14px; 353 | } 354 | 355 | div.chartOptions div.duration { 356 | width: 35em !important; 357 | } 358 | 359 | div.chartOptions .slider { 360 | margin-top: 1em; 361 | margin-bottom: 1em; 362 | } 363 | 364 | input[type="radio"] { 365 | margin-top: 0 !important; 366 | } 367 | 368 | div.chartOptions legend { 369 | font-size: 1.2em; 370 | font-weight: bold; 371 | margin-bottom: 10px; 372 | } 373 | 374 | div.chartOptions tab > div { 375 | margin-left: 1em; 376 | } 377 | 378 | div.chartOptions span.minicolors-swatch { 379 | width: 14px; 380 | height: 14px; 381 | } 382 | 383 | .minicolors-input { 384 | width: 4em; 385 | padding: 0 0 0 24px !important; 386 | } 387 | 388 | div.colorPicker div.colorText { 389 | display: inline-block; 390 | width: 10em; 391 | } 392 | div.colorPicker div:nth-of-type(1), /* first span under div.colorPicker */ 393 | div.minicolors{ 394 | float:left; 395 | margin-right: 0.5em; 396 | } 397 | 398 | div.chartOptions p.sep { 399 | height: 1em; 400 | } 401 | 402 | ul.nav-tabs { 403 | border-bottom: 1px solid #ddd !important; 404 | } 405 | 406 | .chartOptions ul.nav-tabs { 407 | margin-bottom: 0px !important; 408 | } 409 | 410 | div.tabbable div.tab-content { 411 | overflow: visible; 412 | } 413 | 414 | div.tabbable ul.nav-tabs > .active > a { 415 | background-color: #f8f8f8; 416 | border: 1px solid #ddd; 417 | border-bottom-color: transparent; 418 | } 419 | 420 | 421 | div.tabbable .tab-pane { 422 | background-color: #f8f8f8; 423 | padding: 12px; 424 | border-right: 1px solid #ddd; 425 | border-left: 1px solid #ddd; 426 | border-bottom: 1px solid #ddd; 427 | } 428 | div.dlg-large div.tabbable .tab-pane { 429 | margin-left: 11em; 430 | } 431 | 432 | div.tabbable ul.nav-tabs { 433 | margin-bottom: 0; 434 | } 435 | 436 | ul.qdrTopoModes { 437 | position: relative; 438 | top: -10px; 439 | } 440 | .overview.section { 441 | /* width: 35em; */ 442 | } 443 | .overview.section .ngGrid { 444 | height: 12em !important; 445 | min-height: 12em !important; 446 | } 447 | 448 | .overview.routers.section .ngGrid { 449 | height: 16em !important; 450 | min-height: 16em !important; 451 | } 452 | .overview.routers.section { 453 | /*width: 15em; */ 454 | } 455 | 456 | .grid-align-value { 457 | text-align: right; 458 | } 459 | 460 | .grid-align-value .ngCellText { 461 | padding-right: 10px; 462 | } 463 | 464 | .overview .ngRow:hover { 465 | background:#e0e0ff; 466 | } 467 | 468 | .overview-cell .ngCell:hover { 469 | background:#e0e0ff; 470 | } 471 | .overview-cell .ngCell.col0:hover, .overview-cell .ngCell.col1:hover { 472 | background: initial; 473 | } 474 | 475 | 476 | .qdr-overview.pane.left, .qdr-attributes.pane.left { 477 | top: 104px; 478 | } 479 | .qdr-topology.pane.left { 480 | top: 104px; 481 | } 482 | .qdr-overview.pane.left, .qdr-attributes.pane.left, .qdr-topology.pane.left { 483 | left: 10px; 484 | } 485 | 486 | .treeContainer { 487 | width: 100%; 488 | float: left; 489 | } 490 | 491 | .pane-content { 492 | overflow: auto; 493 | } 494 | 495 | #entityNames { 496 | width: 20em; 497 | float: left; 498 | } 499 | 500 | .treeDetails { 501 | margin-left: 260px; 502 | } 503 | 504 | .gridStyle:not(.noHighlight) .ui-grid-row:hover .ui-grid-cell-contents { 505 | background-color: #e0e0ff; 506 | } 507 | 508 | .ngCellText { 509 | padding: 4px 0 0 4px; 510 | } 511 | 512 | .overview { 513 | border-bottom: 1px solid #d4d4d4; 514 | } 515 | 516 | .ui-grid-row.ui-grid-row-selected > [ui-grid-row] > .ui-grid-cell { 517 | background-color: #e0e0ff; 518 | } 519 | 520 | .tab-content .tab-pane { 521 | background-color: #f8f8f8; 522 | padding: 12px; 523 | border-right: 1px solid #ddd; 524 | border-left: 1px solid #ddd; 525 | border-bottom: 1px solid #ddd; 526 | } 527 | 528 | div.chartOptions ul.nav-tabs > .active > a { 529 | background-color: #f8f8f8; 530 | border: 1px solid #ddd; 531 | border-bottom-color: transparent; 532 | } 533 | 534 | div.chartOptions label:nth-of-type(2) { 535 | margin-left: 1em; 536 | } 537 | div.chartOptions label { 538 | font-weight: normal; 539 | display: inline-block; 540 | } 541 | 542 | /* 543 | .form-horizontal .control-label { 544 | float: left; 545 | width: 160px; 546 | padding-top: 5px; 547 | text-align: right; 548 | } 549 | 550 | .form-horizontal .controls { 551 | margin-left: 180px; 552 | } 553 | 554 | .form-horizontal input, { 555 | display: inline-block; 556 | margin-bottom: 0; 557 | vertical-align: middle; 558 | } 559 | 560 | input[type="text"], input[type="number"], input[type="password"] { 561 | background-color: #ffffff; 562 | border: 1px solid #cccccc; 563 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 564 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 565 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 566 | -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; 567 | -moz-transition: border linear 0.2s, box-shadow linear 0.2s; 568 | -o-transition: border linear 0.2s, box-shadow linear 0.2s; 569 | transition: border linear 0.2s, box-shadow linear 0.2s; 570 | } 571 | 572 | input[type="text"], input[type="number"], input[type="password"] { 573 | display: inline-block; 574 | width: 200px; 575 | padding: 4px 6px; 576 | margin-bottom: 10px; 577 | font-size: 14px; 578 | line-height: 20px; 579 | color: #555555; 580 | vertical-align: middle; 581 | -webkit-border-radius: 4px; 582 | -moz-border-radius: 4px; 583 | border-radius: 4px; 584 | } 585 | 586 | .login input[type="checkbox"] { 587 | margin-top: 0.75em; 588 | } 589 | */ 590 | 591 | #dispatch-login-container { 592 | /* width: 18.5em; */ 593 | margin-top: 2em; 594 | } 595 | /* 596 | div.login.container { 597 | width: 550px; 598 | } 599 | */ 600 | 601 | 602 | #overtree .fancytree-container { 603 | border: 0px; 604 | } 605 | 606 | #overtree span.fancytree-alert-icon.ui-icon-refresh { 607 | background-position: -64px -80px; 608 | } 609 | #overtree span.fancytree-alert-icon.ui-icon-transfer-e-w { 610 | background-position: -112px -80px; 611 | } 612 | 613 | #alerts { 614 | position: fixed; 615 | right: 0; 616 | top: 0; 617 | z-index: 100; 618 | } 619 | 620 | .alert-enter, 621 | .alert-leave, 622 | .alert-move { 623 | -webkit-transition: 1s linear all; 624 | -moz-transition: 1s linear all; 625 | -o-transition: 1s linear all; 626 | transition: 1s linear all; 627 | position:relative; 628 | } 629 | 630 | .alert-enter { 631 | left:-10px; 632 | opacity:0; 633 | } 634 | .alert-enter.alert-enter-active { 635 | left:0; 636 | opacity:1; 637 | } 638 | 639 | .alert-leave { 640 | left:0; 641 | opacity:1; 642 | } 643 | .alert-leave.alert-leave-active { 644 | left:-10px; 645 | opacity:0; 646 | } 647 | 648 | .alert-move { 649 | opacity:0.5; 650 | } 651 | .alert-move.alert-move-active { 652 | opacity:1; 653 | } 654 | 655 | .overview .table-striped tr:hover td { 656 | background-color: #e0e0ff !important; 657 | } 658 | 659 | #entityNames div.ngViewport { 660 | overflow-x: hidden; 661 | } 662 | 663 | .connect-column.connect-form { 664 | width: 20em; 665 | } 666 | 667 | .chartLabels button a { 668 | text-decoration: none; 669 | } 670 | 671 | .fancytree-ico-c.router .fancytree-icon { 672 | 673 | } 674 | 675 | .tabs-left .nav-tabs { 676 | float: left; 677 | } 678 | .tabs-left .nav-tabs > li { 679 | /* float: initial; */ 680 | } 681 | 682 | div.modal.dlg-large { 683 | width: 53em; 684 | } 685 | 686 | button.hdash-button a { 687 | text-decoration: none; 688 | color: #fff; 689 | } 690 | 691 | div.widget-body > div { 692 | height: 100%; 693 | } 694 | 695 | div.qdrCharts { 696 | height: 100%; 697 | } 698 | 699 | ul.dispatch-view { 700 | margin-bottom: 0 !important; 701 | } 702 | 703 | .qdr-overview.pane.left span:not(.dynatree-has-children) .dynatree-icon:before, 704 | .qdr-attributes.pane.left span:not(.dynatree-has-children) .dynatree-icon:before { 705 | color: green; 706 | } 707 | 708 | span:not(.dynatree-has-children).address .dynatree-icon:before, 709 | span:not(.dynatree-has-children).router\.address .dynatree-icon:before { 710 | font-family: FontAwesome; 711 | content: "\f0ac"; 712 | } 713 | span:not(.dynatree-has-children).address.mobile .dynatree-icon:before, 714 | span:not(.dynatree-has-children).router\.address.mobile .dynatree-icon:before { 715 | font-family: FontAwesome; 716 | content: "\f109"; 717 | } 718 | span:not(.dynatree-has-children).address.internal.mobile .dynatree-icon:before, 719 | span:not(.dynatree-has-children).router\.address.internal.mobile .dynatree-icon:before { 720 | font-family: FontAwesome; 721 | content: "\f0ac"; 722 | } 723 | span:not(.dynatree-has-children).address.router .dynatree-icon:before, 724 | span:not(.dynatree-has-children).router\.address.router .dynatree-icon:before { 725 | font-family: FontAwesome; 726 | content: "\f047"; 727 | } 728 | 729 | span.address-link .dynatree-icon:before { 730 | font-family: FontAwesome; 731 | content: "\f0ac"; 732 | } 733 | 734 | span:not(.dynatree-has-children).connection.external .dynatree-icon:before { 735 | font-family: FontAwesome; 736 | content: "\f109"; 737 | } 738 | span:not(.dynatree-has-children).connection.normal .dynatree-icon:before { 739 | font-family: FontAwesome; 740 | content: "\f08e"; 741 | } 742 | span:not(.dynatree-has-children).connection.external.quiesced .dynatree-icon:before { 743 | font-family: FontAwesome; 744 | content: "\f14c"; 745 | color: red; 746 | } 747 | span:not(.dynatree-has-children).connection.inter-router .dynatree-icon:before { 748 | font-family: FontAwesome; 749 | content: "\f07e"; 750 | } 751 | span:not(.dynatree-has-children).connection.router-control .dynatree-icon:before { 752 | font-family: FontAwesome; 753 | content: "\f013"; 754 | } 755 | span:not(.dynatree-has-children).no-data .dynatree-icon:before { 756 | font-family: FontAwesome; 757 | content: "\f05e"; 758 | color: red !important; 759 | } 760 | span:not(.dynatree-has-children).loading .dynatree-icon:before { 761 | font-family: FontAwesome; 762 | content: "\f254"; 763 | } 764 | span:not(.dynatree-has-children).connector .dynatree-icon:before { 765 | font-family: FontAwesome; 766 | content: "\f126"; 767 | } 768 | span:not(.dynatree-has-children).container .dynatree-icon:before { 769 | font-family: FontAwesome; 770 | content: "\f16c"; 771 | } 772 | span:not(.dynatree-has-children).log .dynatree-icon:before { 773 | font-family: FontAwesome; 774 | content: "\f0f6"; 775 | } 776 | span:not(.dynatree-has-children).router\.node .dynatree-icon:before { 777 | font-family: FontAwesome; 778 | content: "\f013"; 779 | } 780 | span:not(.dynatree-has-children).link.inter-router .dynatree-icon:before, 781 | span:not(.dynatree-has-children).router\.link.inter-router .dynatree-icon:before{ 782 | font-family: FontAwesome; 783 | content: "\f07e"; 784 | } 785 | span:not(.dynatree-has-children).link.router-control .dynatree-icon:before, 786 | span:not(.dynatree-has-children).router\.link.router-control .dynatree-icon:before{ 787 | font-family: FontAwesome; 788 | content: "\f013"; 789 | } 790 | span:not(.dynatree-has-children).link.endpoint .dynatree-icon:before, 791 | span:not(.dynatree-has-children).router\.link.endpoint .dynatree-icon:before{ 792 | font-family: FontAwesome; 793 | content: "\f109"; 794 | } 795 | span:not(.dynatree-has-children).link.console .dynatree-icon:before, 796 | span:not(.dynatree-has-children).router\.link.console .dynatree-icon:before { 797 | font-family: FontAwesome; 798 | content: "\f108"; 799 | } 800 | span:not(.dynatree-has-children).listener .dynatree-icon:before { 801 | font-family: FontAwesome; 802 | content: "\f025"; 803 | } 804 | span:not(.dynatree-has-children).connection .dynatree-icon:before { 805 | font-family: FontAwesome; 806 | content: "\f07e"; 807 | } 808 | span:not(.dynatree-has-children).connection.console .dynatree-icon:before { 809 | font-family: FontAwesome; 810 | content: "\f108"; 811 | } 812 | span:not(.dynatree-has-children).waypoint .dynatree-icon:before { 813 | font-family: FontAwesome; 814 | content: "\f0ec"; 815 | } 816 | span:not(.dynatree-has-children).router .dynatree-icon:before { 817 | font-family: FontAwesome; 818 | content: "\f047"; 819 | } 820 | span:not(.dynatree-has-children).fixedAddress .dynatree-icon:before { 821 | font-family: FontAwesome; 822 | content: "\f015"; 823 | } 824 | span:not(.dynatree-has-children).linkRoutePattern .dynatree-icon:before { 825 | font-family: FontAwesome; 826 | content: "\f039"; 827 | } 828 | span:not(.dynatree-has-children).allocator .dynatree-icon:before { 829 | font-family: FontAwesome; 830 | content: "\f170"; 831 | } 832 | 833 | .ngCellText { 834 | /* color: #333333; */ 835 | } 836 | 837 | .changed { 838 | color: #339933; 839 | } 840 | 841 | div.dispatch-router div.help { 842 | width: auto; 843 | padding: 1em; 844 | background-color: lavender; 845 | border-radius: 6px; 846 | margin-top: 1em; 847 | text-align: center; 848 | } 849 | 850 | div.operations tr:nth-child(even) { 851 | background: #f3f3f3; 852 | } 853 | div.operations tr:nth-child(odd), div.operations tr:last-child { 854 | background: #fff; 855 | } 856 | 857 | div.operations tr input { 858 | margin: 0; 859 | padding: 3px 6px; 860 | } 861 | div.operations table { 862 | width: 100%; 863 | } 864 | div.operations th { 865 | width: 50%; 866 | border-bottom: 1px solid #cccccc; 867 | text-align: left; 868 | } 869 | div.operations td:nth-child(odd), div.operations th:nth-child(odd) { 870 | border-right: 1px solid #cccccc; 871 | } 872 | div.operations td:nth-child(odd) { 873 | padding-left: 0; 874 | } 875 | div.operations td:nth-child(even), div.operations th:nth-child(even) { 876 | padding-left: 5px; 877 | } 878 | div.operations th { 879 | padding: 5px; 880 | } 881 | div.operations .tab-pane.active { 882 | padding: 12px 12px 12px 0; 883 | } 884 | div.operations label { 885 | padding-top: 4px; 886 | margin-bottom: 4px; 887 | } 888 | .qdrListActions .ngGrid { 889 | /*min-height: 40em; 890 | height: 100%; */ 891 | } 892 | div.qdrListActions .ngViewport { 893 | height: initial !important; 894 | } 895 | 896 | div.operations .boolean { 897 | padding-bottom: 0; 898 | } 899 | 900 | table.log-entry { 901 | margin-bottom: 1em; 902 | border-top: 1px solid black; 903 | } 904 | 905 | table.log-entry pre { 906 | background-color: #f5f5f5; 907 | color: inherit; 908 | margin: 0; 909 | } 910 | 911 | circle.node.normal.console { 912 | fill: lightcyan; 913 | } 914 | circle.node.artemis { 915 | fill: lightgreen; 916 | } 917 | circle.node.route-container { 918 | fill: orange; 919 | } 920 | 921 | text.console, text.on-demand, text.normal { 922 | font-family: FontAwesome; 923 | font-weight: normal; 924 | font-size: 16px; 925 | } 926 | 927 | @font-face { 928 | font-family:"Brokers"; 929 | src: url("brokers.ttf") /* TTF file for CSS3 browsers */ 930 | } 931 | 932 | text.artemis { 933 | font-family: Brokers; 934 | font-size: 20px; 935 | font-weight: bold; 936 | } 937 | 938 | text.qpid-cpp { 939 | font-family: Brokers; 940 | font-size: 18px; 941 | font-weight: bold; 942 | } 943 | 944 | i.red { 945 | color: red; 946 | } 947 | 948 | .qdrListActions div.delete { 949 | width: 20em; 950 | margin: auto; 951 | border: 1px solid #eaeaea; 952 | height: 5em; 953 | padding: 4em; 954 | background-color: #fcfcfc; 955 | } 956 | 957 | .btn:focus { 958 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); 959 | } 960 | 961 | select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { 962 | outline:3px solid rgba(82, 168, 236, 0.6); 963 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6); 964 | outline: 5px auto -webkit-focus-ring-color; 965 | outline-offset: -2px; 966 | } 967 | 968 | btn.disabled, .btn[disabled] { 969 | opacity: 0.35; 970 | } 971 | 972 | #dispatch-login-container .ng-invalid-range { 973 | border-color: #e9322d !important; 974 | } 975 | 976 | div#durationSlider, div#rateSlider { 977 | margin-top: 1em; 978 | } 979 | 980 | .list-grid { 981 | padding-left: 10px; 982 | } 983 | 984 | .ngViewport.ng-scope { 985 | height: auto !important; 986 | } 987 | 988 | div#list-controller { 989 | padding-left: 300px; 990 | } 991 | 992 | .listening-on { 993 | background-color: #CCFFCC; 994 | } -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrCharts.html: -------------------------------------------------------------------------------- 1 | 19 |
20 |
21 |

22 | 23 | 24 |

25 |
26 |
27 |
28 |
29 |

You need to login to Dispatch Router before viewing this chart.

30 |
31 |
32 | 33 | 37 | 82 | 83 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrConnect.html: -------------------------------------------------------------------------------- 1 | 19 |
20 | 88 | 89 |
90 | 91 |

Please wait, connecting now...

92 |
93 |
94 |

There was a connection error: {{connectionErrorText}}

95 |
96 | 97 |
98 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrLayout.html: -------------------------------------------------------------------------------- 1 | 19 | 24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrList.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 22 | 94 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrOverview.html: -------------------------------------------------------------------------------- 1 | 19 |
20 | 23 | 24 | 25 | 26 | 34 | 40 | 41 | 49 | 64 | 65 | 80 | 86 | 87 | 95 | 110 | 111 | 119 | 122 | 130 | 138 | 160 | 161 | 169 | 170 | 178 | 179 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrSchema.html: -------------------------------------------------------------------------------- 1 | 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/qdrTopology.html: -------------------------------------------------------------------------------- 1 | 19 |
20 |
21 |
22 | 23 |
24 |

Router Info

25 |
26 |
27 |
28 |

Connection Info

29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 |
39 |
    40 |
  • Freeze in place
  • 41 |
  • Unfreeze
  • 42 |
43 |
44 |
45 |
46 |

Connections

47 |
48 |
49 | 53 |
54 |
55 | 56 | 57 | 65 | 68 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/tmplListChart.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 26 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/tmplListTree.html: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 |
22 |
23 |
24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/html/tmplOverviewTree.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/dispatchPlugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | * @main QDR 22 | * 23 | * The main entry point for the QDR module 24 | * 25 | */ 26 | var QDR = (function(QDR) { 27 | 28 | /** 29 | * @property pluginName 30 | * @type {string} 31 | * 32 | * The name of this plugin 33 | */ 34 | QDR.pluginName = 'dispatch_hawtio_console'; 35 | QDR.pluginRoot = "/" + QDR.pluginName; 36 | /** 37 | * @property log 38 | * @type {Logging.Logger} 39 | * 40 | * This plugin's logger instance 41 | */ 42 | QDR.log = Logger.get('QDR'); 43 | 44 | /** 45 | * @property contextPath 46 | * @type {string} 47 | * 48 | * The top level path of this plugin on the server 49 | * 50 | */ 51 | QDR.contextPath = "/dispatch-hawtio-console/"; 52 | 53 | /** 54 | * @property templatePath 55 | * @type {string} 56 | * 57 | * The path to this plugin's partials 58 | */ 59 | QDR.templatePath = QDR.contextPath + "plugin/html/"; 60 | 61 | QDR.SETTINGS_KEY = 'QDRSettings'; 62 | QDR.LAST_LOCATION = "QDRLastLocationHawt"; 63 | 64 | /** 65 | * @property module 66 | * @type {object} 67 | * 68 | * This plugin's angularjs module instance. This plugin only 69 | * needs hawtioCore to run, which provides services like 70 | * workspace, viewRegistry and layoutFull used by the 71 | * run function 72 | */ 73 | QDR.module = angular.module(QDR.pluginName, ['bootstrap', 'hawtio-ui', 'hawtio-forms', 'ui.bootstrap.dialog', 'hawtioCore']) 74 | .config(function($routeProvider) { 75 | /** 76 | * Here we define the route for our plugin. One note is 77 | * to avoid using 'otherwise', as hawtio has a handler 78 | * in place when a route doesn't match any routes that 79 | * routeProvider has been configured with. 80 | */ 81 | $routeProvider 82 | .when(QDR.pluginRoot, { 83 | templateUrl: QDR.templatePath + 'qdrConnect.html' 84 | }) 85 | .when(QDR.pluginRoot + '/', { 86 | templateUrl: QDR.templatePath + 'qdrConnect.html' 87 | }) 88 | .when(QDR.pluginRoot + '/connect', { 89 | templateUrl: QDR.templatePath + 'qdrConnect.html' 90 | }) 91 | .when(QDR.pluginRoot + '/overview', { 92 | templateUrl: QDR.templatePath + 'qdrOverview.html' 93 | }) 94 | .when(QDR.pluginRoot + '/topology', { 95 | templateUrl: QDR.templatePath + 'qdrTopology.html' 96 | }) 97 | .when(QDR.pluginRoot + '/list', { 98 | templateUrl: QDR.templatePath + 'qdrList.html' 99 | }) 100 | .when(QDR.pluginRoot + '/schema', { 101 | templateUrl: QDR.templatePath + 'qdrSchema.html' 102 | }) 103 | .when(QDR.pluginRoot + '/charts', { 104 | templateUrl: QDR.templatePath + 'qdrCharts.html' 105 | }) 106 | }) 107 | .config(function ($compileProvider) { 108 | var cur = $compileProvider.urlSanitizationWhitelist(); 109 | $compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/); 110 | cur = $compileProvider.urlSanitizationWhitelist(); 111 | }) 112 | .config(function( $controllerProvider, $provide, $compileProvider ) { 113 | 114 | }) 115 | .filter('to_trusted', function($sce){ 116 | return function(text) { 117 | return $sce.trustAsHtml(text); 118 | }; 119 | }) 120 | .filter('humanify', function (QDRService) { 121 | return function (input) { 122 | return QDRService.humanify(input); 123 | }; 124 | }) 125 | .filter('shortName', function () { 126 | return function (name) { 127 | var nameParts = name.split('/') 128 | return nameParts.length > 1 ? nameParts[nameParts.length-1] : name; 129 | }; 130 | }) 131 | .filter('pretty', function () { 132 | return function (str) { 133 | var formatComma = d3.format(","); 134 | if (!isNaN(parseFloat(str)) && isFinite(str)) 135 | return formatComma(str); 136 | return str; 137 | }; 138 | }) 139 | .filter('Pascalcase', function () { 140 | return function (str) { 141 | if (!str) 142 | return ""; 143 | return str.replace(/(\w)(\w*)/g, 144 | function(g0,g1,g2){return g1.toUpperCase() + g2.toLowerCase();}); 145 | } 146 | }) 147 | .filter('safePlural', function () { 148 | return function (str) { 149 | var es = ['x', 'ch', 'ss', 'sh'] 150 | for (var i=0; i 0) { 204 | $location.search('org', org) 205 | } 206 | } 207 | 208 | Core.addCSS(QDR.contextPath + "plugin/css/dispatch.css"); 209 | Core.addCSS(QDR.contextPath + "plugin/css/plugin.css"); 210 | Core.addCSS(QDR.contextPath + "plugin/css/jquery.tipsy.css"); 211 | Core.addCSS(QDR.contextPath + "plugin/css/jquery-ui.css"); 212 | Core.addCSS(QDR.contextPath + "plugin/css/font-awesome.min.css"); 213 | 214 | // tell hawtio that we have our own custom layout for 215 | // our view 216 | viewRegistry[QDR.pluginName] = QDR.templatePath + "qdrLayout.html"; 217 | 218 | QDRService.addUpdatedAction("initChartService", function() { 219 | QDRService.delUpdatedAction("initChartService") 220 | QDRChartService.init(); // initialize charting service after we are connected 221 | }); 222 | 223 | var settings = angular.fromJson(localStorage[QDR.SETTINGS_KEY]); 224 | if (settings && settings.autostart) { 225 | QDRService.addConnectAction(function() { 226 | QDRService.addDisconnectAction( function () { 227 | $location.path(QDR.pluginRoot + "/connect"); 228 | $location.replace(); 229 | $rootScope.$apply(); 230 | }) 231 | QDRService.getSchema(function () { 232 | QDR.log.debug("got schema after connection") 233 | QDRService.addUpdatedAction("initialized", function () { 234 | QDRService.delUpdatedAction("initialized") 235 | QDR.log.debug("got initial topology") 236 | $timeout(function() { 237 | if ($location.path().startsWith(QDR.pluginRoot)) { 238 | var lastLocation = localStorage[QDR.LAST_LOCATION] || "/overview" 239 | $location.search('org', null) 240 | $location.path(lastLocation); 241 | $location.replace(); 242 | $rootScope.$apply(); 243 | } 244 | }) 245 | }) 246 | QDR.log.debug("requesting a topology") 247 | QDRService.setUpdateEntities([]) 248 | QDRService.topology.get() 249 | }) 250 | }); 251 | QDRService.connect(settings); 252 | } 253 | $rootScope.$on('$routeChangeSuccess', function() { 254 | var path = $location.path(); 255 | if (path.startsWith(QDR.pluginRoot)) { 256 | if (path !== QDR.pluginRoot && path !== (QDR.pluginRoot + "/") && path !== (QDR.pluginRoot + "/connect")) { 257 | localStorage[QDR.LAST_LOCATION] = path; 258 | QDR.log.info("saving page changed to " + path) 259 | } 260 | } 261 | }); 262 | 263 | $rootScope.$on( "$routeChangeStart", function(event, next, current) { 264 | if (next && next.templateUrl == QDR.templatePath + "qdrConnect.html" && QDRService.connected) { 265 | // clicked connect from another dispatch page 266 | if (current && current.loadedTemplateUrl && current.loadedTemplateUrl.startsWith(QDR.contextPath)) { 267 | return; 268 | } 269 | // clicked the Dispatch Router top level tab from a different plugin 270 | var lastLocation = localStorage[QDR.LAST_LOCATION] || (QDR.pluginRoot + "/overview"); 271 | // show the last page visited 272 | QDR.log.info("showing dispatch tab: going to page " + lastLocation) 273 | $location.path(lastLocation) 274 | } 275 | }); 276 | 277 | workspace.topLevelTabs.push({ 278 | id: "dispatch", 279 | content: "Dispatch Router", 280 | title: "Dispatch console", 281 | isValid: function(workspace) { return true; }, 282 | href: function() { return "#/" + QDR.pluginName; }, 283 | isActive: function(workspace) { return workspace.isLinkActive(QDR.pluginName); } 284 | }); 285 | 286 | }); 287 | 288 | return QDR; 289 | 290 | })(QDR || {}); 291 | 292 | // tell the hawtio plugin loader about our plugin so it can be 293 | // bootstrapped with the rest of angular 294 | hawtioPluginLoader.addModule(QDR.pluginName); 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/navbar.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function (QDR) { 23 | 24 | /** 25 | * @property breadcrumbs 26 | * @type {{content: string, title: string, isValid: isValid, href: string}[]} 27 | * 28 | * Data structure that defines the sub-level tabs for 29 | * our plugin, used by the navbar controller to show 30 | * or hide tabs based on some criteria 31 | */ 32 | QDR.breadcrumbs = [ 33 | { 34 | content: ' Connect', 35 | title: "Connect to a router", 36 | isValid: function () { return true; }, 37 | href: "#" + QDR.pluginRoot + "/connect" 38 | }, 39 | { 40 | content: ' Overview', 41 | title: "View router overview", 42 | isValid: function (QDRService) { return QDRService.isConnected(); }, 43 | href: "#" + QDR.pluginRoot + "/overview" 44 | }, 45 | { 46 | content: ' Entities', 47 | title: "View the attributes of the router entities", 48 | isValid: function (QDRService) { return QDRService.isConnected(); }, 49 | href: "#" + QDR.pluginRoot + "/list" 50 | }, 51 | { 52 | content: ' Topology', 53 | title: "View router network topology", 54 | isValid: function (QDRService) { return QDRService.isConnected(); }, 55 | href: "#" + QDR.pluginRoot + "/topology" 56 | }, 57 | { 58 | content: ' Charts', 59 | title: "View charts", 60 | isValid: function (QDRService, $location) { return QDRService.isConnected() && QDR.isStandalone; }, 61 | href: "#/charts" 62 | }, 63 | { 64 | content: ' Schema', 65 | title: "View dispatch schema", 66 | isValid: function (QDRService) { return QDRService.isConnected(); }, 67 | href: "#" + QDR.pluginRoot + "/schema", 68 | right: true 69 | } 70 | ]; 71 | /** 72 | * @function NavBarController 73 | * 74 | * @param $scope 75 | * @param workspace 76 | * 77 | * The controller for this plugin's navigation bar 78 | * 79 | */ 80 | QDR.module.controller("QDR.NavBarController", ['$scope', 'QDRService', 'QDRChartService', '$routeParams', '$location', function($scope, QDRService, QDRChartService, $routeParams, $location) { 81 | $scope.breadcrumbs = QDR.breadcrumbs; 82 | $scope.isValid = function(link) { 83 | return link.isValid(QDRService, $location); 84 | }; 85 | 86 | $scope.isActive = function(href) { 87 | // highlight the connect tab if we are on the root page 88 | if (($location.path() === QDR.pluginRoot) && (href.split("#")[1] === QDR.pluginRoot + "/connect")) 89 | return true 90 | return href.split("#")[1] == $location.path(); 91 | }; 92 | 93 | $scope.isRight = function (link) { 94 | return angular.isDefined(link.right); 95 | }; 96 | 97 | $scope.hasChart = function (link) { 98 | if (link.href == "#/charts") { 99 | return QDRChartService.charts.some(function (c) { return c.dashboard }); 100 | } 101 | } 102 | 103 | $scope.isDashboardable = function () { 104 | return ($location.path().indexOf("schema") < 0 && $location.path().indexOf("connect") < 0); 105 | } 106 | 107 | $scope.addToDashboardLink = function () { 108 | var href = "#" + $location.path(); 109 | var size = angular.toJson({ 110 | size_x: 2, 111 | size_y: 2 112 | }); 113 | 114 | var routeParams = angular.toJson($routeParams); 115 | var title = "Dispatch Router"; 116 | return "/hawtio/#/dashboard/add?tab=dashboard" + 117 | "&href=" + encodeURIComponent(href) + 118 | "&routeParams=" + encodeURIComponent(routeParams) + 119 | "&title=" + encodeURIComponent(title) + 120 | "&size=" + encodeURIComponent(size); 121 | }; 122 | 123 | }]); 124 | 125 | // controller for the edit/configure chart dialog 126 | QDR.module.controller("QDR.ChartDialogController", function($scope, QDRChartService, $location, dialog, chart, updateTick, dashboard, adding) { 127 | var dialogSvgChart = null; 128 | $scope.svgDivId = "dialogEditChart"; // the div id for the svg chart 129 | 130 | var updateTimer = null; 131 | $scope.chart = chart; // the underlying chart object from the dashboard 132 | $scope.dialogChart = $scope.chart.copy(); // the chart object for this dialog 133 | $scope.userTitle = $scope.chart.title(); 134 | 135 | $scope.$watch('userTitle', function(newValue, oldValue) { 136 | if (newValue !== oldValue) { 137 | $scope.dialogChart.title(newValue); 138 | dialogSvgChart.tick($scope.svgDivId); 139 | } 140 | }) 141 | $scope.$watch("dialogChart.areaColor", function (newValue, oldValue) { 142 | if (newValue !== oldValue) { 143 | if (dialogSvgChart) 144 | dialogSvgChart.tick($scope.svgDivId); 145 | } 146 | }) 147 | $scope.$watch("dialogChart.lineColor", function (newValue, oldValue) { 148 | if (newValue !== oldValue) { 149 | if (dialogSvgChart) 150 | dialogSvgChart.tick($scope.svgDivId); 151 | } 152 | }) 153 | $scope.$watch("dialogChart.type", function (newValue, oldValue) { 154 | if (newValue !== oldValue) { 155 | if (dialogSvgChart) 156 | dialogSvgChart.tick($scope.svgDivId); 157 | } 158 | }) 159 | 160 | // the stored rateWindow is in milliseconds, but the slider is in seconds 161 | $scope.rateWindow = $scope.chart.rateWindow / 1000; 162 | 163 | $scope.addChartsPage = function () { 164 | QDRChartService.addDashboard(dialogSvgChart.chart); 165 | }; 166 | 167 | $scope.showChartsPage = function () { 168 | cleanup(); 169 | dialog.close(true); 170 | $location.path(QDR.pluginRoot + "/charts"); 171 | }; 172 | 173 | var cleanup = function () { 174 | if (updateTimer) { 175 | clearTimeout(updateTimer); 176 | updateTimer = null; 177 | } 178 | if (!$scope.isOnChartsPage()) 179 | QDRChartService.unRegisterChart($scope.dialogChart); // remove the chart 180 | } 181 | $scope.okClick = function () { 182 | cleanup(); 183 | dialog.close(true); 184 | }; 185 | 186 | var initRateSlider = function () { 187 | if (document.getElementById('rateSlider')) { 188 | $( "#rateSlider" ).slider({ 189 | value: $scope.rateWindow, 190 | min: 1, 191 | max: 10, 192 | step: 1, 193 | slide: function( event, ui ) { 194 | $scope.rateWindow = ui.value; 195 | $scope.dialogChart.rateWindow = ui.value * 1000; 196 | $scope.$apply(); 197 | if (dialogSvgChart) 198 | dialogSvgChart.tick($scope.svgDivId); 199 | } 200 | }); 201 | } else { 202 | setTimeout(initRateSlider, 100) 203 | } 204 | } 205 | initRateSlider(); 206 | 207 | var initDurationSlider = function () { 208 | if (document.getElementById('durationSlider')) { 209 | $( "#durationSlider" ).slider({ 210 | value: $scope.dialogChart.visibleDuration, 211 | min: 1, 212 | max: 10, 213 | step: 1, 214 | slide: function( event, ui ) { 215 | $scope.visibleDuration = $scope.dialogChart.visibleDuration = ui.value; 216 | $scope.$apply(); 217 | if (dialogSvgChart) 218 | dialogSvgChart.tick($scope.svgDivId); 219 | } 220 | }); 221 | } else { 222 | setTimeout(initDurationSlider, 100) 223 | } 224 | } 225 | initDurationSlider(); 226 | 227 | $scope.adding = function () { 228 | return adding 229 | } 230 | 231 | $scope.isOnChartsPage = function () { 232 | if (adding) 233 | return dialogSvgChart ? dialogSvgChart.chart.dashboard : false; 234 | else 235 | return $scope.chart.dashboard 236 | } 237 | 238 | // handle the Apply button click 239 | // update the dashboard chart's properties 240 | $scope.apply = function () { 241 | $scope.chart.areaColor = $scope.dialogChart.areaColor; 242 | $scope.chart.lineColor = $scope.dialogChart.lineColor; 243 | $scope.chart.type = $scope.dialogChart.type; 244 | $scope.chart.rateWindow = $scope.rateWindow * 1000; 245 | $scope.chart.title($scope.dialogChart.title()); 246 | $scope.chart.visibleDuration = $scope.dialogChart.visibleDuration; 247 | QDRChartService.saveCharts(); 248 | if (typeof updateTick === "function") 249 | updateTick(); 250 | } 251 | 252 | // add a new chart to the dashboard based on the current dialog settings 253 | $scope.copyToDashboard = function () { 254 | var chart = $scope.dialogChart.copy(); 255 | // set the new chart's dashboard state 256 | QDRChartService.addDashboard(chart); 257 | // notify the chart controller that it needs to display a new chart 258 | dashboard.addChart(chart); 259 | } 260 | 261 | // update the chart on the popup dialog 262 | var updateDialogChart = function () { 263 | // draw the chart using the current data 264 | if (dialogSvgChart) 265 | dialogSvgChart.tick($scope.svgDivId); 266 | 267 | // draw the chart again in 1 second 268 | var updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 5000; 269 | if (updateTimer) 270 | clearTimeout(updateTimer); 271 | updateTimer = setTimeout(updateDialogChart, updateRate); 272 | } 273 | 274 | var showChart = function () { 275 | // ensure the div for our chart is loaded in the dom 276 | var div = angular.element("#" + $scope.svgDivId); 277 | if (!div.width()) { 278 | setTimeout(showChart, 100); 279 | return; 280 | } 281 | dialogSvgChart = new QDRChartService.AreaChart($scope.dialogChart); 282 | $('input[name=lineColor]').val($scope.dialogChart.lineColor); 283 | $('input[name=areaColor]').val($scope.dialogChart.areaColor); 284 | $('input[name=areaColor]').on('input', function (e) { 285 | $scope.dialogChart.areaColor = $(this).val(); 286 | updateDialogChart() 287 | }) 288 | $('input[name=lineColor]').on('input', function (e) { 289 | $scope.dialogChart.lineColor = $(this).val(); 290 | updateDialogChart() 291 | }) 292 | if (updateTimer) 293 | clearTimeout(updateTimer); 294 | updateDialogChart(); 295 | } 296 | showChart(); 297 | }); 298 | 299 | return QDR; 300 | 301 | } (QDR || {})); 302 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/qdrCharts.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function (QDR) { 23 | 24 | /** 25 | * @method ChartsController 26 | * 27 | * Controller that handles displaying a chart on the hawtio dashboard page. Only one chart is displayed per instance of this 28 | * page 29 | */ 30 | QDR.module.controller("QDR.ChartsController", function($scope, QDRService, QDRChartService, $dialog, $location, $routeParams) { 31 | 32 | var updateTimer = null; 33 | 34 | if (!QDRService.connected && !$routeParams.chid) { 35 | // we are not connected. we probably got here from a bookmark or manual page reload 36 | QDRService.redirectWhenConnected("charts"); 37 | return; 38 | } 39 | // we are currently connected. setup a handler to get notified if we are ever disconnected 40 | QDRService.addDisconnectAction( function () { 41 | QDRService.redirectWhenConnected("charts") 42 | $scope.$apply(); 43 | }) 44 | 45 | 46 | $scope.svgCharts = []; 47 | // create an svg object for each chart 48 | QDRChartService.charts.some(function (chart) { 49 | // we are generating a chart for the hawtio dashboard 50 | if ( $routeParams.chid == chart.id()) { 51 | var svgChart = new QDRChartService.AreaChart(chart) 52 | svgChart.zoomed = false; 53 | $scope.svgCharts.push(svgChart); 54 | 55 | // a hawtio dashboard has requested to view this chart. mark the chart so the request to get the data is made 56 | if ($routeParams.chid && chart.hdash) { 57 | chart.hreq = true; 58 | } 59 | return true; 60 | } 61 | }) 62 | 63 | // redraw the chart every update period 64 | // this is a $scope function because it is called from the dialog 65 | var updateCharts = function () { 66 | $scope.svgCharts.forEach(function (svgChart) { 67 | svgChart.tick(svgChart.chart.id()); // on this page we are using the chart.id() as the div id in which to render the chart 68 | }) 69 | var updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 5000; 70 | if (updateTimer) { 71 | clearTimeout(updateTimer) 72 | } 73 | updateTimer = setTimeout(updateCharts, updateRate); 74 | } 75 | 76 | // we are showing a chart for the hawtio dashboard page, but we are not logged in 77 | // set this to cause a "need to login" prompt on the dashboard 78 | $scope.dashLogin = !QDRService.connected; 79 | // the link that the above login prompt will display 80 | $scope.loginHref = QDR.pluginName + "/connect"; 81 | 82 | // called by ng-init in the html when the page is loaded 83 | $scope.chartsLoaded = function () { 84 | $scope.svgCharts.forEach(function (svgChart) { 85 | QDRChartService.sendChartRequest(svgChart.chart.request(), true); 86 | }) 87 | if (updateTimer) 88 | clearTimeout(updateTimer) 89 | updateTimer = setTimeout(updateCharts, 100); 90 | } 91 | 92 | $scope.zoomChart = function (chart) { 93 | chart.zoomed = !chart.zoomed; 94 | chart.zoom(chart.chart.id(), chart.zoomed); 95 | } 96 | $scope.editChart = function (chart) { 97 | doDialog("chart-config-template.html", chart.chart); 98 | }; 99 | 100 | $scope.delChart = function (chart) { 101 | QDRChartService.unRegisterChart(chart.chart); 102 | // remove from svgCharts 103 | $scope.svgCharts.forEach(function (svgChart, i) { 104 | if (svgChart === chart) { 105 | delete $scope.svgCharts.splice(i, 1); 106 | } 107 | }) 108 | }; 109 | 110 | // called from dialog when we want to clone the dialog chart 111 | // the chart argument here is a QDRChartService chart 112 | $scope.addChart = function (chart) { 113 | $scope.svgCharts.push(new QDRChartService.AreaChart(chart)); 114 | }; 115 | 116 | $scope.$on("removedFromDashboard", function (event) { 117 | QDRChartService.unRegisterChart($scope.svgCharts[0].chart) 118 | }) 119 | $scope.$on("$destroy", function( event) { 120 | if (updateTimer) { 121 | clearTimeout(updateTimer); 122 | updateTimer = null; 123 | } 124 | if ($scope.svgCharts.length) { 125 | //if ($scope.svgCharts[0].chart) 126 | // $scope.svgCharts[0].chart.hreq = false; // stops the chart service from making requests for this chart 127 | for (var i=$scope.svgCharts.length-1; i>=0; --i) { 128 | delete $scope.svgCharts.splice(i, 1); 129 | } 130 | } 131 | }); 132 | 133 | // clicked on add to dashboard 134 | $scope.addHChart = function (chart) { 135 | QDRChartService.addHDash(chart.chart); 136 | } 137 | 138 | // href that is generated when the page loads 139 | $scope.addToDashboardLink = function (chart) { 140 | var href = "#" + $location.path(); 141 | var size = angular.toJson({ 142 | size_x: 2, 143 | size_y: 2 144 | }); 145 | 146 | var params = angular.toJson({chid: chart.chart.id()}); 147 | var title = "Dispatch Router"; 148 | return "/hawtio/#/dashboard/add?tab=dashboard" + 149 | "&href=" + encodeURIComponent(href) + 150 | "&routeParams=" + encodeURIComponent(params) + 151 | "&title=" + encodeURIComponent(title) + 152 | "&size=" + encodeURIComponent(size); 153 | }; 154 | 155 | function doDialog(template, chart) { 156 | 157 | $dialog.dialog({ 158 | backdrop: true, 159 | keyboard: true, 160 | backdropClick: true, 161 | templateUrl: template, 162 | controller: "QDR.ChartDialogController", 163 | resolve: { 164 | chart: function() { 165 | return chart; 166 | }, 167 | updateTick: function () { 168 | return updateCharts; 169 | } 170 | } 171 | }).open(); 172 | }; 173 | 174 | }); 175 | 176 | // the edit chart properties dialog 177 | QDR.module.controller("QDR.ChartDialogController", function($scope, QDRChartService, $location, dialog, $rootScope, chart, updateTick) { 178 | 179 | UI.colors[0] = "#cbe7f3" 180 | UI.colors[1] = "#058dc7" 181 | UI.colors[UI.colors.length-1] = "#FFFFFF" 182 | 183 | var dialogSvgChart = null; 184 | $scope.svgDivId = "dialogChart"; // the div id for the svg chart 185 | 186 | var updateTimer = null; 187 | $scope.chart = chart; // the underlying chart object from the dashboard 188 | $scope.dialogChart = $scope.chart.copy(); // the chart object for this dialog 189 | $scope.userTitle = $scope.chart.title(); 190 | 191 | $scope.$watch('userTitle', function(newValue, oldValue) { 192 | if (newValue !== oldValue) { 193 | $scope.dialogChart.title(newValue); 194 | dialogSvgChart.tick($scope.svgDivId); 195 | } 196 | }) 197 | $scope.$watch("dialogChart.areaColor", function (newValue, oldValue) { 198 | if (newValue !== oldValue) { 199 | if (dialogSvgChart) { 200 | dialogSvgChart.tick($scope.svgDivId); 201 | } 202 | } 203 | }) 204 | $scope.$watch("dialogChart.lineColor", function (newValue, oldValue) { 205 | if (newValue !== oldValue) { 206 | if (dialogSvgChart) 207 | dialogSvgChart.tick($scope.svgDivId); 208 | } 209 | }) 210 | $scope.$watch("dialogChart.type", function (newValue, oldValue) { 211 | if (newValue !== oldValue) { 212 | if (dialogSvgChart) 213 | dialogSvgChart.tick($scope.svgDivId); 214 | } 215 | }) 216 | 217 | // the stored rateWindow is in milliseconds, but the slider is in seconds 218 | $scope.rateWindow = $scope.chart.rateWindow / 1000; 219 | 220 | var cleanup = function () { 221 | if (updateTimer) { 222 | clearTimeout(updateTimer); 223 | updateTimer = null; 224 | } 225 | QDRChartService.unRegisterChart($scope.dialogChart); // remove the chart 226 | } 227 | $scope.okClick = function () { 228 | cleanup(); 229 | dialog.close(); 230 | }; 231 | 232 | var initRateSlider = function () { 233 | if (document.getElementById('rateSlider')) { 234 | $( "#rateSlider" ).slider({ 235 | value: $scope.rateWindow, 236 | min: 1, 237 | max: 10, 238 | step: 1, 239 | slide: function( event, ui ) { 240 | $scope.rateWindow = ui.value; 241 | $scope.dialogChart.rateWindow = ui.value * 1000; 242 | $scope.$apply(); 243 | if (dialogSvgChart) 244 | dialogSvgChart.tick($scope.svgDivId); 245 | } 246 | }); 247 | 248 | } else { 249 | setTimeout(initRateSlider, 100) 250 | } 251 | } 252 | initRateSlider(); 253 | 254 | var initDurationSlider = function () { 255 | if (document.getElementById('durationSlider')) { 256 | $( "#durationSlider" ).slider({ 257 | value: $scope.dialogChart.visibleDuration, 258 | min: 1, 259 | max: 10, 260 | step: 1, 261 | slide: function( event, ui ) { 262 | $scope.visibleDuration = $scope.dialogChart.visibleDuration = ui.value; 263 | $scope.$apply(); 264 | if (dialogSvgChart) 265 | dialogSvgChart.tick($scope.svgDivId); 266 | } 267 | }); 268 | 269 | } else { 270 | setTimeout(initDurationSlider, 100) 271 | } 272 | } 273 | initDurationSlider(); 274 | 275 | // handle the Apply button click 276 | // update the dashboard chart's properties 277 | $scope.apply = function () { 278 | $scope.chart.areaColor = $scope.dialogChart.areaColor; 279 | $scope.chart.lineColor = $scope.dialogChart.lineColor; 280 | $scope.chart.type = $scope.dialogChart.type; 281 | $scope.chart.rateWindow = $scope.rateWindow * 1000; 282 | $scope.chart.title($scope.dialogChart.title()); 283 | $scope.chart.visibleDuration = $scope.dialogChart.visibleDuration; 284 | QDRChartService.saveCharts(); 285 | if (typeof updateTick === "function") 286 | updateTick(); 287 | } 288 | 289 | // update the chart on the popup dialog 290 | var updateDialogChart = function () { 291 | // draw the chart using the current data 292 | if (dialogSvgChart) { 293 | dialogSvgChart.tick($scope.svgDivId); 294 | } 295 | // draw the chart again in 1 second 296 | var updateRate = localStorage['updateRate'] ? localStorage['updateRate'] : 5000; 297 | if (updateTimer) 298 | clearTimeout(updateTimer); 299 | updateTimer = setTimeout(updateDialogChart, updateRate); 300 | } 301 | 302 | var showChart = function () { 303 | // ensure the div for our chart is loaded in the dom 304 | var div = angular.element("#dialogChart"); 305 | if (!div.width()) { 306 | setTimeout(showChart, 100); 307 | return; 308 | } 309 | dialogSvgChart = new QDRChartService.AreaChart($scope.dialogChart); 310 | $('input[name=lineColor]').val($scope.dialogChart.lineColor); 311 | $('input[name=areaColor]').val($scope.dialogChart.areaColor); 312 | $('input[name=areaColor]').on('input', function (e) { 313 | $scope.dialogChart.areaColor = $(this).val(); 314 | updateDialogChart() 315 | }) 316 | $('input[name=lineColor]').on('input', function (e) { 317 | $scope.dialogChart.lineColor = $(this).val(); 318 | updateDialogChart() 319 | }) 320 | if (updateTimer) 321 | clearTimeout(updateTimer); 322 | updateDialogChart(); 323 | } 324 | showChart(); 325 | 326 | 327 | }); 328 | 329 | return QDR; 330 | 331 | }(QDR || {})); 332 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/qdrListChart.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function(QDR) { 23 | 24 | QDR.module.controller('QDR.ListChartController', function ($scope, dialog, $dialog, $location, QDRChartService, chart, nodeName) { 25 | $scope.chart = chart; 26 | $scope.dialogSvgChart = null; 27 | var updateTimer = null; 28 | $scope.svgDivId = "dialogChart"; // the div id for the svg chart 29 | 30 | $scope.showChartsPage = function () { 31 | cleanup(); 32 | dialog.close(true); 33 | $location.path(QDR.pluginRoot + "/charts"); 34 | }; 35 | 36 | $scope.addHChart = function () { 37 | QDRChartService.addHDash($scope.chart); 38 | cleanup(); 39 | dialog.close(true); 40 | } 41 | 42 | $scope.addToDashboardLink = function () { 43 | var href = "#/" + QDR.pluginName + "/charts"; 44 | var size = angular.toJson({ 45 | size_x: 2, 46 | size_y: 2 47 | }); 48 | 49 | var params = angular.toJson({chid: $scope.chart.id()}); 50 | var title = "Dispatch - " + nodeName; 51 | return "/hawtio/#/dashboard/add?tab=dashboard" + 52 | "&href=" + encodeURIComponent(href) + 53 | "&routeParams=" + encodeURIComponent(params) + 54 | "&title=" + encodeURIComponent(title) + 55 | "&size=" + encodeURIComponent(size); 56 | }; 57 | 58 | 59 | $scope.addChartsPage = function () { 60 | QDRChartService.addDashboard($scope.chart); 61 | }; 62 | 63 | $scope.delChartsPage = function () { 64 | QDRChartService.delDashboard($scope.chart); 65 | }; 66 | 67 | $scope.isOnChartsPage = function () { 68 | return $scope.chart.dashboard; 69 | } 70 | 71 | var showChart = function () { 72 | // the chart divs are generated by angular and aren't available immediately 73 | var div = angular.element("#" + $scope.svgDivId); 74 | if (!div.width()) { 75 | setTimeout(showChart, 100); 76 | return; 77 | } 78 | dialogSvgChart = new QDRChartService.AreaChart($scope.chart); 79 | $scope.dialogSvgChart = dialogSvgChart; 80 | updateDialogChart(); 81 | } 82 | showChart(); 83 | 84 | var updateDialogChart = function () { 85 | if ($scope.dialogSvgChart) 86 | $scope.dialogSvgChart.tick($scope.svgDivId); 87 | if (updateTimer) 88 | clearTimeout(updateTimer) 89 | updateTimer = setTimeout(updateDialogChart, 1000); 90 | } 91 | 92 | var cleanup = function () { 93 | if (updateTimer) { 94 | clearTimeout(updateTimer); 95 | updateTimer = null; 96 | } 97 | if (!$scope.chart.hdash && !$scope.chart.dashboard) 98 | QDRChartService.unRegisterChart($scope.chart); // remove the chart 99 | 100 | } 101 | $scope.ok = function () { 102 | cleanup(); 103 | dialog.close(true); 104 | }; 105 | 106 | $scope.editChart = function () { 107 | doDialog('tmplChartConfig.html', chart) 108 | } 109 | 110 | function doDialog(template, chart) { 111 | 112 | $dialog.dialog({ 113 | backdrop: true, 114 | keyboard: true, 115 | backdropClick: true, 116 | templateUrl: QDR.templatePath + template, 117 | controller: "QDR.ChartDialogController", 118 | resolve: { 119 | chart: function() { 120 | return chart; 121 | }, 122 | updateTick: function () { 123 | return function () {}; 124 | }, 125 | dashboard: function () { 126 | return $scope; 127 | }, 128 | adding: function () { 129 | return true 130 | } 131 | } 132 | }).open().then(function(result) { 133 | $scope.ok() 134 | }); 135 | }; 136 | 137 | }); 138 | 139 | return QDR; 140 | 141 | } (QDR || {})); 142 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/qdrNewNode.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function(QDR) { 23 | 24 | QDR.module.controller("QDR.NodeDialogController", function($scope, QDRService, dialog, newname) { 25 | var schema = QDRService.schema; 26 | var myEntities = ['router', 'log', 'listener']; 27 | var typeMap = { 28 | integer: 'number', 29 | string: 'text', 30 | path: 'text', 31 | boolean: 'boolean' 32 | }; 33 | var newLinks = $('path.temp').toArray(); // jquery array of new links for the added router 34 | var nodeInfo = QDRService.topology.nodeInfo(); 35 | var separatedEntities = []; // additional entities required if a link is reversed 36 | var myPort = 0, 37 | myAddr = '0.0.0.0'; // port and address for new router 38 | $scope.entities = []; 39 | 40 | // find max port number that is used in all the listeners 41 | var getMaxPort = function(nodeInfo) { 42 | var maxPort = 5674; 43 | for (var key in nodeInfo) { 44 | var node = nodeInfo[key]; 45 | var listeners = node['.listener']; 46 | var attrs = listeners.attributeNames; 47 | for (var i = 0; i < listeners.results.length; ++i) { 48 | var res = listeners.results[i]; 49 | var port = QDRService.valFor(attrs, res, 'port'); 50 | if (parseInt(port, 10) > maxPort) 51 | maxPort = parseInt(port, 10); 52 | } 53 | } 54 | return maxPort; 55 | } 56 | var maxPort = getMaxPort(nodeInfo); 57 | 58 | // construct an object that contains all the info needed for a single tab's fields 59 | var entity = function(actualName, tabName, humanName, ent, icon, link) { 60 | var nameIndex = -1; // the index into attributes that the name field was placed 61 | var index = 0; 62 | var info = { 63 | actualName: actualName, 64 | tabName: tabName, 65 | humanName: humanName, 66 | description: ent.description, 67 | icon: angular.isDefined(icon) ? icon : '', 68 | references: ent.references, 69 | link: link, 70 | 71 | attributes: $.map(ent.attributes, function(value, key) { 72 | // skip identity and depricated fields 73 | if (key == 'identity' || value.description.startsWith('Deprecated')) 74 | return null; 75 | var val = value['default']; 76 | if (key == 'name') 77 | nameIndex = index; 78 | index++; 79 | return { 80 | name: key, 81 | humanName: QDRService.humanify(key), 82 | description: value.description, 83 | type: typeMap[value.type], 84 | rawtype: value.type, 85 | input: typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input' : 'select', 86 | selected: val ? val : undefined, 87 | 'default': value['default'], 88 | value: val, 89 | required: value.required, 90 | unique: value.unique 91 | }; 92 | }) 93 | } 94 | // move the 'name' attribute to the 1st position 95 | if (nameIndex > -1) { 96 | var tmp = info.attributes[0]; 97 | info.attributes[0] = info.attributes[nameIndex]; 98 | info.attributes[nameIndex] = tmp; 99 | } 100 | return info; 101 | } 102 | 103 | // remove the annotation fields 104 | var stripAnnotations = function(entityName, ent, annotations) { 105 | if (ent.references) { 106 | var newEnt = { 107 | attributes: {} 108 | }; 109 | ent.references.forEach(function(annoKey) { 110 | if (!annotations[annoKey]) 111 | annotations[annoKey] = {}; 112 | annotations[annoKey][entityName] = true; // create the key/consolidate duplicates 113 | var keys = Object.keys(schema.annotations[annoKey].attributes); 114 | for (var attrib in ent.attributes) { 115 | if (keys.indexOf(attrib) == -1) { 116 | newEnt.attributes[attrib] = ent.attributes[attrib]; 117 | } 118 | } 119 | // add a field for the reference name 120 | newEnt.attributes[annoKey] = { 121 | type: 'string', 122 | description: 'Name of the ' + annoKey + ' section.', 123 | 'default': annoKey, 124 | required: true 125 | }; 126 | }) 127 | newEnt.references = ent.references; 128 | newEnt.description = ent.description; 129 | return newEnt; 130 | } 131 | return ent; 132 | } 133 | 134 | var annotations = {}; 135 | myEntities.forEach(function(entityName) { 136 | var ent = schema.entityTypes[entityName]; 137 | var hName = QDRService.humanify(entityName); 138 | if (entityName == 'listener') 139 | hName = "Listener for clients"; 140 | var noAnnotations = stripAnnotations(entityName, ent, annotations); 141 | var ediv = entity(entityName, entityName, hName, noAnnotations, undefined); 142 | if (ediv.actualName == 'router') { 143 | ediv.attributes.filter(function(attr) { 144 | return attr.name == 'name' 145 | })[0].value = newname; 146 | // if we have any new links (connectors), then the router's mode should be interior 147 | if (newLinks.length) { 148 | var roleAttr = ediv.attributes.filter(function(attr) { 149 | return attr.name == 'mode' 150 | })[0]; 151 | roleAttr.value = roleAttr.selected = "interior"; 152 | } 153 | } 154 | if (ediv.actualName == 'container') { 155 | ediv.attributes.filter(function(attr) { 156 | return attr.name == 'containerName' 157 | })[0].value = newname + "-container"; 158 | } 159 | if (ediv.actualName == 'listener') { 160 | // find max port number that is used in all the listeners 161 | ediv.attributes.filter(function(attr) { 162 | return attr.name == 'port' 163 | })[0].value = ++maxPort; 164 | } 165 | // special case for required log.module since it doesn't have a default 166 | if (ediv.actualName == 'log') { 167 | var moduleAttr = ediv.attributes.filter(function(attr) { 168 | return attr.name == 'module' 169 | })[0]; 170 | moduleAttr.value = moduleAttr.selected = "DEFAULT"; 171 | } 172 | $scope.entities.push(ediv); 173 | }) 174 | 175 | // add a tab for each annotation that was found 176 | var annotationEnts = []; 177 | for (var key in annotations) { 178 | ent = angular.copy(schema.annotations[key]); 179 | ent.attributes.name = { 180 | type: "string", 181 | unique: true, 182 | description: "Unique name that is used to refer to this set of attributes." 183 | } 184 | var ediv = entity(key, key + 'tab', QDRService.humanify(key), ent, undefined); 185 | ediv.attributes.filter(function(attr) { 186 | return attr.name == 'name' 187 | })[0].value = key; 188 | $scope.entities.push(ediv); 189 | annotationEnts.push(ediv); 190 | } 191 | 192 | // add an additional listener tab if any links are reversed 193 | ent = schema.entityTypes['listener']; 194 | newLinks.some(function(link) { 195 | if (link.__data__.right) { 196 | var noAnnotations = stripAnnotations('listener', ent, annotations); 197 | var ediv = entity("listener", "listener0", "Listener (internal)", noAnnotations, undefined); 198 | ediv.attributes.filter(function(attr) { 199 | return attr.name == 'port' 200 | })[0].value = ++maxPort; 201 | // connectors from other routers need to connect to this addr:port 202 | myPort = maxPort; 203 | myAddr = ediv.attributes.filter(function(attr) { 204 | return attr.name == 'host' 205 | })[0].value 206 | 207 | // override the role. 'normal' is the default, but we want inter-router 208 | ediv.attributes.filter(function(attr) { 209 | return attr.name == 'role' 210 | })[0].selected = 'inter-router'; 211 | separatedEntities.push(ediv); 212 | return true; // stop looping 213 | } 214 | return false; // continue looping 215 | }) 216 | 217 | // Add connector tabs for each new link on the topology graph 218 | ent = schema.entityTypes['connector']; 219 | newLinks.forEach(function(link, i) { 220 | var noAnnotations = stripAnnotations('connector', ent, annotations); 221 | var ediv = entity('connector', 'connector' + i, " " + link.__data__.source.name, noAnnotations, link.__data__.right, link) 222 | 223 | // override the connector role. 'normal' is the default, but we want inter-router 224 | ediv.attributes.filter(function(attr) { 225 | return attr.name == 'role' 226 | })[0].selected = 'inter-router'; 227 | 228 | // find the addr:port of the inter-router listener to use 229 | var listener = nodeInfo[link.__data__.source.key]['.listener']; 230 | var attrs = listener.attributeNames; 231 | for (var i = 0; i < listener.results.length; ++i) { 232 | var res = listener.results[i]; 233 | var role = QDRService.valFor(attrs, res, 'role'); 234 | if (role == 'inter-router') { 235 | ediv.attributes.filter(function(attr) { 236 | return attr.name == 'host' 237 | })[0].value = 238 | QDRService.valFor(attrs, res, 'host') 239 | ediv.attributes.filter(function(attr) { 240 | return attr.name == 'port' 241 | })[0].value = 242 | QDRService.valFor(attrs, res, 'port') 243 | break; 244 | } 245 | } 246 | if (link.__data__.right) { 247 | // connectors from other nodes need to connect to the new router's listener addr:port 248 | ediv.attributes.filter(function(attr) { 249 | return attr.name == 'port' 250 | })[0].value = myPort; 251 | ediv.attributes.filter(function(attr) { 252 | return attr.name == 'host' 253 | })[0].value = myAddr; 254 | 255 | separatedEntities.push(ediv) 256 | } else 257 | $scope.entities.push(ediv); 258 | }) 259 | Array.prototype.push.apply($scope.entities, separatedEntities); 260 | 261 | // update the description on all the annotation tabs 262 | annotationEnts.forEach(function(ent) { 263 | var shared = Object.keys(annotations[ent.actualName]); 264 | ent.description += " These fields are shared by " + shared.join(" and ") + "."; 265 | 266 | }) 267 | 268 | $scope.testPattern = function(attr) { 269 | if (attr.rawtype == 'path') 270 | return /^(\/)?([^/\0]+(\/)?)+$/; 271 | //return /^(.*\/)([^/]*)$/; 272 | return /(.*?)/; 273 | } 274 | 275 | $scope.attributeDescription = ''; 276 | $scope.attributeType = ''; 277 | $scope.attributeRequired = ''; 278 | $scope.attributeUnique = ''; 279 | $scope.active = 'router' 280 | $scope.fieldsetDivs = "/fieldsetDivs.html" 281 | $scope.setActive = function(tabName) { 282 | $scope.active = tabName 283 | } 284 | $scope.isActive = function(tabName) { 285 | return $scope.active === tabName 286 | } 287 | $scope.showDescription = function(attr, e) { 288 | $scope.attributeDescription = attr.description; 289 | var offset = jQuery(e.currentTarget).offset() 290 | jQuery('.attr-description').offset({ 291 | top: offset.top 292 | }) 293 | 294 | $scope.attributeType = "Type: " + JSON.stringify(attr.rawtype); 295 | $scope.attributeRequired = attr.required ? 'required' : ''; 296 | $scope.attributeUnique = attr.unique ? 'Must be unique' : ''; 297 | } 298 | // handle the download button click 299 | // copy the dialog's values to the original node 300 | $scope.download = function() { 301 | dialog.close({ 302 | entities: $scope.entities, 303 | annotations: annotations 304 | }); 305 | } 306 | $scope.cancel = function() { 307 | dialog.close() 308 | }; 309 | 310 | $scope.selectAnnotationTab = function(tabName) { 311 | var tabs = $("#tabs").tabs(); 312 | tabs.tabs("select", tabName); 313 | } 314 | 315 | var initTabs = function() { 316 | var div = angular.element("#tabs"); 317 | if (!div.width()) { 318 | setTimeout(initTabs, 100); 319 | return; 320 | } 321 | $("#tabs") 322 | .tabs() 323 | .addClass('ui-tabs-vertical ui-helper-clearfix'); 324 | } 325 | // start the update loop 326 | initTabs(); 327 | 328 | }); 329 | 330 | QDR.module.controller("QDR.DownloadDialogController", function($scope, QDRService, $templateCache, $window, dialog, results) { 331 | var result = results.entities; 332 | var annotations = results.annotations; 333 | var annotationKeys = Object.keys(annotations); 334 | var annotationSections = {}; 335 | 336 | // use the router's name as the file name if present 337 | $scope.newRouterName = 'router'; 338 | result.forEach(function(e) { 339 | if (e.actualName == 'router') { 340 | e.attributes.forEach(function(a) { 341 | if (a.name == 'name') { 342 | $scope.newRouterName = a.value; 343 | } 344 | }) 345 | } 346 | }) 347 | $scope.newRouterName = $scope.newRouterName + ".conf"; 348 | 349 | var template = $templateCache.get('config-file-header.html'); 350 | $scope.verbose = true; 351 | $scope.$watch('verbose', function(newVal) { 352 | if (newVal !== undefined) { 353 | // recreate output using current verbose setting 354 | getOutput(); 355 | } 356 | }) 357 | 358 | var getOutput = function() { 359 | $scope.output = template + '\n'; 360 | $scope.parts = []; 361 | var commentChar = '#' 362 | result.forEach(function(entity) { 363 | // don't output a section for annotations, they get flattened into the entities 364 | var section = ""; 365 | if (entity.icon) { 366 | section += "##\n## Add to " + entity.link.__data__.source.name + "'s configuration file\n##\n"; 367 | } 368 | section += "##\n## " + QDRService.humanify(entity.actualName) + " - " + entity.description + "\n##\n"; 369 | section += entity.actualName + " {\n"; 370 | entity.attributes.forEach(function(attribute) { 371 | if (attribute.input == 'select') 372 | attribute.value = attribute.selected; 373 | 374 | // treat values with all spaces and empty strings as undefined 375 | attribute.value = String(attribute.value).trim(); 376 | if (attribute.value === 'undefined' || attribute.value === '') 377 | attribute.value = undefined; 378 | 379 | if ($scope.verbose) { 380 | commentChar = attribute.required || attribute.value != attribute['default'] ? ' ' : '#'; 381 | if (!attribute.value) { 382 | commentChar = '#'; 383 | attribute.value = ''; 384 | } 385 | section += commentChar + " " + attribute.name + ":" + Array(Math.max(20 - attribute.name.length, 1)).join(" ") + attribute.value + Array(Math.max(20 - ((attribute.value) + "").length, 1)).join(" ") + '# ' + attribute.description + "\n"; 386 | } else { 387 | if (attribute.value) { 388 | if (attribute.value != attribute['default'] || attribute.required) 389 | section += " " + attribute.name + ":" + Array(20 - attribute.name.length).join(" ") + attribute.value + "\n"; 390 | 391 | } 392 | } 393 | }) 394 | section += "}\n\n"; 395 | // if entity.icon is true, this is a connector intended for another router 396 | if (entity.icon) 397 | $scope.parts.push({ 398 | output: section, 399 | link: entity.link, 400 | name: entity.link.__data__.source.name, 401 | references: entity.references 402 | }); 403 | else 404 | $scope.output += section; 405 | 406 | // if this section is actually an annotation 407 | if (annotationKeys.indexOf(entity.actualName) > -1) { 408 | annotationSections[entity.actualName] = section; 409 | } 410 | }) 411 | // go back and add annotation sections to the parts 412 | $scope.parts.forEach(function(part) { 413 | for (var section in annotationSections) { 414 | if (part.references.indexOf(section) > -1) { 415 | part.output += annotationSections[section]; 416 | } 417 | } 418 | }) 419 | QDR.log.debug($scope.output); 420 | } 421 | 422 | // handle the download button click 423 | $scope.download = function() { 424 | var output = $scope.output + "\n\n" 425 | var blob = new Blob([output], { 426 | type: 'text/plain;charset=utf-16' 427 | }); 428 | saveAs(blob, $scope.newRouterName); 429 | } 430 | 431 | $scope.downloadPart = function(part) { 432 | var linkName = part.link.__data__.source.name + 'additional.conf'; 433 | var blob = new Blob([part.output], { 434 | type: 'text/plain;charset=utf-16' 435 | }); 436 | saveAs(blob, linkName); 437 | } 438 | 439 | $scope.done = function() { 440 | dialog.close(); 441 | } 442 | }); 443 | 444 | return QDR; 445 | }(QDR || {})); 446 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/qdrOverviewLogsController.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function(QDR) { 23 | 24 | QDR.module.controller('QDR.OverviewLogsController', function ($scope, dialog, QDRService, $timeout, nodeName, nodeId, module, level) { 25 | 26 | var gotLogInfo = function (nodeId, entity, response, context) { 27 | var statusCode = context.message.application_properties.statusCode; 28 | if (statusCode < 200 || statusCode >= 300) { 29 | Core.notification('error', context.message.statusDescription); 30 | QDR.log.info('Error ' + context.message.statusDescription) 31 | } else { 32 | var levelLogs = response.filter( function (result) { 33 | if (result[1] == null) 34 | result[1] = "error" 35 | return result[1].toUpperCase() === level.toUpperCase() && result[0] === module 36 | }) 37 | var logFields = levelLogs.map( function (result) { 38 | return { 39 | nodeId: QDRService.nameFromId(nodeId), 40 | name: result[0], 41 | type: result[1], 42 | message: result[2], 43 | source: result[3], 44 | line: result[4], 45 | time: Date(result[5]).toString() 46 | } 47 | }) 48 | $timeout(function () { 49 | $scope.loading = false 50 | $scope.logFields = logFields 51 | }) 52 | } 53 | } 54 | QDRService.sendMethod(nodeId, undefined, {}, "GET-LOG", {module: module}, gotLogInfo) 55 | 56 | $scope.loading = true 57 | $scope.module = module 58 | $scope.level = level 59 | $scope.nodeName = nodeName 60 | $scope.logFields = [] 61 | $scope.ok = function () { 62 | dialog.close(true); 63 | }; 64 | 65 | }); 66 | return QDR; 67 | 68 | } (QDR || {})); 69 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/js/qdrSchema.js: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | /** 20 | * @module QDR 21 | */ 22 | var QDR = (function (QDR) { 23 | 24 | QDR.module.controller("QDR.SchemaController", ['$scope', '$location', '$timeout', 'QDRService', function($scope, $location, $timeout, QDRService) { 25 | if (!QDRService.connected) { 26 | QDRService.redirectWhenConnected("schema") 27 | return; 28 | } 29 | var onDisconnect = function () { 30 | $timeout( function () {QDRService.redirectWhenConnected("schema")}) 31 | } 32 | // we are currently connected. setup a handler to get notified if we are ever disconnected 33 | QDRService.addDisconnectAction( onDisconnect ) 34 | 35 | var keys2kids = function (tree, obj) { 36 | if (obj === Object(obj)) { 37 | tree.children = [] 38 | var keys = Object.keys(obj).sort() 39 | for (var i=0; i= 0) { 149 | event.preventDefault(); 150 | return false; 151 | } 152 | // firefox doesn't filter out non-numeric input. it just sets the ctrl to invalid 153 | if (/[!@#$%^&*()]/.test(skey) && event.shiftKey || // prevent shift numbers 154 | !( // prevent all but the following 155 | nkey <= 0 || // arrows 156 | nkey == 8 || // delete|backspace 157 | nkey == 13 || // enter 158 | (nkey >= 37 && nkey <= 40) || // arrows 159 | event.ctrlKey || event.altKey || // ctrl-v, etc. 160 | /[0-9]/.test(skey)) // numbers 161 | ) { 162 | event.preventDefault(); 163 | return false; 164 | } 165 | }); 166 | // check the current value of input 167 | var _isPortInvalid = function(value) { 168 | var port = value + ''; 169 | var isErrRange = false; 170 | // empty string is valid 171 | if (port.length !== 0) { 172 | var n = ~~Number(port); 173 | if (n < 1 || n > 65535) { 174 | isErrRange = true; 175 | } 176 | } 177 | ctrl.$setValidity('range', !isErrRange); 178 | return isErrRange; 179 | }; 180 | 181 | //For DOM -> model validation 182 | ctrl.$parsers.unshift(function(value) { 183 | return _isPortInvalid(value) ? undefined : value; 184 | }); 185 | 186 | //For model -> DOM validation 187 | ctrl.$formatters.unshift(function(value) { 188 | _isPortInvalid(value); 189 | return value; 190 | }); 191 | } 192 | }; 193 | }); 194 | 195 | return QDR; 196 | }(QDR || {})); 197 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/lib/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||"undefined"!==typeof navigator&&navigator.msSaveOrOpenBlob&&navigator.msSaveOrOpenBlob.bind(navigator)||function(a){"use strict";if("undefined"===typeof navigator||!/MSIE [1-9]\./.test(navigator.userAgent)){var k=a.document,n=k.createElementNS("http://www.w3.org/1999/xhtml","a"),w="download"in n,x=function(c){var e=k.createEvent("MouseEvents");e.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null);c.dispatchEvent(e)},q=a.webkitRequestFileSystem,u=a.requestFileSystem||q||a.mozRequestFileSystem, 3 | y=function(c){(a.setImmediate||a.setTimeout)(function(){throw c;},0)},r=0,s=function(c){var e=function(){"string"===typeof c?(a.URL||a.webkitURL||a).revokeObjectURL(c):c.remove()};a.chrome?e():setTimeout(e,500)},t=function(c,a,d){a=[].concat(a);for(var b=a.length;b--;){var l=c["on"+a[b]];if("function"===typeof l)try{l.call(c,d||c)}catch(f){y(f)}}},m=function(c,e){var d=this,b=c.type,l=!1,f,p,k=function(){t(d,["writestart","progress","write","writeend"])},g=function(){if(l||!f)f=(a.URL||a.webkitURL|| 4 | a).createObjectURL(c);p?p.location.href=f:void 0==a.open(f,"_blank")&&"undefined"!==typeof safari&&(a.location.href=f);d.readyState=d.DONE;k();s(f)},h=function(a){return function(){if(d.readyState!==d.DONE)return a.apply(this,arguments)}},m={create:!0,exclusive:!1},v;d.readyState=d.INIT;e||(e="download");if(w)f=(a.URL||a.webkitURL||a).createObjectURL(c),n.href=f,n.download=e,x(n),d.readyState=d.DONE,k(),s(f);else{a.chrome&&b&&"application/octet-stream"!==b&&(v=c.slice||c.webkitSlice,c=v.call(c,0, 5 | c.size,"application/octet-stream"),l=!0);q&&"download"!==e&&(e+=".download");if("application/octet-stream"===b||q)p=a;u?(r+=c.size,u(a.TEMPORARY,r,h(function(a){a.root.getDirectory("saved",m,h(function(a){var b=function(){a.getFile(e,m,h(function(a){a.createWriter(h(function(b){b.onwriteend=function(b){p.location.href=a.toURL();d.readyState=d.DONE;t(d,"writeend",b);s(a)};b.onerror=function(){var a=b.error;a.code!==a.ABORT_ERR&&g()};["writestart","progress","write","abort"].forEach(function(a){b["on"+ 6 | a]=d["on"+a]});b.write(c);d.abort=function(){b.abort();d.readyState=d.DONE};d.readyState=d.WRITING}),g)}),g)};a.getFile(e,{create:!1},h(function(a){a.remove();b()}),h(function(a){a.code===a.NOT_FOUND_ERR?b():g()}))}),g)}),g)):g()}},b=m.prototype;b.abort=function(){this.readyState=this.DONE;t(this,"abort")};b.readyState=b.INIT=0;b.WRITING=1;b.DONE=2;b.error=b.onwritestart=b.onprogress=b.onwrite=b.onabort=b.onerror=b.onwriteend=null;return function(a,b){return new m(a,b)}}}("undefined"!==typeof self&& 7 | self||"undefined"!==typeof window&&window||this.content);"undefined"!==typeof module&&null!==module?module.exports=saveAs:"undefined"!==typeof define&&null!==define&&null!=define.amd&&define([],function(){return saveAs}); -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/lib/jquery.tipsy.js: -------------------------------------------------------------------------------- 1 | // tipsy, facebook style tooltips for jquery 2 | // version 1.0.0a 3 | // (c) 2008-2010 jason frame [jason@onehackoranother.com] 4 | // released under the MIT license 5 | 6 | (function($) { 7 | 8 | function maybeCall(thing, ctx) { 9 | return (typeof thing == 'function') ? (thing.call(ctx)) : thing; 10 | }; 11 | 12 | function isElementInDOM(ele) { 13 | while (ele = ele.parentNode) { 14 | if (ele == document) return true; 15 | } 16 | return false; 17 | }; 18 | 19 | function Tipsy(element, options) { 20 | this.$element = $(element); 21 | this.options = options; 22 | this.enabled = true; 23 | this.fixTitle(); 24 | }; 25 | 26 | Tipsy.prototype = { 27 | show: function() { 28 | var title = this.getTitle(); 29 | if (title && this.enabled) { 30 | var $tip = this.tip(); 31 | 32 | $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title); 33 | $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity 34 | $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body); 35 | 36 | var pos = $.extend({}, this.$element.offset(), { 37 | width: this.$element[0].offsetWidth, 38 | height: this.$element[0].offsetHeight 39 | }); 40 | 41 | var actualWidth = $tip[0].offsetWidth, 42 | actualHeight = $tip[0].offsetHeight, 43 | gravity = maybeCall(this.options.gravity, this.$element[0]); 44 | 45 | var tp; 46 | switch (gravity.charAt(0)) { 47 | case 'n': 48 | tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; 49 | break; 50 | case 's': 51 | tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; 52 | break; 53 | case 'e': 54 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; 55 | break; 56 | case 'w': 57 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; 58 | break; 59 | } 60 | 61 | if (gravity.length == 2) { 62 | if (gravity.charAt(1) == 'w') { 63 | tp.left = pos.left + pos.width / 2 - 15; 64 | } else { 65 | tp.left = pos.left + pos.width / 2 - actualWidth + 15; 66 | } 67 | } 68 | 69 | $tip.css(tp).addClass('tipsy-' + gravity); 70 | $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0); 71 | if (this.options.className) { 72 | $tip.addClass(maybeCall(this.options.className, this.$element[0])); 73 | } 74 | 75 | if (this.options.fade) { 76 | $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}); 77 | } else { 78 | $tip.css({visibility: 'visible', opacity: this.options.opacity}); 79 | } 80 | } 81 | }, 82 | 83 | hide: function() { 84 | if (this.options.fade) { 85 | this.tip().stop().fadeOut(function() { $(this).remove(); }); 86 | } else { 87 | this.tip().remove(); 88 | } 89 | }, 90 | 91 | fixTitle: function() { 92 | var $e = this.$element; 93 | if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') { 94 | $e.attr('original-title', $e.attr('title') || '').removeAttr('title'); 95 | } 96 | }, 97 | 98 | getTitle: function() { 99 | var title, $e = this.$element, o = this.options; 100 | this.fixTitle(); 101 | var title, o = this.options; 102 | if (typeof o.title == 'string') { 103 | title = $e.attr(o.title == 'title' ? 'original-title' : o.title); 104 | } else if (typeof o.title == 'function') { 105 | title = o.title.call($e[0]); 106 | } 107 | title = ('' + title).replace(/(^\s*|\s*$)/, ""); 108 | return title || o.fallback; 109 | }, 110 | 111 | tip: function() { 112 | if (!this.$tip) { 113 | this.$tip = $('
').html('
'); 114 | this.$tip.data('tipsy-pointee', this.$element[0]); 115 | } 116 | return this.$tip; 117 | }, 118 | 119 | validate: function() { 120 | if (!this.$element[0].parentNode) { 121 | this.hide(); 122 | this.$element = null; 123 | this.options = null; 124 | } 125 | }, 126 | 127 | enable: function() { this.enabled = true; }, 128 | disable: function() { this.enabled = false; }, 129 | toggleEnabled: function() { this.enabled = !this.enabled; } 130 | }; 131 | 132 | $.fn.tipsy = function(options) { 133 | 134 | if (options === true) { 135 | return this.data('tipsy'); 136 | } else if (typeof options == 'string') { 137 | var tipsy = this.data('tipsy'); 138 | if (tipsy) tipsy[options](); 139 | return this; 140 | } 141 | 142 | options = $.extend({}, $.fn.tipsy.defaults, options); 143 | 144 | function get(ele) { 145 | var tipsy = $.data(ele, 'tipsy'); 146 | if (!tipsy) { 147 | tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); 148 | $.data(ele, 'tipsy', tipsy); 149 | } 150 | return tipsy; 151 | } 152 | 153 | function enter() { 154 | var tipsy = get(this); 155 | tipsy.hoverState = 'in'; 156 | if (options.delayIn == 0) { 157 | tipsy.show(); 158 | } else { 159 | tipsy.fixTitle(); 160 | setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn); 161 | } 162 | }; 163 | 164 | function leave() { 165 | var tipsy = get(this); 166 | tipsy.hoverState = 'out'; 167 | if (options.delayOut == 0) { 168 | tipsy.hide(); 169 | } else { 170 | setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut); 171 | } 172 | }; 173 | 174 | if (!options.live) this.each(function() { get(this); }); 175 | 176 | if (options.trigger != 'manual') { 177 | var binder = options.live ? 'live' : 'bind', 178 | eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus', 179 | eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'; 180 | this[binder](eventIn, enter)[binder](eventOut, leave); 181 | } 182 | 183 | return this; 184 | 185 | }; 186 | 187 | $.fn.tipsy.defaults = { 188 | className: null, 189 | delayIn: 0, 190 | delayOut: 0, 191 | fade: false, 192 | fallback: '', 193 | gravity: 'n', 194 | html: false, 195 | live: false, 196 | offset: 0, 197 | opacity: 0.8, 198 | title: 'title', 199 | trigger: 'hover' 200 | }; 201 | 202 | $.fn.tipsy.revalidate = function() { 203 | $('.tipsy').each(function() { 204 | var pointee = $.data(this, 'tipsy-pointee'); 205 | if (!pointee || !isElementInDOM(pointee)) { 206 | $(this).remove(); 207 | } 208 | }); 209 | }; 210 | 211 | // Overwrite this method to provide options on a per-element basis. 212 | // For example, you could store the gravity in a 'tipsy-gravity' attribute: 213 | // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); 214 | // (remember - do not modify 'options' in place!) 215 | $.fn.tipsy.elementOptions = function(ele, options) { 216 | return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; 217 | }; 218 | 219 | $.fn.tipsy.autoNS = function() { 220 | return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; 221 | }; 222 | 223 | $.fn.tipsy.autoWE = function() { 224 | return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; 225 | }; 226 | 227 | /** 228 | * yields a closure of the supplied parameters, producing a function that takes 229 | * no arguments and is suitable for use as an autogravity function like so: 230 | * 231 | * @param margin (int) - distance from the viewable region edge that an 232 | * element should be before setting its tooltip's gravity to be away 233 | * from that edge. 234 | * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer 235 | * if there are no viewable region edges effecting the tooltip's 236 | * gravity. It will try to vary from this minimally, for example, 237 | * if 'sw' is preferred and an element is near the right viewable 238 | * region edge, but not the top edge, it will set the gravity for 239 | * that element's tooltip to be 'se', preserving the southern 240 | * component. 241 | */ 242 | $.fn.tipsy.autoBounds = function(margin, prefer) { 243 | return function() { 244 | var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)}, 245 | boundTop = $(document).scrollTop() + margin, 246 | boundLeft = $(document).scrollLeft() + margin, 247 | $this = $(this); 248 | 249 | if ($this.offset().top < boundTop) dir.ns = 'n'; 250 | if ($this.offset().left < boundLeft) dir.ew = 'w'; 251 | if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e'; 252 | if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's'; 253 | 254 | return dir.ns + (dir.ew ? dir.ew : ''); 255 | } 256 | }; 257 | 258 | })(jQuery); 259 | -------------------------------------------------------------------------------- /dispatch/src/main/webapp/plugin/lib/tooltipsy.min.js: -------------------------------------------------------------------------------- 1 | /* tooltipsy by Brian Cray 2 | * Lincensed under GPL2 - http://www.gnu.org/licenses/gpl-2.0.html 3 | * Option quick reference: 4 | * - alignTo: "element" or "cursor" (Defaults to "element") 5 | * - offset: Tooltipsy distance from element or mouse cursor, dependent on alignTo setting. Set as array [x, y] (Defaults to [0, -1]) 6 | * - content: HTML or text content of tooltip. Defaults to "" (empty string), which pulls content from target element's title attribute 7 | * - show: function(event, tooltip) to show the tooltip. Defaults to a show(100) effect 8 | * - hide: function(event, tooltip) to hide the tooltip. Defaults to a fadeOut(100) effect 9 | * - delay: A delay in milliseconds before showing a tooltip. Set to 0 for no delay. Defaults to 200 10 | * - css: object containing CSS properties and values. Defaults to {} to use stylesheet for styles 11 | * - className: DOM class for styling tooltips with CSS. Defaults to "tooltipsy" 12 | * - showEvent: Set a custom event to bind the show function. Defaults to mouseenter 13 | * - hideEvent: Set a custom event to bind the show function. Defaults to mouseleave 14 | * Method quick reference: 15 | * - $('element').data('tooltipsy').show(): Force the tooltip to show 16 | * - $('element').data('tooltipsy').hide(): Force the tooltip to hide 17 | * - $('element').data('tooltipsy').destroy(): Remove tooltip from DOM 18 | * More information visit http://tooltipsy.com/ 19 | */ 20 | ;(function($){$.tooltipsy=function(el,options){this.options=options;this.$el=$(el);this.title=this.$el.attr('title')||'';this.$el.attr('title','');this.random=parseInt(Math.random()*10000);this.ready=!1;this.shown=!1;this.width=0;this.height=0;this.delaytimer=null;this.$el.data("tooltipsy",this);this.init()};$.tooltipsy.prototype={init:function(){var base=this,settings,$el=base.$el,el=$el[0];base.settings=settings=$.extend({},base.defaults,base.options);settings.delay=+settings.delay;if(typeof settings.content==='function'){base.readify()}if(settings.showEvent===settings.hideEvent&&settings.showEvent==='click'){$el.toggle(function(e){if(settings.showEvent==='click'&&el.tagName=='A'){e.preventDefault()}if(settings.delay>0){base.delaytimer=window.setTimeout(function(){base.show(e)},settings.delay)}else{base.show(e)}},function(e){if(settings.showEvent==='click'&&el.tagName=='A'){e.preventDefault()}window.clearTimeout(base.delaytimer);base.delaytimer=null;base.hide(e)})}else{$el.bind(settings.showEvent,function(e){if(settings.showEvent==='click'&&el.tagName=='A'){e.preventDefault()}base.delaytimer=window.setTimeout(function(){base.show(e)},settings.delay||0)}).bind(settings.hideEvent,function(e){if(settings.showEvent==='click'&&el.tagName=='A'){e.preventDefault()}window.clearTimeout(base.delaytimer);base.delaytimer=null;base.hide(e)})}},show:function(e){if(this.ready===!1){this.readify()}var base=this,settings=base.settings,$tipsy=base.$tipsy,$el=base.$el,el=$el[0],offset=base.offset(el);if(base.shown===!1){if((function(o){var s=0,k;for(k in o){if(o.hasOwnProperty(k)){s++}}return s})(settings.css)>0){base.$tip.css(settings.css)}base.width=$tipsy.outerWidth();base.height=$tipsy.outerHeight()}if(settings.alignTo==='cursor'&&e){var tip_position=[e.clientX+settings.offset[0],e.clientY+settings.offset[1]];if(tip_position[0]+base.width>$(window).width()){var tip_css={top:tip_position[1]+'px',right:tip_position[0]+'px',left:'auto'}}else{var tip_css={top:tip_position[1]+'px',left:tip_position[0]+'px',right:'auto'}}}else{var tip_position=[(function(){if(settings.offset[0]<0){return offset.left-Math.abs(settings.offset[0])-base.width}else if(settings.offset[0]===0){return offset.left-((base.width-$el.outerWidth())/2)}else{return offset.left+$el.outerWidth()+settings.offset[0]}})(),(function(){if(settings.offset[1]<0){return offset.top-Math.abs(settings.offset[1])-base.height}else if(settings.offset[1]===0){return offset.top-((base.height-base.$el.outerHeight())/2)}else{return offset.top+base.$el.outerHeight()+settings.offset[1]}})()]}$tipsy.css({top:tip_position[1]+'px',left:tip_position[0]+'px'});base.settings.show(e,$tipsy.stop(!0,!0))},hide:function(e){var base=this;if(base.ready===!1){return}if(e&&e.relatedTarget===base.$tip[0]){base.$tip.bind('mouseleave',function(e){if(e.relatedTarget===base.$el[0]){return}base.settings.hide(e,base.$tipsy.stop(!0,!0))});return}base.settings.hide(e,base.$tipsy.stop(!0,!0))},readify:function(){this.ready=!0;this.$tipsy=$('