From 4fb7df162b08c2f4f173689156151cb192892d68 Mon Sep 17 00:00:00 2001 From: Hai Liang Wang Date: Wed, 5 Sep 2018 16:13:48 +0800 Subject: [PATCH] =?UTF-8?q?#51=20=E5=A2=9E=E5=8A=A0elasticsearch=E5=AE=B9?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 17 + README.md | 2 +- docker-compose.yml | 10 + elasticsearch/config/elasticsearch.yml | 12 + elasticsearch/config/logging.yml | 85 + elasticsearch/config/scripts/.gitignore | 2 + elasticsearch/data/.gitignore | 2 + elasticsearch/plugins/head/.gitignore | 6 + elasticsearch/plugins/head/.jshintrc | 40 + elasticsearch/plugins/head/Gruntfile.js | 115 + elasticsearch/plugins/head/LICENCE | 13 + elasticsearch/plugins/head/README.textile | 98 + elasticsearch/plugins/head/_site/app.css | 704 + elasticsearch/plugins/head/_site/app.js | 4387 +++++ .../plugins/head/_site/base/favicon.png | Bin 0 -> 952 bytes .../plugins/head/_site/base/loading.gif | Bin 0 -> 1849 bytes .../plugins/head/_site/base/reset.css | 44 + .../plugins/head/_site/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes .../head/_site/fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../head/_site/fonts/fontawesome-webfont.svg | 414 + .../head/_site/fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../head/_site/fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes elasticsearch/plugins/head/_site/i18n.js | 93 + elasticsearch/plugins/head/_site/index.html | 33 + .../plugins/head/_site/lang/en_strings.js | 177 + .../plugins/head/_site/lang/fr_strings.js | 164 + .../plugins/head/_site/lang/pt_strings.js | 174 + .../plugins/head/_site/lang/tr_strings.js | 177 + .../plugins/head/_site/lang/zh_strings.js | 172 + elasticsearch/plugins/head/_site/vendor.css | 1338 ++ elasticsearch/plugins/head/_site/vendor.js | 14500 ++++++++++++++++ .../head/elasticsearch-head.sublime-project | 9 + elasticsearch/plugins/head/grunt_fileSets.js | 102 + elasticsearch/plugins/head/index.html | 33 + elasticsearch/plugins/head/package.json | 31 + .../plugins/head/plugin-descriptor.properties | 4 + elasticsearch/plugins/head/src/app/app.css | 39 + elasticsearch/plugins/head/src/app/app.js | 132 + .../plugins/head/src/app/base/boot.js | 241 + .../plugins/head/src/app/base/favicon.png | Bin 0 -> 952 bytes .../plugins/head/src/app/base/loading.gif | Bin 0 -> 1849 bytes .../plugins/head/src/app/base/reset.css | 44 + .../plugins/head/src/app/data/boolQuery.js | 75 + .../head/src/app/data/dataSourceInterface.js | 24 + .../plugins/head/src/app/data/metaData.js | 170 + .../head/src/app/data/metaDataFactory.js | 19 + .../plugins/head/src/app/data/model/model.js | 35 + .../head/src/app/data/model/modelSpec.js | 74 + .../plugins/head/src/app/data/query.js | 204 + .../src/app/data/queryDataSourceInterface.js | 87 + .../src/app/data/resultDataSourceInterface.js | 40 + .../plugins/head/src/app/lang/en_strings.js | 177 + .../plugins/head/src/app/lang/fr_strings.js | 164 + .../plugins/head/src/app/lang/pt_strings.js | 174 + .../plugins/head/src/app/lang/tr_strings.js | 177 + .../plugins/head/src/app/lang/zh_strings.js | 172 + .../head/src/app/services/cluster/cluster.js | 47 + .../src/app/services/cluster/clusterSpec.js | 65 + .../app/services/clusterState/clusterState.js | 73 + .../services/clusterState/clusterStateSpec.js | 62 + .../services/preferences/preferenceSpec.js | 23 + .../app/services/preferences/preferences.js | 35 + .../app/ui/abstractField/abstractField.css | 1 + .../src/app/ui/abstractField/abstractField.js | 49 + .../app/ui/abstractPanel/abstractPanel.css | 43 + .../src/app/ui/abstractPanel/abstractPanel.js | 83 + .../app/ui/abstractWidget/abstractWidget.js | 49 + .../head/src/app/ui/anyRequest/anyRequest.css | 38 + .../head/src/app/ui/anyRequest/anyRequest.js | 235 + .../head/src/app/ui/browser/browser.css | 7 + .../head/src/app/ui/browser/browser.js | 58 + .../plugins/head/src/app/ui/button/button.css | 50 + .../plugins/head/src/app/ui/button/button.js | 53 + .../head/src/app/ui/button/buttonDemo.js | 9 + .../head/src/app/ui/checkField/checkField.js | 25 + .../src/app/ui/checkField/checkFieldDemo.js | 45 + .../src/app/ui/checkField/checkFieldSpec.js | 34 + .../app/ui/clusterConnect/clusterConnect.css | 3 + .../app/ui/clusterConnect/clusterConnect.js | 43 + .../ui/clusterConnect/clusterConnectSpec.js | 38 + .../ui/clusterOverview/clusterOverview.css | 0 .../app/ui/clusterOverview/clusterOverview.js | 302 + .../head/src/app/ui/csvTable/csvTable.js | 84 + .../src/app/ui/dateHistogram/dateHistogram.js | 104 + .../src/app/ui/dialogPanel/dialogPanel.js | 22 + .../app/ui/draggablePanel/draggablePanel.js | 42 + .../app/ui/filterBrowser/filterBrowser.css | 9 + .../src/app/ui/filterBrowser/filterBrowser.js | 205 + .../plugins/head/src/app/ui/header/header.css | 10 + .../plugins/head/src/app/ui/header/header.js | 73 + .../head/src/app/ui/helpPanel/helpPanel.js | 21 + .../src/app/ui/indexOverview/indexOverview.js | 115 + .../src/app/ui/indexSelector/indexSelector.js | 45 + .../head/src/app/ui/infoPanel/infoPanel.css | 38 + .../head/src/app/ui/infoPanel/infoPanel.js | 9 + .../head/src/app/ui/jsonPanel/jsonPanel.css | 4 + .../head/src/app/ui/jsonPanel/jsonPanel.js | 24 + .../head/src/app/ui/jsonPretty/jsonPretty.css | 22 + .../head/src/app/ui/jsonPretty/jsonPretty.js | 89 + .../head/src/app/ui/menuButton/menuButton.css | 11 + .../head/src/app/ui/menuButton/menuButton.js | 22 + .../head/src/app/ui/menuPanel/menuPanel.css | 30 + .../head/src/app/ui/menuPanel/menuPanel.js | 43 + .../head/src/app/ui/nodesView/nodesView.css | 88 + .../head/src/app/ui/nodesView/nodesView.js | 277 + .../src/app/ui/nodesView/nodesViewDemo.js | 17 + .../plugins/head/src/app/ui/page/page.js | 14 + .../head/src/app/ui/panelForm/panelForm.css | 12 + .../head/src/app/ui/panelForm/panelForm.js | 26 + .../src/app/ui/queryFilter/queryFilter.css | 62 + .../src/app/ui/queryFilter/queryFilter.js | 277 + .../src/app/ui/refreshButton/refreshButton.js | 41 + .../app/ui/refreshButton/refreshButtonDemo.js | 12 + .../app/ui/refreshButton/refreshButtonSpec.js | 92 + .../src/app/ui/resultTable/resultTable.js | 55 + .../ui/selectMenuPanel/selectMenuPanel.css | 11 + .../app/ui/selectMenuPanel/selectMenuPanel.js | 33 + .../app/ui/sidebarSection/sidebarSection.css | 28 + .../app/ui/sidebarSection/sidebarSection.js | 41 + .../src/app/ui/splitButton/splitButton.css | 33 + .../src/app/ui/splitButton/splitButton.js | 54 + .../src/app/ui/splitButton/splitButtonDemo.js | 16 + .../ui/structuredQuery/structuredQuery.css | 7 + .../app/ui/structuredQuery/structuredQuery.js | 95 + .../plugins/head/src/app/ui/table/table.css | 92 + .../plugins/head/src/app/ui/table/table.js | 102 + .../head/src/app/ui/textField/textField.js | 24 + .../src/app/ui/textField/textFieldDemo.js | 13 + .../head/src/app/ui/toolbar/toolbar.css | 24 + .../head/src/app/ui/toolbar/toolbar.js | 25 + .../plugins/head/src/app/ux/class.js | 50 + .../plugins/head/src/app/ux/dragdrop.js | 124 + .../head/src/app/ux/fieldCollection.js | 25 + .../plugins/head/src/app/ux/observable.js | 46 + .../plugins/head/src/app/ux/singleton.js | 21 + .../plugins/head/src/app/ux/singletonSpec.js | 41 + .../plugins/head/src/app/ux/table.css | 20 + .../head/src/app/ux/templates/templateSpec.js | 81 + .../head/src/app/ux/templates/templates.js | 32 + .../dateRangeParser/date-range-parser.js | 203 + .../vendor/font-awesome/css/font-awesome.css | 1338 ++ .../font-awesome/css/font-awesome.min.css | 4 + .../vendor/font-awesome/fonts/FontAwesome.otf | Bin 0 -> 62856 bytes .../fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes .../fonts/fontawesome-webfont.svg | 414 + .../fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes .../fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes .../vendor/graphael/g.raphael.standalone.js | 5205 ++++++ .../plugins/head/src/vendor/i18n/i18n.js | 93 + .../plugins/head/src/vendor/joey/joey.js | 57 + .../plugins/head/src/vendor/jquery/jquery.js | 8936 ++++++++++ .../head/src/vendor/nohtml/jquery-nohtml.js | 96 + elasticsearch/plugins/head/test/demo.html | 34 + .../head/test/generators/conflictingField.sh | 50 + .../test/generators/delete_all_indices.sh | 6 + .../head/test/generators/multi_type.sh | 75 + .../head/test/generators/twitter_feed.sh | 45 + .../head/test/generators/twitter_river.sh | 28 + elasticsearch/plugins/head/test/perf.html | 54 + .../plugins/head/test/spec/specHelper.js | 81 + scripts/dev.sh | 13 + 161 files changed, 46240 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 elasticsearch/config/elasticsearch.yml create mode 100644 elasticsearch/config/logging.yml create mode 100644 elasticsearch/config/scripts/.gitignore create mode 100644 elasticsearch/data/.gitignore create mode 100644 elasticsearch/plugins/head/.gitignore create mode 100644 elasticsearch/plugins/head/.jshintrc create mode 100644 elasticsearch/plugins/head/Gruntfile.js create mode 100644 elasticsearch/plugins/head/LICENCE create mode 100644 elasticsearch/plugins/head/README.textile create mode 100644 elasticsearch/plugins/head/_site/app.css create mode 100644 elasticsearch/plugins/head/_site/app.js create mode 100644 elasticsearch/plugins/head/_site/base/favicon.png create mode 100644 elasticsearch/plugins/head/_site/base/loading.gif create mode 100644 elasticsearch/plugins/head/_site/base/reset.css create mode 100644 elasticsearch/plugins/head/_site/fonts/FontAwesome.otf create mode 100644 elasticsearch/plugins/head/_site/fonts/fontawesome-webfont.eot create mode 100644 elasticsearch/plugins/head/_site/fonts/fontawesome-webfont.svg create mode 100644 elasticsearch/plugins/head/_site/fonts/fontawesome-webfont.ttf create mode 100644 elasticsearch/plugins/head/_site/fonts/fontawesome-webfont.woff create mode 100644 elasticsearch/plugins/head/_site/i18n.js create mode 100644 elasticsearch/plugins/head/_site/index.html create mode 100644 elasticsearch/plugins/head/_site/lang/en_strings.js create mode 100644 elasticsearch/plugins/head/_site/lang/fr_strings.js create mode 100644 elasticsearch/plugins/head/_site/lang/pt_strings.js create mode 100644 elasticsearch/plugins/head/_site/lang/tr_strings.js create mode 100644 elasticsearch/plugins/head/_site/lang/zh_strings.js create mode 100644 elasticsearch/plugins/head/_site/vendor.css create mode 100644 elasticsearch/plugins/head/_site/vendor.js create mode 100644 elasticsearch/plugins/head/elasticsearch-head.sublime-project create mode 100644 elasticsearch/plugins/head/grunt_fileSets.js create mode 100644 elasticsearch/plugins/head/index.html create mode 100644 elasticsearch/plugins/head/package.json create mode 100644 elasticsearch/plugins/head/plugin-descriptor.properties create mode 100644 elasticsearch/plugins/head/src/app/app.css create mode 100644 elasticsearch/plugins/head/src/app/app.js create mode 100644 elasticsearch/plugins/head/src/app/base/boot.js create mode 100644 elasticsearch/plugins/head/src/app/base/favicon.png create mode 100644 elasticsearch/plugins/head/src/app/base/loading.gif create mode 100644 elasticsearch/plugins/head/src/app/base/reset.css create mode 100644 elasticsearch/plugins/head/src/app/data/boolQuery.js create mode 100644 elasticsearch/plugins/head/src/app/data/dataSourceInterface.js create mode 100644 elasticsearch/plugins/head/src/app/data/metaData.js create mode 100644 elasticsearch/plugins/head/src/app/data/metaDataFactory.js create mode 100644 elasticsearch/plugins/head/src/app/data/model/model.js create mode 100644 elasticsearch/plugins/head/src/app/data/model/modelSpec.js create mode 100644 elasticsearch/plugins/head/src/app/data/query.js create mode 100644 elasticsearch/plugins/head/src/app/data/queryDataSourceInterface.js create mode 100644 elasticsearch/plugins/head/src/app/data/resultDataSourceInterface.js create mode 100644 elasticsearch/plugins/head/src/app/lang/en_strings.js create mode 100644 elasticsearch/plugins/head/src/app/lang/fr_strings.js create mode 100644 elasticsearch/plugins/head/src/app/lang/pt_strings.js create mode 100644 elasticsearch/plugins/head/src/app/lang/tr_strings.js create mode 100644 elasticsearch/plugins/head/src/app/lang/zh_strings.js create mode 100644 elasticsearch/plugins/head/src/app/services/cluster/cluster.js create mode 100644 elasticsearch/plugins/head/src/app/services/cluster/clusterSpec.js create mode 100644 elasticsearch/plugins/head/src/app/services/clusterState/clusterState.js create mode 100644 elasticsearch/plugins/head/src/app/services/clusterState/clusterStateSpec.js create mode 100644 elasticsearch/plugins/head/src/app/services/preferences/preferenceSpec.js create mode 100644 elasticsearch/plugins/head/src/app/services/preferences/preferences.js create mode 100644 elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.css create mode 100644 elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.js create mode 100644 elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.css create mode 100644 elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/abstractWidget/abstractWidget.js create mode 100644 elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.css create mode 100644 elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.js create mode 100644 elasticsearch/plugins/head/src/app/ui/browser/browser.css create mode 100644 elasticsearch/plugins/head/src/app/ui/browser/browser.js create mode 100644 elasticsearch/plugins/head/src/app/ui/button/button.css create mode 100644 elasticsearch/plugins/head/src/app/ui/button/button.js create mode 100644 elasticsearch/plugins/head/src/app/ui/button/buttonDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/checkField/checkField.js create mode 100644 elasticsearch/plugins/head/src/app/ui/checkField/checkFieldDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/checkField/checkFieldSpec.js create mode 100644 elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.css create mode 100644 elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.js create mode 100644 elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnectSpec.js create mode 100644 elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.css create mode 100644 elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.js create mode 100644 elasticsearch/plugins/head/src/app/ui/csvTable/csvTable.js create mode 100644 elasticsearch/plugins/head/src/app/ui/dateHistogram/dateHistogram.js create mode 100644 elasticsearch/plugins/head/src/app/ui/dialogPanel/dialogPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/draggablePanel/draggablePanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.css create mode 100644 elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.js create mode 100644 elasticsearch/plugins/head/src/app/ui/header/header.css create mode 100644 elasticsearch/plugins/head/src/app/ui/header/header.js create mode 100644 elasticsearch/plugins/head/src/app/ui/helpPanel/helpPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/indexOverview/indexOverview.js create mode 100644 elasticsearch/plugins/head/src/app/ui/indexSelector/indexSelector.js create mode 100644 elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.css create mode 100644 elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.css create mode 100644 elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.css create mode 100644 elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.js create mode 100644 elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.css create mode 100644 elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.js create mode 100644 elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.css create mode 100644 elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.css create mode 100644 elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.js create mode 100644 elasticsearch/plugins/head/src/app/ui/nodesView/nodesViewDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/page/page.js create mode 100644 elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.css create mode 100644 elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.js create mode 100644 elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.css create mode 100644 elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.js create mode 100644 elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButton.js create mode 100644 elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonSpec.js create mode 100644 elasticsearch/plugins/head/src/app/ui/resultTable/resultTable.js create mode 100644 elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.css create mode 100644 elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.js create mode 100644 elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.css create mode 100644 elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.js create mode 100644 elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.css create mode 100644 elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.js create mode 100644 elasticsearch/plugins/head/src/app/ui/splitButton/splitButtonDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.css create mode 100644 elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.js create mode 100644 elasticsearch/plugins/head/src/app/ui/table/table.css create mode 100644 elasticsearch/plugins/head/src/app/ui/table/table.js create mode 100644 elasticsearch/plugins/head/src/app/ui/textField/textField.js create mode 100644 elasticsearch/plugins/head/src/app/ui/textField/textFieldDemo.js create mode 100644 elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.css create mode 100644 elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.js create mode 100644 elasticsearch/plugins/head/src/app/ux/class.js create mode 100644 elasticsearch/plugins/head/src/app/ux/dragdrop.js create mode 100644 elasticsearch/plugins/head/src/app/ux/fieldCollection.js create mode 100644 elasticsearch/plugins/head/src/app/ux/observable.js create mode 100644 elasticsearch/plugins/head/src/app/ux/singleton.js create mode 100644 elasticsearch/plugins/head/src/app/ux/singletonSpec.js create mode 100644 elasticsearch/plugins/head/src/app/ux/table.css create mode 100644 elasticsearch/plugins/head/src/app/ux/templates/templateSpec.js create mode 100644 elasticsearch/plugins/head/src/app/ux/templates/templates.js create mode 100644 elasticsearch/plugins/head/src/vendor/dateRangeParser/date-range-parser.js create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.css create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.min.css create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/fonts/FontAwesome.otf create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/fonts/fontawesome-webfont.eot create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/fonts/fontawesome-webfont.svg create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/fonts/fontawesome-webfont.ttf create mode 100644 elasticsearch/plugins/head/src/vendor/font-awesome/fonts/fontawesome-webfont.woff create mode 100644 elasticsearch/plugins/head/src/vendor/graphael/g.raphael.standalone.js create mode 100644 elasticsearch/plugins/head/src/vendor/i18n/i18n.js create mode 100644 elasticsearch/plugins/head/src/vendor/joey/joey.js create mode 100644 elasticsearch/plugins/head/src/vendor/jquery/jquery.js create mode 100644 elasticsearch/plugins/head/src/vendor/nohtml/jquery-nohtml.js create mode 100644 elasticsearch/plugins/head/test/demo.html create mode 100644 elasticsearch/plugins/head/test/generators/conflictingField.sh create mode 100644 elasticsearch/plugins/head/test/generators/delete_all_indices.sh create mode 100644 elasticsearch/plugins/head/test/generators/multi_type.sh create mode 100644 elasticsearch/plugins/head/test/generators/twitter_feed.sh create mode 100644 elasticsearch/plugins/head/test/generators/twitter_river.sh create mode 100644 elasticsearch/plugins/head/test/perf.html create mode 100644 elasticsearch/plugins/head/test/spec/specHelper.js create mode 100755 scripts/dev.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8926ab39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.swp +*.swo +*.sublime-* +*.pyc +jmeter.log +__pycache__ +tmp/ +node_modules/ +sftp-config.json +.DS_Store +*.iml +*.ipr +*.iws +*.idea +~$*.xls* +~$*.ppt* +~$*.doc* diff --git a/README.md b/README.md index 47241869..2c64d95a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ * 外呼系统:自动外呼,手动外呼,监听和报表等 - 《春松客服产品系列视频》 + 《春松客服产品系列视频》 diff --git a/docker-compose.yml b/docker-compose.yml index a3caf7f6..22aac598 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,6 +24,16 @@ services: ports: - "6379:6379" + elasticsearch: + image: "elasticsearch:2.4.6" + volumes: + - $PWD/elasticsearch/data:/usr/share/elasticsearch/data + - $PWD/elasticsearch/config:/usr/share/elasticsearch/config + - $PWD/elasticsearch/plugins:/usr/share/elasticsearch/plugins + ports: + - "9200:9200" + - "9300:9300" + contact-center: image: "chatopera/contact-center:develop" restart: always diff --git a/elasticsearch/config/elasticsearch.yml b/elasticsearch/config/elasticsearch.yml new file mode 100644 index 00000000..5135487d --- /dev/null +++ b/elasticsearch/config/elasticsearch.yml @@ -0,0 +1,12 @@ +http.host: 0.0.0.0 +# Consider to enable cors features for elasticsearch head +# https://github.com/mobz/elasticsearch-head#enable-cors-in-elasticsearch +http.cors.enabled: true +http.cors.allow-origin: "*" + +# Uncomment the following lines for a production cluster deployment +# #transport.host: 0.0.0.0 +# #discovery.zen.minimum_master_nodes: 1 +cluster.routing.allocation.disk.threshold_enabled: true +cluster.routing.allocation.disk.watermark.low: 1gb +cluster.routing.allocation.disk.watermark.high: 1gb diff --git a/elasticsearch/config/logging.yml b/elasticsearch/config/logging.yml new file mode 100644 index 00000000..2bf9e813 --- /dev/null +++ b/elasticsearch/config/logging.yml @@ -0,0 +1,85 @@ +# you can override this using by setting a system property, for example -Des.logger.level=DEBUG +es.logger.level: DEBUG +rootLogger: ${es.logger.level}, console, file +logger: + # log action execution errors for easier debugging + action: DEBUG + + # deprecation logging, turn to DEBUG to see them + deprecation: INFO, deprecation_log_file + + # reduce the logging for aws, too much is logged under the default INFO + com.amazonaws: WARN + # aws will try to do some sketchy JMX stuff, but its not needed. + com.amazonaws.jmx.SdkMBeanRegistrySupport: ERROR + com.amazonaws.metrics.AwsSdkMetrics: ERROR + + org.apache.http: INFO + + # gateway + #gateway: DEBUG + #index.gateway: DEBUG + + # peer shard recovery + #indices.recovery: DEBUG + + # discovery + #discovery: TRACE + + index.search.slowlog: TRACE, index_search_slow_log_file + index.indexing.slowlog: TRACE, index_indexing_slow_log_file + +additivity: + index.search.slowlog: false + index.indexing.slowlog: false + deprecation: false + +appender: + console: + type: console + layout: + type: consolePattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.10000m%n" + + # Use the following log4j-extras RollingFileAppender to enable gzip compression of log files. + # For more information see https://logging.apache.org/log4j/extras/apidocs/org/apache/log4j/rolling/RollingFileAppender.html + #file: + #type: extrasRollingFile + #file: ${path.logs}/${cluster.name}.log + #rollingPolicy: timeBased + #rollingPolicy.FileNamePattern: ${path.logs}/${cluster.name}.log.%d{yyyy-MM-dd}.gz + #layout: + #type: pattern + #conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + deprecation_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_deprecation.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_search_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_search_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" + + index_indexing_slow_log_file: + type: dailyRollingFile + file: ${path.logs}/${cluster.name}_index_indexing_slowlog.log + datePattern: "'.'yyyy-MM-dd" + layout: + type: pattern + conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n" diff --git a/elasticsearch/config/scripts/.gitignore b/elasticsearch/config/scripts/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/elasticsearch/config/scripts/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/elasticsearch/data/.gitignore b/elasticsearch/data/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/elasticsearch/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/elasticsearch/plugins/head/.gitignore b/elasticsearch/plugins/head/.gitignore new file mode 100644 index 00000000..37acc96d --- /dev/null +++ b/elasticsearch/plugins/head/.gitignore @@ -0,0 +1,6 @@ +.idea +*.iml +*.tmproj +*.sublime-workspace +.project +node_modules \ No newline at end of file diff --git a/elasticsearch/plugins/head/.jshintrc b/elasticsearch/plugins/head/.jshintrc new file mode 100644 index 00000000..757f9692 --- /dev/null +++ b/elasticsearch/plugins/head/.jshintrc @@ -0,0 +1,40 @@ +{ + "maxerr": 1000, + + "predef": [ + "exports", + "describe", + "expect", + "it", + "beforeEach", + "afterEach", + "jasmine", + "test", + "spyOn" + ], + + "jquery" : true, + "browser" : true, + + "curly": true, + "debug": false, + "devel": true, + "eqeqeq": true, + "eqnull": true, + "expr": true, + "forin": false, + "immed": false, + "latedef": true, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "white": false, + "scripturl": true, + "esnext": true +} diff --git a/elasticsearch/plugins/head/Gruntfile.js b/elasticsearch/plugins/head/Gruntfile.js new file mode 100644 index 00000000..d9607ac7 --- /dev/null +++ b/elasticsearch/plugins/head/Gruntfile.js @@ -0,0 +1,115 @@ +module.exports = function(grunt) { + + var fileSets = require("./grunt_fileSets.js"); + + // Project configuration. + grunt.initConfig({ + clean: { + _site: { + src: ['_site'] + } + }, + concat: { + vendorjs: { + src: fileSets.vendorJs, + dest: '_site/vendor.js' + }, + vendorcss: { + src: fileSets.vendorCss, + dest: '_site/vendor.css' + }, + appjs: { + src: fileSets.srcJs, + dest: '_site/app.js' + }, + appcss: { + src: fileSets.srcCss, + dest: '_site/app.css' + } + }, + + copy: { + site_index: { + src: 'index.html', + dest: '_site/index.html', + options: { + process: function( src ) { + return src.replace(/_site\//g, ""); + } + } + }, + base: { + expand: true, + cwd: 'src/app/base/', + src: [ '*.gif', '*.png', '*.css' ], + dest: '_site/base/' + }, + iconFonts: { + expand: true, + cwd: 'src/vendor/font-awesome/fonts/', + src: '**', + dest: '_site/fonts' + }, + i18n: { + src: 'src/vendor/i18n/i18n.js', + dest: '_site/i18n.js' + }, + lang: { + expand: true, + cwd: 'src/app/lang/', + src: '**', + dest: '_site/lang/' + } + }, + + jasmine: { + task: { + src: [ fileSets.vendorJs, 'src/vendor/i18n/i18n.js', 'src/app/lang/en_strings.js', fileSets.srcJs ], + options: { + specs: 'src/app/**/*Spec.js', + helpers: 'test/spec/*Helper.js', + display: "short", + summary: true + } + } + }, + + watch: { + "scripts": { + files: ['src/**/*', 'test/spec/*' ], + tasks: ['default'], + options: { + spawn: false + } + }, + "grunt": { + files: [ 'Gruntfile.js' ] + } + }, + + connect: { + server: { + options: { + port: 9100, + base: '.', + keepalive: true + } + } + } + + }); + + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-jasmine'); + + // Default task(s). + grunt.registerTask('default', ['clean', 'concat', 'copy', 'jasmine']); + grunt.registerTask('server', ['connect:server']); + grunt.registerTask('dev', [ 'default', 'watch' ]); + + +}; diff --git a/elasticsearch/plugins/head/LICENCE b/elasticsearch/plugins/head/LICENCE new file mode 100644 index 00000000..a93b4df4 --- /dev/null +++ b/elasticsearch/plugins/head/LICENCE @@ -0,0 +1,13 @@ +Copyright 2010-2013 Ben Birch + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/elasticsearch/plugins/head/README.textile b/elasticsearch/plugins/head/README.textile new file mode 100644 index 00000000..c46a3bd3 --- /dev/null +++ b/elasticsearch/plugins/head/README.textile @@ -0,0 +1,98 @@ +h1. elasticsearch-head + +h2. A web front end for an Elasticsearch cluster + +h3. "http://mobz.github.io/elasticsearch-head":http://mobz.github.io/elasticsearch-head + +h2. Installing and Running + +There are two main ways of running and installing elasticsearch-head + +h4. Running as a plugin of Elasticsearch + +* Install elasticsearch-head: + - for Elasticsearch 5.x: + site plugins are not supported. Run elasticsearch-head "as a standalone server":#running-with-built-in-server + - for Elasticsearch 2.x - 4.x: + @sudo elasticsearch/bin/plugin install mobz/elasticsearch-head@ + - for Elasticsearch 1.x: + @sudo elasticsearch/bin/plugin -install mobz/elasticsearch-head/1.x@ + - for Elasticsearch 0.9: + @sudo elasticsearch/bin/plugin -install mobz/elasticsearch-head/0.9@ + +* @open http://localhost:9200/_plugin/head/@ + +This will automatically download the latest version of elasticsearch-head from github and run it as a plugin within the elasticsearch cluster. In this mode; +* elasticsearch-head automatically connects to the node that is running it + +Note on different Elasticsearch installation path: +* If you've installed the .deb package, then the plugin executable will be available at @/usr/share/elasticsearch/bin/plugin@. +* If you've installed Elasticsearch via Homebrew, plugin executable will be available at @/usr/local/Cellar/elasticsearch/(elasticsearch version)/bin/plugin@. + +h4. Running with built in server + +* enable "cors":http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-http.html by adding @http.cors.enabled: true@ in elasticsearch configuration. Don't forget to also set @http.cors.allow-origin@ because no origin allowed by default. @http.cors.allow-origin: "*"@ is valid value, however it's considered as a security risk as your cluster is open to cross origin from *anywhere*. Check Elasticsearch documentation on this parameter: https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-http.html#modules-http +* @git clone git://github.com/mobz/elasticsearch-head.git@ +* @cd elasticsearch-head@ +* @npm install@ +* @grunt server@ +* @open@ "http://localhost:9100/":http://localhost:9100/ + +This will start a local webserver running on port 9100 serving elasticsearch-head +* Best option if you are likely to connect to several different clusters + +h4. Alternatives + +h5. Running from the filesystem + +elastisearch-head is a standalone webapp written in good-ol' html5. This means, you can put it up on any webserver, run it directly from the filesystem, use it on an iPad, or put it on a floppy disk and carry it with you. + +h4. URL Parameters + +Parameters may be appended to the url to set an initial state eg. @head/index.html?base_uri=http://node-01.example.com:9200@ + +* @base_uri@ force elasticsearch-head to connect to a particular node. +* @dashboard@ experimental feature to open elasticsearch-head in a mode suitable for dashboard / radiator. Accepts one parameter @dashboard=cluster@ +* @auth_user@ adds basic auth credentials to http requests ( requires "elasticsearch-http-basic":https://github.com/karussell/elasticsearch-http-basic plugin or a reverse proxy ) +* @auth_password@ basic auth password as above (note: without "additional security layers":http://security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https, passwords are sent over the network *in the clear* ) + +h4. Contributing + +To contribute to elasticsearch-head you will need the following developer tools + +# git and a "github":https://github.com/ account +# "node ( including npm )":http://nodejs.org/download +# "grunt-cli":http://gruntjs.com/getting-started +# (to run jasmine tests) "phantomjs":http://phantomjs.org + +Then + +# create a fork of elasticsearch-head on github +# clone your fork to your machine +# @cd elasticsearch-head@ +# @npm install@ # downloads node dev dependencies +# @grunt dev@ # builds the distribution files, then watches the src directory for changes + +Changes to both _site and src directories *must* be committed, to allow people to +run elasticsearch-head without running dev tools and follow existing dev patterns, +such as indenting with tabs. + +h5. Contributing an Internationalisation + + +* Chinese by "darkcount":https://github.com/hangxin1940 +* English (primary) by "Ben Birch":https://twitter.com/mobz +* French by "David Pilato":https://twitter.com/dadoonet +* Portuguese by "caiodangelo":https://github.com/caiodangelo +* Turkish by "Cemre Mengu":https://github.com/cemremengu + +To contribute an internationalisation + +# Follow "Contributing" instructions above +# Find your 2-character "ISO 639-1 language code":http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +# edit _site/index.html to add your 2 letter language code to the data-langs attribute of this line @@ +# make a copy of @src/app/langs/en_strings.js@ prefixed with your language code +# convert english strings and fragments to your language. "Formatting Rules":http://docs.oracle.com/javase/tutorial/i18n/format/messageintro.html +# Submit a pull request + +!http://mobz.github.com/elasticsearch-head/screenshots/clusterOverview.png(ClusterOverview Screenshot)! diff --git a/elasticsearch/plugins/head/_site/app.css b/elasticsearch/plugins/head/_site/app.css new file mode 100644 index 00000000..2fa21522 --- /dev/null +++ b/elasticsearch/plugins/head/_site/app.css @@ -0,0 +1,704 @@ +TABLE.table { + border-collapse: collapse; +} + + +TABLE.table TH { + font-weight: normal; + text-align: left; + vertical-align: middle; +} + +TABLE.table TBODY.striped TR:nth-child(odd) { + background: #eee; +} + +TABLE.table H3 { + margin: 0; + font-weight: bold; + font-size: 140%; +} + +.require { color: #a00; } + +.uiButton { + padding: 0; + border: 0; + margin: 3px; + width: auto; + overflow: visible; + cursor: pointer; + background: transparent; +} + +.uiButton-content { + height: 20px; + border: 1px solid #668dc6; + border-radius: 2px; + background: #96c6eb; + background: -moz-linear-gradient(top, #96c6eb, #5296c7); + background: -webkit-linear-gradient(top, #96c6eb, #5296c7); + color: white; + font-weight: bold; +} + +.moz .uiButton-content { margin: 0 -2px; } + +.uiButton-label { + padding: 2px 6px; + white-space: nowrap; +} +.uiButton:hover .uiButton-content { + background: #2777ba; + background: -moz-linear-gradient(top, #6aaadf, #2777ba); + background: -webkit-linear-gradient(top, #6aaadf, #2777ba); +} +.uiButton.active .uiButton-content, +.uiButton:active .uiButton-content { + background: #2575b7; + background: -moz-linear-gradient(top, #2576b8, #2575b7); + background: -webkit-linear-gradient(top, #2576b8, #2575b7); +} +.uiButton.disabled .uiButton-content, +.uiButton.disabled:active .uiButton-content { + border-color: #c6c6c6; + color: #999999; + background: #ddd; + background: -moz-linear-gradient(top, #ddd, #ddd); + background: -webkit-linear-gradient(top, #ddd, #ddd); +} + +.uiButton.disabled { + cursor: default; +} + +.uiMenuButton { + display: inline-block; +} + +.uiMenuButton .uiButton-label { + background-image: url('data:image/gif;base64,R0lGODlhDwAPAIABAP///////yH5BAEAAAEALAAAAAAPAA8AAAITjI+py+0P4wG0gmavq1HLD4ZiAQA7'); + background-position: right 50%; + background-repeat: no-repeat; + padding-right: 17px; + text-align: left; +} + +.uiSplitButton { + white-space: nowrap; +} + +.uiSplitButton .uiButton:first-child { + margin-right: 0; + display: inline-block; +} + +.uiSplitButton .uiButton:first-child .uiButton-content { + border-right-width: 1; + border-right-color: #5296c7; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.uiSplitButton .uiMenuButton { + margin-left: 0; +} + +.uiSplitButton .uiButton:last-child .uiButton-content { + border-radius: 2px; + border-left-width: 1; + border-left-color: #96c6eb; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + height: 20px; +} + +.uiSplitButton .uiButton:last-child .uiButton-label { + padding: 2px 17px 2px 6px; + margin-left: -8px; +} + +.uiToolbar { + height: 28px; + background: #fdfefe; + background: -moz-linear-gradient(top, #fdfefe, #eaedef); + background: -webkit-linear-gradient(top, #fdfefe, #eaedef); + border-bottom: 1px solid #d2d5d7; + padding: 3px 10px; +} + +.uiToolbar H2 { + display: inline-block; + font-size: 120%; + margin: 0; + padding: 5px 20px 5px 0; +} + +.uiToolbar .uiTextField { + display: inline-block; +} + +.uiToolbar .uiTextField INPUT { + padding-top: 2px; + padding-bottom: 5px; +} +#uiModal { + background: black; +} + +.uiPanel { + box-shadow: -1px 2.5px 4px -3px black, -1px -2.5px 4px -3px black, 3px 2.5px 4px -3px black, 3px -2.5px 4px -3px black; + position: absolute; + background: #eee; + border: 1px solid #666; +} + +.uiPanel-titleBar { + text-align: center; + font-weight: bold; + padding: 2px 0; + background: rgba(223, 223, 223, 0.75); + background: -moz-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); + background: -webkit-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); + border-bottom: 1px solid #bbb; +} + +.uiPanel-close { + cursor: pointer; + border: 1px solid #aaa; + background: #fff; + color: #fff; + float: left; + height: 10px; + left: 3px; + line-height: 9px; + padding: 1px 0; + position: relative; + text-shadow: 0 0 1px #000; + top: 0px; + width: 12px; +} +.uiPanel-close:hover { + background: #eee; +} + +.uiPanel-body { + overflow: auto; +} + + +.uiInfoPanel { + background: rgba(0, 0, 0, 0.75); + color: white; + border-radius: 8px; + padding: 1px; +} +.uiInfoPanel .uiPanel-titleBar { + background: rgba(74, 74, 74, 0.75); + background: -moz-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); + background: -webkit-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); + border-radius: 8px 8px 0 0; + padding: 1px 0 2px 0; + border-bottom: 0; +} +.uiInfoPanel .uiPanel-close { + border-radius: 6px; + height: 13px; + width: 13px; + background: #ccc; + left: 3px; + top: 1px; + color: #333; + text-shadow: #222 0 0 1px; + line-height: 11px; + border: 0; + padding: 0; +} +.uiInfoPanel .uiPanel-close:hover { + background: #eee; +} + +.uiInfoPanel .uiPanel-body { + background: transparent; + padding: 20px; + border-radius: 0 0 8px 8px; + border: 1px solid #222; +} + +.uiMenuPanel { + border: 1px solid #668dc6; + position: absolute; + background: #96c6eb; + color: white; +} + +.uiMenuPanel LI { + list-style: none; + border-bottom: 1px solid #668dc6; +} + +.uiMenuPanel LI:hover { + background: #2575b7; +} + +.uiMenuPanel LI:last-child { + border-bottom: 0; +} + +.uiMenuPanel-label { + white-space: nowrap; + padding: 2px 10px 2px 10px; + cursor: pointer; +} + +.disabled .uiMenuPanel-label { + cursor: auto; + color: #888; +} + +.uiSelectMenuPanel .uiMenuPanel-label { + margin-left: 1em; + padding-left: 4px; +} + +.uiSelectMenuPanel .uiMenuPanel-item.selected .uiMenuPanel-label:before { + content: "\2713"; + width: 12px; + margin-left: -12px; + display: inline-block; +} + +.uiTable TABLE { + border-collapse: collapse; +} + +.uiTable-body { + overflow-y: scroll; + overflow-x: auto; +} + +.uiTable-headers { + overflow-x: hidden; +} + +.uiTable-body TD { + white-space: nowrap; +} + +.uiTable-body .uiTable-header-row TH, +.uiTable-body .uiTable-header-row TH DIV { + padding-top: 0; + padding-bottom: 0; +} + +.uiTable-body .uiTable-header-cell > DIV { + height: 0; + overflow: hidden; +} + +.uiTable-headercell-menu { + float: right; +} + +.uiTable-tools { + padding: 3px 4px; + height: 14px; +} + +.uiTable-header-row { + background: #ddd; + background: -moz-linear-gradient(top, #eee, #ccc); + background: -webkit-linear-gradient(top, #eee, #ccc); +} + +.uiTable-headercell-text { + margin-right: 20px; +} + +.uiTable-headercell-menu { + display: none; +} + +.uiTable-header-row TH { + border-right: 1px solid #bbb; + padding: 0; + text-align: left; +} + +.uiTable-header-row TH > DIV { + padding: 3px 4px; + border-right: 1px solid #eee; +} + +.uiTable-headerEndCap > DIV { + width: 19px; +} + +.uiTable-header-row .uiTable-sort { + background: #ccc; + background: -moz-linear-gradient(top, #bebebe, #ccc); + background: -webkit-linear-gradient(top, #bebebe, #ccc); +} +.uiTable-header-row TH.uiTable-sort > DIV { + border-right: 1px solid #ccc; +} + +.uiTable-sort .uiTable-headercell-menu { + display: block; +} + +.uiTable TABLE TD { + border-right: 1px solid transparent; + padding: 3px 4px; +} + +.uiTable-body TABLE TR:nth-child(even) { + background: #f3f3f3; +} + +.uiTable-body TABLE TR.selected { + color: white; + background: #6060f1; +} + +DIV.uiJsonPretty-object { font-size: 1.26em; font-family: monospace; } +UL.uiJsonPretty-object, +UL.uiJsonPretty-array { margin: 0; padding: 0 0 0 2em; list-style: none; } +UL.uiJsonPretty-object LI, +UL.uiJsonPretty-array LI { padding: 0; margin: 0; } +.expando > SPAN.uiJsonPretty-name:before { content: "\25bc\a0"; color: #555; position: relative; top: 2px; } +.expando.uiJsonPretty-minimised > SPAN.uiJsonPretty-name:before { content: "\25ba\a0"; top: 0; } +.uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before, +.expando .uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before { content: ""; } +SPAN.uiJsonPretty-string, +SPAN.uiJsonPretty-string A { color: green; } +SPAN.uiJsonPretty-string A { text-decoration: underline;} +SPAN.uiJsonPretty-number { color: blue; } +SPAN.uiJsonPretty-null { color: red; } +SPAN.uiJsonPretty-boolean { color: purple; } +.expando > .uiJsonPretty-name { cursor: pointer; } +.expando > .uiJsonPretty-name:hover { text-decoration: underline; } +.uiJsonPretty-minimised { white-space: nowrap; overflow: hidden; } +.uiJsonPretty-minimised > UL { opacity: 0.6; } +.uiJsonPretty-minimised .uiJsonPretty-minimised > UL { opacity: 1; } +.uiJsonPretty-minimised UL, .uiJsonPretty-minimised LI { display: inline; padding: 0; } + + +.uiJsonPanel SPAN.uiJsonPretty-string { color: #6F6; } +.uiJsonPanel SPAN.uiJsonPretty-number { color: #66F; } +.uiJsonPanel SPAN.uiJsonPretty-null { color: #F66; } +.uiJsonPanel SPAN.uiJsonPretty-boolean { color: #F6F; } + +.uiPanelForm-field { + display: block; + padding: 2px 0; + clear: both; +} + +.uiPanelForm-label { + float: left; + width: 200px; + padding: 3px 7px; + text-align: right; +} + +.uiSidebarSection-head { + background-color: #b9cfff; + background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAcCAMAAABifa5OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMAUExURQUCFf///wICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0NDQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///2Oyy/cAAAACdFJOU/8A5bcwSgAAADxJREFUeNq8zzkOACAMA8Hd/3+agiuRcIsrRopIjArOoLK1QAMNNBCRPkhLyzkn35Bjfd7JR1Nr09NoDACnvgDl1zlzoQAAAABJRU5ErkJggg=='); + background-repeat: no-repeat; + background-position: 2px 5px; + margin-bottom: 1px; + padding: 3px 3px 3px 17px; + cursor: pointer; +} + +.shown > .uiSidebarSection-head { + background-position: 2px -13px; +} + +.uiSidebarSection-body { + margin-bottom: 3px; + display: none; +} + +.uiSidebarSection-help { + text-shadow: #228 1px 1px 2px; + color: blue; + cursor: pointer; +} + +.uiSidebarSection-help:hover { + text-decoration: underline; +} + +.uiQueryFilter { + width: 350px; + padding: 5px; + background: #d8e7ff; + background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); + background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); +} + +.uiQueryFilter DIV.uiQueryFilter-section { + margin-bottom: 5px; +} + +.uiQueryFilter HEADER { + display: block; + font-variant: small-caps; + font-weight: bold; + margin: 5px 0; +} + +.uiQueryFilter-aliases SELECT { + width: 100%; +} + +.uiQueryFilter-booble { + cursor: pointer; + background: #e8f1ff; + border: 1px solid #e8f1ff; + border-radius: 5px; + padding: 1px 4px; + margin-bottom: 1px; + overflow: hidden; + white-space: nowrap; +} + +.uiQueryFilter-booble.selected { + background: #dae3f0; + border-top: 1px solid #c8d4e6; + border-left: 1px solid #c8d4e6; + border-bottom: 1px solid #ffffff; + border-right: 1px solid #ffffff; +} + +.uiQueryFilter-filterName { + background-color: #cbdfff; + margin-bottom: 4px; + padding: 3px; + cursor: pointer; +} + +.uiQueryFilter-filters INPUT { + width: 300px; +} + +.uiQueryFilter-subMultiFields { + padding-left: 10px; +} + +.uiQueryFilter-rangeHintFrom, +.uiQueryFilter-rangeHintTo { + margin: 0; + opacity: 0.75; +} +.uiBrowser-filter { + float: left; +} + +.uiBrowser-table { + margin-left: 365px; +} + +.uiAnyRequest-request { + float: left; + width: 350px; + padding: 5px; + background: #d8e7ff; + background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); + background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); +} + +.uiAnyRequest-request INPUT[type=text], +.uiAnyRequest-request TEXTAREA { + width: 340px; +} + +.anyRequest INPUT[name=path] { + width: 259px; +} + +.uiAnyRequest-out { + margin-left: 365px; +} + +.uiAnyRequest-out P { + margin-top: 0; +} + +.uiAnyRequest-jsonErr { + color: red; +} + +.uiAnyRequest-history { + margin: 0; + padding: 0; + list-style: none; + max-height: 100px; + overflow-x: hidden; + overflow-y: auto; +} + +.uiNodesView TH, +.uiNodesView TD { + vertical-align: top; + padding: 2px 20px; +} + +.uiNodesView TH.close, +.uiNodesView TD.close { + color: #888; + background: #f2f2f2; +} + +.uiNodesView .uiMenuButton .uiButton-content { + padding-right: 3px; + border-radius: 8px; + height: 14px; +} + +.uiNodesView .uiMenuButton.active .uiButton-content, +.uiNodesView .uiMenuButton:active .uiButton-content { + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +.uiNodesView .uiMenuButton .uiButton-label { + padding: 0px 17px 0px 7px; +} + +.uiNodesView-hasAlias { + text-align: center; +} +.uiNodesView-hasAlias.max { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} +.uiNodesView-hasAlias.min { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +.uiNodesView-hasAlias-remove { + float: right; + font-weight: bold; + cursor: pointer; +} + +.uiNodesView TD.uiNodesView-icon { + padding: 20px 0px 15px 20px; +} + +.uiNodesView-node:nth-child(odd) { + background: #eee; +} + +.uiNodesView-routing { + position: relative; + min-width: 90px; +} + +.uiNodesView-nullReplica, +.uiNodesView-replica { + box-sizing: border-box; + cursor: pointer; + float: left; + height: 40px; + width: 35px; + margin: 4px; + border: 2px solid #444; + padding: 2px; + font-size: 32px; + line-height: 32px; + text-align: center; + letter-spacing: -5px; + text-indent: -7px; +} + +.uiNodesView-replica.primary { + border-width: 4px; + line-height: 29px; +} + +.uiNodesView-nullReplica { + border-color: transparent; +} + +.uiNodesView-replica.state-UNASSIGNED { background: #eeeeee; color: #999; border-color: #666; float: none; display: inline-block; } +.uiNodesView-replica.state-INITIALIZING { background: #dddc88; } +.uiNodesView-replica.state-STARTED { background: #99dd88; } +.uiNodesView-replica.state-RELOCATING { background: #dc88dd; } + + +.uiClusterConnect-uri { + width: 280px; +} + +.uiStructuredQuery { + padding: 10px; +} + +.uiStructuredQuery-out { + min-height: 30px; +} + +.uiFilterBrowser-row * { + margin-right: 0.4em; +} + +.uiFilterBrowser-row BUTTON { + height: 22px; + position: relative; + top: 1px; +} + +.uiHeader { + padding: 3px 10px; +} + +.uiHeader-name, .uiHeader-status { + font-size: 1.2em; + font-weight: bold; + padding: 0 10px; +} + + +.uiApp-header { + background: #eee; + position: fixed; + width: 100%; + z-index: 9; +} + +.uiApp-header H1 { + margin: -2px 0 -4px 0; + float: left; + padding-right: 25px; +} + +.uiApp-headerMenu { + border-bottom: 1px solid #bbb; + padding: 0px 3px; + height: 22px; +} + +.uiApp-headerMenu .active { + background: white; + border-bottom-color: white; +} + +.uiApp-headerMenuItem { + border: 1px solid #bbb; + padding: 4px 8px 1px ; + margin: 2px 1px 0; + height: 14px; + cursor: pointer; +} + +.uiApp-body { + padding: 51px 0px 0px 0px; +} + +.uiApp-headerNewMenuItem { + color: blue; +} diff --git a/elasticsearch/plugins/head/_site/app.js b/elasticsearch/plugins/head/_site/app.js new file mode 100644 index 00000000..8b7a5117 --- /dev/null +++ b/elasticsearch/plugins/head/_site/app.js @@ -0,0 +1,4387 @@ +(function() { + + var window = this, + $ = jQuery; + + function ns( namespace ) { + return (namespace || "").split(".").reduce( function( space, name ) { + return space[ name ] || ( space[ name ] = { ns: ns } ); + }, this ); + } + + var app = ns("app"); + + var acx = ns("acx"); + + /** + * object iterator, returns an array with one element for each property of the object + * @function + */ + acx.eachMap = function(obj, fn, thisp) { + var ret = []; + for(var n in obj) { + ret.push(fn.call(thisp, n, obj[n], obj)); + } + return ret; + }; + + /** + * augments the first argument with the properties of the second and subsequent arguments + * like {@link $.extend} except that existing properties are not overwritten + */ + acx.augment = function() { + var args = Array.prototype.slice.call(arguments), + src = (args.length === 1) ? this : args.shift(), + augf = function(n, v) { + if(! (n in src)) { + src[n] = v; + } + }; + for(var i = 0; i < args.length; i++) { + $.each(args[i], augf); + } + return src; + }; + + /** + * tests whether the argument is an array + * @function + */ + acx.isArray = $.isArray; + + /** + * tests whether the argument is an object + * @function + */ + acx.isObject = function (value) { + return Object.prototype.toString.call(value) == "[object Object]"; + }; + + /** + * tests whether the argument is a function + * @function + */ + acx.isFunction = $.isFunction; + + /** + * tests whether the argument is a date + * @function + */ + acx.isDate = function (value) { + return Object.prototype.toString.call(value) == "[object Date]"; + }; + + /** + * tests whether the argument is a regexp + * @function + */ + acx.isRegExp = function (value) { + return Object.prototype.toString.call(value) == "[object RegExp]"; + }; + + /** + * tests whether the value is blank or empty + * @function + */ + acx.isEmpty = function (value, allowBlank) { + return value === null || value === undefined || ((acx.isArray(value) && !value.length)) || (!allowBlank ? value === '' : false); + }; + + /** + * data type for performing chainable geometry calculations
+ * can be initialised x,y | {x, y} | {left, top} + */ + acx.vector = function(x, y) { + return new acx.vector.prototype.Init(x, y); + }; + + acx.vector.prototype = { + Init : function(x, y) { + x = x || 0; + this.y = isFinite(x.y) ? x.y : (isFinite(x.top) ? x.top : (isFinite(y) ? y : 0)); + this.x = isFinite(x.x) ? x.x : (isFinite(x.left) ? x.left : (isFinite(x) ? x : 0)); + }, + + add : function(i, j) { + var d = acx.vector(i, j); + return new this.Init(this.x + d.x, this.y + d.y); + }, + + sub : function(i, j) { + var d = acx.vector(i, j); + return new this.Init(this.x - d.x, this.y - d.y); + }, + + addX : function(i) { + return new this.Init(this.x + i, this.y); + }, + + addY : function(j) { + return new this.Init(this.x, this.y + j); + }, + + mod : function(fn) { // runs a function against the x and y values + return new this.Init({x: fn.call(this, this.x, "x"), y: fn.call(this, this.y, "y")}); + }, + + /** returns true if this is within a rectangle formed by the points p and q */ + within : function(p, q) { + return ( this.x >= ((p.x < q.x) ? p.x : q.x) && this.x <= ((p.x > q.x) ? p.x : q.x) && + this.y >= ((p.y < q.y) ? p.y : q.y) && this.y <= ((p.y > q.y) ? p.y : q.y) ); + }, + + asOffset : function() { + return { top: this.y, left: this.x }; + }, + + asSize : function() { + return { height: this.y, width: this.x }; + } + }; + + acx.vector.prototype.Init.prototype = acx.vector.prototype; + + /** + * short cut functions for working with vectors and jquery. + * Each function returns the equivalent jquery value in a two dimentional vector + */ + $.fn.vSize = function() { return acx.vector(this.width(), this.height()); }; + $.fn.vOuterSize = function(margin) { return acx.vector(this.outerWidth(margin), this.outerHeight(margin)); }; + $.fn.vScroll = function() { return acx.vector(this.scrollLeft(), this.scrollTop()); }; + $.fn.vOffset = function() { return acx.vector(this.offset()); }; + $.fn.vPosition = function() { return acx.vector(this.position()); }; + $.Event.prototype.vMouse = function() { return acx.vector(this.pageX, this.pageY); }; + + /** + * object extensions (ecma5 compatible) + */ + acx.augment(Object, { + keys: function(obj) { + var ret = []; + for(var n in obj) if(Object.prototype.hasOwnProperty.call(obj, n)) ret.push(n); + return ret; + } + }); + + /** + * Array prototype extensions + */ + acx.augment(Array.prototype, { + 'contains' : function(needle) { + return this.indexOf(needle) !== -1; + }, + + // returns a new array consisting of all the members that are in both arrays + 'intersection' : function(b) { + var ret = []; + for(var i = 0; i < this.length; i++) { + if(b.contains(this[i])) { + ret.push(this[i]); + } + } + return ret; + }, + + 'remove' : function(value) { + var i = this.indexOf(value); + if(i !== -1) { + this.splice(i, 1); + } + } + }); + + /** + * String prototype extensions + */ + acx.augment(String.prototype, { + 'contains' : function(needle) { + return this.indexOf(needle) !== -1; + }, + + 'equalsIgnoreCase' : function(match) { + return this.toLowerCase() === match.toLowerCase(); + }, + + 'escapeHtml' : function() { + return this.replace(/&/g, '&').replace(//g, '>'); + }, + + 'escapeJS' : function() { + var meta = {'"':'\\"', '\\':'\\\\', '/':'\\/', '\b':'\\b', '\f':'\\f', '\n':'\\n', '\r':'\\r', '\t':'\\t'}, + xfrm = function(c) { return meta[c] || "\\u" + c.charCodeAt(0).toString(16).zeroPad(4); }; + return this.replace(new RegExp('(["\\\\\x00-\x1f\x7f-\uffff])', 'g'), xfrm); + }, + + 'escapeRegExp' : function() { + var ret = "", esc = "\\^$*+?.()=|{,}[]-"; + for ( var i = 0; i < this.length; i++) { + ret += (esc.contains(this.charAt(i)) ? "\\" : "") + this.charAt(i); + } + return ret; + }, + + 'zeroPad' : function(len) { + return ("0000000000" + this).substring(this.length - len + 10); + } + }); + + $.fn.forEach = Array.prototype.forEach; + + // joey / jquery integration + $.joey = function( obj ) { + return $( window.joey( obj ) ); + }; + + window.joey.plugins.push( function( obj ) { + if( obj instanceof jQuery ) { + return obj[0]; + } + }); + +})(); + +/** + * base class for creating inheritable classes + * based on resigs 'Simple Javascript Inheritance Class' (based on base2 and prototypejs) + * modified with static super and auto config + * @name Class + * @constructor + */ +(function( $, app ){ + + var ux = app.ns("ux"); + + var initializing = false, fnTest = /\b_super\b/; + + ux.Class = function(){}; + + ux.Class.extend = function(prop) { + function Class() { + if(!initializing) { + var args = Array.prototype.slice.call(arguments); + this.config = $.extend( function(t) { // automatically construct a config object based on defaults and last item passed into the constructor + return $.extend(t._proto && t._proto() && arguments.callee(t._proto()) || {}, t.defaults); + } (this) , args.pop() ); + this.init && this.init.apply(this, args); // automatically run the init function when class created + } + } + + initializing = true; + var prototype = new this(); + initializing = false; + + var _super = this.prototype; + prototype._proto = function() { + return _super; + }; + + for(var name in prop) { + prototype[name] = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { this._super = _super[name]; return fn.apply(this, arguments); }; + })(name, prop[name]) : prop[name]; + } + + Class.prototype = prototype; + Class.constructor = Class; + + Class.extend = arguments.callee; // make class extendable + + return Class; + }; +})( this.jQuery, this.app ); + +(function( app ) { + + var ut = app.ns("ut"); + + ut.option_template = function(v) { return { tag: "OPTION", value: v, text: v }; }; + + ut.require_template = function(f) { return f.require ? { tag: "SPAN", cls: "require", text: "*" } : null; }; + + + var sib_prefix = ['B','ki','Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + + ut.byteSize_template = function(n) { + var i = 0; + while( n >= 1000 ) { + i++; + n /= 1024; + } + return (i === 0 ? n.toString() : n.toFixed( 3 - parseInt(n,10).toString().length )) + ( sib_prefix[ i ] || "..E" ); + }; + + var sid_prefix = ['','k','M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + + ut.count_template = function(n) { + var i = 0; + while( n >= 1000 ) { + i++; + n /= 1000; + } + return i === 0 ? n.toString() : ( n.toFixed( 3 - parseInt(n,10).toString().length ) + ( sid_prefix[ i ] || "..E" ) ); + }; + +})( this.app ); + +(function( app ) { + + var ux = app.ns("ux"); + + ux.Observable = ux.Class.extend((function() { + return { + init: function() { + this.observers = {}; + for( var opt in this.config ) { // automatically install observers that are defined in the configuration + if( opt.indexOf( 'on' ) === 0 ) { + this.on( opt.substring(2) , this.config[ opt ] ); + } + } + }, + _getObs: function( type ) { + return ( this.observers[ type.toLowerCase() ] || ( this.observers[ type.toLowerCase() ] = [] ) ); + }, + on: function( type, fn, params, thisp ) { + this._getObs( type ).push( { "cb" : fn, "args" : params || [] , "cx" : thisp || this } ); + return this; + }, + fire: function( type ) { + var params = Array.prototype.slice.call( arguments, 1 ); + this._getObs( type ).slice().forEach( function( ob ) { + ob["cb"].apply( ob["cx"], ob["args"].concat( params ) ); + } ); + return this; + }, + removeAllObservers: function() { + this.observers = {}; + }, + removeObserver: function( type, fn ) { + var obs = this._getObs( type ), + index = obs.reduce( function(p, t, i) { return (t.cb === fn) ? i : p; }, -1 ); + if(index !== -1) { + obs.splice( index, 1 ); + } + return this; // make observable functions chainable + }, + hasObserver: function( type ) { + return !! this._getObs( type ).length; + } + }; + })()); + +})( this.app ); +(function( app ) { + + var ux = app.ns("ux"); + + var extend = ux.Observable.extend; + var instance = function() { + if( ! ("me" in this) ) { + this.me = new this(); + } + return this.me; + }; + + ux.Singleton = ux.Observable.extend({}); + + ux.Singleton.extend = function() { + var Self = extend.apply( this, arguments ); + Self.instance = instance; + return Self; + }; + +})( this.app ); + +(function( $, app ) { + + var ux = app.ns("ux"); + + /** + * Provides drag and drop functionality
+ * a DragDrop instance is created for each usage pattern and then used over and over again
+ * first a dragObj is defined - this is the jquery node that will be dragged around
+ * second, the event callbacks are defined - these allow you control the ui during dragging and run functions when successfully dropping
+ * thirdly drop targets are defined - this is a list of DOM nodes, the constructor works in one of two modes: + *
  • without targets - objects can be picked up and dragged around, dragStart and dragStop events fire
  • + *
  • with targets - as objects are dragged over targets dragOver, dragOut and DragDrop events fire + * to start dragging call the DragDrop.pickup_handler() function, dragging stops when the mouse is released. + * @constructor + * The following options are supported + *
    targetSelector
    + *
    an argument passed directly to jquery to create a list of targets, as such it can be a CSS style selector, or an array of DOM nodes
    if target selector is null the DragDrop does Drag only and will not fire dragOver dragOut and dragDrop events
    + *
    pickupSelector
    + *
    a jquery selector. The pickup_handler is automatically bound to matched elements (eg clicking on these elements starts the drag). if pickupSelector is null, the pickup_handler must be manually bound $(el).bind("mousedown", dragdrop.pickup_handler)
    + *
    dragObj
    + *
    the jQuery element to drag around when pickup is called. If not defined, dragObj must be set in onDragStart
    + *
    draggingClass
    + *
    the class(es) added to items when they are being dragged
    + * The following observables are supported + *
    dragStart
    + *
    a callback when start to drag
    function(jEv)
    + *
    dragOver
    + *
    a callback when we drag into a target
    function(jEl)
    + *
    dragOut
    + *
    a callback when we drag out of a target, or when we drop over a target
    function(jEl)
    + *
    dragDrop
    + *
    a callback when we drop on a target
    function(jEl)
    + *
    dragStop
    + *
    a callback when we stop dragging
    function(jEv)
    + */ + ux.DragDrop = ux.Observable.extend({ + defaults : { + targetsSelector : null, + pickupSelector: null, + dragObj : null, + draggingClass : "dragging" + }, + + init: function(options) { + this._super(); // call the class initialiser + + this.drag_handler = this.drag.bind(this); + this.drop_handler = this.drop.bind(this); + this.pickup_handler = this.pickup.bind(this); + this.targets = []; + this.dragObj = null; + this.dragObjOffset = null; + this.currentTarget = null; + if(this.config.pickupSelector) { + $(this.config.pickupSelector).bind("mousedown", this.pickup_handler); + } + }, + + drag : function(jEv) { + jEv.preventDefault(); + var mloc = acx.vector( this.lockX || jEv.pageX, this.lockY || jEv.pageY ); + this.dragObj.css(mloc.add(this.dragObjOffset).asOffset()); + if(this.targets.length === 0) { + return; + } + if(this.currentTarget !== null && mloc.within(this.currentTarget[1], this.currentTarget[2])) { + return; + } + if(this.currentTarget !== null) { + this.fire('dragOut', this.currentTarget[0]); + this.currentTarget = null; + } + for(var i = 0; i < this.targets.length; i++) { + if(mloc.within(this.targets[i][1], this.targets[i][2])) { + this.currentTarget = this.targets[i]; + break; + } + } + if(this.currentTarget !== null) { + this.fire('dragOver', this.currentTarget[0]); + } + }, + + drop : function(jEv) { + $(document).unbind("mousemove", this.drag_handler); + $(document).unbind("mouseup", this.drop_handler); + this.dragObj.removeClass(this.config.draggingClass); + if(this.currentTarget !== null) { + this.fire('dragOut', this.currentTarget[0]); + this.fire('dragDrop', this.currentTarget[0]); + } + this.fire('dragStop', jEv); + this.dragObj = null; + }, + + pickup : function(jEv, opts) { + $.extend(this.config, opts); + this.fire('dragStart', jEv); + this.dragObj = this.dragObj || this.config.dragObj; + this.dragObjOffset = this.config.dragObjOffset || acx.vector(this.dragObj.offset()).sub(jEv.pageX, jEv.pageY); + this.lockX = this.config.lockX ? jEv.pageX : 0; + this.lockY = this.config.lockY ? jEv.pageY : 0; + this.dragObj.addClass(this.config.draggingClass); + if(!this.dragObj.get(0).parentNode || this.dragObj.get(0).parentNode.nodeType === 11) { // 11 = document fragment + $(document.body).append(this.dragObj); + } + if(this.config.targetsSelector) { + this.currentTarget = null; + var targets = ( this.targets = [] ); + // create an array of elements optimised for rapid collision detection calculation + $(this.config.targetsSelector).each(function(i, el) { + var jEl = $(el); + var tl = acx.vector(jEl.offset()); + var br = tl.add(jEl.width(), jEl.height()); + targets.push([jEl, tl, br]); + }); + } + $(document).bind("mousemove", this.drag_handler); + $(document).bind("mouseup", this.drop_handler); + this.drag_handler(jEv); + } + }); + +})( this.jQuery, this.app ); + +(function( app ) { + + var ux = app.ns("ux"); + + ux.FieldCollection = ux.Observable.extend({ + defaults: { + fields: [] // the collection of fields + }, + init: function() { + this._super(); + this.fields = this.config.fields; + }, + validate: function() { + return this.fields.reduce(function(r, field) { + return r && field.validate(); + }, true); + }, + getData: function(type) { + return this.fields.reduce(function(r, field) { + r[field.name] = field.val(); return r; + }, {}); + } + }); + +})( this.app ); + +(function( $, app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.Model = ux.Observable.extend({ + defaults: { + data: null + }, + init: function() { + this.set( this.config.data ); + }, + set: function( key, value ) { + if( arguments.length === 1 ) { + this._data = $.extend( {}, key ); + } else { + key.split(".").reduce(function( ptr, prop, i, props) { + if(i === (props.length - 1) ) { + ptr[prop] = value; + } else { + if( !(prop in ptr) ) { + ptr[ prop ] = {}; + } + return ptr[prop]; + } + }, this._data ); + } + }, + get: function( key ) { + return key.split(".").reduce( function( ptr, prop ) { + return ( ptr && ( prop in ptr ) ) ? ptr[ prop ] : undefined; + }, this._data ); + }, + }); +})( this.jQuery, this.app ); + +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.DataSourceInterface = ux.Observable.extend({ + /* + properties + meta = { total: 0 }, + headers = [ { name: "" } ], + data = [ { column: value, column: value } ], + sort = { column: "name", dir: "desc" } + events + data: function( DataSourceInterface ) + */ + _getSummary: function(res) { + this.summary = i18n.text("TableResults.Summary", res._shards.successful, res._shards.total, res.hits.total, (res.took / 1000).toFixed(3)); + }, + _getMeta: function(res) { + this.meta = { total: res.hits.total, shards: res._shards, tool: res.took }; + } + }); + +})( this.app ); +(function( app ) { + + var data = app.ns("data"); + + data.ResultDataSourceInterface = data.DataSourceInterface.extend({ + results: function(res) { + this._getSummary(res); + this._getMeta(res); + this._getData(res); + this.sort = {}; + this.fire("data", this); + }, + _getData: function(res) { + var columns = this.columns = []; + this.data = res.hits.hits.map(function(hit) { + var row = (function(path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(! columns.contains(dpath)) { + columns.push(dpath); + } + row[dpath] = (spec[prop] || "null").toString(); + } + } + return row; + })([ hit._type ], hit, {}); + row._source = hit; + return row; + }, this); + } + }); + +})( this.app ); + +(function( app ) { + + /* + notes on elasticsearch terminology used in this project + + indices[index] contains one or more + types[type] contains one or more + documents contain one or more + paths[path] + each path contains one element of data + each path maps to one field + + eg PUT, "/twitter/tweet/1" + { + user: "mobz", + date: "2011-01-01", + message: "You know, for browsing elasticsearch", + name: { + first: "Ben", + last: "Birch" + } + } + + creates + 1 index: twitter + this is the collection of index data + 1 type: tweet + this is the type of document (kind of like a table in sql) + 1 document: /twitter/tweet/1 + this is an actual document in the index ( kind of like a row in sql) + 5 paths: [ ["user"], ["date"], ["message"], ["name","first"], ["name","last"] ] + since documents can be heirarchical this maps a path from a document root to a piece of data + 5 fields: [ "user", "date", "message", "first", "last" ] + this is an indexed 'column' of data. fields are not heirarchical + + the relationship between a path and a field is called a mapping. mappings also contain a wealth of information about how es indexes the field + + notes + 1) a path is stored as an array, the dpath is . . path.join("."), + which can be considered the canonical reference for a mapping + 2) confusingly, es uses the term index for both the collection of indexed data, and the individually indexed fields + so the term index_name is the same as field_name in this sense. + + */ + + var data = app.ns("data"); + var ux = app.ns("ux"); + + var coretype_map = { + "string" : "string", + "byte" : "number", + "short" : "number", + "long" : "number", + "integer" : "number", + "float" : "number", + "double" : "number", + "ip" : "number", + "date" : "date", + "boolean" : "boolean", + "binary" : "binary", + "multi_field" : "multi_field" + }; + + var default_property_map = { + "string" : { "store" : "no", "index" : "analysed" }, + "number" : { "store" : "no", "precision_steps" : 4 }, + "date" : { "store" : "no", "format" : "dateOptionalTime", "index": "yes", "precision_steps": 4 }, + "boolean" : { "store" : "no", "index": "yes" }, + "binary" : { }, + "multi_field" : { } + }; + + // parses metatdata from a cluster, into a bunch of useful data structures + data.MetaData = ux.Observable.extend({ + defaults: { + state: null // (required) response from a /_cluster/state request + }, + init: function() { + this._super(); + this.refresh(this.config.state); + }, + getIndices: function(alias) { + return alias ? this.aliases[alias] : this.indicesList; + }, + // returns an array of strings containing all types that are in all of the indices passed in, or all types + getTypes: function(indices) { + var indices = indices || [], types = []; + this.typesList.forEach(function(type) { + for(var i = 0; i < indices.length; i++) { + if(! this.indices[indices[i]].types.contains(type)) + return; + } + types.push(type); + }, this); + return types; + }, + refresh: function(state) { + // currently metadata expects all like named fields to have the same type, even when from different types and indices + var aliases = this.aliases = {}; + var indices = this.indices = {}; + var types = this.types = {}; + var fields = this.fields = {}; + var paths = this.paths = {}; + + function createField( mapping, index, type, path, name ) { + var dpath = [ index, type ].concat( path ).join( "." ); + var field_name = mapping.index_name || path.join( "." ); + var field = paths[ dpath ] = fields[ field_name ] || $.extend({ + field_name : field_name, + core_type : coretype_map[ mapping.type ], + dpaths : [] + }, default_property_map[ coretype_map[ mapping.type ] ], mapping ); + + if (field.type === "multi_field" && typeof field.fields !== "undefined") { + for (var subField in field.fields) { + field.fields[ subField ] = createField( field.fields[ subField ], index, type, path.concat( subField ), name + "." + subField ); + } + } + if (fields.dpaths) { + field.dpaths.push(dpath); + } + return field; + } + function getFields(properties, type, index, listeners) { + (function procPath(prop, path) { + for (var n in prop) { + if ("properties" in prop[n]) { + procPath( prop[ n ].properties, path.concat( n ) ); + } else { + var field = createField(prop[n], index, type, path.concat(n), n); + listeners.forEach( function( listener ) { + listener[ field.field_name ] = field; + } ); + } + } + })(properties, []); + } + for (var index in state.metadata.indices) { + indices[index] = { + types : [], fields : {}, paths : {}, parents : {} + }; + indices[index].aliases = state.metadata.indices[index].aliases; + indices[index].aliases.forEach(function(alias) { + (aliases[alias] || (aliases[alias] = [])).push(index); + }); + var mapping = state.metadata.indices[index].mappings; + for (var type in mapping) { + indices[index].types.push(type); + if ( type in types) { + types[type].indices.push(index); + } else { + types[type] = { + indices : [index], fields : {} + }; + } + getFields(mapping[type].properties, type, index, [fields, types[type].fields, indices[index].fields]); + if ( typeof mapping[type]._parent !== "undefined") { + indices[index].parents[type] = mapping[type]._parent.type; + } + } + } + + this.aliasesList = Object.keys(aliases); + this.indicesList = Object.keys(indices); + this.typesList = Object.keys(types); + this.fieldsList = Object.keys(fields); + } + }); + +})( this.app ); + +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.MetaDataFactory = ux.Observable.extend({ + defaults: { + cluster: null // (required) an app.services.Cluster + }, + init: function() { + this._super(); + this.config.cluster.get("_cluster/state", function(data) { + this.metaData = new app.data.MetaData({state: data}); + this.fire("ready", this.metaData, { originalData: data }); // TODO originalData needed for legacy ui.FilterBrowser + }.bind(this)); + } + }); + +})( this.app ); + +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.Query = ux.Observable.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + size: 50 // size of pages to return + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.refuid = 0; + this.refmap = {}; + this.indices = []; + this.types = []; + this.search = { + fields : [ "_parent", "_source" ], + query: { bool: { must: [], must_not: [], should: [] } }, + from: 0, + size: this.config.size, + sort: [], + aggs: {}, + version: true + }; + this.defaultClause = this.addClause(); + this.history = [ this.getState() ]; + }, + clone: function() { + var q = new data.Query({ cluster: this.cluster }); + q.restoreState(this.getState()); + for(var uqid in q.refmap) { + q.removeClause(uqid); + } + return q; + }, + getState: function() { + return $.extend(true, {}, { search: this.search, indices: this.indices, types: this.types }); + }, + restoreState: function(state) { + state = $.extend(true, {}, state || this.history[this.history.length - 1]); + this.indices = state.indices; + this.types = state.types; + this.search = state.search; + }, + getData: function() { + return JSON.stringify(this.search); + }, + query: function() { + var state = this.getState(); + this.cluster.post( + (this.indices.join(",") || "_all") + "/" + ( this.types.length ? this.types.join(",") + "/" : "") + "_search", + this.getData(), + function(results) { + if(results === null) { + alert(i18n.text("Query.FailAndUndo")); + this.restoreState(); + return; + } + this.history.push(state); + + this.fire("results", this, results); + }.bind(this)); + }, + loadParents: function(res,metadata){ + //create data for mget + var data = { docs :[] }; + var indexToTypeToParentIds = {}; + res.hits.hits.forEach(function(hit) { + if (typeof hit.fields != "undefined"){ + if (typeof hit.fields._parent != "undefined"){ + var parentType = metadata.indices[hit._index].parents[hit._type]; + if (typeof indexToTypeToParentIds[hit._index] == "undefined"){ + indexToTypeToParentIds[hit._index] = new Object(); + } + if (typeof indexToTypeToParentIds[hit._index][hit._type] == "undefined"){ + indexToTypeToParentIds[hit._index][hit._type] = new Object(); + } + if (typeof indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] == "undefined"){ + indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] = null; + data.docs.push({ _index:hit._index, _type:parentType, _id:hit.fields._parent}); + } + } + } + }); + + //load parents + var state = this.getState(); + this.cluster.post("_mget",JSON.stringify(data), + function(results) { + if(results === null) { + alert(i18n.text("Query.FailAndUndo")); + this.restoreState(); + return; + } + this.history.push(state); + var indexToTypeToParentIdToHit = new Object(); + results.docs.forEach(function(doc) { + if (typeof indexToTypeToParentIdToHit[doc._index] == "undefined"){ + indexToTypeToParentIdToHit[doc._index] = new Object(); + } + + if (typeof indexToTypeToParentIdToHit[doc._index][doc._type] == "undefined"){ + indexToTypeToParentIdToHit[doc._index][doc._type] = new Object(); + } + + indexToTypeToParentIdToHit[doc._index][doc._type][doc._id] = doc; + }); + + res.hits.hits.forEach(function(hit) { + if (typeof hit.fields != "undefined"){ + if (typeof hit.fields._parent != "undefined"){ + var parentType = metadata.indices[hit._index].parents[hit._type]; + hit._parent = indexToTypeToParentIdToHit[hit._index][parentType][hit.fields._parent]; + } + } + }); + + this.fire("resultsWithParents", this, res); + }.bind(this)); + }, + setPage: function(page) { + this.search.from = this.config.size * (page - 1); + }, + setSort: function(index, desc) { + var sortd = {}; sortd[index] = { reverse: !!desc }; + this.search.sort.unshift( sortd ); + for(var i = 1; i < this.search.sort.length; i++) { + if(Object.keys(this.search.sort[i])[0] === index) { + this.search.sort.splice(i, 1); + break; + } + } + }, + setIndex: function(index, add) { + if(add) { + if(! this.indices.contains(index)) this.indices.push(index); + } else { + this.indices.remove(index); + } + this.fire("setIndex", this, { index: index, add: !!add }); + }, + setType: function(type, add) { + if(add) { + if(! this.types.contains(type)) this.types.push(type); + } else { + this.types.remove(type); + } + this.fire("setType", this, { type: type, add: !!add }); + }, + addClause: function(value, field, op, bool) { + bool = bool || "should"; + op = op || "match_all"; + field = field || "_all"; + var clause = this._setClause(value, field, op, bool); + var uqid = "q-" + this.refuid++; + this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool }; + if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) { + this.removeClause(this.defaultClause); + } + this.fire("queryChanged", this, { uqid: uqid, search: this.search} ); + return uqid; // returns reference to inner query object to allow fast updating + }, + removeClause: function(uqid) { + var ref = this.refmap[uqid], + bool = this.search.query.bool[ref.bool]; + bool.remove(ref.clause); + if(this.search.query.bool.must.length + this.search.query.bool.should.length === 0) { + this.defaultClause = this.addClause(); + } + }, + addAggs: function(aggs) { + var aggsId = "f-" + this.refuid++; + this.search.aggs[aggsId] = aggs; + this.refmap[aggsId] = { aggsId: aggsId, aggs: aggs }; + return aggsId; + }, + removeAggs: function(aggsId) { + delete this.search.aggs[aggsId]; + delete this.refmap[aggsId]; + }, + _setClause: function(value, field, op, bool) { + var clause = {}, query = {}; + if(op === "match_all") { + } else if(op === "query_string") { + query["default_field"] = field; + query["query"] = value; + } else if(op === "missing") { + op = "constant_score" + var missing = {}, filter = {}; + missing["field"] = field; + filter["missing"] = missing + query["filter"] = filter; + } else { + query[field] = value; + } + clause[op] = query; + this.search.query.bool[bool].push(clause); + return clause; + } + }); + +})( this.app ); + +(function( app ) { + + var data = app.ns("data"); + + data.QueryDataSourceInterface = data.DataSourceInterface.extend({ + defaults: { + metadata: null, // (required) instanceof app.data.MetaData, the cluster metadata + query: null // (required) instanceof app.data.Query the data source + }, + init: function() { + this._super(); + this.config.query.on("results", this._results_handler.bind(this) ); + this.config.query.on("resultsWithParents", this._load_parents.bind(this) ); + }, + _results_handler: function(query, res) { + this._getSummary(res); + this._getMeta(res); + var sort = query.search.sort[0] || { "_score": { reverse: false }}; + var sortField = Object.keys(sort)[0]; + this.sort = { column: sortField, dir: (sort[sortField].reverse ? "asc" : "desc") }; + this._getData(res, this.config.metadata); + this.fire("data", this); + }, + _load_parents: function(query, res) { + query.loadParents(res, this.config.metadata); + }, + _getData: function(res, metadata) { + var metaColumns = ["_index", "_type", "_id", "_score"]; + var columns = this.columns = [].concat(metaColumns); + + this.data = res.hits.hits.map(function(hit) { + var row = (function(path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(metadata.paths[dpath]) { + var field_name = metadata.paths[dpath].field_name; + if(! columns.contains(field_name)) { + columns.push(field_name); + } + row[field_name] = (spec[prop] === null ? "null" : spec[prop] ).toString(); + } else { + // TODO: field not in metadata index + } + } + } + return row; + })([ hit._index, hit._type ], hit._source, {}); + metaColumns.forEach(function(n) { row[n] = hit[n]; }); + row._source = hit; + if (typeof hit._parent!= "undefined") { + (function(prefix, path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(prefix, path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(prefix, path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(metadata.paths[dpath]) { + var field_name = metadata.paths[dpath].field_name; + var column_name = prefix+"."+field_name; + if(! columns.contains(column_name)) { + columns.push(column_name); + } + row[column_name] = (spec[prop] === null ? "null" : spec[prop] ).toString(); + } else { + // TODO: field not in metadata index + } + } + } + })(hit._parent._type,[hit._parent._index, hit._parent._type], hit._parent._source, row); + } + return row; + }, this); + } + }); + +})( this.app ); + +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.BoolQuery = ux.Observable.extend({ + defaults: { + size: 50 // size of pages to return + }, + init: function() { + this._super(); + this.refuid = 0; + this.refmap = {}; + this.search = { + query: { bool: { must: [], must_not: [], should: [] } }, + from: 0, + size: this.config.size, + sort: [], + aggs: {} + }; + this.defaultClause = this.addClause(); + }, + setSize: function(size) { + this.search.size = parseInt( size, 10 ); + }, + setPage: function(page) { + this.search.from = this.config.size * (page - 1) + 1; + }, + addClause: function(value, field, op, bool) { + bool = bool || "should"; + op = op || "match_all"; + field = field || "_all"; + var clause = this._setClause(value, field, op, bool); + var uqid = "q-" + this.refuid++; + this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool }; + if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) { + this.removeClause(this.defaultClause); + } + this.fire("queryChanged", this, { uqid: uqid, search: this.search} ); + return uqid; // returns reference to inner query object to allow fast updating + }, + removeClause: function(uqid) { + var ref = this.refmap[uqid], + bool = this.search.query.bool[ref.bool]; + var clauseIdx = bool.indexOf(ref.clause); + // Check that this clause hasn't already been removed + if (clauseIdx >=0) { + bool.splice(clauseIdx, 1); + } + }, + _setClause: function(value, field, op, bool) { + var clause = {}, query = {}; + if(op === "match_all") { + } else if(op === "query_string") { + query["default_field"] = field; + query["query"] = value; + } else if(op === "missing") { + op = "constant_score" + var missing = {}, filter = {}; + missing["field"] = field; + filter["missing"] = missing + query["filter"] = filter; + } else { + query[field.substring(field.indexOf(".")+1)] = value; + } + clause[op] = query; + this.search.query.bool[bool].push(clause); + return clause; + }, + getData: function() { + return JSON.stringify(this.search); + } + }); + +})( this.app ); +(function( app ) { + + var ux = app.ns("ux"); + var services = app.ns("services"); + + services.Preferences = ux.Singleton.extend({ + init: function() { + this._storage = window.localStorage; + this._setItem("__version", 1 ); + }, + get: function( key ) { + return this._getItem( key ); + }, + set: function( key, val ) { + return this._setItem( key, val ); + }, + _getItem: function( key ) { + try { + return JSON.parse( this._storage.getItem( key ) ); + } catch(e) { + console.warn( e ); + return undefined; + } + }, + _setItem: function( key, val ) { + try { + return this._storage.setItem( key, JSON.stringify( val ) ); + } catch(e) { + console.warn( e ); + return undefined; + } + } + }); + +})( this.app ); + +(function( $, app ) { + + var services = app.ns("services"); + var ux = app.ns("ux"); + + function parse_version( v ) { + return v.match(/^(\d+)\.(\d+)\.(\d+)/).slice(1,4).map( function(d) { return parseInt(d || 0, 10); } ); + } + + services.Cluster = ux.Class.extend({ + defaults: { + base_uri: null + }, + init: function() { + this.base_uri = this.config.base_uri; + }, + setVersion: function( v ) { + this.version = v; + this._version_parts = parse_version( v ); + }, + versionAtLeast: function( v ) { + var testVersion = parse_version( v ); + for( var i = 0; i < 3; i++ ) { + if( testVersion[i] !== this._version_parts[i] ) { + return testVersion[i] < this._version_parts[i]; + } + } + return true; + }, + request: function( params ) { + return $.ajax( $.extend({ + url: this.base_uri + params.path, + dataType: "json", + error: function(xhr, type, message) { + if("console" in window) { + console.log({ "XHR Error": type, "message": message }); + } + } + }, params) ); + }, + "get": function(path, success) { return this.request( { type: "GET", path: path, success: success } ); }, + "post": function(path, data, success) { return this.request( { type: "POST", path: path, data: data, success: success } ); }, + "put": function(path, data, success) { return this.request( { type: "PUT", path: path, data: data, success: success } ); }, + "delete": function(path, data, success) { return this.request( { type: "DELETE", path: path, data: data, success: success } ); } + }); + +})( this.jQuery, this.app ); + +(function( app ) { + + var services = app.ns("services"); + var ux = app.ns("ux"); + + services.ClusterState = ux.Observable.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.clusterState = null; + this.status = null; + this.nodeStats = null; + this.clusterNodes = null; + }, + refresh: function() { + var self = this, clusterState, status, nodeStats, clusterNodes, clusterHealth; + function updateModel() { + if( clusterState && status && nodeStats && clusterNodes && clusterHealth ) { + this.clusterState = clusterState; + this.status = status; + this.nodeStats = nodeStats; + this.clusterNodes = clusterNodes; + this.clusterHealth = clusterHealth; + this.fire( "data", this ); + } + } + this.cluster.get("_cluster/state", function( data ) { + clusterState = data; + updateModel.call( self ); + }); + this.cluster.get("_stats", function( data ) { + status = data; + updateModel.call( self ); + }); + this.cluster.get("_nodes/stats?all=true", function( data ) { + nodeStats = data; + updateModel.call( self ); + }); + this.cluster.get("_nodes", function( data ) { + clusterNodes = data; + updateModel.call( self ); + }); + this.cluster.get("_cluster/health", function( data ) { + clusterHealth = data; + updateModel.call( self ); + }); + }, + _clusterState_handler: function(state) { + this.clusterState = state; + this.redraw("clusterState"); + }, + _status_handler: function(status) { + this.status = status; + this.redraw("status"); + }, + _clusterNodeStats_handler: function(stats) { + this.nodeStats = stats; + this.redraw("nodeStats"); + }, + _clusterNodes_handler: function(nodes) { + this.clusterNodes = nodes; + this.redraw("clusterNodes"); + }, + _clusterHealth_handler: function(health) { + this.clusterHealth = health; + this.redraw("status"); + } + }); + +})( this.app ); +(function( $, joey, app ) { + + var ui = app.ns("ui"); + var ux = app.ns("ux"); + + ui.AbstractWidget = ux.Observable.extend({ + defaults : { + id: null // the id of the widget + }, + + el: null, // this is the jquery wrapped dom element(s) that is the root of the widget + + init: function() { + this._super(); + for(var prop in this) { // automatically bind all the event handlers + if(prop.contains("_handler")) { + this[prop] = this[prop].bind(this); + } + } + }, + + id: function(suffix) { + return this.config.id ? (this.config.id + (suffix ? "-" + suffix : "")) : undefined; + }, + + attach: function( parent, method ) { + if( parent ) { + this.el[ method || "appendTo"]( parent ); + } + this.fire("attached", this ); + return this; + }, + + remove: function() { + this.el.remove(); + this.fire("removed", this ); + this.removeAllObservers(); + this.el = null; + return this; + } + }); + + joey.plugins.push( function( obj ) { + if( obj instanceof ui.AbstractWidget ) { + return obj.el[0]; + } + }); + +})( this.jQuery, this.joey, this.app ); + +(function( $, app, joey ) { + + var ui = app.ns("ui"); + + ui.AbstractField = ui.AbstractWidget.extend({ + + defaults: { + name : "", // (required) - name of the field + require: false, // validation requirements (false, true, regexp, function) + value: "", // default value + label: "" // human readable label of this field + }, + + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + this.field = this.el.find("[name="+this.config.name+"]"); + this.label = this.config.label; + this.require = this.config.require; + this.name = this.config.name; + this.val( this.config.value ); + this.attach( parent ); + }, + + val: function( val ) { + if(val === undefined) { + return this.field.val(); + } else { + this.field.val( val ); + return this; + } + }, + + validate: function() { + var val = this.val(), req = this.require; + if( req === false ) { + return true; + } else if( req === true ) { + return val.length > 0; + } else if( req.test && $.isFunction(req.test) ) { + return req.test( val ); + } else if( $.isFunction(req) ) { + return req( val, this ); + } + } + + }); + +})( this.jQuery, this.app, this.joey ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.TextField = ui.AbstractField.extend({ + init: function() { + this._super(); + }, + _keyup_handler: function() { + this.fire("change", this ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiField uiTextField", children: [ + { tag: "INPUT", + type: "text", + name: this.config.name, + placeholder: this.config.placeholder, + onkeyup: this._keyup_handler + } + ]}; + } + }); + +})( this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.CheckField = ui.AbstractField.extend({ + _main_template: function() { return ( + { tag: "DIV", id: this.id(), cls: "uiCheckField", children: [ + { tag: "INPUT", type: "checkbox", name: this.config.name, checked: !!this.config.value } + ] } + ); }, + validate: function() { + return this.val() || ( ! this.require ); + }, + val: function( val ) { + if( val === undefined ) { + return !!this.field.attr( "checked" ); + } else { + this.field.attr( "checked", !!val ); + } + } + }); + +})( this.app ); + + + +(function( $, joey, app ) { + + var ui = app.ns("ui"); + + ui.Button = ui.AbstractWidget.extend({ + defaults : { + label: "", // the label text + disabled: false, // create a disabled button + autoDisable: false // automatically disable the button when clicked + }, + + _baseCls: "uiButton", + + init: function(parent) { + this._super(); + this.el = $.joey(this.button_template()) + .bind("click", this.click_handler); + this.config.disabled && this.disable(); + this.attach( parent ); + }, + + click_handler: function(jEv) { + if(! this.disabled) { + this.fire("click", jEv, this); + this.config.autoDisable && this.disable(); + } + }, + + enable: function() { + this.el.removeClass("disabled"); + this.disabled = false; + return this; + }, + + disable: function(disable) { + if(disable === false) { + return this.enable(); + } + this.el.addClass("disabled"); + this.disabled = true; + return this; + }, + + button_template: function() { return ( + { tag: 'BUTTON', type: 'button', id: this.id(), cls: this._baseCls, children: [ + { tag: 'DIV', cls: 'uiButton-content', children: [ + { tag: 'DIV', cls: 'uiButton-label', text: this.config.label } + ] } + ] } + ); } + }); + +})( this.jQuery, this.joey, this.app ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.MenuButton = app.ui.Button.extend({ + defaults: { + menu: null + }, + _baseCls: "uiButton uiMenuButton", + init: function(parent) { + this._super(parent); + this.menu = this.config.menu; + this.on("click", this.openMenu_handler); + this.menu.on("open", function() { this.el.addClass("active"); }.bind(this)); + this.menu.on("close", function() { this.el.removeClass("active"); }.bind(this)); + }, + openMenu_handler: function(jEv) { + this.menu && this.menu.open(jEv); + } + }); + +})( this.jQuery, this.app ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.SplitButton = ui.AbstractWidget.extend({ + defaults: { + items: [], + label: "" + }, + _baseCls: "uiSplitButton", + init: function( parent ) { + this._super( parent ); + this.value = null; + this.button = new ui.Button({ + label: this.config.label, + onclick: this._click_handler + }); + this.menu = new ui.SelectMenuPanel({ + value: this.config.value, + items: this._getItems(), + onSelect: this._select_handler + }); + this.menuButton = new ui.MenuButton({ + label: "\u00a0", + menu: this.menu + }); + this.el = $.joey(this._main_template()); + }, + remove: function() { + this.menu.remove(); + }, + disable: function() { + this.button.disable(); + }, + enable: function() { + this.button.enable(); + }, + _click_handler: function() { + this.fire("click", this, { value: this.value } ); + }, + _select_handler: function( panel, event ) { + this.fire( "select", this, event ); + }, + _getItems: function() { + return this.config.items; + }, + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: [ + this.button, this.menuButton + ] }; + } + }); + +})( this.jQuery, this.app ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.RefreshButton = ui.SplitButton.extend({ + defaults: { + timer: -1 + }, + init: function( parent ) { + this.config.label = i18n.text("General.RefreshResults"); + this._super( parent ); + this.set( this.config.timer ); + }, + set: function( value ) { + this.value = value; + window.clearInterval( this._timer ); + if( this.value > 0 ) { + this._timer = window.setInterval( this._refresh_handler, this.value ); + } + }, + _click_handler: function() { + this._refresh_handler(); + }, + _select_handler: function( el, event ) { + this.set( event.value ); + this.fire("change", this ); + }, + _refresh_handler: function() { + this.fire("refresh", this ); + }, + _getItems: function() { + return [ + { text: i18n.text("General.ManualRefresh"), value: -1 }, + { text: i18n.text("General.RefreshQuickly"), value: 100 }, + { text: i18n.text("General.Refresh5seconds"), value: 5000 }, + { text: i18n.text("General.Refresh1minute"), value: 60000 } + ]; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.Toolbar = ui.AbstractWidget.extend({ + defaults: { + label: "", + left: [], + right: [] + }, + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + }, + _main_template: function() { + return { tag: "DIV", cls: "uiToolbar", children: [ + { tag: "DIV", cls: "pull-left", children: [ + { tag: "H2", text: this.config.label } + ].concat(this.config.left) }, + { tag: "DIV", cls: "pull-right", children: this.config.right } + ]}; + } + }); + +})( this.jQuery, this.app ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.AbstractPanel = ui.AbstractWidget.extend({ + defaults: { + body: null, // initial content of the body + modal: true, // create a modal panel - creates a div that blocks interaction with page + height: 'auto', // panel height + width: 400, // panel width (in pixels) + open: false, // show the panel when it is created + parent: 'BODY', // node that panel is attached to + autoRemove: false // remove the panel from the dom and destroy it when the widget is closed + }, + shared: { // shared data for all instances of ui.Panel and decendants + stack: [], // array of all open panels + modal: $( { tag: "DIV", id: "uiModal", css: { opacity: 0.2, position: "absolute", top: "0px", left: "0px" } } ) + }, + init: function() { + this._super(); + }, + open: function( ev ) { + this.el + .css( { visibility: "hidden" } ) + .appendTo( this.config.parent ) + .css( this._getPosition( ev ) ) + .css( { zIndex: (this.shared.stack.length ? (+this.shared.stack[this.shared.stack.length - 1].el.css("zIndex") + 10) : 100) } ) + .css( { visibility: "visible", display: "block" } ); + this.shared.stack.remove(this); + this.shared.stack.push(this); + this._setModal(); + $(document).bind("keyup", this._close_handler ); + this.fire("open", { source: this, event: ev } ); + return this; + }, + close: function() { + var index = this.shared.stack.indexOf(this); + if(index !== -1) { + this.shared.stack.splice(index, 1); + this.el.css( { left: "-2999px" } ); // move the dialog to the left rather than hiding to prevent ie6 rendering artifacts + this._setModal(); + this.fire("close", this ); + if(this.config.autoRemove) { + this.remove(); + } + } + return this; + }, + // close the panel and remove it from the dom, destroying it (you can not reuse the panel after calling remove) + remove: function() { + this.close(); + $(document).unbind("keyup", this._close_handler ); + this._super(); + }, + // starting at the top of the stack, find the first panel that wants a modal and put it just underneath, otherwise remove the modal + _setModal: function() { + for(var stackPtr = this.shared.stack.length - 1; stackPtr >= 0; stackPtr--) { + if(this.shared.stack[stackPtr].config.modal) { + this.shared.modal + .appendTo( document.body ) + .css( { zIndex: this.shared.stack[stackPtr].el.css("zIndex") - 5 } ) + .css( $(document).vSize().asSize() ); + return; + } + } + this.shared.modal.remove(); // no panels that want a modal were found + }, + _getPosition: function() { + return $(window).vSize() // get the current viewport size + .sub(this.el.vSize()) // subtract the size of the panel + .mod(function(s) { return s / 2; }) // divide by 2 (to center it) + .add($(document).vScroll()) // add the current scroll offset + .mod(function(s) { return Math.max(5, s); }) // make sure the panel is not off the edge of the window + .asOffset(); // and return it as a {top, left} object + }, + _close_handler: function( ev ) { + if( ev.type === "keyup" && ev.keyCode !== 27) { return; } // press esc key to close + $(document).unbind("keyup", this._close_handler); + this.close( ev ); + } + }); + +})( this.jQuery, this.app ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.DraggablePanel = ui.AbstractPanel.extend({ + defaults: { + // title: "" // (required) text for the panel title + }, + + _baseCls: "uiPanel", + + init: function() { + this._super(); + this.body = $(this._body_template()); + this.title = $(this._title_template()); + this.el = $.joey( this._main_template() ); + this.el.css( { width: this.config.width } ); + this.dd = new app.ux.DragDrop({ + pickupSelector: this.el.find(".uiPanel-titleBar"), + dragObj: this.el + }); + // open the panel if set in configuration + this.config.open && this.open(); + }, + + setBody: function(body) { + this.body.empty().append(body); + }, + _body_template: function() { return { tag: "DIV", cls: "uiPanel-body", css: { height: this.config.height + (this.config.height === 'auto' ? "" : "px" ) }, children: [ this.config.body ] }; }, + _title_template: function() { return { tag: "SPAN", cls: "uiPanel-title", text: this.config.title }; }, + _main_template: function() { return ( + { tag: "DIV", id: this.id(), cls: this._baseCls, children: [ + { tag: "DIV", cls: "uiPanel-titleBar", children: [ + { tag: "DIV", cls: "uiPanel-close", onclick: this._close_handler, text: "x" }, + this.title + ]}, + this.body + ] } + ); } + }); + +})( this.jQuery, this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.InfoPanel = ui.DraggablePanel.extend({ + _baseCls: "uiPanel uiInfoPanel" + }); + +})( this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.DialogPanel = ui.DraggablePanel.extend({ + _commit_handler: function(jEv) { + this.fire("commit", this, { jEv: jEv }); + }, + _main_template: function() { + var t = this._super(); + t.children.push(this._actionsBar_template()); + return t; + }, + _actionsBar_template: function() { + return { tag: "DIV", cls: "pull-right", children: [ + new app.ui.Button({ label: "Cancel", onclick: this._close_handler }), + new app.ui.Button({ label: "OK", onclick: this._commit_handler }) + ]}; + } + }); + +})( this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.MenuPanel = ui.AbstractPanel.extend({ + defaults: { + items: [], // (required) an array of menu items + modal: false + }, + _baseCls: "uiMenuPanel", + init: function() { + this._super(); + this.el = $(this._main_template()); + }, + open: function(jEv) { + this._super(jEv); + var cx = this; setTimeout(function() { $(document).bind("click", cx._close_handler); }, 50); + }, + _getItems: function() { + return this.config.items; + }, + _close_handler: function(jEv) { + this._super(jEv); + $(document).unbind("click", this._close_handler); + }, + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: this._getItems().map(this._menuItem_template, this) }; + }, + _menuItem_template: function(item) { + var dx = item.disabled ? { onclick: function() {} } : {}; + return { tag: "LI", cls: "uiMenuPanel-item" + (item.disabled ? " disabled" : "") + (item.selected ? " selected" : ""), children: [ $.extend({ tag: "DIV", cls: "uiMenuPanel-label" }, item, dx ) ] }; + }, + _getPosition: function(jEv) { + var right = !! $(jEv.target).parents(".pull-right").length; + var parent = $(jEv.target).closest("BUTTON"); + return parent.vOffset() + .addY(parent.vSize().y) + .addX( right ? parent.vSize().x - this.el.vOuterSize().x : 0 ) + .asOffset(); + } + }); + +})( this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.SelectMenuPanel = ui.MenuPanel.extend({ + defaults: { + items: [], // (required) an array of menu items + value: null + }, + _baseCls: "uiSelectMenuPanel uiMenuPanel", + init: function() { + this.value = this.config.value; + this._super(); + }, + _getItems: function() { + return this.config.items.map( function( item ) { + return { + text: item.text, + selected: this.value === item.value, + onclick: function( jEv ) { + var el = $( jEv.target ).closest("LI"); + el.parent().children().removeClass("selected"); + el.addClass("selected"); + this.fire( "select", this, { value: item.value } ); + this.value = item.value; + }.bind(this) + }; + }, this ); + + } + }); + +})( this.app ); + +( function( $, app ) { + + var ui = app.ns("ui"); + + ui.Table = ui.AbstractWidget.extend({ + defaults: { + store: null, // (required) implements interface app.data.DataSourceInterface + height: 0, + width: 0 + }, + _baseCls: "uiTable", + init: function(parent) { + this._super(); + this.initElements(parent); + this.config.store.on("data", this._data_handler); + }, + attach: function(parent) { + if(parent) { + this._super(parent); + this._reflow(); + } + }, + initElements: function(parent) { + this.el = $.joey(this._main_template()); + this.body = this.el.find(".uiTable-body"); + this.headers = this.el.find(".uiTable-headers"); + this.tools = this.el.find(".uiTable-tools"); + this.attach( parent ); + }, + _data_handler: function(store) { + this.tools.text(store.summary); + this.headers.empty().append(this._header_template(store.columns)); + this.body.empty().append(this._body_template(store.data, store.columns)); + this._reflow(); + }, + _reflow: function() { + var firstCol = this.body.find("TR:first TH.uiTable-header-cell > DIV"), + headers = this.headers.find("TR:first TH.uiTable-header-cell > DIV"); + for(var i = 0; i < headers.length; i++) { + $(headers[i]).width( $(firstCol[i]).width() ); + } + this._scroll_handler(); + }, + _scroll_handler: function(ev) { + this.el.find(".uiTable-headers").scrollLeft(this.body.scrollLeft()); + }, + _dataClick_handler: function(ev) { + var row = $(ev.target).closest("TR"); + if(row.length) { + this.fire("rowClick", this, { row: row } ); + } + }, + _headerClick_handler: function(ev) { + var header = $(ev.target).closest("TH.uiTable-header-cell"); + if(header.length) { + this.fire("headerClick", this, { header: header, column: header.data("column"), dir: header.data("dir") }); + } + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), css: { width: this.config.width + "px" }, cls: this._baseCls, children: [ + { tag: "DIV", cls: "uiTable-tools" }, + { tag: "DIV", cls: "uiTable-headers", onclick: this._headerClick_handler }, + { tag: "DIV", cls: "uiTable-body", + onclick: this._dataClick_handler, + onscroll: this._scroll_handler, + css: { height: this.config.height + "px", width: this.config.width + "px" } + } + ] }; + }, + _header_template: function(columns) { + var ret = { tag: "TABLE", children: [ this._headerRow_template(columns) ] }; + ret.children[0].children.push(this._headerEndCap_template()); + return ret; + }, + _headerRow_template: function(columns) { + return { tag: "TR", cls: "uiTable-header-row", children: columns.map(function(column) { + var dir = ((this.config.store.sort.column === column) && this.config.store.sort.dir) || "none"; + return { tag: "TH", data: { column: column, dir: dir }, cls: "uiTable-header-cell" + ((dir !== "none") ? " uiTable-sort" : ""), children: [ + { tag: "DIV", children: [ + { tag: "DIV", cls: "uiTable-headercell-menu", text: dir === "asc" ? "\u25b2" : "\u25bc" }, + { tag: "DIV", cls: "uiTable-headercell-text", text: column } + ]} + ]}; + }, this)}; + }, + _headerEndCap_template: function() { + return { tag: "TH", cls: "uiTable-headerEndCap", children: [ { tag: "DIV" } ] }; + }, + _body_template: function(data, columns) { + return { tag: "TABLE", children: [] + .concat(this._headerRow_template(columns)) + .concat(data.map(function(row) { + return { tag: "TR", data: { row: row }, cls: "uiTable-row", children: columns.map(function(column){ + return { tag: "TD", cls: "uiTable-cell", children: [ { tag: "DIV", text: (row[column] || "").toString() } ] }; + })}; + })) + }; + } + + }); + +})( this.jQuery, this.app ); + +( function( $, app, joey ) { + + var ui = app.ns("ui"); + + var CELL_SEPARATOR = ","; + var CELL_QUOTE = '"'; + var LINE_SEPARATOR = "\r\n"; + + ui.CSVTable = ui.AbstractWidget.extend({ + defaults: { + results: null + }, + _baseCls: "uiCSVTable", + init: function( parent ) { + this._super(); + var results = this.config.results.hits.hits; + var columns = this._parseResults( results ); + this._downloadButton = new ui.Button({ + label: "Generate Download Link", + onclick: this._downloadLinkGenerator_handler + }); + this._downloadLink = $.joey( { tag: "A", text: "download", }); + this._downloadLink.hide(); + this._csvText = this._csv_template( columns, results ); + this.el = $.joey( this._main_template() ); + this.attach( parent ); + }, + _downloadLinkGenerator_handler: function() { + var csvData = new Blob( [ this._csvText ], { type: 'text/csv' }); + var csvURL = URL.createObjectURL( csvData ); + this._downloadLink.attr( "href", csvURL ); + this._downloadLink.show(); + }, + _parseResults: function( results ) { + var columnPaths = {}; + (function parse( path, obj ) { + if( obj instanceof Array ) { + for( var i = 0; i < obj.length; i++ ) { + parse( path, obj[i] ); + } + } else if( typeof obj === "object" ) { + for( var prop in obj ) { + parse( path + "." + prop, obj[ prop ] ); + } + } else { + columnPaths[ path ] = true; + } + })( "root", results ); + var columns = []; + for( var column in columnPaths ) { + columns.push( column.split(".").slice(1) ); + } + return columns; + }, + _main_template: function() { return ( + { tag: "DIV", cls: this._baseCls, id: this.id(), children: [ + this._downloadButton, + this._downloadLink, + { tag: "PRE", text: this._csvText } + ] } + ); }, + _csv_template: function( columns, results ) { + return this._header_template( columns ) + LINE_SEPARATOR + this._results_template( columns, results ); + }, + _header_template: function( columns ) { + return columns.map( function( column ) { + return column.join("."); + }).join( CELL_SEPARATOR ); + }, + _results_template: function( columns, results ) { + return results.map( function( result ) { + return columns.map( function( column ) { + var l = 0, + ptr = result; + while( l !== column.length && ptr != null ) { + ptr = ptr[ column[ l++ ] ]; + } + return ( ptr == null ) ? "" : ( CELL_QUOTE + ptr.toString().replace(/"/g, '""') + CELL_QUOTE ); + }).join( CELL_SEPARATOR ); + }).join( LINE_SEPARATOR ); + } + }); + +})( this.jQuery, this.app, this.joey ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.JsonPretty = ui.AbstractWidget.extend({ + defaults: { + obj: null + }, + init: function(parent) { + this._super(); + this.el = $(this._main_template()); + this.attach(parent); + this.el.click(this._click_handler); + }, + + _click_handler: function(jEv) { + var t = $(jEv.target).closest(".uiJsonPretty-name").closest("LI"); + if(t.length === 0 || t.parents(".uiJsonPretty-minimised").length > 0) { return; } + t.toggleClass("uiJsonPretty-minimised"); + jEv.stopPropagation(); + }, + + _main_template: function() { + try { + return { tag: "DIV", cls: "uiJsonPretty", children: this.pretty.parse(this.config.obj) }; + } catch (error) { + throw "JsonPretty error: " + error.message; + } + }, + + pretty: { // from https://github.com/RyanAmos/Pretty-JSON/blob/master/pretty_json.js + "expando" : function(value) { + return (value && (/array|object/i).test(value.constructor.name)) ? "expando" : ""; + }, + "parse": function (member) { + return this[(member == null) ? 'null' : member.constructor.name.toLowerCase()](member); + }, + "null": function (value) { + return this['value']('null', 'null'); + }, + "array": function (value) { + var results = []; + var lastItem = value.length - 1; + value.forEach(function( v, i ) { + results.push({ tag: "LI", cls: this.expando(v), children: [ this['parse'](v) ] }); + if( i !== lastItem ) { + results.push(","); + } + }, this); + return [ "[ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-array", children: results } : null), "]" ]; + }, + "object": function (value) { + var results = []; + var keys = Object.keys( value ); + var lastItem = keys.length - 1; + keys.forEach( function( key, i ) { + var children = [ this['value']( 'name', '"' + key + '"' ), ": ", this['parse']( value[ key ]) ]; + if( i !== lastItem ) { + children.push(","); + } + results.push( { tag: "LI", cls: this.expando( value[ key ] ), children: children } ); + }, this); + return [ "{ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-object", children: results } : null ), "}" ]; + }, + "number": function (value) { + return this['value']('number', value.toString()); + }, + "string": function (value) { + if (/^(http|https|file):\/\/[^\s]+$/.test(value)) { + return this['link']( value ); + } else { + return this['value']('string', '"' + value.toString() + '"'); + } + }, + "boolean": function (value) { + return this['value']('boolean', value.toString()); + }, + "link": function( value ) { + return this['value']("string", { tag: "A", href: value, target: "_blank", text: '"' + value + '"' } ); + }, + "value": function (type, value) { + if (/^(http|https|file):\/\/[^\s]+$/.test(value)) { + } + return { tag: "SPAN", cls: "uiJsonPretty-" + type, text: value }; + } + } + }); + +})( this.jQuery, this.app ); + +(function( $, app ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.PanelForm = ui.AbstractWidget.extend({ + defaults: { + fields: null // (required) instanceof app.ux.FieldCollection + }, + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + this.attach( parent ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiPanelForm", children: this.config.fields.fields.map(this._field_template, this) }; + }, + _field_template: function(field) { + return { tag: "LABEL", cls: "uiPanelForm-field", children: [ + { tag: "DIV", cls: "uiPanelForm-label", children: [ field.label, ut.require_template(field) ] }, + field + ]}; + } + }); + +})( this.jQuery, this.app ); + +(function( app ){ + + var ui = app.ns("ui"); + + ui.HelpPanel = ui.InfoPanel.extend({ + defaults: { + ref: "", + open: true, + autoRemove: true, + modal: false, + width: 500, + height: 450, + title: i18n.text("General.Help") + }, + init: function() { + this._super(); + this.body.append(i18n.text(this.config.ref)); + } + }); + +})( this.app ); + +(function( app ) { + + var ui = app.ns("ui"); + + ui.JsonPanel = ui.InfoPanel.extend({ + defaults: { + json: null, // (required) + modal: false, + open: true, + autoRemove: true, + height: 500, + width: 600 + }, + + _baseCls: "uiPanel uiInfoPanel uiJsonPanel", + + _body_template: function() { + var body = this._super(); + body.children = [ new ui.JsonPretty({ obj: this.config.json }) ]; + return body; + } + }); + +})( this.app ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.SidebarSection = ui.AbstractWidget.extend({ + defaults: { + title: "", + help: null, + body: null, + open: false + }, + init: function() { + this._super(); + this.el = $.joey( this._main_template() ); + this.body = this.el.children(".uiSidebarSection-body"); + this.config.open && ( this.el.addClass("shown") && this.body.css("display", "block") ); + }, + _showSection_handler: function( ev ) { + var shown = $( ev.target ).closest(".uiSidebarSection") + .toggleClass("shown") + .children(".uiSidebarSection-body").slideToggle(200, function() { this.fire("animComplete", this); }.bind(this)) + .end() + .hasClass("shown"); + this.fire(shown ? "show" : "hide", this); + }, + _showHelp_handler: function( ev ) { + new ui.HelpPanel({ref: this.config.help}); + ev.stopPropagation(); + }, + _main_template: function() { return ( + { tag: "DIV", cls: "uiSidebarSection", children: [ + (this.config.title && { tag: "DIV", cls: "uiSidebarSection-head", onclick: this._showSection_handler, children: [ + this.config.title, + ( this.config.help && { tag: "SPAN", cls: "uiSidebarSection-help pull-right", onclick: this._showHelp_handler, text: i18n.text("General.HelpGlyph") } ) + ] }), + { tag: "DIV", cls: "uiSidebarSection-body", children: [ this.config.body ] } + ] } + ); } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.ResultTable = ui.Table.extend({ + defaults: { + width: 500, + height: 400 + }, + + init: function() { + this._super(); + this.on("rowClick", this._showPreview_handler); + this.selectedRow = null; + $(document).bind("keydown", this._nav_handler); + }, + remove: function() { + $(document).unbind("keydown", this._nav_handler); + this._super(); + }, + attach: function(parent) { + if(parent) { + var height = parent.height() || ( $(document).height() - parent.offset().top - 41 ); // 41 = height in px of .uiTable-tools + uiTable-header + var width = parent.width(); + this.el.width( width ); + this.body.width( width ).height( height ); + } + this._super(parent); + }, + showPreview: function(row) { + row.addClass("selected"); + this.preview = new app.ui.JsonPanel({ + title: i18n.text("Browser.ResultSourcePanelTitle"), + json: row.data("row")._source, + onClose: function() { row.removeClass("selected"); } + }); + }, + _nav_handler: function(jEv) { + if(jEv.keyCode !== 40 && jEv.keyCode !== 38) { + return; + } + this.selectedRow && this.preview && this.preview.remove(); + if(jEv.keyCode === 40) { // up arrow + this.selectedRow = this.selectedRow ? this.selectedRow.next("TR") : this.body.find("TR:first"); + } else if(jEv.keyCode === 38) { // down arrow + this.selectedRow = this.selectedRow ? this.selectedRow.prev("TR") : this.body.find("TR:last"); + } + this.selectedRow && this.showPreview(this.selectedRow); + }, + _showPreview_handler: function(obj, data) { + this.showPreview(this.selectedRow = data.row); + } + }); + +})( this.jQuery, this.app ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.QueryFilter = ui.AbstractWidget.extend({ + defaults: { + metadata: null, // (required) instanceof app.data.MetaData + query: null // (required) instanceof app.data.Query that the filters will act apon + }, + init: function() { + this._super(); + this.metadata = this.config.metadata; + this.query = this.config.query; + this.el = $(this._main_template()); + }, + helpTypeMap: { + "date" : "QueryFilter.DateRangeHelp" + }, + requestUpdate: function(jEv) { + if(jEv && jEv.originalEvent) { // we only want to update on real user interaction not generated events + this.query.setPage(1); + this.query.query(); + } + }, + getSpec: function(fieldName) { + return this.metadata.fields[fieldName]; + }, + _selectAlias_handler: function(jEv) { + var indices = (jEv.target.selectedIndex === 0) ? [] : this.metadata.getIndices($(jEv.target).val()); + $(".uiQueryFilter-index").each(function(i, el) { + var jEl = $(el); + if(indices.contains(jEl.text()) !== jEl.hasClass("selected")) { + jEl.click(); + } + }); + this.requestUpdate(jEv); + }, + _selectIndex_handler: function(jEv) { + var jEl = $(jEv.target).closest(".uiQueryFilter-index"); + jEl.toggleClass("selected"); + var selected = jEl.hasClass("selected"); + this.query.setIndex(jEl.text(), selected); + if(selected) { + var types = this.metadata.getTypes(this.query.indices); + this.el.find("DIV.uiQueryFilter-type.selected").each(function(n, el) { + if(! types.contains($(el).text())) { + $(el).click(); + } + }); + } + this.requestUpdate(jEv); + }, + _selectType_handler: function(jEv) { + var jEl = $(jEv.target).closest(".uiQueryFilter-type"); + jEl.toggleClass("selected"); + var type = jEl.text(), selected = jEl.hasClass("selected"); + this.query.setType(type, selected); + if(selected) { + var indices = this.metadata.types[type].indices; + // es throws a 500 if searching an index for a type it does not contain - so we prevent that + this.el.find("DIV.uiQueryFilter-index.selected").each(function(n, el) { + if(! indices.contains($(el).text())) { + $(el).click(); + } + }); + // es throws a 500 if you specify types from different indices with _all + jEl.siblings(".uiQueryFilter-type.selected").forEach(function(el) { + if(this.metadata.types[$(el).text()].indices.intersection(indices).length === 0) { + $(el).click(); + } + }, this); + } + this.requestUpdate(jEv); + }, + _openFilter_handler: function(section) { + var field_name = section.config.title; + if(! section.loaded) { + var spec = this.getSpec(field_name); + if(spec.core_type === "string") { + section.body.append(this._textFilter_template(spec)); + } else if(spec.core_type === "date") { + section.body.append(this._dateFilter_template(spec)); + section.body.append(new ui.DateHistogram({ printEl: section.body.find("INPUT"), cluster: this.cluster, query: this.query, spec: spec })); + } else if(spec.core_type === "number") { + section.body.append(this._numericFilter_template(spec)); + } else if(spec.core_type === 'boolean') { + section.body.append(this._booleanFilter_template(spec)); + } else if (spec.core_type === 'multi_field') { + section.body.append(this._multiFieldFilter_template(section, spec)); + } + section.loaded = true; + } + section.on("animComplete", function(section) { section.body.find("INPUT").focus(); }); + }, + _textFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqids = jEl.data("uqids") || []; + uqids.forEach(function(uqid) { + uqid && this.query.removeClause(uqid); + }, this); + if(val.length) { + if(jEl[0] === document.activeElement && jEl[0].selectionStart === jEl[0].selectionEnd) { + val = val.replace(new RegExp("(.{"+jEl[0].selectionStart+"})"), "$&*"); + } + uqids = val.split(/\s+/).map(function(term) { + // Figure out the actual field name - needed for multi_field, because + // querying for "field.field" will not work. Simply "field" must be used + // if nothing is aliased. + var fieldNameParts = spec.field_name.split('.'); + var part = fieldNameParts.length - 1; + var name = fieldNameParts[part]; + while (part >= 1) { + if (fieldNameParts[part] !== fieldNameParts[part - 1]) { + name = fieldNameParts[part - 1] + "." + name; + } + part--; + } + return term && this.query.addClause(term, name, "wildcard", "must"); + }, this); + } + jEl.data("uqids", uqids); + this.requestUpdate(jEv); + }, + _dateFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqid = jEl.data("uqid") || null; + var range = window.dateRangeParser.parse(val); + var lastRange = jEl.data("lastRange"); + if(!range || (lastRange && lastRange.start === range.start && lastRange.end === range.end)) { + return; + } + uqid && this.query.removeClause(uqid); + if((range.start && range.end) === null) { + uqid = null; + } else { + var value = {}; + if( range.start ) { + value["gte"] = range.start; + } + if( range.end ) { + value["lte"] = range.end; + } + uqid = this.query.addClause( value, spec.field_name, "range", "must"); + } + jEl.data("lastRange", range); + jEl.siblings(".uiQueryFilter-rangeHintFrom") + .text(i18n.text("QueryFilter.DateRangeHint.from", range.start && new Date(range.start).toUTCString())); + jEl.siblings(".uiQueryFilter-rangeHintTo") + .text(i18n.text("QueryFilter.DateRangeHint.to", range.end && new Date(range.end).toUTCString())); + jEl.data("uqid", uqid); + this.requestUpdate(jEv); + }, + _numericFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqid = jEl.data("uqid") || null; + var lastRange = jEl.data("lastRange"); + var range = (function(val) { + var ops = val.split(/->|<>|/.test(val)) { + return { gte: (ops[0] - ops[1]), lte: (ops[0] + ops[1]) }; + } else if(/->| .uiBrowser-filter") ); + this.resultTable = new ui.ResultTable( { + onHeaderClick: this._changeSort_handler, + store: this.store + } ); + this.resultTable.attach( this.el.find("> .uiBrowser-table") ); + this.updateResults(); + }.bind(this) + }); + }, + updateResults: function() { + this.query.query(); + }, + _changeSort_handler: function(table, wEv) { + this.query.setSort(wEv.column, wEv.dir === "desc"); + this.query.setPage(1); + this.query.query(); + }, + _main_template: function() { + return { tag: "DIV", cls: "uiBrowser", children: [ + new ui.Toolbar({ + label: i18n.text("Browser.Title"), + left: [ ], + right: [ this._refreshButton ] + }), + { tag: "DIV", cls: "uiBrowser-filter" }, + { tag: "DIV", cls: "uiBrowser-table" } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app, i18n, raphael ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + var services = app.ns("services"); + + ui.AnyRequest = ui.Page.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + path: "_search", // default uri to send a request to + query: { query: { match_all: { }}}, + transform: " return root;" // default transformer function (does nothing) + }, + init: function(parent) { + this._super(); + this.prefs = services.Preferences.instance(); + this.history = this.prefs.get("anyRequest-history") || [ { type: "POST", path: this.config.path, query : JSON.stringify(this.config.query), transform: this.config.transform } ]; + this.el = $.joey(this._main_template()); + this.base_uriEl = this.el.find("INPUT[name=base_uri]"); + this.pathEl = this.el.find("INPUT[name=path]"); + this.typeEl = this.el.find("SELECT[name=method]"); + this.dataEl = this.el.find("TEXTAREA[name=body]"); + this.prettyEl = this.el.find("INPUT[name=pretty]"); + this.transformEl = this.el.find("TEXTAREA[name=transform]"); + this.asGraphEl = this.el.find("INPUT[name=asGraph]"); + this.asTableEl = this.el.find("INPUT[name=asTable]"); + this.asJsonEl = this.el.find("INPUT[name=asJson]"); + this.cronEl = this.el.find("SELECT[name=cron]"); + this.outEl = this.el.find("DIV.uiAnyRequest-out"); + this.errEl = this.el.find("DIV.uiAnyRequest-jsonErr"); + this.typeEl.val("GET"); + this.attach(parent); + this.setHistoryItem(this.history[this.history.length - 1]); + }, + setHistoryItem: function(item) { + this.pathEl.val(item.path); + this.typeEl.val(item.type); + this.dataEl.val(item.query); + this.transformEl.val(item.transform); + }, + _request_handler: function( ev ) { + if(! this._validateJson_handler()) { + return; + } + var path = this.pathEl.val(), + type = this.typeEl.val(), + query = JSON.stringify(JSON.parse(this.dataEl.val())), + transform = this.transformEl.val(), + base_uri = this.base_uriEl.val(); + if( ev ) { // if the user click request + if(this.timer) { + window.clearTimeout(this.timer); // stop any cron jobs + } + delete this.prevData; // remove data from previous cron runs + this.outEl.text(i18n.text("AnyRequest.Requesting")); + if( ! /\/$/.test( base_uri )) { + base_uri += "/"; + this.base_uriEl.val( base_uri ); + } + for(var i = 0; i < this.history.length; i++) { + if(this.history[i].path === path && + this.history[i].type === type && + this.history[i].query === query && + this.history[i].transform === transform) { + this.history.splice(i, 1); + } + } + this.history.push({ + path: path, + type: type, + query: query, + transform: transform + }); + this.history.slice(250); // make sure history does not get too large + this.prefs.set( "anyRequest-history", this.history ); + this.el.find("UL.uiAnyRequest-history") + .empty() + .append($( { tag: "UL", children: this.history.map(this._historyItem_template, this) }).children()) + .children().find(":last-child").each(function(i, j) { j.scrollIntoView(false); }).end() + .scrollLeft(0); + } + this.config.cluster.request({ + url: base_uri + path, + type: type, + data: query, + success: this._responseWriter_handler, + error: this._responseError_handler + }); + }, + _responseError_handler: function (response) { + var obj; + try { + obj = JSON.parse(response.responseText); + if (obj) { + this._responseWriter_handler(obj); + } + } catch (err) { + } + }, + _responseWriter_handler: function(data) { + this.outEl.empty(); + try { + data = (new Function("root", "prev", this.transformEl.val()))(data, this.prevData) + } catch(e) { + this.errEl.text(e.message); + return; + } + if(this.asGraphEl.attr("checked")) { + var w = this.outEl.width(); + raphael(this.outEl[0], w - 10, 300) + .g.barchart(10, 10, w - 20, 280, [data]); + } + if(this.asTableEl.attr("checked")) { + try { + var store = new app.data.ResultDataSourceInterface(); + this.outEl.append(new app.ui.ResultTable({ + width: this.outEl.width() - 23, + store: store + } ) ); + store.results(data); + } catch(e) { + this.errEl.text("Results Table Failed: " + e.message); + } + } + if(this.asJsonEl.attr("checked")) { + this.outEl.append(new ui.JsonPretty({ obj: data })); + } + if(this.cronEl.val() > 0) { + this.timer = window.setTimeout(function(){ + this._request_handler(); + }.bind(this), this.cronEl.val()); + } + this.prevData = data; + }, + _validateJson_handler: function( ev ) { + /* if the textarea is empty, we replace its value by an empty JSON object : "{}" and the request goes on as usual */ + var jsonData = this.dataEl.val().trim(); + var j; + if(jsonData === "") { + jsonData = "{}"; + this.dataEl.val( jsonData ); + } + try { + j = JSON.parse(jsonData); + } catch(e) { + this.errEl.text(e.message); + return false; + } + this.errEl.text(""); + if(this.prettyEl.attr("checked")) { + this.dataEl.val(JSON.stringify(j, null, " ")); + } + return true; + }, + _historyClick_handler: function( ev ) { + var item = $( ev.target ).closest( "LI" ).data( "item" ); + this.setHistoryItem( item ); + }, + _main_template: function() { + return { tag: "DIV", cls: "anyRequest", children: [ + { tag: "DIV", cls: "uiAnyRequest-request", children: [ + new app.ui.SidebarSection({ + open: false, + title: i18n.text("AnyRequest.History"), + body: { tag: "UL", onclick: this._historyClick_handler, cls: "uiAnyRequest-history", children: this.history.map(this._historyItem_template, this) } + }), + new app.ui.SidebarSection({ + open: true, + title: i18n.text("AnyRequest.Query"), + body: { tag: "DIV", children: [ + { tag: "INPUT", type: "text", name: "base_uri", value: this.config.cluster.config.base_uri }, + { tag: "BR" }, + { tag: "INPUT", type: "text", name: "path", value: this.config.path }, + { tag: "SELECT", name: "method", children: ["POST", "GET", "PUT", "HEAD", "DELETE"].map(ut.option_template) }, + { tag: "TEXTAREA", name: "body", rows: 20, text: JSON.stringify(this.config.query) }, + { tag: "BUTTON", css: { cssFloat: "right" }, type: "button", children: [ { tag: "B", text: i18n.text("AnyRequest.Request") } ], onclick: this._request_handler }, + { tag: "BUTTON", type: "button", text: i18n.text("AnyRequest.ValidateJSON"), onclick: this._validateJson_handler }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "pretty" }, i18n.text("AnyRequest.Pretty") ] }, + { tag: "DIV", cls: "uiAnyRequest-jsonErr" } + ]} + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.Transformer"), + help: "AnyRequest.TransformerHelp", + body: { tag: "DIV", children: [ + { tag: "CODE", text: "function(root, prev) {" }, + { tag: "BR" }, + { tag: "TEXTAREA", name: "transform", rows: 5, text: this.config.transform }, + { tag: "BR" }, + { tag: "CODE", text: "}" } + ] } + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.RepeatRequest"), + body: { tag: "DIV", children: [ + i18n.text("AnyRequest.RepeatRequestSelect"), " ", + { tag: "SELECT", name: "cron", children: [ + { value: 0, text: "do not repeat" }, + { value: 1000, text: "second" }, + { value: 1000 * 2, text: "2 seconds" }, + { value: 1000 * 5, text: "5 seconds" }, + { value: 1000 * 20, text: "20 seconds" }, + { value: 1000 * 60, text: "minute" }, + { value: 1000 * 60 * 10, text: "10 minutes" }, + { value: 1000 * 60 * 60, text: "hour" } + ].map(function(op) { return $.extend({ tag: "OPTION"}, op); }) } + ] } + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.DisplayOptions"), + help: "AnyRequest.DisplayOptionsHelp", + body: { tag: "DIV", children: [ + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", checked: true, name: "asJson" }, i18n.text("AnyRequest.AsJson") ] }, + { tag: "BR" }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "asGraph" }, i18n.text("AnyRequest.AsGraph") ] }, + { tag: "BR" }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "asTable" }, i18n.text("AnyRequest.AsTable") ] } + ] } + }) + ] }, + { tag: "DIV", cls: "uiAnyRequest-out" } + ] }; + }, + _historyItem_template: function(item) { + return { tag: "LI", cls: "booble", data: { item: item }, children: [ + { tag: "SPAN", text: item.path }, + " ", + { tag: "EM", text: item.query }, + " ", + { tag: "SPAN", text: item.transform } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n, this.Raphael ); + +(function( app, i18n, joey ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.NodesView = ui.AbstractWidget.extend({ + defaults: { + interactive: true, + aliasRenderer: "list", + scaleReplicas: 1, + cluster: null, + data: null + }, + init: function() { + this._super(); + this.interactive = this.config.interactive; + this.cluster = this.config.cluster; + this._aliasRenderFunction = { + "none": this._aliasRender_template_none, + "list": this._aliasRender_template_list, + "full": this._aliasRender_template_full + }[ this.config.aliasRenderer ]; + this._styleSheetEl = joey({ tag: "STYLE", text: ".uiNodesView-nullReplica, .uiNodesView-replica { zoom: " + this.config.scaleReplicas + " }" }); + this.el = $( this._main_template( this.config.data.cluster, this.config.data.indices ) ); + }, + + _newAliasAction_handler: function( index ) { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("AliasForm.AliasName"), name: "alias", require: true }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("AliasForm.NewAliasForIndexName", index.name), + body: new ui.PanelForm({ fields: fields }), + onCommit: function(panel, args) { + if(fields.validate()) { + var data = fields.getData(); + var command = { + "actions" : [ + { "add" : { "index" : index.name, "alias" : data["alias"] } } + ] + }; + this.config.cluster.post('_aliases', JSON.stringify(command), function(d) { + dialog.close(); + alert(JSON.stringify(d)); + this.fire("redraw"); + }.bind(this) ); + } + }.bind(this) + }).open(); + }, + _postIndexAction_handler: function(action, index, redraw) { + this.cluster.post(index.name + "/" + action, null, function(r) { + alert(JSON.stringify(r)); + redraw && this.fire("redraw"); + }.bind(this)); + }, + _optimizeIndex_handler: function(index) { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("OptimizeForm.MaxSegments"), name: "max_num_segments", value: "1", require: true }), + new ui.CheckField({ label: i18n.text("OptimizeForm.ExpungeDeletes"), name: "only_expunge_deletes", value: false }), + new ui.CheckField({ label: i18n.text("OptimizeForm.FlushAfter"), name: "flush", value: true }), + new ui.CheckField({ label: i18n.text("OptimizeForm.WaitForMerge"), name: "wait_for_merge", value: false }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("OptimizeForm.OptimizeIndex", index.name), + body: new ui.PanelForm({ fields: fields }), + onCommit: function( panel, args ) { + if(fields.validate()) { + this.cluster.post(index.name + "/_optimize", fields.getData(), function(r) { + alert(JSON.stringify(r)); + }); + dialog.close(); + } + }.bind(this) + }).open(); + }, + _testAnalyser_handler: function(index) { + this.cluster.get(index.name + "/_analyze?text=" + prompt( i18n.text("IndexCommand.TextToAnalyze") ), function(r) { + alert(JSON.stringify(r, true, " ")); + }); + }, + _deleteIndexAction_handler: function(index) { + if( prompt( i18n.text("AliasForm.DeleteAliasMessage", i18n.text("Command.DELETE"), index.name ) ) === i18n.text("Command.DELETE") ) { + this.cluster["delete"](index.name, null, function(r) { + alert(JSON.stringify(r)); + this.fire("redraw"); + }.bind(this) ); + } + }, + _shutdownNode_handler: function(node) { + if(prompt( i18n.text("IndexCommand.ShutdownMessage", i18n.text("Command.SHUTDOWN"), node.cluster.name ) ) === i18n.text("Command.SHUTDOWN") ) { + this.cluster.post( "_cluster/nodes/" + node.name + "/_shutdown", null, function(r) { + alert(JSON.stringify(r)); + this.fire("redraw"); + }.bind(this)); + } + }, + _deleteAliasAction_handler: function( index, alias ) { + if( confirm( i18n.text("Command.DeleteAliasMessage" ) ) ) { + var command = { + "actions" : [ + { "remove" : { "index" : index.name, "alias" : alias.name } } + ] + }; + this.config.cluster.post('_aliases', JSON.stringify(command), function(d) { + alert(JSON.stringify(d)); + this.fire("redraw"); + }.bind(this) ); + } + }, + + _replica_template: function(replica) { + var r = replica.replica; + return { tag: "DIV", + cls: "uiNodesView-replica" + (r.primary ? " primary" : "") + ( " state-" + r.state ), + text: r.shard.toString(), + onclick: function() { new ui.JsonPanel({ + json: replica.status || r, + title: r.index + "/" + r.node + " [" + r.shard + "]" }); + } + }; + }, + _routing_template: function(routing) { + var cell = { tag: "TD", cls: "uiNodesView-routing" + (routing.open ? "" : " close"), children: [] }; + for(var i = 0; i < routing.replicas.length; i++) { + if(i % routing.max_number_of_shards === 0 && i > 0) { + cell.children.push({ tag: "BR" }); + } + if( routing.replicas[i] ) { + cell.children.push(this._replica_template(routing.replicas[i])); + } else { + cell.children.push( { tag: "DIV", cls: "uiNodesView-nullReplica" } ); + } + } + return cell; + }, + _nodeControls_template: function( node ) { return ( + { tag: "DIV", cls: "uiNodesView-controls", children: [ + new ui.MenuButton({ + label: i18n.text("NodeInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("NodeInfoMenu.ClusterNodeInfo"), onclick: function() { new ui.JsonPanel({ json: node.cluster, title: node.name });} }, + { text: i18n.text("NodeInfoMenu.NodeStats"), onclick: function() { new ui.JsonPanel({ json: node.stats, title: node.name });} } + ] + }) + }), + new ui.MenuButton({ + label: i18n.text("NodeActionsMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("NodeActionsMenu.Shutdown"), onclick: function() { this._shutdownNode_handler(node); }.bind(this) } + ] + }) + }) + ] } + ); }, + _nodeIcon_template: function( node ) { + var icon, alt; + if( node.name === "Unassigned" ) { + icon = "fa-exclamation-triangle"; + alt = i18n.text( "NodeType.Unassigned" ); + } else if( node.cluster.settings && "tribe" in node.cluster.settings) { + icon = "fa-sitemap"; + alt = i18n.text("NodeType.Tribe" ); + } else { + icon = "fa-" + (node.master_node ? "star" : "circle") + (node.data_node ? "" : "-o" ); + alt = i18n.text( node.master_node ? ( node.data_node ? "NodeType.Master" : "NodeType.Coord" ) : ( node.data_node ? "NodeType.Worker" : "NodeType.Client" ) ); + } + return { tag: "TD", title: alt, cls: "uiNodesView-icon", children: [ + { tag: "SPAN", cls: "fa fa-2x " + icon } + ] }; + }, + _node_template: function(node) { + return { tag: "TR", cls: "uiNodesView-node" + (node.master_node ? " master": ""), children: [ + this._nodeIcon_template( node ), + { tag: "TH", children: node.name === "Unassigned" ? [ + { tag: "H3", text: node.name } + ] : [ + { tag: "H3", text: node.cluster.name }, + { tag: "DIV", text: node.cluster.hostname }, + this.interactive ? this._nodeControls_template( node ) : null + ] } + ].concat(node.routings.map(this._routing_template, this))}; + }, + _indexHeaderControls_template: function( index ) { return ( + { tag: "DIV", cls: "uiNodesView-controls", children: [ + new ui.MenuButton({ + label: i18n.text("IndexInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("IndexInfoMenu.Status"), onclick: function() { new ui.JsonPanel({ json: index.status, title: index.name }); } }, + { text: i18n.text("IndexInfoMenu.Metadata"), onclick: function() { new ui.JsonPanel({ json: index.metadata, title: index.name }); } } + ] + }) + }), + new ui.MenuButton({ + label: i18n.text("IndexActionsMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("IndexActionsMenu.NewAlias"), onclick: function() { this._newAliasAction_handler(index); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Refresh"), onclick: function() { this._postIndexAction_handler("_refresh", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Flush"), onclick: function() { this._postIndexAction_handler("_flush", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Optimize"), onclick: function () { this._optimizeIndex_handler(index); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Snapshot"), disabled: closed, onclick: function() { this._postIndexAction_handler("_gateway/snapshot", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Analyser"), onclick: function() { this._testAnalyser_handler(index); }.bind(this) }, + { text: (index.state === "close") ? i18n.text("IndexActionsMenu.Open") : i18n.text("IndexActionsMenu.Close"), onclick: function() { this._postIndexAction_handler((index.state === "close") ? "_open" : "_close", index, true); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Delete"), onclick: function() { this._deleteIndexAction_handler(index); }.bind(this) } + ] + }) + }) + ] } + ); }, + _indexHeader_template: function( index ) { + var closed = index.state === "close"; + var line1 = closed ? "index: close" : ( "size: " + (index.status && index.status.primaries && index.status.total ? ut.byteSize_template( index.status.primaries.store.size_in_bytes ) + " (" + ut.byteSize_template( index.status.total.store.size_in_bytes ) + ")" : "unknown" ) ); + var line2 = closed ? "\u00A0" : ( "docs: " + (index.status && index.status.primaries && index.status.primaries.docs && index.status.total && index.status.total.docs ? index.status.primaries.docs.count.toLocaleString() + " (" + (index.status.total.docs.count + index.status.total.docs.deleted).toLocaleString() + ")" : "unknown" ) ); + return index.name ? { tag: "TH", cls: (closed ? "close" : ""), children: [ + { tag: "H3", text: index.name }, + { tag: "DIV", text: line1 }, + { tag: "DIV", text: line2 }, + this.interactive ? this._indexHeaderControls_template( index ) : null + ] } : [ { tag: "TD" }, { tag: "TH" } ]; + }, + _aliasRender_template_none: function( cluster, indices ) { + return null; + }, + _aliasRender_template_list: function( cluster, indices ) { + return cluster.aliases.length && { tag: "TBODY", children: [ + { tag: "TR", children: [ + { tag: "TD" } + ].concat( indices.map( function( index ) { + return { tag: "TD", children: index.metadata && index.metadata.aliases.map( function( alias ) { + return { tag: "LI", text: alias }; + } ) }; + })) } + ] }; + }, + _aliasRender_template_full: function( cluster, indices ) { + return cluster.aliases.length && { tag: "TBODY", children: cluster.aliases.map( function(alias, row) { + return { tag: "TR", children: [ { tag: "TD" },{ tag: "TD" } ].concat(alias.indices.map(function(index, i) { + if (index) { + return { + tag: "TD", + css: { background: "#" + "9ce9c7fc9".substr((row+6)%7,3) }, + cls: "uiNodesView-hasAlias" + ( alias.min === i ? " min" : "" ) + ( alias.max === i ? " max" : "" ), + text: alias.name, + children: this.interactive ? [ + { tag: 'SPAN', + text: i18n.text("General.CloseGlyph"), + cls: 'uiNodesView-hasAlias-remove', + onclick: this._deleteAliasAction_handler.bind( this, index, alias ) + } + ]: null + }; + } else { + return { tag: "TD" }; + } + }, this ) ) }; + }, this ) }; + }, + _main_template: function(cluster, indices) { + return { tag: "TABLE", cls: "table uiNodesView", children: [ + this._styleSheetEl, + { tag: "THEAD", children: [ { tag: "TR", children: indices.map(this._indexHeader_template, this) } ] }, + this._aliasRenderFunction( cluster, indices ), + { tag: "TBODY", children: cluster.nodes.map(this._node_template, this) } + ] }; + } + + }); + +})( this.app, this.i18n, this.joey ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + // ( master ) master = true, data = true + // ( coordinator ) master = true, data = false + // ( worker ) master = false, data = true; + // ( client ) master = false, data = false; + // http enabled ? + + function nodeSort_name(a, b) { + if (!(a.cluster && b.cluster)) { + return 0; + } + return a.cluster.name.toString().localeCompare( b.cluster.name.toString() ); + } + + function nodeSort_addr( a, b ) { + if (!(a.cluster && b.cluster)) { + return 0; + } + return a.cluster.transport_address.toString().localeCompare( b.cluster.transport_address.toString() ); + } + + function nodeSort_type( a, b ) { + if (!(a.cluster && b.cluster)) { + return 0; + } + if( a.master_node ) { + return -1; + } else if( b.master_node ) { + return 1; + } else if( a.data_node && !b.data_node ) { + return -1; + } else if( b.data_node && !a.data_node ) { + return 1; + } else { + return a.cluster.name.toString().localeCompare( b.cluster.name.toString() ); + } + } + + var NODE_SORT_TYPES = { + "Sort.ByName": nodeSort_name, + "Sort.ByAddress": nodeSort_addr, + "Sort.ByType": nodeSort_type + }; + + function nodeFilter_none( a ) { + return true; + } + + function nodeFilter_clients( a ) { + return (a.master_node || a.data_node ); + } + + + ui.ClusterOverview = ui.Page.extend({ + defaults: { + cluster: null // (reqired) an instanceof app.services.Cluster + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.prefs = services.Preferences.instance(); + this._clusterState = this.config.clusterState; + this._clusterState.on("data", this.draw_handler ); + this._refreshButton = new ui.RefreshButton({ + onRefresh: this.refresh.bind(this), + onChange: function( btn ) { + if( btn.value === -1 ) { + this.draw_handler(); + } + }.bind( this ) + }); + var nodeSortPref = this.prefs.get("clusterOverview-nodeSort") || Object.keys(NODE_SORT_TYPES)[0]; + this._nodeSort = NODE_SORT_TYPES[ nodeSortPref ]; + this._nodeSortMenu = new ui.MenuButton({ + label: i18n.text( "Preference.SortCluster" ), + menu: new ui.SelectMenuPanel({ + value: nodeSortPref, + items: Object.keys( NODE_SORT_TYPES ).map( function( k ) { + return { text: i18n.text( k ), value: k }; + }), + onSelect: function( panel, event ) { + this._nodeSort = NODE_SORT_TYPES[ event.value ]; + this.prefs.set("clusterOverview-nodeSort", event.value ); + this.draw_handler(); + }.bind(this) + }) + }); + this._indicesSort = this.prefs.get( "clusterOverview-indicesSort") || "desc"; + this._indicesSortMenu = new ui.MenuButton({ + label: i18n.text( "Preference.SortIndices" ), + menu: new ui.SelectMenuPanel({ + value: this._indicesSort, + items: [ + { value: "desc", text: i18n.text( "SortIndices.Descending" ) }, + { value: "asc", text: i18n.text( "SortIndices.Ascending" ) } ], + onSelect: function( panel, event ) { + this._indicesSort = event.value; + this.prefs.set( "clusterOverview-indicesSort", this._indicesSort ); + this.draw_handler(); + }.bind(this) + }) + }); + this._aliasRenderer = this.prefs.get( "clusterOverview-aliasRender" ) || "full"; + this._aliasMenu = new ui.MenuButton({ + label: i18n.text( "Preference.ViewAliases" ), + menu: new ui.SelectMenuPanel({ + value: this._aliasRenderer, + items: [ + { value: "full", text: i18n.text( "ViewAliases.Grouped" ) }, + { value: "list", text: i18n.text( "ViewAliases.List" ) }, + { value: "none", text: i18n.text( "ViewAliases.None" ) } ], + onSelect: function( panel, event ) { + this._aliasRenderer = event.value; + this.prefs.set( "clusterOverview-aliasRender", this._aliasRenderer ); + this.draw_handler(); + }.bind(this) + }) + }); + this._indexFilter = new ui.TextField({ + value: this.prefs.get("clusterOverview-indexFilter"), + placeholder: i18n.text( "Overview.IndexFilter" ), + onchange: function( indexFilter ) { + this.prefs.set("clusterOverview-indexFilter", indexFilter.val() ); + this.draw_handler(); + }.bind(this) + }); + this.el = $(this._main_template()); + this.tablEl = this.el.find(".uiClusterOverview-table"); + this.refresh(); + }, + remove: function() { + this._clusterState.removeObserver( "data", this.draw_handler ); + }, + refresh: function() { + this._refreshButton.disable(); + this._clusterState.refresh(); + }, + draw_handler: function() { + var data = this._clusterState; + var indexFilter; + try { + var indexFilterRe = new RegExp( this._indexFilter.val() ); + indexFilter = function(s) { return indexFilterRe.test(s); }; + } catch(e) { + indexFilter = function() { return true; }; + } + var clusterState = data.clusterState; + var status = data.status; + var nodeStats = data.nodeStats; + var clusterNodes = data.clusterNodes; + var nodes = []; + var indices = []; + var cluster = {}; + var nodeIndices = {}; + var indexIndices = {}, indexIndicesIndex = 0; + function newNode(n) { + return { + name: n, + routings: [], + master_node: clusterState.master_node === n + }; + } + function newIndex(i) { + return { + name: i, + replicas: [] + }; + } + function getIndexForNode(n) { + return nodeIndices[n] = (n in nodeIndices) ? nodeIndices[n] : nodes.push(newNode(n)) - 1; + } + function getIndexForIndex(routings, i) { + var index = indexIndices[i] = (i in indexIndices) ? + (routings[indexIndices[i]] = routings[indexIndices[i]] || newIndex(i)) && indexIndices[i] + : ( ( routings[indexIndicesIndex] = newIndex(i) ) && indexIndicesIndex++ ); + indices[index] = i; + return index; + } + $.each(clusterNodes.nodes, function(name, node) { + getIndexForNode(name); + }); + + var indexNames = []; + $.each(clusterState.routing_table.indices, function(name, index){ + indexNames.push(name); + }); + indexNames.sort(); + if (this._indicesSort === "desc") indexNames.reverse(); + indexNames.filter( indexFilter ).forEach(function(name) { + var indexObject = clusterState.routing_table.indices[name]; + $.each(indexObject.shards, function(name, shard) { + shard.forEach(function(replica){ + var node = replica.node; + if(node === null) { node = "Unassigned"; } + var index = replica.index; + var shard = replica.shard; + var routings = nodes[getIndexForNode(node)].routings; + var indexIndex = getIndexForIndex(routings, index); + var replicas = routings[indexIndex].replicas; + if(node === "Unassigned" || !indexObject.shards[shard]) { + replicas.push({ replica: replica }); + } else { + replicas[shard] = { + replica: replica, + status: indexObject.shards[shard].filter(function(replica) { + return replica.node === node; + })[0] + }; + } + }); + }); + }); + indices = indices.map(function(index){ + return { + name: index, + state: "open", + metadata: clusterState.metadata.indices[index], + status: status.indices[index] + }; + }, this); + $.each(clusterState.metadata.indices, function(name, index) { + if(index.state === "close" && indexFilter( name )) { + indices.push({ + name: name, + state: "close", + metadata: index, + status: null + }); + } + }); + nodes.forEach(function(node) { + node.stats = nodeStats.nodes[node.name]; + var cluster = clusterNodes.nodes[node.name]; + node.cluster = cluster || { name: "" }; + node.data_node = !( cluster && cluster.attributes && cluster.attributes.data === "false" ); + for(var i = 0; i < indices.length; i++) { + node.routings[i] = node.routings[i] || { name: indices[i].name, replicas: [] }; + node.routings[i].max_number_of_shards = indices[i].metadata.settings["index.number_of_shards"]; + node.routings[i].open = indices[i].state === "open"; + } + }); + var aliasesIndex = {}; + var aliases = []; + var indexClone = indices.map(function() { return false; }); + $.each(clusterState.metadata.indices, function(name, index) { + index.aliases.forEach(function(alias) { + var aliasIndex = aliasesIndex[alias] = (alias in aliasesIndex) ? aliasesIndex[alias] : aliases.push( { name: alias, max: -1, min: 999, indices: [].concat(indexClone) }) - 1; + var indexIndex = indexIndices[name]; + var aliasRow = aliases[aliasIndex]; + aliasRow.min = Math.min(aliasRow.min, indexIndex); + aliasRow.max = Math.max(aliasRow.max, indexIndex); + aliasRow.indices[indexIndex] = indices[indexIndex]; + }); + }); + cluster.aliases = aliases; + cluster.nodes = nodes + .filter( nodeFilter_none ) + .sort( this._nodeSort ); + indices.unshift({ name: null }); + this._drawNodesView( cluster, indices ); + this._refreshButton.enable(); + }, + _drawNodesView: function( cluster, indices ) { + this._nodesView && this._nodesView.remove(); + this._nodesView = new ui.NodesView({ + onRedraw: function() { + this.refresh(); + }.bind(this), + interactive: ( this._refreshButton.value === -1 ), + aliasRenderer: this._aliasRenderer, + cluster: this.cluster, + data: { + cluster: cluster, + indices: indices + } + }); + this._nodesView.attach( this.tablEl ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiClusterOverview", children: [ + new ui.Toolbar({ + label: i18n.text("Overview.PageTitle"), + left: [ + this._nodeSortMenu, + this._indicesSortMenu, + this._aliasMenu, + this._indexFilter + ], + right: [ + this._refreshButton + ] + }), + { tag: "DIV", cls: "uiClusterOverview-table" } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( app, i18n, raphael ) { + + var ui = app.ns("ui"); + + ui.DateHistogram = ui.AbstractWidget.extend({ + defaults: { + printEl: null, // (optional) if supplied, clicking on elements in the histogram changes the query + cluster: null, // (required) + query: null, // (required) the current query + spec: null // (required) // date field spec + }, + init: function() { + this._super(); + this.el = $(this._main_template()); + this.query = this.config.query.clone(); + // check if the index/types have changed and rebuild the histogram + this.config.query.on("results", function(query) { + if(this.queryChanged) { + this.buildHistogram(query); + this.queryChanged = false; + } + }.bind(this)); + this.config.query.on("setIndex", function(query, params) { + this.query.setIndex(params.index, params.add); + this.queryChanged = true; + }.bind(this)); + this.config.query.on("setType", function(query, params) { + this.query.setType(params.type, params.add); + this.queryChanged = true; + }.bind(this)); + this.query.search.size = 0; + this.query.on("results", this._stat_handler); + this.query.on("results", this._aggs_handler); + this.buildHistogram(); + }, + buildHistogram: function(query) { + this.statAggs = this.query.addAggs({ + stats: { field: this.config.spec.field_name } + }); + this.query.query(); + this.query.removeAggs(this.statAggs); + }, + _stat_handler: function(query, results) { + if(! results.aggregations[this.statAggs]) { return; } + this.stats = results.aggregations[this.statAggs]; + // here we are calculating the approximate range that will give us less than 121 columns + var rangeNames = [ "year", "year", "month", "day", "hour", "minute" ]; + var rangeFactors = [100000, 12, 30, 24, 60, 60000 ]; + this.intervalRange = 1; + var range = this.stats.max - this.stats.min; + do { + this.intervalName = rangeNames.pop(); + var factor = rangeFactors.pop(); + this.intervalRange *= factor; + range = range / factor; + } while(range > 70); + this.dateAggs = this.query.addAggs({ + date_histogram : { + field: this.config.spec.field_name, + interval: this.intervalName + } + }); + this.query.query(); + this.query.removeAggs(this.dateAggs); + }, + _aggs_handler: function(query, results) { + if(! results.aggregations[this.dateAggs]) { return; } + var buckets = [], range = this.intervalRange; + var min = Math.floor(this.stats.min / range) * range; + var prec = [ "year", "month", "day", "hour", "minute", "second" ].indexOf(this.intervalName); + results.aggregations[this.dateAggs].buckets.forEach(function(entry) { + buckets[parseInt((entry.key - min) / range , 10)] = entry.doc_count; + }, this); + for(var i = 0; i < buckets.length; i++) { + buckets[i] = buckets[i] || 0; + } + this.el.removeClass("loading"); + var el = this.el.empty(); + var w = el.width(), h = el.height(); + var r = raphael(el[0], w, h ); + var printEl = this.config.printEl; + query = this.config.query; + r.g.barchart(0, 0, w, h, [buckets], { gutter: "0", vgutter: 0 }).hover( + function() { + this.flag = r.g.popup(this.bar.x, h - 5, this.value || "0").insertBefore(this); + }, function() { + this.flag.animate({opacity: 0}, 200, ">", function () {this.remove();}); + } + ).click(function() { + if(printEl) { + printEl.val(window.dateRangeParser.print(min + this.bar.index * range, prec)); + printEl.trigger("keyup"); + query.query(); + } + }); + }, + _main_template: function() { return ( + { tag: "DIV", cls: "uiDateHistogram loading", css: { height: "50px" }, children: [ + i18n.text("General.LoadingAggs") + ] } + ); } + }); + +})( this.app, this.i18n, this.Raphael ); +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + ui.ClusterConnect = ui.AbstractWidget.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.prefs = services.Preferences.instance(); + this.cluster = this.config.cluster; + this.el = $.joey(this._main_template()); + this.cluster.get( "", this._node_handler ); + }, + + _node_handler: function(data) { + if(data) { + this.prefs.set("app-base_uri", this.cluster.base_uri); + } + }, + + _reconnect_handler: function() { + var base_uri = this.el.find(".uiClusterConnect-uri").val(); + $("body").empty().append(new app.App("body", { id: "es", base_uri: base_uri })); + }, + + _main_template: function() { + return { tag: "SPAN", cls: "uiClusterConnect", children: [ + { tag: "INPUT", type: "text", cls: "uiClusterConnect-uri", onkeyup: function( ev ) { + if(ev.which === 13) { + ev.preventDefault(); + this._reconnect_handler(); + } + }.bind(this), id: this.id("baseUri"), value: this.cluster.base_uri }, + { tag: "BUTTON", type: "button", text: i18n.text("Header.Connect"), onclick: this._reconnect_handler } + ]}; + } + }); + +})( this.jQuery, this.app, this.i18n ); + + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var data = app.ns("data"); + + var StructuredQuery = ui.AbstractWidget.extend({ + defaults: { + cluster: null // (required) instanceof app.services.Cluster + }, + _baseCls: "uiStructuredQuery", + init: function(parent) { + this._super(); + this.selector = new ui.IndexSelector({ + onIndexChanged: this._indexChanged_handler, + cluster: this.config.cluster + }); + this.el = $(this._main_template()); + this.out = this.el.find("DIV.uiStructuredQuery-out"); + this.attach( parent ); + }, + + _indexChanged_handler: function( index ) { + this.filter && this.filter.remove(); + this.filter = new ui.FilterBrowser({ + cluster: this.config.cluster, + index: index, + onStartingSearch: function() { this.el.find("DIV.uiStructuredQuery-out").text( i18n.text("General.Searching") ); this.el.find("DIV.uiStructuredQuery-src").hide(); }.bind(this), + onSearchSource: this._searchSource_handler, + onResults: this._results_handler + }); + this.el.find(".uiStructuredQuery-body").append(this.filter); + }, + + _results_handler: function( filter, event ) { + var typeMap = { + "json": this._jsonResults_handler, + "table": this._tableResults_handler, + "csv": this._csvResults_handler + }; + typeMap[ event.type ].call( this, event.data, event.metadata ); + }, + _jsonResults_handler: function( results ) { + this.el.find("DIV.uiStructuredQuery-out").empty().append( new ui.JsonPretty({ obj: results })); + }, + _csvResults_handler: function( results ) { + this.el.find("DIV.uiStructuredQuery-out").empty().append( new ui.CSVTable({ results: results })); + }, + _tableResults_handler: function( results, metadata ) { + // hack up a QueryDataSourceInterface so that StructuredQuery keeps working without using a Query object + var qdi = new data.QueryDataSourceInterface({ metadata: metadata, query: new data.Query() }); + var tab = new ui.Table( { + store: qdi, + height: 400, + width: this.out.innerWidth() + } ).attach(this.out.empty()); + qdi._results_handler(qdi.config.query, results); + }, + + _showRawJSON : function() { + if($("#rawJsonText").length === 0) { + var hiddenButton = $("#showRawJSON"); + var jsonText = $({tag: "P", type: "p", id: "rawJsonText"}); + jsonText.text(hiddenButton[0].value); + hiddenButton.parent().append(jsonText); + } + }, + + _searchSource_handler: function(src) { + var searchSourceDiv = this.el.find("DIV.uiStructuredQuery-src"); + searchSourceDiv.empty().append(new app.ui.JsonPretty({ obj: src })); + if(typeof JSON !== "undefined") { + var showRawJSON = $({ tag: "BUTTON", type: "button", text: i18n.text("StructuredQuery.ShowRawJson"), id: "showRawJSON", value: JSON.stringify(src), onclick: this._showRawJSON }); + searchSourceDiv.append(showRawJSON); + } + searchSourceDiv.show(); + }, + + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: [ + this.selector, + { tag: "DIV", cls: "uiStructuredQuery-body" }, + { tag: "DIV", cls: "uiStructuredQuery-src", css: { display: "none" } }, + { tag: "DIV", cls: "uiStructuredQuery-out" } + ]}; + } + }); + + ui.StructuredQuery = ui.Page.extend({ + init: function() { + this.q = new StructuredQuery( this.config ); + this.el = this.q.el; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var data = app.ns("data"); + var ut = app.ns("ut"); + + ui.FilterBrowser = ui.AbstractWidget.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + index: "" // (required) name of the index to query + }, + + init: function(parent) { + this._super(); + this._cluster = this.config.cluster; + this.el = $(this._main_template()); + this.filtersEl = this.el.find(".uiFilterBrowser-filters"); + this.attach( parent ); + new data.MetaDataFactory({ cluster: this._cluster, onReady: function(metadata, eventData) { + this.metadata = metadata; + this._createFilters_handler(eventData.originalData.metadata.indices); + }.bind(this) }); + }, + + _createFilters_handler: function(data) { + var filters = []; + function scan_properties(path, obj) { + if (obj.properties) { + for (var prop in obj.properties) { + scan_properties(path.concat(prop), obj.properties[prop]); + } + } else { + // handle multi_field + if (obj.fields) { + for (var subField in obj.fields) { + filters.push({ path: (path[path.length - 1] !== subField) ? path.concat(subField) : path, type: obj.fields[subField].type, meta: obj.fields[subField] }); + } + } else { + filters.push({ path: path, type: obj.type, meta: obj }); + } + } + } + for(var type in data[this.config.index].mappings) { + scan_properties([type], data[this.config.index].mappings[type]); + } + + filters.sort( function(a, b) { + var x = a.path.join("."); + var y = b.path.join("."); + return (x < y) ? -1 : (x > y) ? 1 : 0; + }); + + this.filters = [ + { path: ["match_all"], type: "match_all", meta: {} }, + { path: ["_all"], type: "_all", meta: {}} + ].concat(filters); + + this._addFilterRow_handler(); + }, + + _addFilterRow_handler: function() { + this.filtersEl.append(this._filter_template()); + }, + + _removeFilterRow_handler: function(jEv) { + $(jEv.target).closest("DIV.uiFilterBrowser-row").remove(); + if(this.filtersEl.children().length === 0) { + this._addFilterRow_handler(); + } + }, + + _search_handler: function() { + var search = new data.BoolQuery(); + search.setSize( this.el.find(".uiFilterBrowser-outputSize").val() ) + this.fire("startingSearch"); + this.filtersEl.find(".uiFilterBrowser-row").each(function(i, row) { + row = $(row); + var bool = row.find(".bool").val(); + var field = row.find(".field").val(); + var op = row.find(".op").val(); + var value = {}; + if(field === "match_all") { + op = "match_all"; + } else if(op === "range") { + var lowqual = row.find(".lowqual").val(), + highqual = row.find(".highqual").val(); + if(lowqual.length) { + value[row.find(".lowop").val()] = lowqual; + } + if(highqual.length) { + value[row.find(".highop").val()] = highqual; + } + } else if(op === "fuzzy") { + var qual = row.find(".qual").val(), + fuzzyqual = row.find(".fuzzyqual").val(); + if(qual.length) { + value["value"] = qual; + } + if(fuzzyqual.length) { + value[row.find(".fuzzyop").val()] = fuzzyqual; + } + } else { + value = row.find(".qual").val(); + } + search.addClause(value, field, op, bool); + }); + if(this.el.find(".uiFilterBrowser-showSrc").attr("checked")) { + this.fire("searchSource", search.search); + } + this._cluster.post( this.config.index + "/_search", search.getData(), this._results_handler ); + }, + + _results_handler: function( data ) { + var type = this.el.find(".uiFilterBrowser-outputFormat").val(); + this.fire("results", this, { type: type, data: data, metadata: this.metadata }); + }, + + _changeQueryField_handler: function(jEv) { + var select = $(jEv.target); + var spec = select.children(":selected").data("spec"); + select.siblings().remove(".op,.qual,.range,.fuzzy"); + var ops = []; + if(spec.type === 'match_all') { + } else if(spec.type === '_all') { + ops = ["query_string"]; + } else if(spec.type === 'string') { + ops = ["term", "wildcard", "prefix", "fuzzy", "range", "query_string", "text", "missing"]; + } else if(spec.type === 'long' || spec.type === 'integer' || spec.type === 'float' || + spec.type === 'byte' || spec.type === 'short' || spec.type === 'double') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'date') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'geo_point') { + ops = ["missing"]; + } else if(spec.type === 'ip') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'boolean') { + ops = ["term"] + } + select.after({ tag: "SELECT", cls: "op", onchange: this._changeQueryOp_handler, children: ops.map(ut.option_template) }); + select.next().change(); + }, + + _changeQueryOp_handler: function(jEv) { + var op = $(jEv.target), opv = op.val(); + op.siblings().remove(".qual,.range,.fuzzy"); + if(opv === 'term' || opv === 'wildcard' || opv === 'prefix' || opv === "query_string" || opv === 'text') { + op.after({ tag: "INPUT", cls: "qual", type: "text" }); + } else if(opv === 'range') { + op.after(this._range_template()); + } else if(opv === 'fuzzy') { + op.after(this._fuzzy_template()); + } + }, + + _main_template: function() { + return { tag: "DIV", children: [ + { tag: "DIV", cls: "uiFilterBrowser-filters" }, + { tag: "BUTTON", type: "button", text: i18n.text("General.Search"), onclick: this._search_handler }, + { tag: "LABEL", children: + i18n.complex("FilterBrowser.OutputType", { tag: "SELECT", cls: "uiFilterBrowser-outputFormat", children: [ + { text: i18n.text("Output.Table"), value: "table" }, + { text: i18n.text("Output.JSON"), value: "json" }, + { text: i18n.text("Output.CSV"), value: "csv" } + ].map(function( o ) { return $.extend({ tag: "OPTION" }, o ); } ) } ) + }, + { tag: "LABEL", children: + i18n.complex("FilterBrowser.OutputSize", { tag: "SELECT", cls: "uiFilterBrowser-outputSize", + children: [ "10", "50", "250", "1000", "5000", "25000" ].map( ut.option_template ) + } ) + }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", cls: "uiFilterBrowser-showSrc" }, i18n.text("Output.ShowSource") ] } + ]}; + }, + + _filter_template: function() { + return { tag: "DIV", cls: "uiFilterBrowser-row", children: [ + { tag: "SELECT", cls: "bool", children: ["must", "must_not", "should"].map(ut.option_template) }, + { tag: "SELECT", cls: "field", onchange: this._changeQueryField_handler, children: this.filters.map(function(f) { + return { tag: "OPTION", data: { spec: f }, value: f.path.join("."), text: f.path.join(".") }; + })}, + { tag: "BUTTON", type: "button", text: "+", onclick: this._addFilterRow_handler }, + { tag: "BUTTON", type: "button", text: "-", onclick: this._removeFilterRow_handler } + ]}; + }, + + _range_template: function() { + return { tag: "SPAN", cls: "range", children: [ + { tag: "SELECT", cls: "lowop", children: ["gt", "gte"].map(ut.option_template) }, + { tag: "INPUT", type: "text", cls: "lowqual" }, + { tag: "SELECT", cls: "highop", children: ["lt", "lte"].map(ut.option_template) }, + { tag: "INPUT", type: "text", cls: "highqual" } + ]}; + }, + + _fuzzy_template: function() { + return { tag: "SPAN", cls: "fuzzy", children: [ + { tag: "INPUT", cls: "qual", type: "text" }, + { tag: "SELECT", cls: "fuzzyop", children: ["max_expansions", "min_similarity"].map(ut.option_template) }, + { tag: "INPUT", cls: "fuzzyqual", type: "text" } + ]}; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.IndexSelector = ui.AbstractWidget.extend({ + init: function(parent) { + this._super(); + this.el = $(this._main_template()); + this.attach( parent ); + this.cluster = this.config.cluster; + this.update(); + }, + update: function() { + this.cluster.get( "_stats", this._update_handler ); + }, + + _update_handler: function(data) { + var options = []; + var index_names = Object.keys(data.indices).sort(); + for(var i=0; i < index_names.length; i++) { + name = index_names[i]; + options.push(this._option_template(name, data.indices[name])); + } + this.el.find(".uiIndexSelector-select").empty().append(this._select_template(options)); + this._indexChanged_handler(); + }, + + _main_template: function() { + return { tag: "DIV", cls: "uiIndexSelector", children: i18n.complex( "IndexSelector.SearchIndexForDocs", { tag: "SPAN", cls: "uiIndexSelector-select" } ) }; + }, + + _indexChanged_handler: function() { + this.fire("indexChanged", this.el.find("SELECT").val()); + }, + + _select_template: function(options) { + return { tag: "SELECT", children: options, onChange: this._indexChanged_handler }; + }, + + _option_template: function(name, index) { + return { tag: "OPTION", value: name, text: i18n.text("IndexSelector.NameWithDocs", name, index.primaries.docs.count ) }; + } + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.Header = ui.AbstractWidget.extend({ + defaults: { + cluster: null, + clusterState: null + }, + _baseCls: "uiHeader", + init: function() { + this._clusterConnect = new ui.ClusterConnect({ + cluster: this.config.cluster + }); + var quicks = [ + { text: i18n.text("Nav.Info"), path: "" }, + { text: i18n.text("Nav.Status"), path: "_stats" }, + { text: i18n.text("Nav.NodeStats"), path: "_nodes/stats" }, + { text: i18n.text("Nav.ClusterNodes"), path: "_nodes" }, + { text: i18n.text("Nav.Plugins"), path: "_nodes/plugins" }, + { text: i18n.text("Nav.ClusterState"), path: "_cluster/state" }, + { text: i18n.text("Nav.ClusterHealth"), path: "_cluster/health" }, + { text: i18n.text("Nav.Templates"), path: "_template" } + ]; + var cluster = this.config.cluster; + var quickPanels = {}; + var menuItems = quicks.map( function( item ) { + return { text: item.text, onclick: function() { + cluster.get( item.path, function( data ) { + quickPanels[ item.path ] && quickPanels[ item.path ].el && quickPanels[ item.path ].remove(); + quickPanels[ item.path ] = new ui.JsonPanel({ + title: item.text, + json: data + }); + } ); + } }; + }, this ); + this._quickMenu = new ui.MenuButton({ + label: i18n.text("NodeInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: menuItems + }) + }); + this.el = $.joey( this._main_template() ); + this.nameEl = this.el.find(".uiHeader-name"); + this.statEl = this.el.find(".uiHeader-status"); + this._clusterState = this.config.clusterState; + this._clusterState.on("data", function( state ) { + var shards = state.status._shards; + var colour = state.clusterHealth.status; + var name = state.clusterState.cluster_name; + this.nameEl.text( name ); + this.statEl + .text( i18n.text("Header.ClusterHealth", colour, shards.successful, shards.total ) ) + .css( "background", colour ); + }.bind(this)); + this.statEl.text( i18n.text("Header.ClusterNotConnected") ).css("background", "grey"); + this._clusterState.refresh(); + }, + _main_template: function() { return ( + { tag: "DIV", cls: this._baseCls, children: [ + this._clusterConnect, + { tag: "SPAN", cls: "uiHeader-name" }, + { tag: "SPAN", cls: "uiHeader-status" }, + { tag: "H1", text: i18n.text("General.Elasticsearch") }, + { tag: "SPAN", cls: "pull-right", children: [ + this._quickMenu + ] } + ] } + ); } + } ); + +})( this.jQuery, this.app, this.i18n ); + +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.IndexOverview = ui.Page.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this._clusterState = this.config.clusterState; + this._clusterState.on("data", this._refresh_handler ); + this.el = $(this._main_template()); + this._refresh_handler(); + }, + remove: function() { + this._clusterState.removeObserver( "data", this._refresh_handler ); + }, + _refresh_handler: function() { + var state = this._clusterState; + var view = { + indices: acx.eachMap( state.status.indices, function( name, index ) { + return { + name: name, + state: index + }; + }).sort( function( a, b ) { + return a.name < b.name ? -1 : 1; + }) + }; + this._indexViewEl && this._indexViewEl.remove(); + this._indexViewEl = $( this._indexTable_template( view ) ); + this.el.find(".uiIndexOverview-table").append( this._indexViewEl ); + }, + _newIndex_handler: function() { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("ClusterOverView.IndexName"), name: "_name", require: true }), + new ui.TextField({ + label: i18n.text("ClusterOverview.NumShards"), + name: "number_of_shards", + value: "5", + require: function( val ) { return parseInt( val, 10 ) >= 1; } + }), + new ui.TextField({ + label: i18n.text("ClusterOverview.NumReplicas"), + name: "number_of_replicas", + value: "1", + require: function( val ) { return parseInt( val, 10 ) >= 0; } + }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("ClusterOverview.NewIndex"), + body: new ui.PanelForm({ fields: fields }), + onCommit: function(panel, args) { + if(fields.validate()) { + var data = fields.getData(); + var name = data["_name"]; + delete data["_name"]; + this.config.cluster.put( name, JSON.stringify({ settings: { index: data } }), function(d) { + dialog.close(); + alert(JSON.stringify(d)); + this._clusterState.refresh(); + }.bind(this) ); + } + }.bind(this) + }).open(); + }, + _indexTable_template: function( view ) { return ( + { tag: "TABLE", cls: "table", children: [ + { tag: "THEAD", children: [ + { tag: "TR", children: [ + { tag: "TH" }, + { tag: "TH", children: [ + { tag: "H3", text: "Size" } + ] }, + { tag: "TH", children: [ + { tag: "H3", text: "Docs" } + ] } + ] } + ] }, + { tag: "TBODY", cls: "striped", children: view.indices.map( this._index_template, this ) } + ] } + ); }, + + _index_template: function( index ) { return ( + { tag: "TR", children: [ + { tag: "TD", children: [ + { tag: "H3", text: index.name } + ] }, + { tag: "TD", text: ut.byteSize_template( index.state.primaries.store.size_in_bytes ) + "/" + ut.byteSize_template( index.state.total.store.size_in_bytes ) }, + { tag: "TD", text: ut.count_template( index.state.primaries.docs.count ) } + ] } + ); }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiIndexOverview", children: [ + new ui.Toolbar({ + label: i18n.text("IndexOverview.PageTitle"), + left: [ + new ui.Button({ + label: i18n.text("ClusterOverview.NewIndex"), + onclick: this._newIndex_handler + }), + ] + }), + { tag: "DIV", cls: "uiIndexOverview-table", children: this._indexViewEl } + ] }; + } + + }); + +})( this.jQuery, this.app, this.i18n ); + +(function( app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + app.App = ui.AbstractWidget.extend({ + defaults: { + base_uri: null + }, + init: function(parent) { + this._super(); + this.prefs = services.Preferences.instance(); + this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://localhost:9200"; + if( this.base_uri.charAt( this.base_uri.length - 1 ) !== "/" ) { + // XHR request fails if the URL is not ending with a "/" + this.base_uri += "/"; + } + if( this.config.auth_user ) { + var credentials = window.btoa( this.config.auth_user + ":" + this.config.auth_password ); + $.ajaxSetup({ + headers: { + "Authorization": "Basic " + credentials + } + }); + } + this.cluster = new services.Cluster({ base_uri: this.base_uri }); + this._clusterState = new services.ClusterState({ + cluster: this.cluster + }); + + this._header = new ui.Header({ cluster: this.cluster, clusterState: this._clusterState }); + this.$body = $.joey( this._body_template() ); + this.el = $.joey(this._main_template()); + this.attach( parent ); + this.instances = {}; + this.el.find(".uiApp-headerMenuItem:first").click(); + if( this.config.dashboard ) { + if( this.config.dashboard === "cluster" ) { + var page = this.instances["ClusterOverview"]; + page._refreshButton.set( 5000 ); + } + } + }, + + navigateTo: function( type, config, ev ) { + if( ev.target.classList.contains( "uiApp-headerNewMenuItem" ) ) { + this.showNew( type, config, ev ); + } else { + var ref = type + "0"; + if(! this.instances[ ref ]) { + this.createPage( type, 0, config ); + } + this.show( ref, ev ); + } + }, + + createPage: function( type, id, config ) { + var page = this.instances[ type + id ] = new ui[ type ]( config ); + this.$body.append( page ); + return page; + }, + + show: function( ref, ev ) { + $( ev.target ).closest("DIV.uiApp-headerMenuItem").addClass("active").siblings().removeClass("active"); + for(var p in this.instances) { + this.instances[p][ p === ref ? "show" : "hide" ](); + } + }, + + showNew: function( type, config, jEv ) { + var ref, page, $tab, + type_index = 0; + + while ( ! page ) { + ref = type + ( ++type_index ); + if (! ( ref in this.instances ) ) { + page = this.createPage( type, type_index, config ); + } + } + + // Add the tab and its click handlers + $tab = $.joey({ + tag: "DIV", + cls: "uiApp-headerMenuItem pull-left", + text: i18n.text("Nav." + type ) + " " + type_index, + onclick: function( ev ) { this.show( ref, ev ); }.bind(this), + children: [ + { tag: "A", text: " [-]", onclick: function (ev) { + $tab.remove(); + page.remove(); + delete this.instances[ ref ]; + }.bind(this) } + ] + }); + + $('.uiApp-headerMenu').append( $tab ); + $tab.trigger("click"); + }, + + _openAnyRequest_handler: function(ev) { this.navigateTo("AnyRequest", { cluster: this.cluster }, ev ); }, + _openStructuredQuery_handler: function(ev) { this.navigateTo("StructuredQuery", { cluster: this.cluster }, ev ); }, + _openBrowser_handler: function(ev) { this.navigateTo("Browser", { cluster: this.cluster }, ev ); }, + _openClusterOverview_handler: function(ev) { this.navigateTo("ClusterOverview", { cluster: this.cluster, clusterState: this._clusterState }, ev ); }, + _openIndexOverview_handler: function(ev) { this.navigateTo("IndexOverview", { cluster: this.cluster, clusterState: this._clusterState }, ev ); }, + + _body_template: function() { return ( + { tag: "DIV", id: this.id("body"), cls: "uiApp-body" } + ); }, + + _main_template: function() { + return { tag: "DIV", cls: "uiApp", children: [ + { tag: "DIV", id: this.id("header"), cls: "uiApp-header", children: [ + this._header, + { tag: "DIV", cls: "uiApp-headerMenu", children: [ + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Overview"), onclick: this._openClusterOverview_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Indices"), onclick: this._openIndexOverview_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Browser"), onclick: this._openBrowser_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.StructuredQuery"), onclick: this._openStructuredQuery_handler, children: [ + { tag: "A", cls: "uiApp-headerNewMenuItem ", text: ' [+]' } + ] }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.AnyRequest"), onclick: this._openAnyRequest_handler, children: [ + { tag: "A", cls: "uiApp-headerNewMenuItem ", text: ' [+]' } + ] }, + ]} + ]}, + this.$body + ]}; + } + + }); + +})( this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/_site/base/favicon.png b/elasticsearch/plugins/head/_site/base/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..f433ec6d0b71ed31200590a2e8eff1a56a844e4c GIT binary patch literal 952 zcmV;p14sOcP){g2SBohT)!X&Ufy)L@5P(*V)fU|9dAuQZXa; zDFK+KgxE2|uF1!SUlS>Y77*s8lYEbuKx1`;e6C$XNEw`uKsD3+aw-qc4+q{LgzotNd?Y z4l@Q{8I--GiFEgu&*LcP5GNHx2W2E)?J!506Qk!JbZ2{r)t&&m-EJH^deRyjd^T{K zwPRB9UIb9>0b)9eB8K&pnJdiVbbXoj+_s%eDKHGe7*$RniadhN$1(@D{m?U;f!mJ& zibT-Rjj&!PNvCQ+tj?8dn{m^M$70ZCF-W3~eh6!oT7e5GP`j)l@Rkw~2_Xas!O712 zu;T~fYN>kTk%zVAKFf%!MBJ2?VTkp6ORIIrK#Dj1mMQsX)(dYf=;`3*U!P z#p=PH6Ke@GUA%R7E%*i5cs-N2@M=N zeE8|1(Wx%hBA}`E3ZWq&|HJnpS$lGGYUs^hlm5tT zZ6yW4wguE^5%o4h5Cr4sM;6umgx@~X+t`w>6)Y_-&irxnW+T&;P1m;lJe4f2CHA(krBr9<3UYqg8{sONIF5BhAH{N|vwaXTGNY}vm04$Z+uhD+hql_ZVaxL3qWQxNh z6Q?Kq5hAg-cLc+LHqo&CxtieW?;o*0(*;>upUE&;vXa*6u@%9SvHzE5G}>)@>^o)6 zjy*LpTdl37ZntgcI>)Z+-AL>sz^8({G@&~}NCa<|$xxIZ`^6ZVX(q3k2F7!ueRr-0 zCPe42*e0>pGu;9BB`16ClGt>A`@;dtD7W+%`8JRHd|@2_qq}BTdHTtl^wq?(B1VGm z{&+)@Lb?~@0HtWvBm*H$vA|s!hmt&m-3i)L0WbNOoPIHtrtPueEPF#12WMa3muVv3 zH^H9i?zyE|L4UB6tLa;X{W{Jw)uk0ckt?Mg0{rS3CrlCa6eaxdo>oMhym!%g zj=cVcb>5;pY9Kv#Po0o1ZqxlJ8A6Bdb%r2 zZ3e?BL)cvEmP;fF5a4Z15a5=Ro+as=nmU-yIE&85qu$9lJ#X~5^|WkOy7p?-uyQmK8_MOl z=$OloUAZU{OLd7?99RnM3Rq`~&q;j%Gk@+c212%JzgJ#G5V)&f>Eh@wFHUe z+v^VS3A)~~9{~hgZ!b#-OZr2Iv;_u90Xg~9e+_f$z?eKO>*i6fi_2Cj^Vi&VHq>&Z z9dGy^y%Qm0y#J2p&H;!wPn4=;(Ce(jlvIDG>-M=ml5F_CNxd?XAd1#h@gSeD5<-)tE{#-i! z*Dbc1SYGzAv-TFrzUc?K`Y!ur!)$;nZrwJHgQb*rE@jqFcf>0MH~;jFQ^HXEastmr zuoga_-H&hypcOC-DkPprpoqjHT7H%-6|GZ+w=5^48d(^7Wvyu>@66%1?tI#-dG*py z0&v8aRj#b_jGS4Mo+|6T15-trzPYG&{k@p;$!^<(UMo7ZYT%+aS}_0k=~>AT=)3B1 z=A?)rUd)H_tOFx3p^$Qx70FxqXb(yX2IHtkj?(6;X7z8^X<4Jm8&W^IG>0A< z)1Rbm$J76Q=+@+lXN|5M)|C6ZMq}u}n0W`{-tlfrv>(?@UAY|*_XaxB%3 z2>*fuDI5a*9+QQ0u7(*rHa-(!Ej$Q>g((IG4`NtHl>>!jL9M=-^{T?R)UQ=q$29oI zC`~p>znKe>M&(=4xdTRq?95nPP)Brk9yDLj3`_euzFs5QNOib+F8nh}R5Qhtv}xgc zgVuQspXogM{isCqU=xNyUOL4ZiwZVsXsiQnOx!Aj6+D#sV-*-Yww7&pK|ygLO?v1f HS{}au)wm43 literal 0 HcmV?d00001 diff --git a/elasticsearch/plugins/head/_site/base/reset.css b/elasticsearch/plugins/head/_site/base/reset.css new file mode 100644 index 00000000..29a5f1b6 --- /dev/null +++ b/elasticsearch/plugins/head/_site/base/reset.css @@ -0,0 +1,44 @@ +BODY { + font-family: Verdana, sans-serif; + font-size: 73%; + padding: 0; + margin: 0; +} + +INPUT, SELECT, TEXTAREA { + border: 1px solid #cecece; + padding: 1px 3px; + background: white; +} + +SELECT { + padding: 0; +} + +.saf SELECT { + margin-top: 0; + margin-bottom: 0; +} + +TEXTAREA, CODE { + font-family: monospace; + font-size: 13px; +} + +BUTTON::-moz-focus-inner { + border: none; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +.loading { + background-image: url(loading.gif); + background-repeat: no-repeat; + text-indent: 20px; +} diff --git a/elasticsearch/plugins/head/_site/fonts/FontAwesome.otf b/elasticsearch/plugins/head/_site/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 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
  • 序号
    a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function click() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + div.detachEvent( "onclick", click ); + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains it's value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + // We use our own, invisible, body + body = document.createElement( "body" ); + bodyStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + // Set background to avoid IE crashes when removing (#9028) + background: "none" + }; + for ( i in bodyStyle ) { + body.style[ i ] = bodyStyle[ i ]; + } + body.appendChild( div ); + documentElement.insertBefore( body, documentElement.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
    "; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
    t
    "; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Remove the body element we added + body.innerHTML = ""; + documentElement.removeChild( body ); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery.data( elem, deferDataKey, undefined, true ); + if ( defer && + ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && + ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery.data( elem, queueDataKey, undefined, true ) && + !jQuery.data( elem, markDataKey, undefined, true ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.resolve(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + if ( count ) { + jQuery.data( elem, key, count, true ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + if ( elem ) { + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type, undefined, true ); + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + defer; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + rinvalidChar = /\:/, + formHook, boolHook; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.addClass( value.call(this, i, self.attr("class") || "") ); + }); + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspace ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", + setClass = elem.className; + + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.removeClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split( rspace ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + return (elem.value || "").replace(rreturn, ""); + } + + return undefined; + } + + var isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex" + }, + + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + name = notxml && jQuery.attrFix[ name ] || name; + + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) && + (typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase()) ) { + + hooks = boolHook; + + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + hooks = formHook; + } + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml ) { + return hooks.get( elem, name ); + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, name ) { + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabIndex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Try to normalize/fix the name + name = notxml && jQuery.propFix[ name ] || name; + + hooks = jQuery.propHooks[ name ]; + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return elem[ jQuery.propFix[ name ] || name ] ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = value; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// Use the value property for back compat +// Use the formHook for button elements in IE6/7 (#1954) +jQuery.attrHooks.value = { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return elem.value; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return value; + } + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); +}); + + + + +var hasOwn = Object.prototype.hasOwnProperty, + rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Event object or event type + var type = event.type || event, + namespaces = [], + exclusive; + + if ( type.indexOf("!") >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.exclusive = exclusive; + event.namespace = namespaces.join("."); + event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle a global trigger + if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + return; + } + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + var cur = elem, + // IE doesn't like method names with a colon (#3533, #8272) + ontype = type.indexOf(":") < 0 ? "on" + type : ""; + + // Fire event on the current element, then bubble up the DOM tree + do { + var handle = jQuery._data( cur, "handle" ); + + event.currentTarget = cur; + if ( handle ) { + handle.apply( cur, data ); + } + + // Trigger an inline bound script + if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { + event.result = false; + event.preventDefault(); + } + + // Bubble up to document, then to window + cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; + } while ( cur && !event.isPropagationStopped() ); + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + var old, + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction)() check here because IE6/7 fails that test. + // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. + try { + if ( ontype && elem[ type ] ) { + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + jQuery.event.triggered = type; + elem[ type ](); + } + } catch ( ieError ) {} + + if ( old ) { + elem[ ontype ] = old; + } + + jQuery.event.triggered = undefined; + } + } + + return event.result; + }, + + handle: function( event ) { + event = jQuery.event.fix( event || window.event ); + // Snapshot the handlers list since a called handler may add/remove events. + var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + run_all = !event.exclusive && !event.namespace, + args = Array.prototype.slice.call( arguments, 0 ); + + // Use the fix-ed Event rather than the (read-only) native event + args[0] = event; + event.currentTarget = this; + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Triggered event must 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event. + if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + // Check if mouse(over|out) are still within the same parent element + var parent = event.relatedTarget; + + // set the correct event type + event.type = event.data; + + // Firefox sometimes assigns relatedTarget a XUL element + // which we cannot access the parentNode property of + try { + + // Chrome does something similar, the parentNode property + // can be accessed but is null. + if ( parent && parent !== document && !parent.parentNode ) { + return; + } + + // Traverse up the tree + while ( parent && parent !== this ) { + parent = parent.parentNode; + } + + if ( parent !== this ) { + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } + + // assuming we've left the element since we most likely mousedover a xul element + } catch(e) { } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( !jQuery.nodeName( this, "form" ) ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( jQuery.nodeName( elem, "select" ) ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( arguments.length === 2 || data === false ) { + fn = data; + data = undefined; + } + + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( name === "die" && !types && + origSelector && origSelector.charAt(0) === "." ) { + + context.unbind( origSelector ); + + return this; + } + + if ( data === false || jQuery.isFunction( data ) ) { + fn = data || returnFalse; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( liveMap[ type ] ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + + // Make sure not to accidentally match a child element with the same selector + if ( related && jQuery.contains( elem, related ) ) { + related = elem; + } + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[ selector ]; + + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and + + + + + + + diff --git a/elasticsearch/plugins/head/package.json b/elasticsearch/plugins/head/package.json new file mode 100644 index 00000000..4aefb7a1 --- /dev/null +++ b/elasticsearch/plugins/head/package.json @@ -0,0 +1,31 @@ +{ + "name": "elasticsearch-head", + "version": "0.0.0", + "description": "Front end for an elasticsearch cluster", + "main": "_site/index.html", + "directories": { + "test": "test" + }, + "scripts": { + "test": "grunt jasmine" + }, + "repository": { + "type": "git", + "url": "https://github.com/mobz/elasticsearch-head.git" + }, + "author": "", + "license": "Apache2", + "gitHead": "0c2ac0b5723b493e4454baa7398f386ecb829412", + "readmeFilename": "README.textile", + "devDependencies": { + "grunt": "0.4.5", + "grunt-contrib-concat": "0.4.0", + "grunt-contrib-watch": "0.5.1", + "grunt-contrib-connect": "0.3.0", + "grunt-contrib-copy": "0.5.0", + "grunt-contrib-clean": "0.5.0", + "grunt-contrib-jasmine": "0.6.4", + "karma": "0.12.16", + "grunt-karma": "0.8.3" + } +} diff --git a/elasticsearch/plugins/head/plugin-descriptor.properties b/elasticsearch/plugins/head/plugin-descriptor.properties new file mode 100644 index 00000000..37a90649 --- /dev/null +++ b/elasticsearch/plugins/head/plugin-descriptor.properties @@ -0,0 +1,4 @@ +description=head - A web front end for an elastic search cluster +version=master +site=true +name=head diff --git a/elasticsearch/plugins/head/src/app/app.css b/elasticsearch/plugins/head/src/app/app.css new file mode 100644 index 00000000..08e2dcf9 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/app.css @@ -0,0 +1,39 @@ +.uiApp-header { + background: #eee; + position: fixed; + width: 100%; + z-index: 9; +} + +.uiApp-header H1 { + margin: -2px 0 -4px 0; + float: left; + padding-right: 25px; +} + +.uiApp-headerMenu { + border-bottom: 1px solid #bbb; + padding: 0px 3px; + height: 22px; +} + +.uiApp-headerMenu .active { + background: white; + border-bottom-color: white; +} + +.uiApp-headerMenuItem { + border: 1px solid #bbb; + padding: 4px 8px 1px ; + margin: 2px 1px 0; + height: 14px; + cursor: pointer; +} + +.uiApp-body { + padding: 51px 0px 0px 0px; +} + +.uiApp-headerNewMenuItem { + color: blue; +} diff --git a/elasticsearch/plugins/head/src/app/app.js b/elasticsearch/plugins/head/src/app/app.js new file mode 100644 index 00000000..036e97f5 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/app.js @@ -0,0 +1,132 @@ +(function( app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + app.App = ui.AbstractWidget.extend({ + defaults: { + base_uri: null + }, + init: function(parent) { + this._super(); + this.prefs = services.Preferences.instance(); + this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://localhost:9200"; + if( this.base_uri.charAt( this.base_uri.length - 1 ) !== "/" ) { + // XHR request fails if the URL is not ending with a "/" + this.base_uri += "/"; + } + if( this.config.auth_user ) { + var credentials = window.btoa( this.config.auth_user + ":" + this.config.auth_password ); + $.ajaxSetup({ + headers: { + "Authorization": "Basic " + credentials + } + }); + } + this.cluster = new services.Cluster({ base_uri: this.base_uri }); + this._clusterState = new services.ClusterState({ + cluster: this.cluster + }); + + this._header = new ui.Header({ cluster: this.cluster, clusterState: this._clusterState }); + this.$body = $.joey( this._body_template() ); + this.el = $.joey(this._main_template()); + this.attach( parent ); + this.instances = {}; + this.el.find(".uiApp-headerMenuItem:first").click(); + if( this.config.dashboard ) { + if( this.config.dashboard === "cluster" ) { + var page = this.instances["ClusterOverview"]; + page._refreshButton.set( 5000 ); + } + } + }, + + navigateTo: function( type, config, ev ) { + if( ev.target.classList.contains( "uiApp-headerNewMenuItem" ) ) { + this.showNew( type, config, ev ); + } else { + var ref = type + "0"; + if(! this.instances[ ref ]) { + this.createPage( type, 0, config ); + } + this.show( ref, ev ); + } + }, + + createPage: function( type, id, config ) { + var page = this.instances[ type + id ] = new ui[ type ]( config ); + this.$body.append( page ); + return page; + }, + + show: function( ref, ev ) { + $( ev.target ).closest("DIV.uiApp-headerMenuItem").addClass("active").siblings().removeClass("active"); + for(var p in this.instances) { + this.instances[p][ p === ref ? "show" : "hide" ](); + } + }, + + showNew: function( type, config, jEv ) { + var ref, page, $tab, + type_index = 0; + + while ( ! page ) { + ref = type + ( ++type_index ); + if (! ( ref in this.instances ) ) { + page = this.createPage( type, type_index, config ); + } + } + + // Add the tab and its click handlers + $tab = $.joey({ + tag: "DIV", + cls: "uiApp-headerMenuItem pull-left", + text: i18n.text("Nav." + type ) + " " + type_index, + onclick: function( ev ) { this.show( ref, ev ); }.bind(this), + children: [ + { tag: "A", text: " [-]", onclick: function (ev) { + $tab.remove(); + page.remove(); + delete this.instances[ ref ]; + }.bind(this) } + ] + }); + + $('.uiApp-headerMenu').append( $tab ); + $tab.trigger("click"); + }, + + _openAnyRequest_handler: function(ev) { this.navigateTo("AnyRequest", { cluster: this.cluster }, ev ); }, + _openStructuredQuery_handler: function(ev) { this.navigateTo("StructuredQuery", { cluster: this.cluster }, ev ); }, + _openBrowser_handler: function(ev) { this.navigateTo("Browser", { cluster: this.cluster }, ev ); }, + _openClusterOverview_handler: function(ev) { this.navigateTo("ClusterOverview", { cluster: this.cluster, clusterState: this._clusterState }, ev ); }, + _openIndexOverview_handler: function(ev) { this.navigateTo("IndexOverview", { cluster: this.cluster, clusterState: this._clusterState }, ev ); }, + + _body_template: function() { return ( + { tag: "DIV", id: this.id("body"), cls: "uiApp-body" } + ); }, + + _main_template: function() { + return { tag: "DIV", cls: "uiApp", children: [ + { tag: "DIV", id: this.id("header"), cls: "uiApp-header", children: [ + this._header, + { tag: "DIV", cls: "uiApp-headerMenu", children: [ + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Overview"), onclick: this._openClusterOverview_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Indices"), onclick: this._openIndexOverview_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.Browser"), onclick: this._openBrowser_handler }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.StructuredQuery"), onclick: this._openStructuredQuery_handler, children: [ + { tag: "A", cls: "uiApp-headerNewMenuItem ", text: ' [+]' } + ] }, + { tag: "DIV", cls: "uiApp-headerMenuItem pull-left", text: i18n.text("Nav.AnyRequest"), onclick: this._openAnyRequest_handler, children: [ + { tag: "A", cls: "uiApp-headerNewMenuItem ", text: ' [+]' } + ] }, + ]} + ]}, + this.$body + ]}; + } + + }); + +})( this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/base/boot.js b/elasticsearch/plugins/head/src/app/base/boot.js new file mode 100644 index 00000000..4dd0c0d0 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/base/boot.js @@ -0,0 +1,241 @@ +(function() { + + var window = this, + $ = jQuery; + + function ns( namespace ) { + return (namespace || "").split(".").reduce( function( space, name ) { + return space[ name ] || ( space[ name ] = { ns: ns } ); + }, this ); + } + + var app = ns("app"); + + var acx = ns("acx"); + + /** + * object iterator, returns an array with one element for each property of the object + * @function + */ + acx.eachMap = function(obj, fn, thisp) { + var ret = []; + for(var n in obj) { + ret.push(fn.call(thisp, n, obj[n], obj)); + } + return ret; + }; + + /** + * augments the first argument with the properties of the second and subsequent arguments + * like {@link $.extend} except that existing properties are not overwritten + */ + acx.augment = function() { + var args = Array.prototype.slice.call(arguments), + src = (args.length === 1) ? this : args.shift(), + augf = function(n, v) { + if(! (n in src)) { + src[n] = v; + } + }; + for(var i = 0; i < args.length; i++) { + $.each(args[i], augf); + } + return src; + }; + + /** + * tests whether the argument is an array + * @function + */ + acx.isArray = $.isArray; + + /** + * tests whether the argument is an object + * @function + */ + acx.isObject = function (value) { + return Object.prototype.toString.call(value) == "[object Object]"; + }; + + /** + * tests whether the argument is a function + * @function + */ + acx.isFunction = $.isFunction; + + /** + * tests whether the argument is a date + * @function + */ + acx.isDate = function (value) { + return Object.prototype.toString.call(value) == "[object Date]"; + }; + + /** + * tests whether the argument is a regexp + * @function + */ + acx.isRegExp = function (value) { + return Object.prototype.toString.call(value) == "[object RegExp]"; + }; + + /** + * tests whether the value is blank or empty + * @function + */ + acx.isEmpty = function (value, allowBlank) { + return value === null || value === undefined || ((acx.isArray(value) && !value.length)) || (!allowBlank ? value === '' : false); + }; + + /** + * data type for performing chainable geometry calculations
    + * can be initialised x,y | {x, y} | {left, top} + */ + acx.vector = function(x, y) { + return new acx.vector.prototype.Init(x, y); + }; + + acx.vector.prototype = { + Init : function(x, y) { + x = x || 0; + this.y = isFinite(x.y) ? x.y : (isFinite(x.top) ? x.top : (isFinite(y) ? y : 0)); + this.x = isFinite(x.x) ? x.x : (isFinite(x.left) ? x.left : (isFinite(x) ? x : 0)); + }, + + add : function(i, j) { + var d = acx.vector(i, j); + return new this.Init(this.x + d.x, this.y + d.y); + }, + + sub : function(i, j) { + var d = acx.vector(i, j); + return new this.Init(this.x - d.x, this.y - d.y); + }, + + addX : function(i) { + return new this.Init(this.x + i, this.y); + }, + + addY : function(j) { + return new this.Init(this.x, this.y + j); + }, + + mod : function(fn) { // runs a function against the x and y values + return new this.Init({x: fn.call(this, this.x, "x"), y: fn.call(this, this.y, "y")}); + }, + + /** returns true if this is within a rectangle formed by the points p and q */ + within : function(p, q) { + return ( this.x >= ((p.x < q.x) ? p.x : q.x) && this.x <= ((p.x > q.x) ? p.x : q.x) && + this.y >= ((p.y < q.y) ? p.y : q.y) && this.y <= ((p.y > q.y) ? p.y : q.y) ); + }, + + asOffset : function() { + return { top: this.y, left: this.x }; + }, + + asSize : function() { + return { height: this.y, width: this.x }; + } + }; + + acx.vector.prototype.Init.prototype = acx.vector.prototype; + + /** + * short cut functions for working with vectors and jquery. + * Each function returns the equivalent jquery value in a two dimentional vector + */ + $.fn.vSize = function() { return acx.vector(this.width(), this.height()); }; + $.fn.vOuterSize = function(margin) { return acx.vector(this.outerWidth(margin), this.outerHeight(margin)); }; + $.fn.vScroll = function() { return acx.vector(this.scrollLeft(), this.scrollTop()); }; + $.fn.vOffset = function() { return acx.vector(this.offset()); }; + $.fn.vPosition = function() { return acx.vector(this.position()); }; + $.Event.prototype.vMouse = function() { return acx.vector(this.pageX, this.pageY); }; + + /** + * object extensions (ecma5 compatible) + */ + acx.augment(Object, { + keys: function(obj) { + var ret = []; + for(var n in obj) if(Object.prototype.hasOwnProperty.call(obj, n)) ret.push(n); + return ret; + } + }); + + /** + * Array prototype extensions + */ + acx.augment(Array.prototype, { + 'contains' : function(needle) { + return this.indexOf(needle) !== -1; + }, + + // returns a new array consisting of all the members that are in both arrays + 'intersection' : function(b) { + var ret = []; + for(var i = 0; i < this.length; i++) { + if(b.contains(this[i])) { + ret.push(this[i]); + } + } + return ret; + }, + + 'remove' : function(value) { + var i = this.indexOf(value); + if(i !== -1) { + this.splice(i, 1); + } + } + }); + + /** + * String prototype extensions + */ + acx.augment(String.prototype, { + 'contains' : function(needle) { + return this.indexOf(needle) !== -1; + }, + + 'equalsIgnoreCase' : function(match) { + return this.toLowerCase() === match.toLowerCase(); + }, + + 'escapeHtml' : function() { + return this.replace(/&/g, '&').replace(//g, '>'); + }, + + 'escapeJS' : function() { + var meta = {'"':'\\"', '\\':'\\\\', '/':'\\/', '\b':'\\b', '\f':'\\f', '\n':'\\n', '\r':'\\r', '\t':'\\t'}, + xfrm = function(c) { return meta[c] || "\\u" + c.charCodeAt(0).toString(16).zeroPad(4); }; + return this.replace(new RegExp('(["\\\\\x00-\x1f\x7f-\uffff])', 'g'), xfrm); + }, + + 'escapeRegExp' : function() { + var ret = "", esc = "\\^$*+?.()=|{,}[]-"; + for ( var i = 0; i < this.length; i++) { + ret += (esc.contains(this.charAt(i)) ? "\\" : "") + this.charAt(i); + } + return ret; + }, + + 'zeroPad' : function(len) { + return ("0000000000" + this).substring(this.length - len + 10); + } + }); + + $.fn.forEach = Array.prototype.forEach; + + // joey / jquery integration + $.joey = function( obj ) { + return $( window.joey( obj ) ); + }; + + window.joey.plugins.push( function( obj ) { + if( obj instanceof jQuery ) { + return obj[0]; + } + }); + +})(); diff --git a/elasticsearch/plugins/head/src/app/base/favicon.png b/elasticsearch/plugins/head/src/app/base/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..f433ec6d0b71ed31200590a2e8eff1a56a844e4c GIT binary patch literal 952 zcmV;p14sOcP){g2SBohT)!X&Ufy)L@5P(*V)fU|9dAuQZXa; zDFK+KgxE2|uF1!SUlS>Y77*s8lYEbuKx1`;e6C$XNEw`uKsD3+aw-qc4+q{LgzotNd?Y z4l@Q{8I--GiFEgu&*LcP5GNHx2W2E)?J!506Qk!JbZ2{r)t&&m-EJH^deRyjd^T{K zwPRB9UIb9>0b)9eB8K&pnJdiVbbXoj+_s%eDKHGe7*$RniadhN$1(@D{m?U;f!mJ& zibT-Rjj&!PNvCQ+tj?8dn{m^M$70ZCF-W3~eh6!oT7e5GP`j)l@Rkw~2_Xas!O712 zu;T~fYN>kTk%zVAKFf%!MBJ2?VTkp6ORIIrK#Dj1mMQsX)(dYf=;`3*U!P z#p=PH6Ke@GUA%R7E%*i5cs-N2@M=N zeE8|1(Wx%hBA}`E3ZWq&|HJnpS$lGGYUs^hlm5tT zZ6yW4wguE^5%o4h5Cr4sM;6umgx@~X+t`w>6)Y_-&irxnW+T&;P1m;lJe4f2CHA(krBr9<3UYqg8{sONIF5BhAH{N|vwaXTGNY}vm04$Z+uhD+hql_ZVaxL3qWQxNh z6Q?Kq5hAg-cLc+LHqo&CxtieW?;o*0(*;>upUE&;vXa*6u@%9SvHzE5G}>)@>^o)6 zjy*LpTdl37ZntgcI>)Z+-AL>sz^8({G@&~}NCa<|$xxIZ`^6ZVX(q3k2F7!ueRr-0 zCPe42*e0>pGu;9BB`16ClGt>A`@;dtD7W+%`8JRHd|@2_qq}BTdHTtl^wq?(B1VGm z{&+)@Lb?~@0HtWvBm*H$vA|s!hmt&m-3i)L0WbNOoPIHtrtPueEPF#12WMa3muVv3 zH^H9i?zyE|L4UB6tLa;X{W{Jw)uk0ckt?Mg0{rS3CrlCa6eaxdo>oMhym!%g zj=cVcb>5;pY9Kv#Po0o1ZqxlJ8A6Bdb%r2 zZ3e?BL)cvEmP;fF5a4Z15a5=Ro+as=nmU-yIE&85qu$9lJ#X~5^|WkOy7p?-uyQmK8_MOl z=$OloUAZU{OLd7?99RnM3Rq`~&q;j%Gk@+c212%JzgJ#G5V)&f>Eh@wFHUe z+v^VS3A)~~9{~hgZ!b#-OZr2Iv;_u90Xg~9e+_f$z?eKO>*i6fi_2Cj^Vi&VHq>&Z z9dGy^y%Qm0y#J2p&H;!wPn4=;(Ce(jlvIDG>-M=ml5F_CNxd?XAd1#h@gSeD5<-)tE{#-i! z*Dbc1SYGzAv-TFrzUc?K`Y!ur!)$;nZrwJHgQb*rE@jqFcf>0MH~;jFQ^HXEastmr zuoga_-H&hypcOC-DkPprpoqjHT7H%-6|GZ+w=5^48d(^7Wvyu>@66%1?tI#-dG*py z0&v8aRj#b_jGS4Mo+|6T15-trzPYG&{k@p;$!^<(UMo7ZYT%+aS}_0k=~>AT=)3B1 z=A?)rUd)H_tOFx3p^$Qx70FxqXb(yX2IHtkj?(6;X7z8^X<4Jm8&W^IG>0A< z)1Rbm$J76Q=+@+lXN|5M)|C6ZMq}u}n0W`{-tlfrv>(?@UAY|*_XaxB%3 z2>*fuDI5a*9+QQ0u7(*rHa-(!Ej$Q>g((IG4`NtHl>>!jL9M=-^{T?R)UQ=q$29oI zC`~p>znKe>M&(=4xdTRq?95nPP)Brk9yDLj3`_euzFs5QNOib+F8nh}R5Qhtv}xgc zgVuQspXogM{isCqU=xNyUOL4ZiwZVsXsiQnOx!Aj6+D#sV-*-Yww7&pK|ygLO?v1f HS{}au)wm43 literal 0 HcmV?d00001 diff --git a/elasticsearch/plugins/head/src/app/base/reset.css b/elasticsearch/plugins/head/src/app/base/reset.css new file mode 100644 index 00000000..29a5f1b6 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/base/reset.css @@ -0,0 +1,44 @@ +BODY { + font-family: Verdana, sans-serif; + font-size: 73%; + padding: 0; + margin: 0; +} + +INPUT, SELECT, TEXTAREA { + border: 1px solid #cecece; + padding: 1px 3px; + background: white; +} + +SELECT { + padding: 0; +} + +.saf SELECT { + margin-top: 0; + margin-bottom: 0; +} + +TEXTAREA, CODE { + font-family: monospace; + font-size: 13px; +} + +BUTTON::-moz-focus-inner { + border: none; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +.loading { + background-image: url(loading.gif); + background-repeat: no-repeat; + text-indent: 20px; +} diff --git a/elasticsearch/plugins/head/src/app/data/boolQuery.js b/elasticsearch/plugins/head/src/app/data/boolQuery.js new file mode 100644 index 00000000..782d4907 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/boolQuery.js @@ -0,0 +1,75 @@ +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.BoolQuery = ux.Observable.extend({ + defaults: { + size: 50 // size of pages to return + }, + init: function() { + this._super(); + this.refuid = 0; + this.refmap = {}; + this.search = { + query: { bool: { must: [], must_not: [], should: [] } }, + from: 0, + size: this.config.size, + sort: [], + aggs: {} + }; + this.defaultClause = this.addClause(); + }, + setSize: function(size) { + this.search.size = parseInt( size, 10 ); + }, + setPage: function(page) { + this.search.from = this.config.size * (page - 1) + 1; + }, + addClause: function(value, field, op, bool) { + bool = bool || "should"; + op = op || "match_all"; + field = field || "_all"; + var clause = this._setClause(value, field, op, bool); + var uqid = "q-" + this.refuid++; + this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool }; + if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) { + this.removeClause(this.defaultClause); + } + this.fire("queryChanged", this, { uqid: uqid, search: this.search} ); + return uqid; // returns reference to inner query object to allow fast updating + }, + removeClause: function(uqid) { + var ref = this.refmap[uqid], + bool = this.search.query.bool[ref.bool]; + var clauseIdx = bool.indexOf(ref.clause); + // Check that this clause hasn't already been removed + if (clauseIdx >=0) { + bool.splice(clauseIdx, 1); + } + }, + _setClause: function(value, field, op, bool) { + var clause = {}, query = {}; + if(op === "match_all") { + } else if(op === "query_string") { + query["default_field"] = field; + query["query"] = value; + } else if(op === "missing") { + op = "constant_score" + var missing = {}, filter = {}; + missing["field"] = field; + filter["missing"] = missing + query["filter"] = filter; + } else { + query[field.substring(field.indexOf(".")+1)] = value; + } + clause[op] = query; + this.search.query.bool[bool].push(clause); + return clause; + }, + getData: function() { + return JSON.stringify(this.search); + } + }); + +})( this.app ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/data/dataSourceInterface.js b/elasticsearch/plugins/head/src/app/data/dataSourceInterface.js new file mode 100644 index 00000000..001d5322 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/dataSourceInterface.js @@ -0,0 +1,24 @@ +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.DataSourceInterface = ux.Observable.extend({ + /* + properties + meta = { total: 0 }, + headers = [ { name: "" } ], + data = [ { column: value, column: value } ], + sort = { column: "name", dir: "desc" } + events + data: function( DataSourceInterface ) + */ + _getSummary: function(res) { + this.summary = i18n.text("TableResults.Summary", res._shards.successful, res._shards.total, res.hits.total, (res.took / 1000).toFixed(3)); + }, + _getMeta: function(res) { + this.meta = { total: res.hits.total, shards: res._shards, tool: res.took }; + } + }); + +})( this.app ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/data/metaData.js b/elasticsearch/plugins/head/src/app/data/metaData.js new file mode 100644 index 00000000..5868cd8e --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/metaData.js @@ -0,0 +1,170 @@ +(function( app ) { + + /* + notes on elasticsearch terminology used in this project + + indices[index] contains one or more + types[type] contains one or more + documents contain one or more + paths[path] + each path contains one element of data + each path maps to one field + + eg PUT, "/twitter/tweet/1" + { + user: "mobz", + date: "2011-01-01", + message: "You know, for browsing elasticsearch", + name: { + first: "Ben", + last: "Birch" + } + } + + creates + 1 index: twitter + this is the collection of index data + 1 type: tweet + this is the type of document (kind of like a table in sql) + 1 document: /twitter/tweet/1 + this is an actual document in the index ( kind of like a row in sql) + 5 paths: [ ["user"], ["date"], ["message"], ["name","first"], ["name","last"] ] + since documents can be heirarchical this maps a path from a document root to a piece of data + 5 fields: [ "user", "date", "message", "first", "last" ] + this is an indexed 'column' of data. fields are not heirarchical + + the relationship between a path and a field is called a mapping. mappings also contain a wealth of information about how es indexes the field + + notes + 1) a path is stored as an array, the dpath is . . path.join("."), + which can be considered the canonical reference for a mapping + 2) confusingly, es uses the term index for both the collection of indexed data, and the individually indexed fields + so the term index_name is the same as field_name in this sense. + + */ + + var data = app.ns("data"); + var ux = app.ns("ux"); + + var coretype_map = { + "string" : "string", + "byte" : "number", + "short" : "number", + "long" : "number", + "integer" : "number", + "float" : "number", + "double" : "number", + "ip" : "number", + "date" : "date", + "boolean" : "boolean", + "binary" : "binary", + "multi_field" : "multi_field" + }; + + var default_property_map = { + "string" : { "store" : "no", "index" : "analysed" }, + "number" : { "store" : "no", "precision_steps" : 4 }, + "date" : { "store" : "no", "format" : "dateOptionalTime", "index": "yes", "precision_steps": 4 }, + "boolean" : { "store" : "no", "index": "yes" }, + "binary" : { }, + "multi_field" : { } + }; + + // parses metatdata from a cluster, into a bunch of useful data structures + data.MetaData = ux.Observable.extend({ + defaults: { + state: null // (required) response from a /_cluster/state request + }, + init: function() { + this._super(); + this.refresh(this.config.state); + }, + getIndices: function(alias) { + return alias ? this.aliases[alias] : this.indicesList; + }, + // returns an array of strings containing all types that are in all of the indices passed in, or all types + getTypes: function(indices) { + var indices = indices || [], types = []; + this.typesList.forEach(function(type) { + for(var i = 0; i < indices.length; i++) { + if(! this.indices[indices[i]].types.contains(type)) + return; + } + types.push(type); + }, this); + return types; + }, + refresh: function(state) { + // currently metadata expects all like named fields to have the same type, even when from different types and indices + var aliases = this.aliases = {}; + var indices = this.indices = {}; + var types = this.types = {}; + var fields = this.fields = {}; + var paths = this.paths = {}; + + function createField( mapping, index, type, path, name ) { + var dpath = [ index, type ].concat( path ).join( "." ); + var field_name = mapping.index_name || path.join( "." ); + var field = paths[ dpath ] = fields[ field_name ] || $.extend({ + field_name : field_name, + core_type : coretype_map[ mapping.type ], + dpaths : [] + }, default_property_map[ coretype_map[ mapping.type ] ], mapping ); + + if (field.type === "multi_field" && typeof field.fields !== "undefined") { + for (var subField in field.fields) { + field.fields[ subField ] = createField( field.fields[ subField ], index, type, path.concat( subField ), name + "." + subField ); + } + } + if (fields.dpaths) { + field.dpaths.push(dpath); + } + return field; + } + function getFields(properties, type, index, listeners) { + (function procPath(prop, path) { + for (var n in prop) { + if ("properties" in prop[n]) { + procPath( prop[ n ].properties, path.concat( n ) ); + } else { + var field = createField(prop[n], index, type, path.concat(n), n); + listeners.forEach( function( listener ) { + listener[ field.field_name ] = field; + } ); + } + } + })(properties, []); + } + for (var index in state.metadata.indices) { + indices[index] = { + types : [], fields : {}, paths : {}, parents : {} + }; + indices[index].aliases = state.metadata.indices[index].aliases; + indices[index].aliases.forEach(function(alias) { + (aliases[alias] || (aliases[alias] = [])).push(index); + }); + var mapping = state.metadata.indices[index].mappings; + for (var type in mapping) { + indices[index].types.push(type); + if ( type in types) { + types[type].indices.push(index); + } else { + types[type] = { + indices : [index], fields : {} + }; + } + getFields(mapping[type].properties, type, index, [fields, types[type].fields, indices[index].fields]); + if ( typeof mapping[type]._parent !== "undefined") { + indices[index].parents[type] = mapping[type]._parent.type; + } + } + } + + this.aliasesList = Object.keys(aliases); + this.indicesList = Object.keys(indices); + this.typesList = Object.keys(types); + this.fieldsList = Object.keys(fields); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/data/metaDataFactory.js b/elasticsearch/plugins/head/src/app/data/metaDataFactory.js new file mode 100644 index 00000000..694546e0 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/metaDataFactory.js @@ -0,0 +1,19 @@ +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.MetaDataFactory = ux.Observable.extend({ + defaults: { + cluster: null // (required) an app.services.Cluster + }, + init: function() { + this._super(); + this.config.cluster.get("_cluster/state", function(data) { + this.metaData = new app.data.MetaData({state: data}); + this.fire("ready", this.metaData, { originalData: data }); // TODO originalData needed for legacy ui.FilterBrowser + }.bind(this)); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/data/model/model.js b/elasticsearch/plugins/head/src/app/data/model/model.js new file mode 100644 index 00000000..a35e53b9 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/model/model.js @@ -0,0 +1,35 @@ +(function( $, app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.Model = ux.Observable.extend({ + defaults: { + data: null + }, + init: function() { + this.set( this.config.data ); + }, + set: function( key, value ) { + if( arguments.length === 1 ) { + this._data = $.extend( {}, key ); + } else { + key.split(".").reduce(function( ptr, prop, i, props) { + if(i === (props.length - 1) ) { + ptr[prop] = value; + } else { + if( !(prop in ptr) ) { + ptr[ prop ] = {}; + } + return ptr[prop]; + } + }, this._data ); + } + }, + get: function( key ) { + return key.split(".").reduce( function( ptr, prop ) { + return ( ptr && ( prop in ptr ) ) ? ptr[ prop ] : undefined; + }, this._data ); + }, + }); +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/data/model/modelSpec.js b/elasticsearch/plugins/head/src/app/data/model/modelSpec.js new file mode 100644 index 00000000..fa9d904e --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/model/modelSpec.js @@ -0,0 +1,74 @@ +describe("app.data.Model", function() { + + var Model = window.app.data.Model; + + it("test setting model does a shallow copy", function() { + var test = {}; + var array = [ 1, 2, 3 ]; + var m = new Model({ + data: { + "foo": "bar", + "test": test, + "array": array + } + }); + expect( m.get("foo") ).toBe("bar"); + expect( m.get("array").length ).toBe( 3 ); + expect( m.get("array")[1] ).toBe( 2 ); + expect( m.get("array") ).toBe( array ); + expect( m.get("test") ).toBe( test ); + }); + + it("should replace model with shallow copy when put with no path", function() { + var m = new Model({ foo: "bar" }); + m.set({ bar: "blat" }); + expect( m.get("foo")).toBe( undefined ); + expect( m.get("bar")).toBe("blat"); + }); + + it("test getting values from deep in a model", function() { + var m = new Model({ + data: { + "foo": { + "bar": { + "baz": { + "quix": "abcdefg" + } + } + } + } + }); + + expect( m.get("foo.bar.baz.quix") ).toBe( "abcdefg" ); + }); + + it("test setting non-existant values creates new values", function() { + var m = new Model({ + data: { + "foo": { + "bar": "abc" + } + } + }); + m.set("foo.bar", "123" ); + m.set("foo.baz", "456" ); + expect( m.get("foo.bar") ).toBe( "123" ); + expect( m.get("foo.baz") ).toBe( "456" ); + }); + + it("test setting values deep in a model", function() { + var m = new Model({ + data: { + "foo": { + "bar": "abc" + } + } + }); + m.set("foo.bar", "123" ); + m.set("foo.baz", "456" ); + m.set("foo.something.else.is.here", "xyz" ); + expect( m.get("foo.something.else.is").here ).toBe( "xyz" ); + expect( m.get("foo.something.else.is.here") ).toBe( "xyz" ); + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/data/query.js b/elasticsearch/plugins/head/src/app/data/query.js new file mode 100644 index 00000000..4d0e70f0 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/query.js @@ -0,0 +1,204 @@ +(function( app ) { + + var data = app.ns("data"); + var ux = app.ns("ux"); + + data.Query = ux.Observable.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + size: 50 // size of pages to return + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.refuid = 0; + this.refmap = {}; + this.indices = []; + this.types = []; + this.search = { + fields : [ "_parent", "_source" ], + query: { bool: { must: [], must_not: [], should: [] } }, + from: 0, + size: this.config.size, + sort: [], + aggs: {}, + version: true + }; + this.defaultClause = this.addClause(); + this.history = [ this.getState() ]; + }, + clone: function() { + var q = new data.Query({ cluster: this.cluster }); + q.restoreState(this.getState()); + for(var uqid in q.refmap) { + q.removeClause(uqid); + } + return q; + }, + getState: function() { + return $.extend(true, {}, { search: this.search, indices: this.indices, types: this.types }); + }, + restoreState: function(state) { + state = $.extend(true, {}, state || this.history[this.history.length - 1]); + this.indices = state.indices; + this.types = state.types; + this.search = state.search; + }, + getData: function() { + return JSON.stringify(this.search); + }, + query: function() { + var state = this.getState(); + this.cluster.post( + (this.indices.join(",") || "_all") + "/" + ( this.types.length ? this.types.join(",") + "/" : "") + "_search", + this.getData(), + function(results) { + if(results === null) { + alert(i18n.text("Query.FailAndUndo")); + this.restoreState(); + return; + } + this.history.push(state); + + this.fire("results", this, results); + }.bind(this)); + }, + loadParents: function(res,metadata){ + //create data for mget + var data = { docs :[] }; + var indexToTypeToParentIds = {}; + res.hits.hits.forEach(function(hit) { + if (typeof hit.fields != "undefined"){ + if (typeof hit.fields._parent != "undefined"){ + var parentType = metadata.indices[hit._index].parents[hit._type]; + if (typeof indexToTypeToParentIds[hit._index] == "undefined"){ + indexToTypeToParentIds[hit._index] = new Object(); + } + if (typeof indexToTypeToParentIds[hit._index][hit._type] == "undefined"){ + indexToTypeToParentIds[hit._index][hit._type] = new Object(); + } + if (typeof indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] == "undefined"){ + indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] = null; + data.docs.push({ _index:hit._index, _type:parentType, _id:hit.fields._parent}); + } + } + } + }); + + //load parents + var state = this.getState(); + this.cluster.post("_mget",JSON.stringify(data), + function(results) { + if(results === null) { + alert(i18n.text("Query.FailAndUndo")); + this.restoreState(); + return; + } + this.history.push(state); + var indexToTypeToParentIdToHit = new Object(); + results.docs.forEach(function(doc) { + if (typeof indexToTypeToParentIdToHit[doc._index] == "undefined"){ + indexToTypeToParentIdToHit[doc._index] = new Object(); + } + + if (typeof indexToTypeToParentIdToHit[doc._index][doc._type] == "undefined"){ + indexToTypeToParentIdToHit[doc._index][doc._type] = new Object(); + } + + indexToTypeToParentIdToHit[doc._index][doc._type][doc._id] = doc; + }); + + res.hits.hits.forEach(function(hit) { + if (typeof hit.fields != "undefined"){ + if (typeof hit.fields._parent != "undefined"){ + var parentType = metadata.indices[hit._index].parents[hit._type]; + hit._parent = indexToTypeToParentIdToHit[hit._index][parentType][hit.fields._parent]; + } + } + }); + + this.fire("resultsWithParents", this, res); + }.bind(this)); + }, + setPage: function(page) { + this.search.from = this.config.size * (page - 1); + }, + setSort: function(index, desc) { + var sortd = {}; sortd[index] = { reverse: !!desc }; + this.search.sort.unshift( sortd ); + for(var i = 1; i < this.search.sort.length; i++) { + if(Object.keys(this.search.sort[i])[0] === index) { + this.search.sort.splice(i, 1); + break; + } + } + }, + setIndex: function(index, add) { + if(add) { + if(! this.indices.contains(index)) this.indices.push(index); + } else { + this.indices.remove(index); + } + this.fire("setIndex", this, { index: index, add: !!add }); + }, + setType: function(type, add) { + if(add) { + if(! this.types.contains(type)) this.types.push(type); + } else { + this.types.remove(type); + } + this.fire("setType", this, { type: type, add: !!add }); + }, + addClause: function(value, field, op, bool) { + bool = bool || "should"; + op = op || "match_all"; + field = field || "_all"; + var clause = this._setClause(value, field, op, bool); + var uqid = "q-" + this.refuid++; + this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool }; + if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) { + this.removeClause(this.defaultClause); + } + this.fire("queryChanged", this, { uqid: uqid, search: this.search} ); + return uqid; // returns reference to inner query object to allow fast updating + }, + removeClause: function(uqid) { + var ref = this.refmap[uqid], + bool = this.search.query.bool[ref.bool]; + bool.remove(ref.clause); + if(this.search.query.bool.must.length + this.search.query.bool.should.length === 0) { + this.defaultClause = this.addClause(); + } + }, + addAggs: function(aggs) { + var aggsId = "f-" + this.refuid++; + this.search.aggs[aggsId] = aggs; + this.refmap[aggsId] = { aggsId: aggsId, aggs: aggs }; + return aggsId; + }, + removeAggs: function(aggsId) { + delete this.search.aggs[aggsId]; + delete this.refmap[aggsId]; + }, + _setClause: function(value, field, op, bool) { + var clause = {}, query = {}; + if(op === "match_all") { + } else if(op === "query_string") { + query["default_field"] = field; + query["query"] = value; + } else if(op === "missing") { + op = "constant_score" + var missing = {}, filter = {}; + missing["field"] = field; + filter["missing"] = missing + query["filter"] = filter; + } else { + query[field] = value; + } + clause[op] = query; + this.search.query.bool[bool].push(clause); + return clause; + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/data/queryDataSourceInterface.js b/elasticsearch/plugins/head/src/app/data/queryDataSourceInterface.js new file mode 100644 index 00000000..3e2b45a1 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/queryDataSourceInterface.js @@ -0,0 +1,87 @@ +(function( app ) { + + var data = app.ns("data"); + + data.QueryDataSourceInterface = data.DataSourceInterface.extend({ + defaults: { + metadata: null, // (required) instanceof app.data.MetaData, the cluster metadata + query: null // (required) instanceof app.data.Query the data source + }, + init: function() { + this._super(); + this.config.query.on("results", this._results_handler.bind(this) ); + this.config.query.on("resultsWithParents", this._load_parents.bind(this) ); + }, + _results_handler: function(query, res) { + this._getSummary(res); + this._getMeta(res); + var sort = query.search.sort[0] || { "_score": { reverse: false }}; + var sortField = Object.keys(sort)[0]; + this.sort = { column: sortField, dir: (sort[sortField].reverse ? "asc" : "desc") }; + this._getData(res, this.config.metadata); + this.fire("data", this); + }, + _load_parents: function(query, res) { + query.loadParents(res, this.config.metadata); + }, + _getData: function(res, metadata) { + var metaColumns = ["_index", "_type", "_id", "_score"]; + var columns = this.columns = [].concat(metaColumns); + + this.data = res.hits.hits.map(function(hit) { + var row = (function(path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(metadata.paths[dpath]) { + var field_name = metadata.paths[dpath].field_name; + if(! columns.contains(field_name)) { + columns.push(field_name); + } + row[field_name] = (spec[prop] === null ? "null" : spec[prop] ).toString(); + } else { + // TODO: field not in metadata index + } + } + } + return row; + })([ hit._index, hit._type ], hit._source, {}); + metaColumns.forEach(function(n) { row[n] = hit[n]; }); + row._source = hit; + if (typeof hit._parent!= "undefined") { + (function(prefix, path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(prefix, path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(prefix, path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(metadata.paths[dpath]) { + var field_name = metadata.paths[dpath].field_name; + var column_name = prefix+"."+field_name; + if(! columns.contains(column_name)) { + columns.push(column_name); + } + row[column_name] = (spec[prop] === null ? "null" : spec[prop] ).toString(); + } else { + // TODO: field not in metadata index + } + } + } + })(hit._parent._type,[hit._parent._index, hit._parent._type], hit._parent._source, row); + } + return row; + }, this); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/data/resultDataSourceInterface.js b/elasticsearch/plugins/head/src/app/data/resultDataSourceInterface.js new file mode 100644 index 00000000..b985b042 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/data/resultDataSourceInterface.js @@ -0,0 +1,40 @@ +(function( app ) { + + var data = app.ns("data"); + + data.ResultDataSourceInterface = data.DataSourceInterface.extend({ + results: function(res) { + this._getSummary(res); + this._getMeta(res); + this._getData(res); + this.sort = {}; + this.fire("data", this); + }, + _getData: function(res) { + var columns = this.columns = []; + this.data = res.hits.hits.map(function(hit) { + var row = (function(path, spec, row) { + for(var prop in spec) { + if(acx.isObject(spec[prop])) { + arguments.callee(path.concat(prop), spec[prop], row); + } else if(acx.isArray(spec[prop])) { + if(spec[prop].length) { + arguments.callee(path.concat(prop), spec[prop][0], row) + } + } else { + var dpath = path.concat(prop).join("."); + if(! columns.contains(dpath)) { + columns.push(dpath); + } + row[dpath] = (spec[prop] || "null").toString(); + } + } + return row; + })([ hit._type ], hit, {}); + row._source = hit; + return row; + }, this); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/lang/en_strings.js b/elasticsearch/plugins/head/src/app/lang/en_strings.js new file mode 100644 index 00000000..3679dfbb --- /dev/null +++ b/elasticsearch/plugins/head/src/app/lang/en_strings.js @@ -0,0 +1,177 @@ +i18n.setKeys({ + "General.Elasticsearch": "Elasticsearch", + "General.LoadingAggs": "Loading Aggregations...", + "General.Searching": "Searching...", + "General.Search": "Search", + "General.Help": "Help", + "General.HelpGlyph": "?", + "General.CloseGlyph": "X", + "General.RefreshResults": "Refresh", + "General.ManualRefresh": "Manual Refresh", + "General.RefreshQuickly": "Refresh quickly", + "General.Refresh5seconds": "Refresh every 5 seconds", + "General.Refresh1minute": "Refresh every minute", + "AliasForm.AliasName": "Alias Name", + "AliasForm.NewAliasForIndex": "New Alias for {0}", + "AliasForm.DeleteAliasMessage": "type ''{0}'' to delete {1}. There is no undo", + "AnyRequest.DisplayOptions" : "Display Options", + "AnyRequest.AsGraph" : "Graph Results", + "AnyRequest.AsJson" : "Show Raw JSON", + "AnyRequest.AsTable" : "Show Search Results Table", + "AnyRequest.History" : "History", + "AnyRequest.RepeatRequest" : "Repeat Request", + "AnyRequest.RepeatRequestSelect" : "Repeat request every ", + "AnyRequest.Transformer" : "Result Transformer", + "AnyRequest.Pretty": "Pretty", + "AnyRequest.Query" : "Query", + "AnyRequest.Request": "Request", + "AnyRequest.Requesting": "Requesting...", + "AnyRequest.ValidateJSON": "Validate JSON", + "Browser.Title": "Browser", + "Browser.ResultSourcePanelTitle": "Result Source", + "Command.DELETE": "DELETE", + "Command.SHUTDOWN": "SHUTDOWN", + "Command.DeleteAliasMessage": "Delete Alias?", + "ClusterOverView.IndexName": "Index Name", + "ClusterOverview.NumShards": "Number of Shards", + "ClusterOverview.NumReplicas": "Number of Replicas", + "ClusterOverview.NewIndex": "New Index", + "IndexActionsMenu.Title": "Actions", + "IndexActionsMenu.NewAlias": "New Alias...", + "IndexActionsMenu.Refresh": "Refresh", + "IndexActionsMenu.Flush": "Flush", + "IndexActionsMenu.Optimize": "Optimize...", + "IndexActionsMenu.Snapshot": "Gateway Snapshot", + "IndexActionsMenu.Analyser": "Test Analyser", + "IndexActionsMenu.Open": "Open", + "IndexActionsMenu.Close": "Close", + "IndexActionsMenu.Delete": "Delete...", + "IndexInfoMenu.Title": "Info", + "IndexInfoMenu.Status": "Index Status", + "IndexInfoMenu.Metadata": "Index Metadata", + "IndexCommand.TextToAnalyze": "Text to Analyse", + "IndexCommand.ShutdownMessage": "type ''{0}'' to shutdown {1}. Node can NOT be restarted from this interface", + "IndexOverview.PageTitle": "Indices Overview", + "IndexSelector.NameWithDocs": "{0} ({1} docs)", + "IndexSelector.SearchIndexForDocs": "Search {0} for documents where:", + "FilterBrowser.OutputType": "Output Results: {0}", + "FilterBrowser.OutputSize": "Number of Results: {0}", + "Header.ClusterHealth": "cluster health: {0} ({1} of {2})", + "Header.ClusterNotConnected": "cluster health: not connected", + "Header.Connect": "Connect", + "Nav.AnyRequest": "Any Request", + "Nav.Browser": "Browser", + "Nav.ClusterHealth": "Cluster Health", + "Nav.ClusterState": "Cluster State", + "Nav.ClusterNodes": "Nodes Info", + "Nav.Info": "Info", + "Nav.NodeStats": "Nodes Stats", + "Nav.Overview": "Overview", + "Nav.Indices": "Indices", + "Nav.Plugins": "Plugins", + "Nav.Status": "Indices Stats", + "Nav.Templates": "Templates", + "Nav.StructuredQuery": "Structured Query", + "NodeActionsMenu.Title": "Actions", + "NodeActionsMenu.Shutdown": "Shutdown...", + "NodeInfoMenu.Title": "Info", + "NodeInfoMenu.ClusterNodeInfo": "Cluster Node Info", + "NodeInfoMenu.NodeStats": "Node Stats", + "NodeType.Client": "Client Node", + "NodeType.Coord": "Coordinator", + "NodeType.Master": "Master Node", + "NodeType.Tribe": "Tribe Node", + "NodeType.Worker": "Worker Node", + "NodeType.Unassigned": "Unassigned", + "OptimizeForm.OptimizeIndex": "Optimize {0}", + "OptimizeForm.MaxSegments": "Maximum # Of Segments", + "OptimizeForm.ExpungeDeletes": "Only Expunge Deletes", + "OptimizeForm.FlushAfter": "Flush After Optimize", + "OptimizeForm.WaitForMerge": "Wait For Merge", + "Overview.PageTitle" : "Cluster Overview", + "Output.JSON": "JSON", + "Output.Table": "Table", + "Output.CSV": "CSV", + "Output.ShowSource": "Show query source", + "Preference.SortCluster": "Sort Cluster", + "Sort.ByName": "By Name", + "Sort.ByAddress": "By Address", + "Sort.ByType": "By Type", + "Preference.SortIndices": "Sort Indices", + "SortIndices.Descending": "Descending", + "SortIndices.Ascending": "Ascending", + "Preference.ViewAliases": "View Aliases", + "ViewAliases.Grouped": "Grouped", + "ViewAliases.List": "List", + "ViewAliases.None": "None", + "Overview.IndexFilter": "Index Filter", + "TableResults.Summary": "Searched {0} of {1} shards. {2} hits. {3} seconds", + "QueryFilter.AllIndices": "All Indices", + "QueryFilter.AnyValue": "any", + "QueryFilter-Header-Indices": "Indices", + "QueryFilter-Header-Types": "Types", + "QueryFilter-Header-Fields": "Fields", + "QueryFilter.DateRangeHint.from": "From : {0}", + "QueryFilter.DateRangeHint.to": " To : {0}", + "Query.FailAndUndo": "Query Failed. Undoing last changes", + "StructuredQuery.ShowRawJson": "Show Raw JSON" +}); + +i18n.setKeys({ + "AnyRequest.TransformerHelp" : "\ +

    The Result Transformer can be used to post process the raw json results from a request into a more useful format.

    \ +

    The transformer should contain the body of a javascript function. The return value from the function becomes the new value passed to the json printer

    \ +

    Example:
    \ + return root.hits.hits[0]; would traverse a result set to show just the first match
    \ + return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); would return the total memory used across an entire cluster

    \ +

    The following functions are available and can be useful processing arrays and objects
    \ +

      \ +
    • Object.keys(object) := array
    • \ +
    • array.forEach(function(prop, index))
    • \ +
    • array.map(function(prop, index)) := array
    • \ +
    • array.reduce(function(accumulator, prop, index), initial_value) := final_value
    • \ +
    \ +

    When Repeat Request is running, an extra parameter called prev is passed to the transformation function. This allows comparisons, and cumulative graphing

    \ +

    Example:
    \ + var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; would return the load average on the first cluster node over the last minute\ + This could be fed into the Graph to produce a load graph for the node\ + " +}); + +i18n.setKeys({ + "AnyRequest.DisplayOptionsHelp" : "\ +

    Raw Json: shows complete results of the query and transformation in raw JSON format

    \ +

    Graph Results: To produce a graph of your results, use the result transformer to produce an array of values

    \ +

    Search Results Table: If your query is a search, you can display the results of the search in a table.

    \ + " +}); + +i18n.setKeys({ + "QueryFilter.DateRangeHelp" : "\ +

    Date fields accept a natural language query to produce a From and To date that form a range that the results are queried over.

    \ +

    The following formats are supported:

    \ +
      \ +
    • Keywords / Key Phrases
      \ + now
      today
      tomorrow
      yesterday
      last / this / next + week / month / year

      \ + searches for dates matching the keyword. last year would search all of last year.
    • \ +
    • Ranges
      \ + 1000 secs
      5mins
      1day
      2days
      80d
      9 months
      2yrs
      (spaces optional, many synonyms for range qualifiers)
      \ + Create a search range centered on now extending into the past and future by the amount specified.
    • \ +
    • DateTime and Partial DateTime
      \ + 2011
      2011-01
      2011-01-18
      2011-01-18 12
      2011-01-18 12:32
      2011-01-18 12:32:45

      \ + these formats specify a specific date range. 2011 would search the whole of 2011, while 2011-01-18 12:32:45 would only search for results in that 1 second range
    • \ +
    • Time and Time Partials
      \ + 12
      12:32
      12:32:45

      \ + these formats search for a particular time during the current day. 12:32 would search that minute during today
    • \ +
    • Date Ranges
      \ + 2010 -> 2011
      last week -> next week
      2011-05 ->
      < now

      \ + A Date Range is created by specifying two dates in any format (Keyword / DateTime / Time) separated by < or -> (both do the same thing). If either end of the date range is missing, it is the same as having no constraint in that direction.
    • \ +
    • Date Range using Offset
      \ + 2010 -> 1yr
      3mins < now
      \ + Searches the specified date including the range in the direction specified.
    • \ +
    • Anchored Ranges
      \ + 2010-05-13 05:13 <> 10m
      now <> 1yr
      lastweek <> 1month

      \ + Similar to above except the range is extend in both directions from the anchor date
    • \ +
    \ + " +}); diff --git a/elasticsearch/plugins/head/src/app/lang/fr_strings.js b/elasticsearch/plugins/head/src/app/lang/fr_strings.js new file mode 100644 index 00000000..2cc9bbb0 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/lang/fr_strings.js @@ -0,0 +1,164 @@ +i18n.setKeys({ +// "General.Elasticsearch": "Elasticsearch", + "General.LoadingAggs" : "Chargement des facettes...", + "General.Searching": "Recherche en cours...", + "General.Search": "Recherche", + "General.Help": "Aide", +// "General.HelpGlyph": "?", +// "General.CloseGlyph": "X", + "General.RefreshResults": "Rafraîchir", + "General.ManualRefresh": "Rafraîchissement manuel", + "General.RefreshQuickly": "Rafraîchissement rapide", + "General.Refresh5seconds": "Rafraîchissement toutes les 5 secondes", + "General.Refresh1minute": "Rafraîchissement toutes les minutes", + "AliasForm.AliasName": "Alias", + "AliasForm.NewAliasForIndex": "Nouvel Alias pour {0}", + "AliasForm.DeleteAliasMessage": "Entrez ''{0}'' pour effacer {1}. Attention, action irréversible.", + "AnyRequest.DisplayOptions" : "Options d'affichage", + "AnyRequest.AsGraph" : "En graphe", + "AnyRequest.AsJson" : "En JSON brut", + "AnyRequest.AsTable" : "En tableau", + "AnyRequest.History" : "Historique", + "AnyRequest.RepeatRequest" : "Répétition automatique de la requête", + "AnyRequest.RepeatRequestSelect" : "Répéter la requête toutes les ", + "AnyRequest.Transformer" : "Transformation des résultats", +// "AnyRequest.Pretty": "Pretty", + "AnyRequest.Query" : "Recherche", + "AnyRequest.Request": "Requête", + "AnyRequest.Requesting": "Requête en cours...", + "AnyRequest.ValidateJSON": "Valider le JSON", + "Browser.Title": "Navigateur", + "Browser.ResultSourcePanelTitle": "Résultat au format JSON", + "Command.DELETE": "SUPPRIMER", + "Command.SHUTDOWN": "ETEINDRE", + "Command.DeleteAliasMessage": "Supprimer l'Alias?", + "ClusterOverView.IndexName": "Index", + "ClusterOverview.NumShards": "Nombre de shards", + "ClusterOverview.NumReplicas": "Nombre de replica", + "ClusterOverview.NewIndex": "Nouvel Index", +// "IndexActionsMenu.Title": "Actions", + "IndexActionsMenu.NewAlias": "Nouvel Alias...", + "IndexActionsMenu.Refresh": "Rafraîchir", + "IndexActionsMenu.Flush": "Flusher", + "IndexActionsMenu.Optimize": "Optimiser...", + "IndexActionsMenu.Snapshot": "Dupliquer l'index (Snapshot)", + "IndexActionsMenu.Analyser": "Tester un analyseur", + "IndexActionsMenu.Open": "Ouvrir", + "IndexActionsMenu.Close": "Fermer", + "IndexActionsMenu.Delete": "Effacer...", +// "IndexInfoMenu.Title": "Info", + "IndexInfoMenu.Status": "Etat de l'Index", + "IndexInfoMenu.Metadata": "Métadonnées de l'Index", + "IndexCommand.TextToAnalyze": "Texte à analyser", + "IndexCommand.ShutdownMessage": "Entrez ''{0}'' pour éteindre {1}. Le noeud NE PEUT PAS être redémarré depuis cette interface.", +// "IndexSelector.NameWithDocs": "{0} ({1} docs)", + "IndexSelector.SearchIndexForDocs": "Chercher dans {0} les documents correspondant à", + "FilterBrowser.OutputType": "Format d'affichage des résultats {0}", + "FilterBrowser.OutputSize": "Nombre de Résultats: {0}", + "Header.ClusterHealth": "Santé du cluster: {0} ({1} {2})", + "Header.ClusterNotConnected": "Santé du cluster: non connecté", + "Header.Connect": "Se connecter", + "Nav.AnyRequest": "Autres requêtes", + "Nav.StructuredQuery": "Requêtes structurées", + "Nav.Browser": "Navigateur", + "Nav.ClusterHealth": "Santé du cluster", + "Nav.ClusterState": "Etat du cluster", + "Nav.ClusterNodes": "Noeuds du cluster", +// "Nav.Info": "Info", + "Nav.NodeStats": "Statistiques sur les noeuds", + "Nav.Overview": "Aperçu", + "Nav.Indices": "Index", + "Nav.Plugins": "Plugins", + "Nav.Status": "Etat", + "Nav.Templates": "Templates", + "Nav.StructuredQuery": "Recherche Structurée", +// "NodeActionsMenu.Title": "Actions", + "NodeActionsMenu.Shutdown": "Eteindre...", +// "NodeInfoMenu.Title": "Info", + "NodeInfoMenu.ClusterNodeInfo": "Infos sur le noeud du cluster", + "NodeInfoMenu.NodeStats": "Statistiques du noeud", + "NodeType.Client": "Noeud Client", + "NodeType.Coord": "Coordinateur", + "NodeType.Master": "Noeud Master", + "NodeType.Tribe": "Noeud Tribe", + "NodeType.Worker": "Noeud Worker", + "NodeType.Unassigned": "Non assigné", + "OptimizeForm.OptimizeIndex": "Optimiser {0}", + "OptimizeForm.MaxSegments": "Nombre maximum de segments", + "OptimizeForm.ExpungeDeletes": "Seulement purger les suppressions", + "OptimizeForm.FlushAfter": "Flusher après l'optimisation", + "OptimizeForm.WaitForMerge": "Attendre la fin de la fusion", + "Overview.PageTitle" : "Aperçu du cluster", +// "Output.JSON": "JSON", + "Output.Table": "Tableau", + "Output.ShowSource": "Voir la requête source", + "TableResults.Summary": "Recherche sur {0} des {1} shards. {2} résultats. {3} secondes", + "QueryFilter.AllIndices": "Tous les index", + "QueryFilter.AnyValue": "Tout", + "QueryFilter-Header-Indices": "Index", +// "QueryFilter-Header-Types": "Types", + "QueryFilter-Header-Fields": "Champs", + "QueryFilter.DateRangeHint.from": "De : {0}", + "QueryFilter.DateRangeHint.to": " A : {0}", + "Query.FailAndUndo": "Requête en échec. Annulation des dernières modifications.", + "StructuredQuery.ShowRawJson": "Voir le JSON brut" +}); + +i18n.setKeys({ + "AnyRequest.TransformerHelp" : "\ +

    Le transformateur de résultats peut être utilisé pour modifier a posteriori les résultats JSON bruts dans un format plus utile.

    \ +

    Le transformateur devrait contenir le corps d'une fonction javascript. La valeur de retour de la fonction devient la nouvelle valeur qui sera passée à l'afficheur des documents JSON.

    \ +

    Exemple:
    \ + return root.hits.hits[0]; ne renverra que le premier élément de l'ensemble des résultats.
    \ + return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); retournera la mémoire totale utilisée dans l'ensemble du cluster.

    \ +

    Les fonctions suivantes sont disponibles et peuvent vous être utiles pour travailler sur les tableaux et les objets:
    \ +

      \ +
    • Object.keys(object) := array
    • \ +
    • array.forEach(function(prop, index))
    • \ +
    • array.map(function(prop, index)) := array
    • \ +
    • array.reduce(function(accumulator, prop, index), initial_value) := final_value
    • \ +
    \ +

    Lorsque vous activez la répétition automatique de la requête, un paramètre supplémentaire nommé prev est passé à la fonction de transformation. Cela permet les comparaisons et les graphes cumulatifs.

    \ +

    Exemple:
    \ + var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; retournera la charge moyenne du premier noeud du cluster pour la dernière minute écoulée.\ + Cela peut alimenter ensuite le graphe pour produire un graphe de charge du noeud.\ + " +}); + +i18n.setKeys({ + "AnyRequest.DisplayOptionsHelp" : "\ +

    En JSON brut: affiche les résultats complets de la recherche éventuellement transformée au format JSON brut.

    \ +

    En graphe: pour fabriquer un graphe de vos résultats, utilsez la transformation de résultats pour générer un tableau de valeurs.

    \ +

    En tableau: si votre requête est une recherche, vous pouvez alors afficher les résultats dans un tableau.

    \ + " +}); + +i18n.setKeys({ + "QueryFilter.DateRangeHelp" : "\ +

    Les champs Date acceptent une requête en langage naturel pour produire un écart de date (from/to) correspondant.

    \ +

    Les formats suivants sont acceptés :

    \ +
      \ +
    • Mots clés
      \ + now
      today
      tomorrow
      yesterday
      last / this / next + week / month / year

      \ + Cherchera pour des dates correspondant au mot clé. last year cherchera sur toute l'année précédente.
    • \ +
    • Ecarts
      \ + 1000 secs
      5mins
      1day
      2days
      80d
      9 months
      2yrs
      (les espaces sont optionnels et il existe beaucoup de synonymes pour qualifier les écarts)
      \ + Créé un écart de date basé sur l'heure courante (maintenant) avec plus ou moins l'écart indiqué.
    • \ +
    • Dates et Dates partielles
      \ + 2011
      2011-01
      2011-01-18
      2011-01-18 12
      2011-01-18 12:32
      2011-01-18 12:32:45

      \ + Ces formats indiquent un écart de date spécifique. 2011 cherchera sur toute l'année 2011, alors que 2011-01-18 12:32:45 ne cherchera que pour la date précise à la seconde près.
    • \ +
    • Heures et heures partielles
      \ + 12
      12:32
      12:32:45

      \ + Ces formats indiquent un espace de temps pour la date du jour. 12:32 cherchera les éléments d'aujourd'hui à cette minute précise.
    • \ +
    • Ecart de Date
      \ + 2010 -> 2011
      last week -> next week
      2011-05 ->
      < now

      \ + Un écart de date est créé en spécifiant deux dates dans n'importe lequel des formats précédents (Mot clé / Dates / Heures) séparées par < ou -> (les deux produisent le même effet). Si la date de fin n'est pas indiquée, alors il n'y aura aucune contrainte de fin.
    • \ +
    • Ecart de date avec décalage
      \ + 2010 -> 1yr
      3mins < now
      \ + Cherche en incluant un décalage de la date dans la direction indiquée.
    • \ +
    • Ecart de date avec bornes
      \ + 2010-05-13 05:13 <> 10m
      now <> 1yr
      lastweek <> 1month

      \ + Similaire à ci-dessus excepté que le décalage est appliqué dans les deux sens à partir de la date indiquée.
    • \ +
    \ + " +}); diff --git a/elasticsearch/plugins/head/src/app/lang/pt_strings.js b/elasticsearch/plugins/head/src/app/lang/pt_strings.js new file mode 100644 index 00000000..d4b6abff --- /dev/null +++ b/elasticsearch/plugins/head/src/app/lang/pt_strings.js @@ -0,0 +1,174 @@ +i18n.setKeys({ + "General.Elasticsearch": "Elasticsearch", + "General.LoadingAggs": "Carregando Facetas...", + "General.Searching": "Buscando...", + "General.Search": "Busca", + "General.Help": "Ajuda", + "General.HelpGlyph": "?", + "General.CloseGlyph": "X", + "General.RefreshResults": "Atualizar", + "General.ManualRefresh": "Atualização Manual", + "General.RefreshQuickly": "Atualização rápida", + "General.Refresh5seconds": "Atualização a cada 5 segundos", + "General.Refresh1minute": "Atualização a cada minuto", + "AliasForm.AliasName": "Apelido", + "AliasForm.NewAliasForIndex": "Novo apelido para {0}", + "AliasForm.DeleteAliasMessage": "digite ''{0}'' para deletar {1}. Não há como voltar atrás", + "AnyRequest.DisplayOptions" : "Mostrar Opções", + "AnyRequest.AsGraph" : "Mostrar como gráfico", + "AnyRequest.AsJson" : "Mostrar JSON bruto", + "AnyRequest.AsTable" : "Mostrar tabela de resultados da consulta", + "AnyRequest.History" : "Histórico", + "AnyRequest.RepeatRequest" : "Refazer requisição", + "AnyRequest.RepeatRequestSelect" : "Repetir requisição a cada ", + "AnyRequest.Transformer" : "Transformador de resultado", + "AnyRequest.Pretty": "Amigável", + "AnyRequest.Query" : "Consulta", + "AnyRequest.Request": "Requisição", + "AnyRequest.Requesting": "Realizando requisição...", + "AnyRequest.ValidateJSON": "Validar JSON", + "Browser.Title": "Navegador", + "Browser.ResultSourcePanelTitle": "Resultado", + "Command.DELETE": "DELETAR", + "Command.SHUTDOWN": "DESLIGAR", + "Command.DeleteAliasMessage": "Remover apelido?", + "ClusterOverView.IndexName": "Nome do índice", + "ClusterOverview.NumShards": "Número de Shards", + "ClusterOverview.NumReplicas": "Número de Réplicas", + "ClusterOverview.NewIndex": "Novo índice", + "IndexActionsMenu.Title": "Ações", + "IndexActionsMenu.NewAlias": "Novo apelido...", + "IndexActionsMenu.Refresh": "Atualizar", + "IndexActionsMenu.Flush": "Flush", + "IndexActionsMenu.Optimize": "Otimizar...", + "IndexActionsMenu.Snapshot": "Snapshot do Gateway", + "IndexActionsMenu.Analyser": "Analizador de teste", + "IndexActionsMenu.Open": "Abrir", + "IndexActionsMenu.Close": "Fechar", + "IndexActionsMenu.Delete": "Deletar...", + "IndexInfoMenu.Title": "Info", + "IndexInfoMenu.Status": "Status do índice", + "IndexInfoMenu.Metadata": "Metadados do índice", + "IndexCommand.TextToAnalyze": "Texto para analizar", + "IndexCommand.ShutdownMessage": "digite ''{0}'' para desligar {1}. Nó NÃO PODE ser reiniciado à partir dessa interface", + "IndexOverview.PageTitle": "Visão geral dos índices", + "IndexSelector.NameWithDocs": "{0} ({1} documentoss)", + "IndexSelector.SearchIndexForDocs": "Busca {0} por documentos onde:", + "FilterBrowser.OutputType": "Resultados: {0}", + "FilterBrowser.OutputSize": "Número de Resultados: {0}", + "Header.ClusterHealth": "saúde do cluster: {0} ({1} {2})", + "Header.ClusterNotConnected": "saúde do cluster: não conectado", + "Header.Connect": "Conectar", + "Nav.AnyRequest": "Qualquer requisição", + "Nav.Browser": "Navegador", + "Nav.ClusterHealth": "Saúde do Cluster", + "Nav.ClusterState": "Estado do Cluster", + "Nav.ClusterNodes": "Nós do Cluster", + "Nav.Info": "Informações", + "Nav.NodeStats": "Estatísticas do nó", + "Nav.Overview": "Visão Geral", + "Nav.Indices": "Índices", + "Nav.Plugins": "Plugins", + "Nav.Status": "Status", + "Nav.Templates": "Modelos", + "Nav.StructuredQuery": "Consulta Estruturada", + "NodeActionsMenu.Title": "Ações", + "NodeActionsMenu.Shutdown": "Desligar...", + "NodeInfoMenu.Title": "Informações", + "NodeInfoMenu.ClusterNodeInfo": "Informações do Nó do Cluster", + "NodeInfoMenu.NodeStats": "Estatísticas do Nó", + "NodeType.Client": "Nó cliente", + "NodeType.Coord": "Coordenador", + "NodeType.Master": "Nó mestre", + "NodeType.Tribe": "Nó tribo", + "NodeType.Worker": "Nó trabalhador", + "NodeType.Unassigned": "Não atribuido", + "OptimizeForm.OptimizeIndex": "Otimizar {0}", + "OptimizeForm.MaxSegments": "# Máximo De Segmentos", + "OptimizeForm.ExpungeDeletes": "Apenas Expurgar Exclusões", + "OptimizeForm.FlushAfter": "Flush após Otimizar", + "OptimizeForm.WaitForMerge": "Esperar Por Merge", + "Overview.PageTitle": "Visão geral do Cluster", + "Output.JSON": "JSON", + "Output.Table": "Tabela", + "Output.CSV": "CSV", + "Output.ShowSource": "Mostrar consulta original", + "Preference.SortCluster": "Ordenar Cluster", + "Sort.ByName": "Por nome", + "Sort.ByAddress": "Por endereço", + "Sort.ByType": "Por tipo", + "Preference.ViewAliases": "Ver Alias", + "ViewAliases.Grouped": "Agrupado", + "ViewAliases.List": "Lista", + "ViewAliases.None": "Nenhum", + "Overview.IndexFilter": "Filtar Índice", + "TableResults.Summary": "Buscado {0} de {1} shards. {2} resultados. {3} segundos", + "QueryFilter.AllIndices": "Todos os Índices", + "QueryFilter.AnyValue": "qualquer", + "QueryFilter-Header-Indices": "Índices", + "QueryFilter-Header-Types": "Tipos", + "QueryFilter-Header-Fields": "Campos", + "QueryFilter.DateRangeHint.from": "De : {0}", + "QueryFilter.DateRangeHint.to": " A : {0}", + "Query.FailAndUndo": "Consulta falhou. Desfazendo últimas alterações", + "StructuredQuery.ShowRawJson": "Mostrar JSON bruto" +}); + +i18n.setKeys({ + "AnyRequest.TransformerHelp" : "\ +

    O Transformador de Resultados pode ser usado para transformar os resultados de uma consulta de json bruto para um formato mais útil.

    \ +

    O transformador deve possuir o corpo de uma função javascript. O retorno da função se torna o novo valor passado para o json printer

    \ +

    Exemplo:
    \ + return root.hits.hits[0]; irá alterar a resposta para mostrar apenas o primeiro resultado
    \ + return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); irá retornar o total de memória utilizada pelo cluster

    \ +

    As seguintes funções estão disponíveis e podem ser úteis no processamento de vetores e objetos
    \ +

      \ +
    • Object.keys(object) := array
    • \ +
    • array.forEach(function(prop, index))
    • \ +
    • array.map(function(prop, index)) := array
    • \ +
    • array.reduce(function(accumulator, prop, index), initial_value) := final_value
    • \ +
    \ +

    Durante a execução da opção Refazer Requisição, um parâmetro extra chamado prev é passado para a função de transformação. Isso permite fazer comparações e marcações cumulativas

    \ +

    Exemplo:
    \ + var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; irá retornar a carga média no primeiro nó do cluster no último minuto\ + Essa informação pode ser inserida no Gráfico para fazer um gráfico de carga do nó\ + " +}); + +i18n.setKeys({ + "AnyRequest.DisplayOptionsHelp" : "\ +

    Json Bruto: Exibe o resultado completo da consulta e da transformação no formato de JSON bruto

    \ +

    Gráfico de Resultados: Para gerar um gráfico com seus resultados, utilize o tranformador de resultados para produzir um vetor de valores

    \ +

    Tabela de Resultados da Consulta: Se sua consulta for uma busca, você pode exibir seus resultados no formato de uma tabela.

    \ + " +}); + +i18n.setKeys({ + "QueryFilter.DateRangeHelp" : "\ +

    Campos do tipo Data aceitam consultas em linguagem natural (em inglês) para produzir um From e um To de modo a formar um intervalo dentro do qual os resultados são filtrados.

    \ +

    Os seguintes formatos são suportados:

    \ +
      \ +
    • Palavras-chave
      \ + now
      today
      tomorrow
      yesterday
      last / this / next + week / month / year

      \ + buscam por datas de acordo com a palavra-chave. last year irá buscar tudo do último ano.
    • \ +
    • Intervalos
      \ + 1000 secs
      5mins
      1day
      2days
      80d
      9 months
      2yrs
      (espaços são opcionais, diversos sinônimos para qualificadores de intervalo)
      \ + Cria um intervalo de busca a partir de agora (now), extendendo este intervalo no passado e no futuro de acordo com intervalo especificado.
    • \ +
    • Data/Hora (DateTime) e Data/Hora parcial
      \ + 2011
      2011-01
      2011-01-18
      2011-01-18 12
      2011-01-18 12:32
      2011-01-18 12:32:45

      \ + esses formatos especificam um intervalo especifico. 2011 irá buscar todo o ano de 2011, enquanto 2011-01-18 12:32:45 irá buscar apenas por resultados dentro deste intervalo de 1 segundo
    • \ +
    • Tempo (Time) e Tempo parcial
      \ + 12
      12:32
      12:32:45

      \ + esses formatos buscam por um horário específico no dia atual. 12:32 irá buscar este minuto específico do dia
    • \ +
    • Intervalos de Data
      \ + 2010 -> 2011
      last week -> next week
      2011-05 ->
      < now

      \ + Um intervalo de data é criado especificando-se duas datas em qualquer formato (Palavras-chave, Data/Hora ou Tempo) separados por < ou -> (ambos fazem a mesma coisa). Se a data de início ou fim do intervalo não for especificada é a mesma coisa que não impor limites na busca nesta direção.
    • \ +
    • Intervalo de Data com Deslocamento
      \ + 2010 -> 1yr
      3mins < now
      \ + Busca a data especificada incluindo o intervalo na direção determinada pelo deslocamento
    • \ +
    • Intervalos Bidirecionais
      \ + 2010-05-13 05:13 <> 10m
      now <> 1yr
      lastweek <> 1month

      \ + Idêntico ao exemplo anterior porém o intervalo é extendido em ambas as direções a partir da data especificada
    • \ +
    \ + " +}); diff --git a/elasticsearch/plugins/head/src/app/lang/tr_strings.js b/elasticsearch/plugins/head/src/app/lang/tr_strings.js new file mode 100644 index 00000000..f6119561 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/lang/tr_strings.js @@ -0,0 +1,177 @@ +i18n.setKeys({ + "General.Elasticsearch": "Elasticsearch", + "General.LoadingAggs": "Gruplar Yükleniyor...", + "General.Searching": "Aranıyor...", + "General.Search": "Ara", + "General.Help": "Yardım", + "General.HelpGlyph": "?", + "General.CloseGlyph": "X", + "General.RefreshResults": "Yenile", + "General.ManualRefresh": "Manuel Yenileme", + "General.RefreshQuickly": "Hızlı yenile", + "General.Refresh5seconds": "5 saniyede bir yenile", + "General.Refresh1minute": "Her dakika yenile", + "AliasForm.AliasName": "Alternatif İsim", + "AliasForm.NewAliasForIndex": "{0} için yeni alternatif isim", + "AliasForm.DeleteAliasMessage": "{1} silmek için ''{0}'' . Geriye dönüş yoktur.", + "AnyRequest.DisplayOptions" : "Seçenekleri Göster", + "AnyRequest.AsGraph" : "Sonuçları Çizdir", + "AnyRequest.AsJson" : "JSON formatında göster", + "AnyRequest.AsTable" : "Arama sonuçlarını tablo halinde göster", + "AnyRequest.History" : "Geçmiş", + "AnyRequest.RepeatRequest" : "İsteği Tekrarla", + "AnyRequest.RepeatRequestSelect" : "İsteği sürekli tekrarla ", + "AnyRequest.Transformer" : "Sonuç Dönüştürücü", + "AnyRequest.Pretty": "Düzenli", + "AnyRequest.Query" : "Sorgu", + "AnyRequest.Request": "Gönder", + "AnyRequest.Requesting": "İsteniyor...", + "AnyRequest.ValidateJSON": "JSON Doğrula", + "Browser.Title": "Browser", + "Browser.ResultSourcePanelTitle": "Sonuç Kaynağı", + "Command.DELETE": "SİL", + "Command.SHUTDOWN": "KAPA", + "Command.DeleteAliasMessage": "Alternatif ismi sil?", + "ClusterOverView.IndexName": "Indeks İsmi", + "ClusterOverview.NumShards": "Sektör Sayısı", + "ClusterOverview.NumReplicas": "Yedek Sayısı", + "ClusterOverview.NewIndex": "Yeni Indeks", + "IndexActionsMenu.Title": "İşlemler", + "IndexActionsMenu.NewAlias": "Yeni Alternatif İsim...", + "IndexActionsMenu.Refresh": "Yenile", + "IndexActionsMenu.Flush": "Boşalt", + "IndexActionsMenu.Optimize": "Optimize et...", + "IndexActionsMenu.Snapshot": "Gateway Snapshot (Kopya Al)", + "IndexActionsMenu.Analyser": "Analizi test et", + "IndexActionsMenu.Open": "Aç", + "IndexActionsMenu.Close": "Kapa", + "IndexActionsMenu.Delete": "Sil...", + "IndexInfoMenu.Title": "Bilgi", + "IndexInfoMenu.Status": "Indeks Durumu", + "IndexInfoMenu.Metadata": "Indeks Metaveri", + "IndexCommand.TextToAnalyze": "Analiz edilecek metin", + "IndexCommand.ShutdownMessage": "{1} kapatmak için ''{0}'' yazın . Nod bu arayüzden tekrar BAŞLATILAMAZ", + "IndexOverview.PageTitle": "Indeksler Genel Bakış", + "IndexSelector.NameWithDocs": "{0} ({1} döküman)", + "IndexSelector.SearchIndexForDocs": "{0} indeksinde ara:", + "FilterBrowser.OutputType": "Sonuç Formatı: {0}", + "FilterBrowser.OutputSize": "Sonuç Sayısı: {0}", + "Header.ClusterHealth": "Küme Durumu: {0} ({1} de {2})", + "Header.ClusterNotConnected": "Küme Durumu: Bağlı Değil", + "Header.Connect": "Bağlan", + "Nav.AnyRequest": "Özel Sorgu", + "Nav.Browser": "Görüntüle", + "Nav.ClusterHealth": "Küme Durumu", + "Nav.ClusterState": "Küme Statüsü", + "Nav.ClusterNodes": "Nod Bilgileri", + "Nav.Info": "Bilgi", + "Nav.NodeStats": "Nod İstatistikleri", + "Nav.Overview": "Genel Bakış", + "Nav.Indices": "Indeksler", + "Nav.Plugins": "Eklentiler", + "Nav.Status": "Indeks İstatistikleri", + "Nav.Templates": "Şablonlar", + "Nav.StructuredQuery": "Yapılandırılmış Sorgu", + "NodeActionsMenu.Title": "İşlemler", + "NodeActionsMenu.Shutdown": "Kapat...", + "NodeInfoMenu.Title": "Bilgi", + "NodeInfoMenu.ClusterNodeInfo": "Küme Nod Bilgileri", + "NodeInfoMenu.NodeStats": "Nod İstatistikleri", + "NodeType.Client": "Client Nod", + "NodeType.Coord": "Coordinator", + "NodeType.Master": "Master Nod", + "NodeType.Tribe": "Tribe Nod", + "NodeType.Worker": "Worker Nod", + "NodeType.Unassigned": "Sahipsiz", + "OptimizeForm.OptimizeIndex": "{0} Optimize Et", + "OptimizeForm.MaxSegments": "Maksimum Segment Sayısı", + "OptimizeForm.ExpungeDeletes": "Silme İşlemi Artıklarını Temizle", + "OptimizeForm.FlushAfter": "Optimize Ettikten Sonra Boşalt", + "OptimizeForm.WaitForMerge": "Birleştirme İçin Bekle", + "Overview.PageTitle" : "Kümeler Genelbakış", + "Output.JSON": "JSON", + "Output.Table": "Tablo", + "Output.CSV": "CSV", + "Output.ShowSource": "Sorgu kaynağını göster", + "Preference.SortCluster": "Kümeyi Sırala", + "Sort.ByName": "İsme göre", + "Sort.ByAddress": "Adrese göre", + "Sort.ByType": "Tipe göre", + "Preference.SortIndices": "Indeksleri sırala", + "SortIndices.Descending": "Azalan", + "SortIndices.Ascending": "Artan", + "Preference.ViewAliases": "Alternatif isimleri görüntüle", + "ViewAliases.Grouped": "Gruplanmış", + "ViewAliases.List": "Liste", + "ViewAliases.None": "Karışık", + "Overview.IndexFilter": "Indeks Filtresi", + "TableResults.Summary": "{0} parçanın {1} tanesi arandı. {2} sonuç. {3} saniye", + "QueryFilter.AllIndices": "Tüm Indeksler", + "QueryFilter.AnyValue": "herhangi", + "QueryFilter-Header-Indices": "Indeksler", + "QueryFilter-Header-Types": "Tipler", + "QueryFilter-Header-Fields": "Alanlar", + "QueryFilter.DateRangeHint.from": "{0}'dan", + "QueryFilter.DateRangeHint.to": " {0}'a", + "Query.FailAndUndo": "Sorgu Başarısız. Son değişiklikler geri alınıyor.", + "StructuredQuery.ShowRawJson": "Formatsız JSON göster" +}); + +i18n.setKeys({ + "AnyRequest.TransformerHelp" : "\ +

    Sonuç Dönüştürücü sorgudan dönen JSON sonuçlarını işleyip daha kullanışlı bir formata dönüştürmek için kullanılabilir.

    \ +

    Dönüştürücü içierisinde javascript fonksiyonu tanımlanmalıdır. Bu fonksiyondan dönen yeni sonuç çıktı kısmına yazdırılır.

    \ +

    Örnek:
    \ + return root.hits.hits[0]; sonucu dolaşarak ilk eşleşmeyi göster
    \ + return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); tüm kümede kullanılan toplam belleği gösterir

    \ +

    Aşağıdaki fonksiyonlar dizi ve objelerin işlenmesinde yardımcı olması için kullanılabilir
    \ +

      \ +
    • Object.keys(object) := array
    • \ +
    • array.forEach(function(prop, index))
    • \ +
    • array.map(function(prop, index)) := array
    • \ +
    • array.reduce(function(accumulator, prop, index), initial_value) := final_value
    • \ +
    \ +

    Sorgu tekrarlama çalışırken, prev isimli ekstra bir parametre dönüştürücü fonksiyonuna verilir. Bu sayede karşılaştırmalar ve toplu grafik gösterimleri yapılabilir.

    \ +

    Örnek:
    \ + var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; önceki dakika boyunca kümede bulunan ilk nod üzerindeki averaj yükü verir.\ + Bu sonuç nod için yük grafiği yaratılmasında kullanılabilir.\ + " +}); + +i18n.setKeys({ + "AnyRequest.DisplayOptionsHelp" : "\ +

    Sade Json: Sorgunun tüm sonuçlarını ve (yapıldıysa) dönüştürüldükten sonraki halini sade JSON formatında gösterir

    \ +

    Sonuçları Çizdir: Sonuçları grafiksel olarak görüntülemek için sonuç dörücüyü kullanarak değerleri dizi haline getirin.

    \ +

    Arama Sonuçları Tablosu: Eğer sorgunuz bir arama ise, sonuçları bir tabloda görüntüleyebilirsiniz.

    \ + " +}); + +i18n.setKeys({ + "QueryFilter.DateRangeHelp" : "\ +

    Tarih alanları ana dile yakın kelimeler kullanarak iki tarih aralığında sorgu yapılabilmesini sağlar.

    \ +

    Aşağıdaki tanımlar kullanılabilir:

    \ +
      \ +
    • Anahtar Kelimeler
      \ + now
      today
      tomorrow
      yesterday
      last / this / next + week / month / year

      \ + kelimeleri eşleşen tarihleri verir. Örneğin last year geçen yıl tarihli bütün verileri döndürür.
    • \ +
    • Aralıklar
      \ + 1000 secs
      5mins
      1day
      2days
      80d
      9 months
      2yrs
      (boşluklar isteğe bağlıdır, ayni kelime için farklı yazım şekilleri kullanılabilir)
      \ + Şu anki tarihi (now) baz alarak geçmiş veya ileriki bir tarih aralığındaki kayıtları verir.
    • \ +
    • Tarih ve Kısmi Tarihler
      \ + 2011
      2011-01
      2011-01-18
      2011-01-18 12
      2011-01-18 12:32
      2011-01-18 12:32:45

      \ + bu formatlar spesifik bir tarihi tanımlarlar. 2011 tüm 2011 yılını ararken, 2011-01-18 12:32:45 şeklinde bir sorgu sadece o saniyedeki sonuçları verir.
    • \ +
    • Zaman ve Kısmi Zamanlar
      \ + 12
      12:32
      12:32:45

      \ + bu formatlar gün içerisinde spesifik bir zamanı tanımlarlar. Örneğin 12:32 sadece bu saat ve dakikadaki kayıtları verir.
    • \ +
    • Tarih Aralıkları
      \ + 2010 -> 2011
      last week -> next week
      2011-05 ->
      < now

      \ + Tarih aralıkları yukarda belirtilen herhangi bir formatı < or -> ile ayırarak yapılabilir. Eğer aralığın bir tarafı eksikse, sorgu ucu açıkmış gibi davranır.
    • \ +
    • Ofsetli Tarih Aralığı
      \ + 2010 -> 1yr
      3mins < now
      \ + Verilen yöndeki tarih aralığına bakar.
    • \ +
    • Çakılı Aralıklar
      \ + 2010-05-13 05:13 <> 10m
      now <> 1yr
      lastweek <> 1month

      \ + Yukarıdakiyle ayni fakat belirtilen tarihten her iki yöne de bakılır.
    • \ +
    \ + " +}); diff --git a/elasticsearch/plugins/head/src/app/lang/zh_strings.js b/elasticsearch/plugins/head/src/app/lang/zh_strings.js new file mode 100644 index 00000000..0038de17 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/lang/zh_strings.js @@ -0,0 +1,172 @@ +i18n.setKeys({ + "General.Elasticsearch": "Elasticsearch", + "General.LoadingAggs": "加载聚合查询...", + "General.Searching": "搜索中...", + "General.Search": "搜索", + "General.Help": "帮助", + "General.HelpGlyph": "?", + "General.CloseGlyph": "X", + "General.RefreshResults": "刷新", + "General.ManualRefresh": "手动刷新", + "General.RefreshQuickly": "快速刷新", + "General.Refresh5seconds": "每5秒刷新", + "General.Refresh1minute": "每1分钟刷新", + "AliasForm.AliasName": "别名", + "AliasForm.NewAliasForIndex": "为 {0} 创建新别名", + "AliasForm.DeleteAliasMessage": "输入 ''{0}'' 删除 {1}. 此操作无法恢复", + "AnyRequest.DisplayOptions" : "显示选项", + "AnyRequest.AsGraph" : "图形视图", + "AnyRequest.AsJson" : "原始 JSON", + "AnyRequest.AsTable" : "表格视图", + "AnyRequest.History" : "历史记录", + "AnyRequest.RepeatRequest" : "重复请求", + "AnyRequest.RepeatRequestSelect" : "重复周期 ", + "AnyRequest.Transformer" : "结果转换器", + "AnyRequest.Pretty": "易读", + "AnyRequest.Query" : "查询", + "AnyRequest.Request": "提交请求", + "AnyRequest.Requesting": "请求中...", + "AnyRequest.ValidateJSON": "验证 JSON", + "Browser.Title": "数据浏览", + "Browser.ResultSourcePanelTitle": "原始数据", + "Command.DELETE": "删除", + "Command.SHUTDOWN": "关闭", + "Command.DeleteAliasMessage": "删除别名?", + "ClusterOverView.IndexName": "索引名称", + "ClusterOverview.NumShards": "分片数", + "ClusterOverview.NumReplicas": "副本数", + "ClusterOverview.NewIndex": "新建索引", + "IndexActionsMenu.Title": "动作", + "IndexActionsMenu.NewAlias": "新建别名...", + "IndexActionsMenu.Refresh": "刷新", + "IndexActionsMenu.Flush": "Flush刷新", + "IndexActionsMenu.Optimize": "优化...", + "IndexActionsMenu.Snapshot": "网关快照", + "IndexActionsMenu.Analyser": "测试分析器", + "IndexActionsMenu.Open": "开启", + "IndexActionsMenu.Close": "关闭", + "IndexActionsMenu.Delete": "删除...", + "IndexInfoMenu.Title": "信息", + "IndexInfoMenu.Status": "索引状态", + "IndexInfoMenu.Metadata": "索引信息", + "IndexCommand.TextToAnalyze": "文本分析", + "IndexCommand.ShutdownMessage": "输入 ''{0}'' 以关闭 {1} 节点. 关闭的节点无法从此界面重新启动", + "IndexOverview.PageTitle": "索引概览", + "IndexSelector.NameWithDocs": "{0} ({1} 个文档)", + "IndexSelector.SearchIndexForDocs": "搜索 {0} 的文档, 查询条件:", + "FilterBrowser.OutputType": "返回格式: {0}", + "FilterBrowser.OutputSize": "显示数量: {0}", + "Header.ClusterHealth": "集群健康值: {0} ({1} of {2})", + "Header.ClusterNotConnected": "集群健康值: 未连接", + "Header.Connect": "连接", + "Nav.AnyRequest": "复合查询", + "Nav.Browser": "数据浏览", + "Nav.ClusterHealth": "集群健康值", + "Nav.ClusterState": "群集状态", + "Nav.ClusterNodes": "集群节点", + "Nav.Info": "信息", + "Nav.NodeStats": "节点状态", + "Nav.Overview": "概览", + "Nav.Indices": "索引", + "Nav.Plugins": "插件", + "Nav.Status": "状态", + "Nav.Templates": "模板", + "Nav.StructuredQuery": "基本查询", + "NodeActionsMenu.Title": "动作", + "NodeActionsMenu.Shutdown": "关停...", + "NodeInfoMenu.Title": "信息", + "NodeInfoMenu.ClusterNodeInfo": "集群节点信息", + "NodeInfoMenu.NodeStats": "节点状态", + "NodeType.Client": "节点客户端", + "NodeType.Coord": "协调器", + "NodeType.Master": "主节点", + "NodeType.Tribe": "分支结点", + "NodeType.Worker": "工作节点", + "NodeType.Unassigned": "未分配", + "OptimizeForm.OptimizeIndex": "优化 {0}", + "OptimizeForm.MaxSegments": "最大索引段数", + "OptimizeForm.ExpungeDeletes": "只删除被标记为删除的", + "OptimizeForm.FlushAfter": "优化后刷新", + "OptimizeForm.WaitForMerge": "等待合并", + "Overview.PageTitle" : "集群概览", + "Output.JSON": "JSON", + "Output.Table": "Table", + "Output.CSV": "CSV", + "Output.ShowSource": "显示查询语句", + "Preference.SortCluster": "集群排序", + "Sort.ByName": "按名称", + "Sort.ByAddress": "按地址", + "Sort.ByType": "按类型", + "TableResults.Summary": "查询 {1} 个分片中用的 {0} 个. {2} 命中. 耗时 {3} 秒", + "QueryFilter.AllIndices": "所有索引", + "QueryFilter.AnyValue": "任意", + "QueryFilter-Header-Indices": "索引", + "QueryFilter-Header-Types": "类型", + "QueryFilter-Header-Fields": "字段", + "QueryFilter.DateRangeHint.from": "从 : {0}", + "QueryFilter.DateRangeHint.to": " 到 : {0}", + "Query.FailAndUndo": "查询失败. 撤消最近的更改", + "StructuredQuery.ShowRawJson": "显示原始 JSON" +}); + +i18n.setKeys({ + "AnyRequest.TransformerHelp" : "\ +

    结果转换器用于返回结果原始JSON的后续处理, 将结果转换为更有用的格式.

    \ +

    转换器应当包含javascript函数体. 函数的返回值将传递给json分析器

    \ +

    Example:
    \ + return root.hits.hits[0];
    \ + 遍历结果并只显示第一个元素
    \ + return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0);
    \ + 将返回整个集群使用的总内存

    \ +

    以下函数可以方便的处理数组与对象
    \ +

      \ +
    • Object.keys(object) := array
    • \ +
    • array.forEach(function(prop, index))
    • \ +
    • array.map(function(prop, index)) := array
    • \ +
    • array.reduce(function(accumulator, prop, index), initial_value) := final_value
    • \ +
    \ +

    当启用重复请求时, prev 参数将会传递给转换器函数. 这将用于比较并累加图形

    \ +

    Example:
    \ + var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la;
    \ + 将返回第一个集群节点最近一分钟内的平均负载\ + 将会把结果送人图表以产生一个负载曲线图\ + " +}); + +i18n.setKeys({ + "AnyRequest.DisplayOptionsHelp" : "\ +

    原始 Json: 将完整的查询结果转换为原始JSON格式

    \ +

    图形视图: 将查询结果图形化, 将查询结果转换为数组值的形式

    \ +

    表格视图: 如果查询是一个搜索, 可以将搜索结果以表格形式显示.

    \ + " +}); + +i18n.setKeys({ + "QueryFilter.DateRangeHelp" : "\ +

    Date 字段接受日期范围的形式查询.

    \ +

    一下格式被支持:

    \ +
      \ +
    • 关键词 / 关键短语
      \ + now
      today
      tomorrow
      yesterday
      last / this / next + week / month / year

      \ + 搜索关键字匹配的日期. last year 将搜索过去全年.
    • \ +
    • 范围
      \ + 1000 secs
      5mins
      1day
      2days
      80d
      9 months
      2yrs
      (空格可选, 同等于多个范围修饰词)
      \ + 创建一个指定时间范围的搜索, 将围绕现在 并延伸至过去与未来时间段.
    • \ +
    • DateTime 与 DateTime局部
      \ + 2011
      2011-01
      2011-01-18
      2011-01-18 12
      2011-01-18 12:32
      2011-01-18 12:32:45

      \ + 指定一个特定的日期范围. 2011会搜索整个 2011年, 而 2011-01-18 12:32:45 将只搜索1秒范围内
    • \ +
    • Time 与 Time局部
      \ + 12
      12:32
      12:32:45

      \ + 这些格式只搜索当天的特定时间. 12:32 将搜索当天的那一分钟
    • \ +
    • 日期范围
      \ + 2010 -> 2011
      last week -> next week
      2011-05 ->
      < now

      \ + 日期范围是将两个日期格式串 (日期关键字 / DateTime / Time) 用 < 或 -> (效果相同) 分隔. 如果缺少任意一端,那么在这个方向上时间将没有限制.
    • \ +
    • 偏移日期范围
      \ + 2010 -> 1yr
      3mins < now
      \ + 搜索包括指定方向上偏移的日期.
    • \ +
    • 锚定范围
      \ + 2010-05-13 05:13 <> 10m
      now <> 1yr
      lastweek <> 1month

      \ + 类似于上面的便宜日期,在两个方向上将锚定的日期延长
    • \ +
    \ + " +}); diff --git a/elasticsearch/plugins/head/src/app/services/cluster/cluster.js b/elasticsearch/plugins/head/src/app/services/cluster/cluster.js new file mode 100644 index 00000000..c4ddf655 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/cluster/cluster.js @@ -0,0 +1,47 @@ +(function( $, app ) { + + var services = app.ns("services"); + var ux = app.ns("ux"); + + function parse_version( v ) { + return v.match(/^(\d+)\.(\d+)\.(\d+)/).slice(1,4).map( function(d) { return parseInt(d || 0, 10); } ); + } + + services.Cluster = ux.Class.extend({ + defaults: { + base_uri: null + }, + init: function() { + this.base_uri = this.config.base_uri; + }, + setVersion: function( v ) { + this.version = v; + this._version_parts = parse_version( v ); + }, + versionAtLeast: function( v ) { + var testVersion = parse_version( v ); + for( var i = 0; i < 3; i++ ) { + if( testVersion[i] !== this._version_parts[i] ) { + return testVersion[i] < this._version_parts[i]; + } + } + return true; + }, + request: function( params ) { + return $.ajax( $.extend({ + url: this.base_uri + params.path, + dataType: "json", + error: function(xhr, type, message) { + if("console" in window) { + console.log({ "XHR Error": type, "message": message }); + } + } + }, params) ); + }, + "get": function(path, success) { return this.request( { type: "GET", path: path, success: success } ); }, + "post": function(path, data, success) { return this.request( { type: "POST", path: path, data: data, success: success } ); }, + "put": function(path, data, success) { return this.request( { type: "PUT", path: path, data: data, success: success } ); }, + "delete": function(path, data, success) { return this.request( { type: "DELETE", path: path, data: data, success: success } ); } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/services/cluster/clusterSpec.js b/elasticsearch/plugins/head/src/app/services/cluster/clusterSpec.js new file mode 100644 index 00000000..ff4e8a96 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/cluster/clusterSpec.js @@ -0,0 +1,65 @@ +describe("app.services.Cluster", function() { + + var Cluster = window.app.services.Cluster; + var test = window.test; + + var cluster; + + beforeEach( function() { + cluster = new Cluster({ base_uri: "http://localhost:9200/" }); + }); + + describe( "when it is initialised", function() { + + it("should have a localhost base_uri", function() { + expect( cluster.base_uri ).toBe( "http://localhost:9200/" ); + }); + + it("should have no version", function() { + expect( cluster.version ).toBe( undefined ); + }); + + }); + + describe( "setVersion()", function() { + + it("have a version", function() { + cluster.setVersion( "1.12.3-5" ); + expect( cluster.version ).toBe( "1.12.3-5" ); + }); + + }); + + describe("versionAtLeast()", function() { + var vs = [ "0.0.3", "0.13.5", "0.90.3", "1.0.0", "1.1.0", "1.2.3", "1.12.4.rc2", "13.0.0" ]; + + it("should return true for versions that are less than or equal to the current version", function() { + cluster.setVersion("1.12.5"); + expect( cluster.versionAtLeast("1.12.5" ) ).toBe( true ); + expect( cluster.versionAtLeast("1.12.5rc2" ) ).toBe( true ); + expect( cluster.versionAtLeast("1.12.5-6" ) ).toBe( true ); + expect( cluster.versionAtLeast("1.12.5-6.beta7" ) ).toBe( true ); + expect( cluster.versionAtLeast("1.12.4" ) ).toBe( true ); + expect( cluster.versionAtLeast("0.12.4" ) ).toBe( true ); + expect( cluster.versionAtLeast("1.1.8" ) ).toBe( true ); + + for( var i = 0; i < vs.length - 1; i++ ) { + cluster.setVersion( vs[i+1] ); + expect( cluster.versionAtLeast( vs[i] ) ).toBe( true ); + } + }); + + it("should return false for versions that are greater than the current version", function() { + cluster.setVersion("1.12.5"); + expect( cluster.versionAtLeast("1.12.6" ) ).toBe( false ); + expect( cluster.versionAtLeast("1.13.4" ) ).toBe( false ); + expect( cluster.versionAtLeast("2.0.0" ) ).toBe( false ); + + for( var i = 0; i < vs.length - 1; i++ ) { + cluster.setVersion( vs[i] ); + expect( cluster.versionAtLeast( vs[i+1] ) ).toBe( false ); + } + }); + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/services/clusterState/clusterState.js b/elasticsearch/plugins/head/src/app/services/clusterState/clusterState.js new file mode 100644 index 00000000..da07863f --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/clusterState/clusterState.js @@ -0,0 +1,73 @@ +(function( app ) { + + var services = app.ns("services"); + var ux = app.ns("ux"); + + services.ClusterState = ux.Observable.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.clusterState = null; + this.status = null; + this.nodeStats = null; + this.clusterNodes = null; + }, + refresh: function() { + var self = this, clusterState, status, nodeStats, clusterNodes, clusterHealth; + function updateModel() { + if( clusterState && status && nodeStats && clusterNodes && clusterHealth ) { + this.clusterState = clusterState; + this.status = status; + this.nodeStats = nodeStats; + this.clusterNodes = clusterNodes; + this.clusterHealth = clusterHealth; + this.fire( "data", this ); + } + } + this.cluster.get("_cluster/state", function( data ) { + clusterState = data; + updateModel.call( self ); + }); + this.cluster.get("_stats", function( data ) { + status = data; + updateModel.call( self ); + }); + this.cluster.get("_nodes/stats?all=true", function( data ) { + nodeStats = data; + updateModel.call( self ); + }); + this.cluster.get("_nodes", function( data ) { + clusterNodes = data; + updateModel.call( self ); + }); + this.cluster.get("_cluster/health", function( data ) { + clusterHealth = data; + updateModel.call( self ); + }); + }, + _clusterState_handler: function(state) { + this.clusterState = state; + this.redraw("clusterState"); + }, + _status_handler: function(status) { + this.status = status; + this.redraw("status"); + }, + _clusterNodeStats_handler: function(stats) { + this.nodeStats = stats; + this.redraw("nodeStats"); + }, + _clusterNodes_handler: function(nodes) { + this.clusterNodes = nodes; + this.redraw("clusterNodes"); + }, + _clusterHealth_handler: function(health) { + this.clusterHealth = health; + this.redraw("status"); + } + }); + +})( this.app ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/services/clusterState/clusterStateSpec.js b/elasticsearch/plugins/head/src/app/services/clusterState/clusterStateSpec.js new file mode 100644 index 00000000..657408c3 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/clusterState/clusterStateSpec.js @@ -0,0 +1,62 @@ +describe("app.services.ClusterState", function() { + + var ClusterState = window.app.services.ClusterState; + var test = window.test; + + var c; + var dummyData = {}; + var dataEventCallback; + + function expectAllDataToBeNull() { + expect( c.clusterState ).toBe( null ); + expect( c.status ).toBe( null ); + expect( c.nodeStats ).toBe( null ); + expect( c.clusterNodes ).toBe( null ); + } + + beforeEach( function() { + test.cb.use(); + dataEventCallback = jasmine.createSpy("onData"); + c = new ClusterState({ + cluster: { + get: test.cb.createSpy("get", 1, [ dummyData ] ) + }, + onData: dataEventCallback + }); + }); + + describe( "when it is initialised", function() { + + it("should have null data", function() { + expectAllDataToBeNull(); + }); + + }); + + describe( "when refresh is called", function() { + + beforeEach( function() { + c.refresh(); + }); + + it("should not not update models until all network requests have completed", function() { + test.cb.execOne(); + expectAllDataToBeNull(); + test.cb.execOne(); + expectAllDataToBeNull(); + test.cb.execOne(); + expectAllDataToBeNull(); + test.cb.execOne(); + expect( c.clusterState ).toBe( dummyData ); + expect( c.status ).toBe( dummyData ); + expect( c.nodeStats ).toBe( dummyData ); + expect( c.clusterNodes ).toBe( dummyData ); + }); + + it("should fire a 'data' event when all data is ready", function() { + test.cb.execAll(); + expect( dataEventCallback ).toHaveBeenCalledWith( c ); + }); + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/services/preferences/preferenceSpec.js b/elasticsearch/plugins/head/src/app/services/preferences/preferenceSpec.js new file mode 100644 index 00000000..968815d4 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/preferences/preferenceSpec.js @@ -0,0 +1,23 @@ +describe("app.services.Preferences", function(){ + +var Preferences = window.app.services.Preferences; + + var prefs; + + beforeEach( function() { + spyOn(window.localStorage, "getItem").and.returnValue( '{"foo":true}' ); + spyOn(window.localStorage, "setItem"); + prefs = Preferences.instance(); + }); + + it("should return a preference from localStorage", function() { + expect( prefs.get("foo") ).toEqual( {foo:true} ); + }); + + it("should set a preference in localStorage", function() { + prefs.set("foo", { foo: false } ); + expect( window.localStorage.setItem ).toHaveBeenCalledWith('foo', '{"foo":false}'); + }); + + +}); diff --git a/elasticsearch/plugins/head/src/app/services/preferences/preferences.js b/elasticsearch/plugins/head/src/app/services/preferences/preferences.js new file mode 100644 index 00000000..a45eb966 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/services/preferences/preferences.js @@ -0,0 +1,35 @@ +(function( app ) { + + var ux = app.ns("ux"); + var services = app.ns("services"); + + services.Preferences = ux.Singleton.extend({ + init: function() { + this._storage = window.localStorage; + this._setItem("__version", 1 ); + }, + get: function( key ) { + return this._getItem( key ); + }, + set: function( key, val ) { + return this._setItem( key, val ); + }, + _getItem: function( key ) { + try { + return JSON.parse( this._storage.getItem( key ) ); + } catch(e) { + console.warn( e ); + return undefined; + } + }, + _setItem: function( key, val ) { + try { + return this._storage.setItem( key, JSON.stringify( val ) ); + } catch(e) { + console.warn( e ); + return undefined; + } + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.css b/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.css new file mode 100644 index 00000000..3042e7b3 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.css @@ -0,0 +1 @@ +.require { color: #a00; } diff --git a/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.js b/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.js new file mode 100644 index 00000000..3d629b78 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/abstractField/abstractField.js @@ -0,0 +1,49 @@ +(function( $, app, joey ) { + + var ui = app.ns("ui"); + + ui.AbstractField = ui.AbstractWidget.extend({ + + defaults: { + name : "", // (required) - name of the field + require: false, // validation requirements (false, true, regexp, function) + value: "", // default value + label: "" // human readable label of this field + }, + + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + this.field = this.el.find("[name="+this.config.name+"]"); + this.label = this.config.label; + this.require = this.config.require; + this.name = this.config.name; + this.val( this.config.value ); + this.attach( parent ); + }, + + val: function( val ) { + if(val === undefined) { + return this.field.val(); + } else { + this.field.val( val ); + return this; + } + }, + + validate: function() { + var val = this.val(), req = this.require; + if( req === false ) { + return true; + } else if( req === true ) { + return val.length > 0; + } else if( req.test && $.isFunction(req.test) ) { + return req.test( val ); + } else if( $.isFunction(req) ) { + return req( val, this ); + } + } + + }); + +})( this.jQuery, this.app, this.joey ); diff --git a/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.css b/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.css new file mode 100644 index 00000000..6cce209b --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.css @@ -0,0 +1,43 @@ +#uiModal { + background: black; +} + +.uiPanel { + box-shadow: -1px 2.5px 4px -3px black, -1px -2.5px 4px -3px black, 3px 2.5px 4px -3px black, 3px -2.5px 4px -3px black; + position: absolute; + background: #eee; + border: 1px solid #666; +} + +.uiPanel-titleBar { + text-align: center; + font-weight: bold; + padding: 2px 0; + background: rgba(223, 223, 223, 0.75); + background: -moz-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); + background: -webkit-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); + border-bottom: 1px solid #bbb; +} + +.uiPanel-close { + cursor: pointer; + border: 1px solid #aaa; + background: #fff; + color: #fff; + float: left; + height: 10px; + left: 3px; + line-height: 9px; + padding: 1px 0; + position: relative; + text-shadow: 0 0 1px #000; + top: 0px; + width: 12px; +} +.uiPanel-close:hover { + background: #eee; +} + +.uiPanel-body { + overflow: auto; +} diff --git a/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.js b/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.js new file mode 100644 index 00000000..17f62a79 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/abstractPanel/abstractPanel.js @@ -0,0 +1,83 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.AbstractPanel = ui.AbstractWidget.extend({ + defaults: { + body: null, // initial content of the body + modal: true, // create a modal panel - creates a div that blocks interaction with page + height: 'auto', // panel height + width: 400, // panel width (in pixels) + open: false, // show the panel when it is created + parent: 'BODY', // node that panel is attached to + autoRemove: false // remove the panel from the dom and destroy it when the widget is closed + }, + shared: { // shared data for all instances of ui.Panel and decendants + stack: [], // array of all open panels + modal: $( { tag: "DIV", id: "uiModal", css: { opacity: 0.2, position: "absolute", top: "0px", left: "0px" } } ) + }, + init: function() { + this._super(); + }, + open: function( ev ) { + this.el + .css( { visibility: "hidden" } ) + .appendTo( this.config.parent ) + .css( this._getPosition( ev ) ) + .css( { zIndex: (this.shared.stack.length ? (+this.shared.stack[this.shared.stack.length - 1].el.css("zIndex") + 10) : 100) } ) + .css( { visibility: "visible", display: "block" } ); + this.shared.stack.remove(this); + this.shared.stack.push(this); + this._setModal(); + $(document).bind("keyup", this._close_handler ); + this.fire("open", { source: this, event: ev } ); + return this; + }, + close: function() { + var index = this.shared.stack.indexOf(this); + if(index !== -1) { + this.shared.stack.splice(index, 1); + this.el.css( { left: "-2999px" } ); // move the dialog to the left rather than hiding to prevent ie6 rendering artifacts + this._setModal(); + this.fire("close", this ); + if(this.config.autoRemove) { + this.remove(); + } + } + return this; + }, + // close the panel and remove it from the dom, destroying it (you can not reuse the panel after calling remove) + remove: function() { + this.close(); + $(document).unbind("keyup", this._close_handler ); + this._super(); + }, + // starting at the top of the stack, find the first panel that wants a modal and put it just underneath, otherwise remove the modal + _setModal: function() { + for(var stackPtr = this.shared.stack.length - 1; stackPtr >= 0; stackPtr--) { + if(this.shared.stack[stackPtr].config.modal) { + this.shared.modal + .appendTo( document.body ) + .css( { zIndex: this.shared.stack[stackPtr].el.css("zIndex") - 5 } ) + .css( $(document).vSize().asSize() ); + return; + } + } + this.shared.modal.remove(); // no panels that want a modal were found + }, + _getPosition: function() { + return $(window).vSize() // get the current viewport size + .sub(this.el.vSize()) // subtract the size of the panel + .mod(function(s) { return s / 2; }) // divide by 2 (to center it) + .add($(document).vScroll()) // add the current scroll offset + .mod(function(s) { return Math.max(5, s); }) // make sure the panel is not off the edge of the window + .asOffset(); // and return it as a {top, left} object + }, + _close_handler: function( ev ) { + if( ev.type === "keyup" && ev.keyCode !== 27) { return; } // press esc key to close + $(document).unbind("keyup", this._close_handler); + this.close( ev ); + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/abstractWidget/abstractWidget.js b/elasticsearch/plugins/head/src/app/ui/abstractWidget/abstractWidget.js new file mode 100644 index 00000000..bf3b4c8b --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/abstractWidget/abstractWidget.js @@ -0,0 +1,49 @@ +(function( $, joey, app ) { + + var ui = app.ns("ui"); + var ux = app.ns("ux"); + + ui.AbstractWidget = ux.Observable.extend({ + defaults : { + id: null // the id of the widget + }, + + el: null, // this is the jquery wrapped dom element(s) that is the root of the widget + + init: function() { + this._super(); + for(var prop in this) { // automatically bind all the event handlers + if(prop.contains("_handler")) { + this[prop] = this[prop].bind(this); + } + } + }, + + id: function(suffix) { + return this.config.id ? (this.config.id + (suffix ? "-" + suffix : "")) : undefined; + }, + + attach: function( parent, method ) { + if( parent ) { + this.el[ method || "appendTo"]( parent ); + } + this.fire("attached", this ); + return this; + }, + + remove: function() { + this.el.remove(); + this.fire("removed", this ); + this.removeAllObservers(); + this.el = null; + return this; + } + }); + + joey.plugins.push( function( obj ) { + if( obj instanceof ui.AbstractWidget ) { + return obj.el[0]; + } + }); + +})( this.jQuery, this.joey, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.css b/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.css new file mode 100644 index 00000000..f00412cf --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.css @@ -0,0 +1,38 @@ +.uiAnyRequest-request { + float: left; + width: 350px; + padding: 5px; + background: #d8e7ff; + background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); + background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); +} + +.uiAnyRequest-request INPUT[type=text], +.uiAnyRequest-request TEXTAREA { + width: 340px; +} + +.anyRequest INPUT[name=path] { + width: 259px; +} + +.uiAnyRequest-out { + margin-left: 365px; +} + +.uiAnyRequest-out P { + margin-top: 0; +} + +.uiAnyRequest-jsonErr { + color: red; +} + +.uiAnyRequest-history { + margin: 0; + padding: 0; + list-style: none; + max-height: 100px; + overflow-x: hidden; + overflow-y: auto; +} diff --git a/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.js b/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.js new file mode 100644 index 00000000..926bbcce --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/anyRequest/anyRequest.js @@ -0,0 +1,235 @@ +(function( $, app, i18n, raphael ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + var services = app.ns("services"); + + ui.AnyRequest = ui.Page.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + path: "_search", // default uri to send a request to + query: { query: { match_all: { }}}, + transform: " return root;" // default transformer function (does nothing) + }, + init: function(parent) { + this._super(); + this.prefs = services.Preferences.instance(); + this.history = this.prefs.get("anyRequest-history") || [ { type: "POST", path: this.config.path, query : JSON.stringify(this.config.query), transform: this.config.transform } ]; + this.el = $.joey(this._main_template()); + this.base_uriEl = this.el.find("INPUT[name=base_uri]"); + this.pathEl = this.el.find("INPUT[name=path]"); + this.typeEl = this.el.find("SELECT[name=method]"); + this.dataEl = this.el.find("TEXTAREA[name=body]"); + this.prettyEl = this.el.find("INPUT[name=pretty]"); + this.transformEl = this.el.find("TEXTAREA[name=transform]"); + this.asGraphEl = this.el.find("INPUT[name=asGraph]"); + this.asTableEl = this.el.find("INPUT[name=asTable]"); + this.asJsonEl = this.el.find("INPUT[name=asJson]"); + this.cronEl = this.el.find("SELECT[name=cron]"); + this.outEl = this.el.find("DIV.uiAnyRequest-out"); + this.errEl = this.el.find("DIV.uiAnyRequest-jsonErr"); + this.typeEl.val("GET"); + this.attach(parent); + this.setHistoryItem(this.history[this.history.length - 1]); + }, + setHistoryItem: function(item) { + this.pathEl.val(item.path); + this.typeEl.val(item.type); + this.dataEl.val(item.query); + this.transformEl.val(item.transform); + }, + _request_handler: function( ev ) { + if(! this._validateJson_handler()) { + return; + } + var path = this.pathEl.val(), + type = this.typeEl.val(), + query = JSON.stringify(JSON.parse(this.dataEl.val())), + transform = this.transformEl.val(), + base_uri = this.base_uriEl.val(); + if( ev ) { // if the user click request + if(this.timer) { + window.clearTimeout(this.timer); // stop any cron jobs + } + delete this.prevData; // remove data from previous cron runs + this.outEl.text(i18n.text("AnyRequest.Requesting")); + if( ! /\/$/.test( base_uri )) { + base_uri += "/"; + this.base_uriEl.val( base_uri ); + } + for(var i = 0; i < this.history.length; i++) { + if(this.history[i].path === path && + this.history[i].type === type && + this.history[i].query === query && + this.history[i].transform === transform) { + this.history.splice(i, 1); + } + } + this.history.push({ + path: path, + type: type, + query: query, + transform: transform + }); + this.history.slice(250); // make sure history does not get too large + this.prefs.set( "anyRequest-history", this.history ); + this.el.find("UL.uiAnyRequest-history") + .empty() + .append($( { tag: "UL", children: this.history.map(this._historyItem_template, this) }).children()) + .children().find(":last-child").each(function(i, j) { j.scrollIntoView(false); }).end() + .scrollLeft(0); + } + this.config.cluster.request({ + url: base_uri + path, + type: type, + data: query, + success: this._responseWriter_handler, + error: this._responseError_handler + }); + }, + _responseError_handler: function (response) { + var obj; + try { + obj = JSON.parse(response.responseText); + if (obj) { + this._responseWriter_handler(obj); + } + } catch (err) { + } + }, + _responseWriter_handler: function(data) { + this.outEl.empty(); + try { + data = (new Function("root", "prev", this.transformEl.val()))(data, this.prevData) + } catch(e) { + this.errEl.text(e.message); + return; + } + if(this.asGraphEl.attr("checked")) { + var w = this.outEl.width(); + raphael(this.outEl[0], w - 10, 300) + .g.barchart(10, 10, w - 20, 280, [data]); + } + if(this.asTableEl.attr("checked")) { + try { + var store = new app.data.ResultDataSourceInterface(); + this.outEl.append(new app.ui.ResultTable({ + width: this.outEl.width() - 23, + store: store + } ) ); + store.results(data); + } catch(e) { + this.errEl.text("Results Table Failed: " + e.message); + } + } + if(this.asJsonEl.attr("checked")) { + this.outEl.append(new ui.JsonPretty({ obj: data })); + } + if(this.cronEl.val() > 0) { + this.timer = window.setTimeout(function(){ + this._request_handler(); + }.bind(this), this.cronEl.val()); + } + this.prevData = data; + }, + _validateJson_handler: function( ev ) { + /* if the textarea is empty, we replace its value by an empty JSON object : "{}" and the request goes on as usual */ + var jsonData = this.dataEl.val().trim(); + var j; + if(jsonData === "") { + jsonData = "{}"; + this.dataEl.val( jsonData ); + } + try { + j = JSON.parse(jsonData); + } catch(e) { + this.errEl.text(e.message); + return false; + } + this.errEl.text(""); + if(this.prettyEl.attr("checked")) { + this.dataEl.val(JSON.stringify(j, null, " ")); + } + return true; + }, + _historyClick_handler: function( ev ) { + var item = $( ev.target ).closest( "LI" ).data( "item" ); + this.setHistoryItem( item ); + }, + _main_template: function() { + return { tag: "DIV", cls: "anyRequest", children: [ + { tag: "DIV", cls: "uiAnyRequest-request", children: [ + new app.ui.SidebarSection({ + open: false, + title: i18n.text("AnyRequest.History"), + body: { tag: "UL", onclick: this._historyClick_handler, cls: "uiAnyRequest-history", children: this.history.map(this._historyItem_template, this) } + }), + new app.ui.SidebarSection({ + open: true, + title: i18n.text("AnyRequest.Query"), + body: { tag: "DIV", children: [ + { tag: "INPUT", type: "text", name: "base_uri", value: this.config.cluster.config.base_uri }, + { tag: "BR" }, + { tag: "INPUT", type: "text", name: "path", value: this.config.path }, + { tag: "SELECT", name: "method", children: ["POST", "GET", "PUT", "HEAD", "DELETE"].map(ut.option_template) }, + { tag: "TEXTAREA", name: "body", rows: 20, text: JSON.stringify(this.config.query) }, + { tag: "BUTTON", css: { cssFloat: "right" }, type: "button", children: [ { tag: "B", text: i18n.text("AnyRequest.Request") } ], onclick: this._request_handler }, + { tag: "BUTTON", type: "button", text: i18n.text("AnyRequest.ValidateJSON"), onclick: this._validateJson_handler }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "pretty" }, i18n.text("AnyRequest.Pretty") ] }, + { tag: "DIV", cls: "uiAnyRequest-jsonErr" } + ]} + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.Transformer"), + help: "AnyRequest.TransformerHelp", + body: { tag: "DIV", children: [ + { tag: "CODE", text: "function(root, prev) {" }, + { tag: "BR" }, + { tag: "TEXTAREA", name: "transform", rows: 5, text: this.config.transform }, + { tag: "BR" }, + { tag: "CODE", text: "}" } + ] } + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.RepeatRequest"), + body: { tag: "DIV", children: [ + i18n.text("AnyRequest.RepeatRequestSelect"), " ", + { tag: "SELECT", name: "cron", children: [ + { value: 0, text: "do not repeat" }, + { value: 1000, text: "second" }, + { value: 1000 * 2, text: "2 seconds" }, + { value: 1000 * 5, text: "5 seconds" }, + { value: 1000 * 20, text: "20 seconds" }, + { value: 1000 * 60, text: "minute" }, + { value: 1000 * 60 * 10, text: "10 minutes" }, + { value: 1000 * 60 * 60, text: "hour" } + ].map(function(op) { return $.extend({ tag: "OPTION"}, op); }) } + ] } + }), + new app.ui.SidebarSection({ + title: i18n.text("AnyRequest.DisplayOptions"), + help: "AnyRequest.DisplayOptionsHelp", + body: { tag: "DIV", children: [ + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", checked: true, name: "asJson" }, i18n.text("AnyRequest.AsJson") ] }, + { tag: "BR" }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "asGraph" }, i18n.text("AnyRequest.AsGraph") ] }, + { tag: "BR" }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", name: "asTable" }, i18n.text("AnyRequest.AsTable") ] } + ] } + }) + ] }, + { tag: "DIV", cls: "uiAnyRequest-out" } + ] }; + }, + _historyItem_template: function(item) { + return { tag: "LI", cls: "booble", data: { item: item }, children: [ + { tag: "SPAN", text: item.path }, + " ", + { tag: "EM", text: item.query }, + " ", + { tag: "SPAN", text: item.transform } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n, this.Raphael ); diff --git a/elasticsearch/plugins/head/src/app/ui/browser/browser.css b/elasticsearch/plugins/head/src/app/ui/browser/browser.css new file mode 100644 index 00000000..66e2110a --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/browser/browser.css @@ -0,0 +1,7 @@ +.uiBrowser-filter { + float: left; +} + +.uiBrowser-table { + margin-left: 365px; +} diff --git a/elasticsearch/plugins/head/src/app/ui/browser/browser.js b/elasticsearch/plugins/head/src/app/ui/browser/browser.js new file mode 100644 index 00000000..329aa4d6 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/browser/browser.js @@ -0,0 +1,58 @@ +(function( $, app, i18n ){ + + var ui = app.ns("ui"); + var data = app.ns("data"); + + ui.Browser = ui.Page.extend({ + defaults: { + cluster: null // (required) instanceof app.services.Cluster + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.query = new app.data.Query( { cluster: this.cluster } ); + this._refreshButton = new ui.Button({ + label: i18n.text("General.RefreshResults"), + onclick: function( btn ) { + this.query.query(); + }.bind(this) + }); + this.el = $(this._main_template()); + new data.MetaDataFactory({ + cluster: this.cluster, + onReady: function(metadata) { + this.metadata = metadata; + this.store = new data.QueryDataSourceInterface( { metadata: metadata, query: this.query } ); + this.queryFilter = new ui.QueryFilter({ metadata: metadata, query: this.query }); + this.queryFilter.attach(this.el.find("> .uiBrowser-filter") ); + this.resultTable = new ui.ResultTable( { + onHeaderClick: this._changeSort_handler, + store: this.store + } ); + this.resultTable.attach( this.el.find("> .uiBrowser-table") ); + this.updateResults(); + }.bind(this) + }); + }, + updateResults: function() { + this.query.query(); + }, + _changeSort_handler: function(table, wEv) { + this.query.setSort(wEv.column, wEv.dir === "desc"); + this.query.setPage(1); + this.query.query(); + }, + _main_template: function() { + return { tag: "DIV", cls: "uiBrowser", children: [ + new ui.Toolbar({ + label: i18n.text("Browser.Title"), + left: [ ], + right: [ this._refreshButton ] + }), + { tag: "DIV", cls: "uiBrowser-filter" }, + { tag: "DIV", cls: "uiBrowser-table" } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/button/button.css b/elasticsearch/plugins/head/src/app/ui/button/button.css new file mode 100644 index 00000000..17123dcd --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/button/button.css @@ -0,0 +1,50 @@ +.uiButton { + padding: 0; + border: 0; + margin: 3px; + width: auto; + overflow: visible; + cursor: pointer; + background: transparent; +} + +.uiButton-content { + height: 20px; + border: 1px solid #668dc6; + border-radius: 2px; + background: #96c6eb; + background: -moz-linear-gradient(top, #96c6eb, #5296c7); + background: -webkit-linear-gradient(top, #96c6eb, #5296c7); + color: white; + font-weight: bold; +} + +.moz .uiButton-content { margin: 0 -2px; } + +.uiButton-label { + padding: 2px 6px; + white-space: nowrap; +} +.uiButton:hover .uiButton-content { + background: #2777ba; + background: -moz-linear-gradient(top, #6aaadf, #2777ba); + background: -webkit-linear-gradient(top, #6aaadf, #2777ba); +} +.uiButton.active .uiButton-content, +.uiButton:active .uiButton-content { + background: #2575b7; + background: -moz-linear-gradient(top, #2576b8, #2575b7); + background: -webkit-linear-gradient(top, #2576b8, #2575b7); +} +.uiButton.disabled .uiButton-content, +.uiButton.disabled:active .uiButton-content { + border-color: #c6c6c6; + color: #999999; + background: #ddd; + background: -moz-linear-gradient(top, #ddd, #ddd); + background: -webkit-linear-gradient(top, #ddd, #ddd); +} + +.uiButton.disabled { + cursor: default; +} diff --git a/elasticsearch/plugins/head/src/app/ui/button/button.js b/elasticsearch/plugins/head/src/app/ui/button/button.js new file mode 100644 index 00000000..57f83a08 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/button/button.js @@ -0,0 +1,53 @@ +(function( $, joey, app ) { + + var ui = app.ns("ui"); + + ui.Button = ui.AbstractWidget.extend({ + defaults : { + label: "", // the label text + disabled: false, // create a disabled button + autoDisable: false // automatically disable the button when clicked + }, + + _baseCls: "uiButton", + + init: function(parent) { + this._super(); + this.el = $.joey(this.button_template()) + .bind("click", this.click_handler); + this.config.disabled && this.disable(); + this.attach( parent ); + }, + + click_handler: function(jEv) { + if(! this.disabled) { + this.fire("click", jEv, this); + this.config.autoDisable && this.disable(); + } + }, + + enable: function() { + this.el.removeClass("disabled"); + this.disabled = false; + return this; + }, + + disable: function(disable) { + if(disable === false) { + return this.enable(); + } + this.el.addClass("disabled"); + this.disabled = true; + return this; + }, + + button_template: function() { return ( + { tag: 'BUTTON', type: 'button', id: this.id(), cls: this._baseCls, children: [ + { tag: 'DIV', cls: 'uiButton-content', children: [ + { tag: 'DIV', cls: 'uiButton-label', text: this.config.label } + ] } + ] } + ); } + }); + +})( this.jQuery, this.joey, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/button/buttonDemo.js b/elasticsearch/plugins/head/src/app/ui/button/buttonDemo.js new file mode 100644 index 00000000..060e8f65 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/button/buttonDemo.js @@ -0,0 +1,9 @@ +$( function() { + + var ui = window.app.ns("ui"); + + window.builder = function() { + return new ui.Button({ label: "Default" }); + } ; + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/checkField/checkField.js b/elasticsearch/plugins/head/src/app/ui/checkField/checkField.js new file mode 100644 index 00000000..d5f50127 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/checkField/checkField.js @@ -0,0 +1,25 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.CheckField = ui.AbstractField.extend({ + _main_template: function() { return ( + { tag: "DIV", id: this.id(), cls: "uiCheckField", children: [ + { tag: "INPUT", type: "checkbox", name: this.config.name, checked: !!this.config.value } + ] } + ); }, + validate: function() { + return this.val() || ( ! this.require ); + }, + val: function( val ) { + if( val === undefined ) { + return !!this.field.attr( "checked" ); + } else { + this.field.attr( "checked", !!val ); + } + } + }); + +})( this.app ); + + diff --git a/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldDemo.js b/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldDemo.js new file mode 100644 index 00000000..9a3b3cae --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldDemo.js @@ -0,0 +1,45 @@ +$( function() { + + var ui = window.app.ns("ui"); + var ux = window.app.ns("ux"); + var ut = window.app.ns("ut"); + + window.builder = function() { + var form = new ux.FieldCollection({ + fields: [ + new ui.CheckField({ + label: "default", + name: "check_default" + }), + new ui.CheckField({ + label: "checked", + name: "check_true", + value: true + }), + new ui.CheckField({ + label: "unchecked", + name: "check_false", + value: false + }), + new ui.CheckField({ + label: "required", + name: "check_required", + require: true + }) + ] + }); + + return ( + { tag: "DIV", children: form.fields.map( function( field ) { + return { tag: "LABEL", cls: "uiPanelForm-field", children: [ + { tag: "DIV", cls: "uiPanelForm-label", children: [ field.label, ut.require_template(field) ] }, + field + ]}; + }).concat( new ui.Button({ + label: "Evaluate Form", + onclick: function() { console.log( "valid=" + form.validate(), form.getData() ); } + })) } + ); + }; + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldSpec.js b/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldSpec.js new file mode 100644 index 00000000..26ddc6f6 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/checkField/checkFieldSpec.js @@ -0,0 +1,34 @@ +describe("app.ui.CheckField", function() { + + var CheckField = window.app.ui.CheckField; + + it("should have a label", function() { + expect( ( new CheckField({ label: "foo" }) ).label ).toBe( "foo" ); + }); + + it("should have a name", function() { + expect( ( new CheckField({ name: "foo" }) ).name ).toBe( "foo" ); + }); + + it("should have a val that is false when then field is not checked", function() { + expect( ( new CheckField({ name: "foo", value: false }) ).val() ).toBe( false ); + }); + + it("should have a val that is true when the field is checked", function() { + expect( ( new CheckField({ name: "foo", value: true }) ).val() ).toBe( true ); + }); + + it("should be valid if the field value is true", function() { + expect( ( new CheckField({ name: "foo", value: true }) ).validate() ).toBe( true ); + }); + + it("should be valid if require is false", function() { + expect( ( new CheckField({ name: "foo", require: false, value: true }) ).validate() ).toBe( true ); + expect( ( new CheckField({ name: "foo", require: false, value: false }) ).validate() ).toBe( true ); + }); + + it("should be invalid if require is true and value is false", function() { + expect( ( new CheckField({ name: "foo", require: true, value: false }) ).validate() ).toBe( false ); + }); + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.css b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.css new file mode 100644 index 00000000..e5b8fb45 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.css @@ -0,0 +1,3 @@ +.uiClusterConnect-uri { + width: 280px; +} diff --git a/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.js b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.js new file mode 100644 index 00000000..63d09772 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnect.js @@ -0,0 +1,43 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + ui.ClusterConnect = ui.AbstractWidget.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.prefs = services.Preferences.instance(); + this.cluster = this.config.cluster; + this.el = $.joey(this._main_template()); + this.cluster.get( "", this._node_handler ); + }, + + _node_handler: function(data) { + if(data) { + this.prefs.set("app-base_uri", this.cluster.base_uri); + } + }, + + _reconnect_handler: function() { + var base_uri = this.el.find(".uiClusterConnect-uri").val(); + $("body").empty().append(new app.App("body", { id: "es", base_uri: base_uri })); + }, + + _main_template: function() { + return { tag: "SPAN", cls: "uiClusterConnect", children: [ + { tag: "INPUT", type: "text", cls: "uiClusterConnect-uri", onkeyup: function( ev ) { + if(ev.which === 13) { + ev.preventDefault(); + this._reconnect_handler(); + } + }.bind(this), id: this.id("baseUri"), value: this.cluster.base_uri }, + { tag: "BUTTON", type: "button", text: i18n.text("Header.Connect"), onclick: this._reconnect_handler } + ]}; + } + }); + +})( this.jQuery, this.app, this.i18n ); + diff --git a/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnectSpec.js b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnectSpec.js new file mode 100644 index 00000000..8b8be82d --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/clusterConnect/clusterConnectSpec.js @@ -0,0 +1,38 @@ +describe("clusterConnect", function() { + + var ClusterConnect = window.app.ui.ClusterConnect; + + describe("when created", function() { + + var prefs, success_callback, cluster, clusterConnect; + + beforeEach( function() { + prefs = { + set: jasmine.createSpy("set") + }; + spyOn( window.app.services.Preferences, "instance" ).and.callFake( function() { + return prefs; + }); + cluster = { + get: jasmine.createSpy("get").and.callFake( function(uri, success) { + success_callback = success; + }) + }; + clusterConnect = new ClusterConnect({ + base_uri: "http://localhost:9200", + cluster: cluster + }); + }); + + it("should test the connection to the cluster", function() { + expect( cluster.get ).toHaveBeenCalled(); + }); + + it("should store successful connection in preferences", function() { + success_callback("fakePayload"); + expect( prefs.set ).toHaveBeenCalled(); + }); + + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.css b/elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.css new file mode 100644 index 00000000..e69de29b diff --git a/elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.js b/elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.js new file mode 100644 index 00000000..745ab42e --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/clusterOverview/clusterOverview.js @@ -0,0 +1,302 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var services = app.ns("services"); + + // ( master ) master = true, data = true + // ( coordinator ) master = true, data = false + // ( worker ) master = false, data = true; + // ( client ) master = false, data = false; + // http enabled ? + + function nodeSort_name(a, b) { + if (!(a.cluster && b.cluster)) { + return 0; + } + return a.cluster.name.toString().localeCompare( b.cluster.name.toString() ); + } + + function nodeSort_addr( a, b ) { + if (!(a.cluster && b.cluster)) { + return 0; + } + return a.cluster.transport_address.toString().localeCompare( b.cluster.transport_address.toString() ); + } + + function nodeSort_type( a, b ) { + if (!(a.cluster && b.cluster)) { + return 0; + } + if( a.master_node ) { + return -1; + } else if( b.master_node ) { + return 1; + } else if( a.data_node && !b.data_node ) { + return -1; + } else if( b.data_node && !a.data_node ) { + return 1; + } else { + return a.cluster.name.toString().localeCompare( b.cluster.name.toString() ); + } + } + + var NODE_SORT_TYPES = { + "Sort.ByName": nodeSort_name, + "Sort.ByAddress": nodeSort_addr, + "Sort.ByType": nodeSort_type + }; + + function nodeFilter_none( a ) { + return true; + } + + function nodeFilter_clients( a ) { + return (a.master_node || a.data_node ); + } + + + ui.ClusterOverview = ui.Page.extend({ + defaults: { + cluster: null // (reqired) an instanceof app.services.Cluster + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this.prefs = services.Preferences.instance(); + this._clusterState = this.config.clusterState; + this._clusterState.on("data", this.draw_handler ); + this._refreshButton = new ui.RefreshButton({ + onRefresh: this.refresh.bind(this), + onChange: function( btn ) { + if( btn.value === -1 ) { + this.draw_handler(); + } + }.bind( this ) + }); + var nodeSortPref = this.prefs.get("clusterOverview-nodeSort") || Object.keys(NODE_SORT_TYPES)[0]; + this._nodeSort = NODE_SORT_TYPES[ nodeSortPref ]; + this._nodeSortMenu = new ui.MenuButton({ + label: i18n.text( "Preference.SortCluster" ), + menu: new ui.SelectMenuPanel({ + value: nodeSortPref, + items: Object.keys( NODE_SORT_TYPES ).map( function( k ) { + return { text: i18n.text( k ), value: k }; + }), + onSelect: function( panel, event ) { + this._nodeSort = NODE_SORT_TYPES[ event.value ]; + this.prefs.set("clusterOverview-nodeSort", event.value ); + this.draw_handler(); + }.bind(this) + }) + }); + this._indicesSort = this.prefs.get( "clusterOverview-indicesSort") || "desc"; + this._indicesSortMenu = new ui.MenuButton({ + label: i18n.text( "Preference.SortIndices" ), + menu: new ui.SelectMenuPanel({ + value: this._indicesSort, + items: [ + { value: "desc", text: i18n.text( "SortIndices.Descending" ) }, + { value: "asc", text: i18n.text( "SortIndices.Ascending" ) } ], + onSelect: function( panel, event ) { + this._indicesSort = event.value; + this.prefs.set( "clusterOverview-indicesSort", this._indicesSort ); + this.draw_handler(); + }.bind(this) + }) + }); + this._aliasRenderer = this.prefs.get( "clusterOverview-aliasRender" ) || "full"; + this._aliasMenu = new ui.MenuButton({ + label: i18n.text( "Preference.ViewAliases" ), + menu: new ui.SelectMenuPanel({ + value: this._aliasRenderer, + items: [ + { value: "full", text: i18n.text( "ViewAliases.Grouped" ) }, + { value: "list", text: i18n.text( "ViewAliases.List" ) }, + { value: "none", text: i18n.text( "ViewAliases.None" ) } ], + onSelect: function( panel, event ) { + this._aliasRenderer = event.value; + this.prefs.set( "clusterOverview-aliasRender", this._aliasRenderer ); + this.draw_handler(); + }.bind(this) + }) + }); + this._indexFilter = new ui.TextField({ + value: this.prefs.get("clusterOverview-indexFilter"), + placeholder: i18n.text( "Overview.IndexFilter" ), + onchange: function( indexFilter ) { + this.prefs.set("clusterOverview-indexFilter", indexFilter.val() ); + this.draw_handler(); + }.bind(this) + }); + this.el = $(this._main_template()); + this.tablEl = this.el.find(".uiClusterOverview-table"); + this.refresh(); + }, + remove: function() { + this._clusterState.removeObserver( "data", this.draw_handler ); + }, + refresh: function() { + this._refreshButton.disable(); + this._clusterState.refresh(); + }, + draw_handler: function() { + var data = this._clusterState; + var indexFilter; + try { + var indexFilterRe = new RegExp( this._indexFilter.val() ); + indexFilter = function(s) { return indexFilterRe.test(s); }; + } catch(e) { + indexFilter = function() { return true; }; + } + var clusterState = data.clusterState; + var status = data.status; + var nodeStats = data.nodeStats; + var clusterNodes = data.clusterNodes; + var nodes = []; + var indices = []; + var cluster = {}; + var nodeIndices = {}; + var indexIndices = {}, indexIndicesIndex = 0; + function newNode(n) { + return { + name: n, + routings: [], + master_node: clusterState.master_node === n + }; + } + function newIndex(i) { + return { + name: i, + replicas: [] + }; + } + function getIndexForNode(n) { + return nodeIndices[n] = (n in nodeIndices) ? nodeIndices[n] : nodes.push(newNode(n)) - 1; + } + function getIndexForIndex(routings, i) { + var index = indexIndices[i] = (i in indexIndices) ? + (routings[indexIndices[i]] = routings[indexIndices[i]] || newIndex(i)) && indexIndices[i] + : ( ( routings[indexIndicesIndex] = newIndex(i) ) && indexIndicesIndex++ ); + indices[index] = i; + return index; + } + $.each(clusterNodes.nodes, function(name, node) { + getIndexForNode(name); + }); + + var indexNames = []; + $.each(clusterState.routing_table.indices, function(name, index){ + indexNames.push(name); + }); + indexNames.sort(); + if (this._indicesSort === "desc") indexNames.reverse(); + indexNames.filter( indexFilter ).forEach(function(name) { + var indexObject = clusterState.routing_table.indices[name]; + $.each(indexObject.shards, function(name, shard) { + shard.forEach(function(replica){ + var node = replica.node; + if(node === null) { node = "Unassigned"; } + var index = replica.index; + var shard = replica.shard; + var routings = nodes[getIndexForNode(node)].routings; + var indexIndex = getIndexForIndex(routings, index); + var replicas = routings[indexIndex].replicas; + if(node === "Unassigned" || !indexObject.shards[shard]) { + replicas.push({ replica: replica }); + } else { + replicas[shard] = { + replica: replica, + status: indexObject.shards[shard].filter(function(replica) { + return replica.node === node; + })[0] + }; + } + }); + }); + }); + indices = indices.map(function(index){ + return { + name: index, + state: "open", + metadata: clusterState.metadata.indices[index], + status: status.indices[index] + }; + }, this); + $.each(clusterState.metadata.indices, function(name, index) { + if(index.state === "close" && indexFilter( name )) { + indices.push({ + name: name, + state: "close", + metadata: index, + status: null + }); + } + }); + nodes.forEach(function(node) { + node.stats = nodeStats.nodes[node.name]; + var cluster = clusterNodes.nodes[node.name]; + node.cluster = cluster || { name: "" }; + node.data_node = !( cluster && cluster.attributes && cluster.attributes.data === "false" ); + for(var i = 0; i < indices.length; i++) { + node.routings[i] = node.routings[i] || { name: indices[i].name, replicas: [] }; + node.routings[i].max_number_of_shards = indices[i].metadata.settings["index.number_of_shards"]; + node.routings[i].open = indices[i].state === "open"; + } + }); + var aliasesIndex = {}; + var aliases = []; + var indexClone = indices.map(function() { return false; }); + $.each(clusterState.metadata.indices, function(name, index) { + index.aliases.forEach(function(alias) { + var aliasIndex = aliasesIndex[alias] = (alias in aliasesIndex) ? aliasesIndex[alias] : aliases.push( { name: alias, max: -1, min: 999, indices: [].concat(indexClone) }) - 1; + var indexIndex = indexIndices[name]; + var aliasRow = aliases[aliasIndex]; + aliasRow.min = Math.min(aliasRow.min, indexIndex); + aliasRow.max = Math.max(aliasRow.max, indexIndex); + aliasRow.indices[indexIndex] = indices[indexIndex]; + }); + }); + cluster.aliases = aliases; + cluster.nodes = nodes + .filter( nodeFilter_none ) + .sort( this._nodeSort ); + indices.unshift({ name: null }); + this._drawNodesView( cluster, indices ); + this._refreshButton.enable(); + }, + _drawNodesView: function( cluster, indices ) { + this._nodesView && this._nodesView.remove(); + this._nodesView = new ui.NodesView({ + onRedraw: function() { + this.refresh(); + }.bind(this), + interactive: ( this._refreshButton.value === -1 ), + aliasRenderer: this._aliasRenderer, + cluster: this.cluster, + data: { + cluster: cluster, + indices: indices + } + }); + this._nodesView.attach( this.tablEl ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiClusterOverview", children: [ + new ui.Toolbar({ + label: i18n.text("Overview.PageTitle"), + left: [ + this._nodeSortMenu, + this._indicesSortMenu, + this._aliasMenu, + this._indexFilter + ], + right: [ + this._refreshButton + ] + }), + { tag: "DIV", cls: "uiClusterOverview-table" } + ] }; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/csvTable/csvTable.js b/elasticsearch/plugins/head/src/app/ui/csvTable/csvTable.js new file mode 100644 index 00000000..2daaf5cc --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/csvTable/csvTable.js @@ -0,0 +1,84 @@ +( function( $, app, joey ) { + + var ui = app.ns("ui"); + + var CELL_SEPARATOR = ","; + var CELL_QUOTE = '"'; + var LINE_SEPARATOR = "\r\n"; + + ui.CSVTable = ui.AbstractWidget.extend({ + defaults: { + results: null + }, + _baseCls: "uiCSVTable", + init: function( parent ) { + this._super(); + var results = this.config.results.hits.hits; + var columns = this._parseResults( results ); + this._downloadButton = new ui.Button({ + label: "Generate Download Link", + onclick: this._downloadLinkGenerator_handler + }); + this._downloadLink = $.joey( { tag: "A", text: "download", }); + this._downloadLink.hide(); + this._csvText = this._csv_template( columns, results ); + this.el = $.joey( this._main_template() ); + this.attach( parent ); + }, + _downloadLinkGenerator_handler: function() { + var csvData = new Blob( [ this._csvText ], { type: 'text/csv' }); + var csvURL = URL.createObjectURL( csvData ); + this._downloadLink.attr( "href", csvURL ); + this._downloadLink.show(); + }, + _parseResults: function( results ) { + var columnPaths = {}; + (function parse( path, obj ) { + if( obj instanceof Array ) { + for( var i = 0; i < obj.length; i++ ) { + parse( path, obj[i] ); + } + } else if( typeof obj === "object" ) { + for( var prop in obj ) { + parse( path + "." + prop, obj[ prop ] ); + } + } else { + columnPaths[ path ] = true; + } + })( "root", results ); + var columns = []; + for( var column in columnPaths ) { + columns.push( column.split(".").slice(1) ); + } + return columns; + }, + _main_template: function() { return ( + { tag: "DIV", cls: this._baseCls, id: this.id(), children: [ + this._downloadButton, + this._downloadLink, + { tag: "PRE", text: this._csvText } + ] } + ); }, + _csv_template: function( columns, results ) { + return this._header_template( columns ) + LINE_SEPARATOR + this._results_template( columns, results ); + }, + _header_template: function( columns ) { + return columns.map( function( column ) { + return column.join("."); + }).join( CELL_SEPARATOR ); + }, + _results_template: function( columns, results ) { + return results.map( function( result ) { + return columns.map( function( column ) { + var l = 0, + ptr = result; + while( l !== column.length && ptr != null ) { + ptr = ptr[ column[ l++ ] ]; + } + return ( ptr == null ) ? "" : ( CELL_QUOTE + ptr.toString().replace(/"/g, '""') + CELL_QUOTE ); + }).join( CELL_SEPARATOR ); + }).join( LINE_SEPARATOR ); + } + }); + +})( this.jQuery, this.app, this.joey ); diff --git a/elasticsearch/plugins/head/src/app/ui/dateHistogram/dateHistogram.js b/elasticsearch/plugins/head/src/app/ui/dateHistogram/dateHistogram.js new file mode 100644 index 00000000..7c750b60 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/dateHistogram/dateHistogram.js @@ -0,0 +1,104 @@ +(function( app, i18n, raphael ) { + + var ui = app.ns("ui"); + + ui.DateHistogram = ui.AbstractWidget.extend({ + defaults: { + printEl: null, // (optional) if supplied, clicking on elements in the histogram changes the query + cluster: null, // (required) + query: null, // (required) the current query + spec: null // (required) // date field spec + }, + init: function() { + this._super(); + this.el = $(this._main_template()); + this.query = this.config.query.clone(); + // check if the index/types have changed and rebuild the histogram + this.config.query.on("results", function(query) { + if(this.queryChanged) { + this.buildHistogram(query); + this.queryChanged = false; + } + }.bind(this)); + this.config.query.on("setIndex", function(query, params) { + this.query.setIndex(params.index, params.add); + this.queryChanged = true; + }.bind(this)); + this.config.query.on("setType", function(query, params) { + this.query.setType(params.type, params.add); + this.queryChanged = true; + }.bind(this)); + this.query.search.size = 0; + this.query.on("results", this._stat_handler); + this.query.on("results", this._aggs_handler); + this.buildHistogram(); + }, + buildHistogram: function(query) { + this.statAggs = this.query.addAggs({ + stats: { field: this.config.spec.field_name } + }); + this.query.query(); + this.query.removeAggs(this.statAggs); + }, + _stat_handler: function(query, results) { + if(! results.aggregations[this.statAggs]) { return; } + this.stats = results.aggregations[this.statAggs]; + // here we are calculating the approximate range that will give us less than 121 columns + var rangeNames = [ "year", "year", "month", "day", "hour", "minute" ]; + var rangeFactors = [100000, 12, 30, 24, 60, 60000 ]; + this.intervalRange = 1; + var range = this.stats.max - this.stats.min; + do { + this.intervalName = rangeNames.pop(); + var factor = rangeFactors.pop(); + this.intervalRange *= factor; + range = range / factor; + } while(range > 70); + this.dateAggs = this.query.addAggs({ + date_histogram : { + field: this.config.spec.field_name, + interval: this.intervalName + } + }); + this.query.query(); + this.query.removeAggs(this.dateAggs); + }, + _aggs_handler: function(query, results) { + if(! results.aggregations[this.dateAggs]) { return; } + var buckets = [], range = this.intervalRange; + var min = Math.floor(this.stats.min / range) * range; + var prec = [ "year", "month", "day", "hour", "minute", "second" ].indexOf(this.intervalName); + results.aggregations[this.dateAggs].buckets.forEach(function(entry) { + buckets[parseInt((entry.key - min) / range , 10)] = entry.doc_count; + }, this); + for(var i = 0; i < buckets.length; i++) { + buckets[i] = buckets[i] || 0; + } + this.el.removeClass("loading"); + var el = this.el.empty(); + var w = el.width(), h = el.height(); + var r = raphael(el[0], w, h ); + var printEl = this.config.printEl; + query = this.config.query; + r.g.barchart(0, 0, w, h, [buckets], { gutter: "0", vgutter: 0 }).hover( + function() { + this.flag = r.g.popup(this.bar.x, h - 5, this.value || "0").insertBefore(this); + }, function() { + this.flag.animate({opacity: 0}, 200, ">", function () {this.remove();}); + } + ).click(function() { + if(printEl) { + printEl.val(window.dateRangeParser.print(min + this.bar.index * range, prec)); + printEl.trigger("keyup"); + query.query(); + } + }); + }, + _main_template: function() { return ( + { tag: "DIV", cls: "uiDateHistogram loading", css: { height: "50px" }, children: [ + i18n.text("General.LoadingAggs") + ] } + ); } + }); + +})( this.app, this.i18n, this.Raphael ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/dialogPanel/dialogPanel.js b/elasticsearch/plugins/head/src/app/ui/dialogPanel/dialogPanel.js new file mode 100644 index 00000000..80176fda --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/dialogPanel/dialogPanel.js @@ -0,0 +1,22 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.DialogPanel = ui.DraggablePanel.extend({ + _commit_handler: function(jEv) { + this.fire("commit", this, { jEv: jEv }); + }, + _main_template: function() { + var t = this._super(); + t.children.push(this._actionsBar_template()); + return t; + }, + _actionsBar_template: function() { + return { tag: "DIV", cls: "pull-right", children: [ + new app.ui.Button({ label: "Cancel", onclick: this._close_handler }), + new app.ui.Button({ label: "OK", onclick: this._commit_handler }) + ]}; + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/draggablePanel/draggablePanel.js b/elasticsearch/plugins/head/src/app/ui/draggablePanel/draggablePanel.js new file mode 100644 index 00000000..ee0a98a1 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/draggablePanel/draggablePanel.js @@ -0,0 +1,42 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.DraggablePanel = ui.AbstractPanel.extend({ + defaults: { + // title: "" // (required) text for the panel title + }, + + _baseCls: "uiPanel", + + init: function() { + this._super(); + this.body = $(this._body_template()); + this.title = $(this._title_template()); + this.el = $.joey( this._main_template() ); + this.el.css( { width: this.config.width } ); + this.dd = new app.ux.DragDrop({ + pickupSelector: this.el.find(".uiPanel-titleBar"), + dragObj: this.el + }); + // open the panel if set in configuration + this.config.open && this.open(); + }, + + setBody: function(body) { + this.body.empty().append(body); + }, + _body_template: function() { return { tag: "DIV", cls: "uiPanel-body", css: { height: this.config.height + (this.config.height === 'auto' ? "" : "px" ) }, children: [ this.config.body ] }; }, + _title_template: function() { return { tag: "SPAN", cls: "uiPanel-title", text: this.config.title }; }, + _main_template: function() { return ( + { tag: "DIV", id: this.id(), cls: this._baseCls, children: [ + { tag: "DIV", cls: "uiPanel-titleBar", children: [ + { tag: "DIV", cls: "uiPanel-close", onclick: this._close_handler, text: "x" }, + this.title + ]}, + this.body + ] } + ); } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.css b/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.css new file mode 100644 index 00000000..d9acccf3 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.css @@ -0,0 +1,9 @@ +.uiFilterBrowser-row * { + margin-right: 0.4em; +} + +.uiFilterBrowser-row BUTTON { + height: 22px; + position: relative; + top: 1px; +} diff --git a/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.js b/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.js new file mode 100644 index 00000000..7b58d7e4 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/filterBrowser/filterBrowser.js @@ -0,0 +1,205 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var data = app.ns("data"); + var ut = app.ns("ut"); + + ui.FilterBrowser = ui.AbstractWidget.extend({ + defaults: { + cluster: null, // (required) instanceof app.services.Cluster + index: "" // (required) name of the index to query + }, + + init: function(parent) { + this._super(); + this._cluster = this.config.cluster; + this.el = $(this._main_template()); + this.filtersEl = this.el.find(".uiFilterBrowser-filters"); + this.attach( parent ); + new data.MetaDataFactory({ cluster: this._cluster, onReady: function(metadata, eventData) { + this.metadata = metadata; + this._createFilters_handler(eventData.originalData.metadata.indices); + }.bind(this) }); + }, + + _createFilters_handler: function(data) { + var filters = []; + function scan_properties(path, obj) { + if (obj.properties) { + for (var prop in obj.properties) { + scan_properties(path.concat(prop), obj.properties[prop]); + } + } else { + // handle multi_field + if (obj.fields) { + for (var subField in obj.fields) { + filters.push({ path: (path[path.length - 1] !== subField) ? path.concat(subField) : path, type: obj.fields[subField].type, meta: obj.fields[subField] }); + } + } else { + filters.push({ path: path, type: obj.type, meta: obj }); + } + } + } + for(var type in data[this.config.index].mappings) { + scan_properties([type], data[this.config.index].mappings[type]); + } + + filters.sort( function(a, b) { + var x = a.path.join("."); + var y = b.path.join("."); + return (x < y) ? -1 : (x > y) ? 1 : 0; + }); + + this.filters = [ + { path: ["match_all"], type: "match_all", meta: {} }, + { path: ["_all"], type: "_all", meta: {}} + ].concat(filters); + + this._addFilterRow_handler(); + }, + + _addFilterRow_handler: function() { + this.filtersEl.append(this._filter_template()); + }, + + _removeFilterRow_handler: function(jEv) { + $(jEv.target).closest("DIV.uiFilterBrowser-row").remove(); + if(this.filtersEl.children().length === 0) { + this._addFilterRow_handler(); + } + }, + + _search_handler: function() { + var search = new data.BoolQuery(); + search.setSize( this.el.find(".uiFilterBrowser-outputSize").val() ) + this.fire("startingSearch"); + this.filtersEl.find(".uiFilterBrowser-row").each(function(i, row) { + row = $(row); + var bool = row.find(".bool").val(); + var field = row.find(".field").val(); + var op = row.find(".op").val(); + var value = {}; + if(field === "match_all") { + op = "match_all"; + } else if(op === "range") { + var lowqual = row.find(".lowqual").val(), + highqual = row.find(".highqual").val(); + if(lowqual.length) { + value[row.find(".lowop").val()] = lowqual; + } + if(highqual.length) { + value[row.find(".highop").val()] = highqual; + } + } else if(op === "fuzzy") { + var qual = row.find(".qual").val(), + fuzzyqual = row.find(".fuzzyqual").val(); + if(qual.length) { + value["value"] = qual; + } + if(fuzzyqual.length) { + value[row.find(".fuzzyop").val()] = fuzzyqual; + } + } else { + value = row.find(".qual").val(); + } + search.addClause(value, field, op, bool); + }); + if(this.el.find(".uiFilterBrowser-showSrc").attr("checked")) { + this.fire("searchSource", search.search); + } + this._cluster.post( this.config.index + "/_search", search.getData(), this._results_handler ); + }, + + _results_handler: function( data ) { + var type = this.el.find(".uiFilterBrowser-outputFormat").val(); + this.fire("results", this, { type: type, data: data, metadata: this.metadata }); + }, + + _changeQueryField_handler: function(jEv) { + var select = $(jEv.target); + var spec = select.children(":selected").data("spec"); + select.siblings().remove(".op,.qual,.range,.fuzzy"); + var ops = []; + if(spec.type === 'match_all') { + } else if(spec.type === '_all') { + ops = ["query_string"]; + } else if(spec.type === 'string') { + ops = ["term", "wildcard", "prefix", "fuzzy", "range", "query_string", "text", "missing"]; + } else if(spec.type === 'long' || spec.type === 'integer' || spec.type === 'float' || + spec.type === 'byte' || spec.type === 'short' || spec.type === 'double') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'date') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'geo_point') { + ops = ["missing"]; + } else if(spec.type === 'ip') { + ops = ["term", "range", "fuzzy", "query_string", "missing"]; + } else if(spec.type === 'boolean') { + ops = ["term"] + } + select.after({ tag: "SELECT", cls: "op", onchange: this._changeQueryOp_handler, children: ops.map(ut.option_template) }); + select.next().change(); + }, + + _changeQueryOp_handler: function(jEv) { + var op = $(jEv.target), opv = op.val(); + op.siblings().remove(".qual,.range,.fuzzy"); + if(opv === 'term' || opv === 'wildcard' || opv === 'prefix' || opv === "query_string" || opv === 'text') { + op.after({ tag: "INPUT", cls: "qual", type: "text" }); + } else if(opv === 'range') { + op.after(this._range_template()); + } else if(opv === 'fuzzy') { + op.after(this._fuzzy_template()); + } + }, + + _main_template: function() { + return { tag: "DIV", children: [ + { tag: "DIV", cls: "uiFilterBrowser-filters" }, + { tag: "BUTTON", type: "button", text: i18n.text("General.Search"), onclick: this._search_handler }, + { tag: "LABEL", children: + i18n.complex("FilterBrowser.OutputType", { tag: "SELECT", cls: "uiFilterBrowser-outputFormat", children: [ + { text: i18n.text("Output.Table"), value: "table" }, + { text: i18n.text("Output.JSON"), value: "json" }, + { text: i18n.text("Output.CSV"), value: "csv" } + ].map(function( o ) { return $.extend({ tag: "OPTION" }, o ); } ) } ) + }, + { tag: "LABEL", children: + i18n.complex("FilterBrowser.OutputSize", { tag: "SELECT", cls: "uiFilterBrowser-outputSize", + children: [ "10", "50", "250", "1000", "5000", "25000" ].map( ut.option_template ) + } ) + }, + { tag: "LABEL", children: [ { tag: "INPUT", type: "checkbox", cls: "uiFilterBrowser-showSrc" }, i18n.text("Output.ShowSource") ] } + ]}; + }, + + _filter_template: function() { + return { tag: "DIV", cls: "uiFilterBrowser-row", children: [ + { tag: "SELECT", cls: "bool", children: ["must", "must_not", "should"].map(ut.option_template) }, + { tag: "SELECT", cls: "field", onchange: this._changeQueryField_handler, children: this.filters.map(function(f) { + return { tag: "OPTION", data: { spec: f }, value: f.path.join("."), text: f.path.join(".") }; + })}, + { tag: "BUTTON", type: "button", text: "+", onclick: this._addFilterRow_handler }, + { tag: "BUTTON", type: "button", text: "-", onclick: this._removeFilterRow_handler } + ]}; + }, + + _range_template: function() { + return { tag: "SPAN", cls: "range", children: [ + { tag: "SELECT", cls: "lowop", children: ["gt", "gte"].map(ut.option_template) }, + { tag: "INPUT", type: "text", cls: "lowqual" }, + { tag: "SELECT", cls: "highop", children: ["lt", "lte"].map(ut.option_template) }, + { tag: "INPUT", type: "text", cls: "highqual" } + ]}; + }, + + _fuzzy_template: function() { + return { tag: "SPAN", cls: "fuzzy", children: [ + { tag: "INPUT", cls: "qual", type: "text" }, + { tag: "SELECT", cls: "fuzzyop", children: ["max_expansions", "min_similarity"].map(ut.option_template) }, + { tag: "INPUT", cls: "fuzzyqual", type: "text" } + ]}; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/header/header.css b/elasticsearch/plugins/head/src/app/ui/header/header.css new file mode 100644 index 00000000..b61badf1 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/header/header.css @@ -0,0 +1,10 @@ +.uiHeader { + padding: 3px 10px; +} + +.uiHeader-name, .uiHeader-status { + font-size: 1.2em; + font-weight: bold; + padding: 0 10px; +} + diff --git a/elasticsearch/plugins/head/src/app/ui/header/header.js b/elasticsearch/plugins/head/src/app/ui/header/header.js new file mode 100644 index 00000000..a6e1155e --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/header/header.js @@ -0,0 +1,73 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.Header = ui.AbstractWidget.extend({ + defaults: { + cluster: null, + clusterState: null + }, + _baseCls: "uiHeader", + init: function() { + this._clusterConnect = new ui.ClusterConnect({ + cluster: this.config.cluster + }); + var quicks = [ + { text: i18n.text("Nav.Info"), path: "" }, + { text: i18n.text("Nav.Status"), path: "_stats" }, + { text: i18n.text("Nav.NodeStats"), path: "_nodes/stats" }, + { text: i18n.text("Nav.ClusterNodes"), path: "_nodes" }, + { text: i18n.text("Nav.Plugins"), path: "_nodes/plugins" }, + { text: i18n.text("Nav.ClusterState"), path: "_cluster/state" }, + { text: i18n.text("Nav.ClusterHealth"), path: "_cluster/health" }, + { text: i18n.text("Nav.Templates"), path: "_template" } + ]; + var cluster = this.config.cluster; + var quickPanels = {}; + var menuItems = quicks.map( function( item ) { + return { text: item.text, onclick: function() { + cluster.get( item.path, function( data ) { + quickPanels[ item.path ] && quickPanels[ item.path ].el && quickPanels[ item.path ].remove(); + quickPanels[ item.path ] = new ui.JsonPanel({ + title: item.text, + json: data + }); + } ); + } }; + }, this ); + this._quickMenu = new ui.MenuButton({ + label: i18n.text("NodeInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: menuItems + }) + }); + this.el = $.joey( this._main_template() ); + this.nameEl = this.el.find(".uiHeader-name"); + this.statEl = this.el.find(".uiHeader-status"); + this._clusterState = this.config.clusterState; + this._clusterState.on("data", function( state ) { + var shards = state.status._shards; + var colour = state.clusterHealth.status; + var name = state.clusterState.cluster_name; + this.nameEl.text( name ); + this.statEl + .text( i18n.text("Header.ClusterHealth", colour, shards.successful, shards.total ) ) + .css( "background", colour ); + }.bind(this)); + this.statEl.text( i18n.text("Header.ClusterNotConnected") ).css("background", "grey"); + this._clusterState.refresh(); + }, + _main_template: function() { return ( + { tag: "DIV", cls: this._baseCls, children: [ + this._clusterConnect, + { tag: "SPAN", cls: "uiHeader-name" }, + { tag: "SPAN", cls: "uiHeader-status" }, + { tag: "H1", text: i18n.text("General.Elasticsearch") }, + { tag: "SPAN", cls: "pull-right", children: [ + this._quickMenu + ] } + ] } + ); } + } ); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/helpPanel/helpPanel.js b/elasticsearch/plugins/head/src/app/ui/helpPanel/helpPanel.js new file mode 100644 index 00000000..a76be2db --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/helpPanel/helpPanel.js @@ -0,0 +1,21 @@ +(function( app ){ + + var ui = app.ns("ui"); + + ui.HelpPanel = ui.InfoPanel.extend({ + defaults: { + ref: "", + open: true, + autoRemove: true, + modal: false, + width: 500, + height: 450, + title: i18n.text("General.Help") + }, + init: function() { + this._super(); + this.body.append(i18n.text(this.config.ref)); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/indexOverview/indexOverview.js b/elasticsearch/plugins/head/src/app/ui/indexOverview/indexOverview.js new file mode 100644 index 00000000..def34131 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/indexOverview/indexOverview.js @@ -0,0 +1,115 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.IndexOverview = ui.Page.extend({ + defaults: { + cluster: null + }, + init: function() { + this._super(); + this.cluster = this.config.cluster; + this._clusterState = this.config.clusterState; + this._clusterState.on("data", this._refresh_handler ); + this.el = $(this._main_template()); + this._refresh_handler(); + }, + remove: function() { + this._clusterState.removeObserver( "data", this._refresh_handler ); + }, + _refresh_handler: function() { + var state = this._clusterState; + var view = { + indices: acx.eachMap( state.status.indices, function( name, index ) { + return { + name: name, + state: index + }; + }).sort( function( a, b ) { + return a.name < b.name ? -1 : 1; + }) + }; + this._indexViewEl && this._indexViewEl.remove(); + this._indexViewEl = $( this._indexTable_template( view ) ); + this.el.find(".uiIndexOverview-table").append( this._indexViewEl ); + }, + _newIndex_handler: function() { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("ClusterOverView.IndexName"), name: "_name", require: true }), + new ui.TextField({ + label: i18n.text("ClusterOverview.NumShards"), + name: "number_of_shards", + value: "5", + require: function( val ) { return parseInt( val, 10 ) >= 1; } + }), + new ui.TextField({ + label: i18n.text("ClusterOverview.NumReplicas"), + name: "number_of_replicas", + value: "1", + require: function( val ) { return parseInt( val, 10 ) >= 0; } + }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("ClusterOverview.NewIndex"), + body: new ui.PanelForm({ fields: fields }), + onCommit: function(panel, args) { + if(fields.validate()) { + var data = fields.getData(); + var name = data["_name"]; + delete data["_name"]; + this.config.cluster.put( name, JSON.stringify({ settings: { index: data } }), function(d) { + dialog.close(); + alert(JSON.stringify(d)); + this._clusterState.refresh(); + }.bind(this) ); + } + }.bind(this) + }).open(); + }, + _indexTable_template: function( view ) { return ( + { tag: "TABLE", cls: "table", children: [ + { tag: "THEAD", children: [ + { tag: "TR", children: [ + { tag: "TH" }, + { tag: "TH", children: [ + { tag: "H3", text: "Size" } + ] }, + { tag: "TH", children: [ + { tag: "H3", text: "Docs" } + ] } + ] } + ] }, + { tag: "TBODY", cls: "striped", children: view.indices.map( this._index_template, this ) } + ] } + ); }, + + _index_template: function( index ) { return ( + { tag: "TR", children: [ + { tag: "TD", children: [ + { tag: "H3", text: index.name } + ] }, + { tag: "TD", text: ut.byteSize_template( index.state.primaries.store.size_in_bytes ) + "/" + ut.byteSize_template( index.state.total.store.size_in_bytes ) }, + { tag: "TD", text: ut.count_template( index.state.primaries.docs.count ) } + ] } + ); }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiIndexOverview", children: [ + new ui.Toolbar({ + label: i18n.text("IndexOverview.PageTitle"), + left: [ + new ui.Button({ + label: i18n.text("ClusterOverview.NewIndex"), + onclick: this._newIndex_handler + }), + ] + }), + { tag: "DIV", cls: "uiIndexOverview-table", children: this._indexViewEl } + ] }; + } + + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/indexSelector/indexSelector.js b/elasticsearch/plugins/head/src/app/ui/indexSelector/indexSelector.js new file mode 100644 index 00000000..7f51d5f7 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/indexSelector/indexSelector.js @@ -0,0 +1,45 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.IndexSelector = ui.AbstractWidget.extend({ + init: function(parent) { + this._super(); + this.el = $(this._main_template()); + this.attach( parent ); + this.cluster = this.config.cluster; + this.update(); + }, + update: function() { + this.cluster.get( "_stats", this._update_handler ); + }, + + _update_handler: function(data) { + var options = []; + var index_names = Object.keys(data.indices).sort(); + for(var i=0; i < index_names.length; i++) { + name = index_names[i]; + options.push(this._option_template(name, data.indices[name])); + } + this.el.find(".uiIndexSelector-select").empty().append(this._select_template(options)); + this._indexChanged_handler(); + }, + + _main_template: function() { + return { tag: "DIV", cls: "uiIndexSelector", children: i18n.complex( "IndexSelector.SearchIndexForDocs", { tag: "SPAN", cls: "uiIndexSelector-select" } ) }; + }, + + _indexChanged_handler: function() { + this.fire("indexChanged", this.el.find("SELECT").val()); + }, + + _select_template: function(options) { + return { tag: "SELECT", children: options, onChange: this._indexChanged_handler }; + }, + + _option_template: function(name, index) { + return { tag: "OPTION", value: name, text: i18n.text("IndexSelector.NameWithDocs", name, index.primaries.docs.count ) }; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.css b/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.css new file mode 100644 index 00000000..7d4a86c7 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.css @@ -0,0 +1,38 @@ + +.uiInfoPanel { + background: rgba(0, 0, 0, 0.75); + color: white; + border-radius: 8px; + padding: 1px; +} +.uiInfoPanel .uiPanel-titleBar { + background: rgba(74, 74, 74, 0.75); + background: -moz-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); + background: -webkit-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); + border-radius: 8px 8px 0 0; + padding: 1px 0 2px 0; + border-bottom: 0; +} +.uiInfoPanel .uiPanel-close { + border-radius: 6px; + height: 13px; + width: 13px; + background: #ccc; + left: 3px; + top: 1px; + color: #333; + text-shadow: #222 0 0 1px; + line-height: 11px; + border: 0; + padding: 0; +} +.uiInfoPanel .uiPanel-close:hover { + background: #eee; +} + +.uiInfoPanel .uiPanel-body { + background: transparent; + padding: 20px; + border-radius: 0 0 8px 8px; + border: 1px solid #222; +} diff --git a/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.js b/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.js new file mode 100644 index 00000000..db244007 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/infoPanel/infoPanel.js @@ -0,0 +1,9 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.InfoPanel = ui.DraggablePanel.extend({ + _baseCls: "uiPanel uiInfoPanel" + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.css b/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.css new file mode 100644 index 00000000..20aff881 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.css @@ -0,0 +1,4 @@ +.uiJsonPanel SPAN.uiJsonPretty-string { color: #6F6; } +.uiJsonPanel SPAN.uiJsonPretty-number { color: #66F; } +.uiJsonPanel SPAN.uiJsonPretty-null { color: #F66; } +.uiJsonPanel SPAN.uiJsonPretty-boolean { color: #F6F; } diff --git a/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.js b/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.js new file mode 100644 index 00000000..4e83baad --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/jsonPanel/jsonPanel.js @@ -0,0 +1,24 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.JsonPanel = ui.InfoPanel.extend({ + defaults: { + json: null, // (required) + modal: false, + open: true, + autoRemove: true, + height: 500, + width: 600 + }, + + _baseCls: "uiPanel uiInfoPanel uiJsonPanel", + + _body_template: function() { + var body = this._super(); + body.children = [ new ui.JsonPretty({ obj: this.config.json }) ]; + return body; + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.css b/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.css new file mode 100644 index 00000000..7a1a0ba6 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.css @@ -0,0 +1,22 @@ +DIV.uiJsonPretty-object { font-size: 1.26em; font-family: monospace; } +UL.uiJsonPretty-object, +UL.uiJsonPretty-array { margin: 0; padding: 0 0 0 2em; list-style: none; } +UL.uiJsonPretty-object LI, +UL.uiJsonPretty-array LI { padding: 0; margin: 0; } +.expando > SPAN.uiJsonPretty-name:before { content: "\25bc\a0"; color: #555; position: relative; top: 2px; } +.expando.uiJsonPretty-minimised > SPAN.uiJsonPretty-name:before { content: "\25ba\a0"; top: 0; } +.uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before, +.expando .uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before { content: ""; } +SPAN.uiJsonPretty-string, +SPAN.uiJsonPretty-string A { color: green; } +SPAN.uiJsonPretty-string A { text-decoration: underline;} +SPAN.uiJsonPretty-number { color: blue; } +SPAN.uiJsonPretty-null { color: red; } +SPAN.uiJsonPretty-boolean { color: purple; } +.expando > .uiJsonPretty-name { cursor: pointer; } +.expando > .uiJsonPretty-name:hover { text-decoration: underline; } +.uiJsonPretty-minimised { white-space: nowrap; overflow: hidden; } +.uiJsonPretty-minimised > UL { opacity: 0.6; } +.uiJsonPretty-minimised .uiJsonPretty-minimised > UL { opacity: 1; } +.uiJsonPretty-minimised UL, .uiJsonPretty-minimised LI { display: inline; padding: 0; } + diff --git a/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.js b/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.js new file mode 100644 index 00000000..76820bb3 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/jsonPretty/jsonPretty.js @@ -0,0 +1,89 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.JsonPretty = ui.AbstractWidget.extend({ + defaults: { + obj: null + }, + init: function(parent) { + this._super(); + this.el = $(this._main_template()); + this.attach(parent); + this.el.click(this._click_handler); + }, + + _click_handler: function(jEv) { + var t = $(jEv.target).closest(".uiJsonPretty-name").closest("LI"); + if(t.length === 0 || t.parents(".uiJsonPretty-minimised").length > 0) { return; } + t.toggleClass("uiJsonPretty-minimised"); + jEv.stopPropagation(); + }, + + _main_template: function() { + try { + return { tag: "DIV", cls: "uiJsonPretty", children: this.pretty.parse(this.config.obj) }; + } catch (error) { + throw "JsonPretty error: " + error.message; + } + }, + + pretty: { // from https://github.com/RyanAmos/Pretty-JSON/blob/master/pretty_json.js + "expando" : function(value) { + return (value && (/array|object/i).test(value.constructor.name)) ? "expando" : ""; + }, + "parse": function (member) { + return this[(member == null) ? 'null' : member.constructor.name.toLowerCase()](member); + }, + "null": function (value) { + return this['value']('null', 'null'); + }, + "array": function (value) { + var results = []; + var lastItem = value.length - 1; + value.forEach(function( v, i ) { + results.push({ tag: "LI", cls: this.expando(v), children: [ this['parse'](v) ] }); + if( i !== lastItem ) { + results.push(","); + } + }, this); + return [ "[ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-array", children: results } : null), "]" ]; + }, + "object": function (value) { + var results = []; + var keys = Object.keys( value ); + var lastItem = keys.length - 1; + keys.forEach( function( key, i ) { + var children = [ this['value']( 'name', '"' + key + '"' ), ": ", this['parse']( value[ key ]) ]; + if( i !== lastItem ) { + children.push(","); + } + results.push( { tag: "LI", cls: this.expando( value[ key ] ), children: children } ); + }, this); + return [ "{ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-object", children: results } : null ), "}" ]; + }, + "number": function (value) { + return this['value']('number', value.toString()); + }, + "string": function (value) { + if (/^(http|https|file):\/\/[^\s]+$/.test(value)) { + return this['link']( value ); + } else { + return this['value']('string', '"' + value.toString() + '"'); + } + }, + "boolean": function (value) { + return this['value']('boolean', value.toString()); + }, + "link": function( value ) { + return this['value']("string", { tag: "A", href: value, target: "_blank", text: '"' + value + '"' } ); + }, + "value": function (type, value) { + if (/^(http|https|file):\/\/[^\s]+$/.test(value)) { + } + return { tag: "SPAN", cls: "uiJsonPretty-" + type, text: value }; + } + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.css b/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.css new file mode 100644 index 00000000..21c36bc5 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.css @@ -0,0 +1,11 @@ +.uiMenuButton { + display: inline-block; +} + +.uiMenuButton .uiButton-label { + background-image: url('data:image/gif;base64,R0lGODlhDwAPAIABAP///////yH5BAEAAAEALAAAAAAPAA8AAAITjI+py+0P4wG0gmavq1HLD4ZiAQA7'); + background-position: right 50%; + background-repeat: no-repeat; + padding-right: 17px; + text-align: left; +} diff --git a/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.js b/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.js new file mode 100644 index 00000000..aa7fc8a8 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/menuButton/menuButton.js @@ -0,0 +1,22 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.MenuButton = app.ui.Button.extend({ + defaults: { + menu: null + }, + _baseCls: "uiButton uiMenuButton", + init: function(parent) { + this._super(parent); + this.menu = this.config.menu; + this.on("click", this.openMenu_handler); + this.menu.on("open", function() { this.el.addClass("active"); }.bind(this)); + this.menu.on("close", function() { this.el.removeClass("active"); }.bind(this)); + }, + openMenu_handler: function(jEv) { + this.menu && this.menu.open(jEv); + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.css b/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.css new file mode 100644 index 00000000..8236a856 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.css @@ -0,0 +1,30 @@ +.uiMenuPanel { + border: 1px solid #668dc6; + position: absolute; + background: #96c6eb; + color: white; +} + +.uiMenuPanel LI { + list-style: none; + border-bottom: 1px solid #668dc6; +} + +.uiMenuPanel LI:hover { + background: #2575b7; +} + +.uiMenuPanel LI:last-child { + border-bottom: 0; +} + +.uiMenuPanel-label { + white-space: nowrap; + padding: 2px 10px 2px 10px; + cursor: pointer; +} + +.disabled .uiMenuPanel-label { + cursor: auto; + color: #888; +} diff --git a/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.js b/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.js new file mode 100644 index 00000000..631ea5b4 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/menuPanel/menuPanel.js @@ -0,0 +1,43 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.MenuPanel = ui.AbstractPanel.extend({ + defaults: { + items: [], // (required) an array of menu items + modal: false + }, + _baseCls: "uiMenuPanel", + init: function() { + this._super(); + this.el = $(this._main_template()); + }, + open: function(jEv) { + this._super(jEv); + var cx = this; setTimeout(function() { $(document).bind("click", cx._close_handler); }, 50); + }, + _getItems: function() { + return this.config.items; + }, + _close_handler: function(jEv) { + this._super(jEv); + $(document).unbind("click", this._close_handler); + }, + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: this._getItems().map(this._menuItem_template, this) }; + }, + _menuItem_template: function(item) { + var dx = item.disabled ? { onclick: function() {} } : {}; + return { tag: "LI", cls: "uiMenuPanel-item" + (item.disabled ? " disabled" : "") + (item.selected ? " selected" : ""), children: [ $.extend({ tag: "DIV", cls: "uiMenuPanel-label" }, item, dx ) ] }; + }, + _getPosition: function(jEv) { + var right = !! $(jEv.target).parents(".pull-right").length; + var parent = $(jEv.target).closest("BUTTON"); + return parent.vOffset() + .addY(parent.vSize().y) + .addX( right ? parent.vSize().x - this.el.vOuterSize().x : 0 ) + .asOffset(); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.css b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.css new file mode 100644 index 00000000..e7a7778f --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.css @@ -0,0 +1,88 @@ +.uiNodesView TH, +.uiNodesView TD { + vertical-align: top; + padding: 2px 20px; +} + +.uiNodesView TH.close, +.uiNodesView TD.close { + color: #888; + background: #f2f2f2; +} + +.uiNodesView .uiMenuButton .uiButton-content { + padding-right: 3px; + border-radius: 8px; + height: 14px; +} + +.uiNodesView .uiMenuButton.active .uiButton-content, +.uiNodesView .uiMenuButton:active .uiButton-content { + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} + +.uiNodesView .uiMenuButton .uiButton-label { + padding: 0px 17px 0px 7px; +} + +.uiNodesView-hasAlias { + text-align: center; +} +.uiNodesView-hasAlias.max { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} +.uiNodesView-hasAlias.min { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +.uiNodesView-hasAlias-remove { + float: right; + font-weight: bold; + cursor: pointer; +} + +.uiNodesView TD.uiNodesView-icon { + padding: 20px 0px 15px 20px; +} + +.uiNodesView-node:nth-child(odd) { + background: #eee; +} + +.uiNodesView-routing { + position: relative; + min-width: 90px; +} + +.uiNodesView-nullReplica, +.uiNodesView-replica { + box-sizing: border-box; + cursor: pointer; + float: left; + height: 40px; + width: 35px; + margin: 4px; + border: 2px solid #444; + padding: 2px; + font-size: 32px; + line-height: 32px; + text-align: center; + letter-spacing: -5px; + text-indent: -7px; +} + +.uiNodesView-replica.primary { + border-width: 4px; + line-height: 29px; +} + +.uiNodesView-nullReplica { + border-color: transparent; +} + +.uiNodesView-replica.state-UNASSIGNED { background: #eeeeee; color: #999; border-color: #666; float: none; display: inline-block; } +.uiNodesView-replica.state-INITIALIZING { background: #dddc88; } +.uiNodesView-replica.state-STARTED { background: #99dd88; } +.uiNodesView-replica.state-RELOCATING { background: #dc88dd; } diff --git a/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.js b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.js new file mode 100644 index 00000000..a90529cc --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesView.js @@ -0,0 +1,277 @@ +(function( app, i18n, joey ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.NodesView = ui.AbstractWidget.extend({ + defaults: { + interactive: true, + aliasRenderer: "list", + scaleReplicas: 1, + cluster: null, + data: null + }, + init: function() { + this._super(); + this.interactive = this.config.interactive; + this.cluster = this.config.cluster; + this._aliasRenderFunction = { + "none": this._aliasRender_template_none, + "list": this._aliasRender_template_list, + "full": this._aliasRender_template_full + }[ this.config.aliasRenderer ]; + this._styleSheetEl = joey({ tag: "STYLE", text: ".uiNodesView-nullReplica, .uiNodesView-replica { zoom: " + this.config.scaleReplicas + " }" }); + this.el = $( this._main_template( this.config.data.cluster, this.config.data.indices ) ); + }, + + _newAliasAction_handler: function( index ) { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("AliasForm.AliasName"), name: "alias", require: true }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("AliasForm.NewAliasForIndexName", index.name), + body: new ui.PanelForm({ fields: fields }), + onCommit: function(panel, args) { + if(fields.validate()) { + var data = fields.getData(); + var command = { + "actions" : [ + { "add" : { "index" : index.name, "alias" : data["alias"] } } + ] + }; + this.config.cluster.post('_aliases', JSON.stringify(command), function(d) { + dialog.close(); + alert(JSON.stringify(d)); + this.fire("redraw"); + }.bind(this) ); + } + }.bind(this) + }).open(); + }, + _postIndexAction_handler: function(action, index, redraw) { + this.cluster.post(index.name + "/" + action, null, function(r) { + alert(JSON.stringify(r)); + redraw && this.fire("redraw"); + }.bind(this)); + }, + _optimizeIndex_handler: function(index) { + var fields = new app.ux.FieldCollection({ + fields: [ + new ui.TextField({ label: i18n.text("OptimizeForm.MaxSegments"), name: "max_num_segments", value: "1", require: true }), + new ui.CheckField({ label: i18n.text("OptimizeForm.ExpungeDeletes"), name: "only_expunge_deletes", value: false }), + new ui.CheckField({ label: i18n.text("OptimizeForm.FlushAfter"), name: "flush", value: true }), + new ui.CheckField({ label: i18n.text("OptimizeForm.WaitForMerge"), name: "wait_for_merge", value: false }) + ] + }); + var dialog = new ui.DialogPanel({ + title: i18n.text("OptimizeForm.OptimizeIndex", index.name), + body: new ui.PanelForm({ fields: fields }), + onCommit: function( panel, args ) { + if(fields.validate()) { + this.cluster.post(index.name + "/_optimize", fields.getData(), function(r) { + alert(JSON.stringify(r)); + }); + dialog.close(); + } + }.bind(this) + }).open(); + }, + _testAnalyser_handler: function(index) { + this.cluster.get(index.name + "/_analyze?text=" + prompt( i18n.text("IndexCommand.TextToAnalyze") ), function(r) { + alert(JSON.stringify(r, true, " ")); + }); + }, + _deleteIndexAction_handler: function(index) { + if( prompt( i18n.text("AliasForm.DeleteAliasMessage", i18n.text("Command.DELETE"), index.name ) ) === i18n.text("Command.DELETE") ) { + this.cluster["delete"](index.name, null, function(r) { + alert(JSON.stringify(r)); + this.fire("redraw"); + }.bind(this) ); + } + }, + _shutdownNode_handler: function(node) { + if(prompt( i18n.text("IndexCommand.ShutdownMessage", i18n.text("Command.SHUTDOWN"), node.cluster.name ) ) === i18n.text("Command.SHUTDOWN") ) { + this.cluster.post( "_cluster/nodes/" + node.name + "/_shutdown", null, function(r) { + alert(JSON.stringify(r)); + this.fire("redraw"); + }.bind(this)); + } + }, + _deleteAliasAction_handler: function( index, alias ) { + if( confirm( i18n.text("Command.DeleteAliasMessage" ) ) ) { + var command = { + "actions" : [ + { "remove" : { "index" : index.name, "alias" : alias.name } } + ] + }; + this.config.cluster.post('_aliases', JSON.stringify(command), function(d) { + alert(JSON.stringify(d)); + this.fire("redraw"); + }.bind(this) ); + } + }, + + _replica_template: function(replica) { + var r = replica.replica; + return { tag: "DIV", + cls: "uiNodesView-replica" + (r.primary ? " primary" : "") + ( " state-" + r.state ), + text: r.shard.toString(), + onclick: function() { new ui.JsonPanel({ + json: replica.status || r, + title: r.index + "/" + r.node + " [" + r.shard + "]" }); + } + }; + }, + _routing_template: function(routing) { + var cell = { tag: "TD", cls: "uiNodesView-routing" + (routing.open ? "" : " close"), children: [] }; + for(var i = 0; i < routing.replicas.length; i++) { + if(i % routing.max_number_of_shards === 0 && i > 0) { + cell.children.push({ tag: "BR" }); + } + if( routing.replicas[i] ) { + cell.children.push(this._replica_template(routing.replicas[i])); + } else { + cell.children.push( { tag: "DIV", cls: "uiNodesView-nullReplica" } ); + } + } + return cell; + }, + _nodeControls_template: function( node ) { return ( + { tag: "DIV", cls: "uiNodesView-controls", children: [ + new ui.MenuButton({ + label: i18n.text("NodeInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("NodeInfoMenu.ClusterNodeInfo"), onclick: function() { new ui.JsonPanel({ json: node.cluster, title: node.name });} }, + { text: i18n.text("NodeInfoMenu.NodeStats"), onclick: function() { new ui.JsonPanel({ json: node.stats, title: node.name });} } + ] + }) + }), + new ui.MenuButton({ + label: i18n.text("NodeActionsMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("NodeActionsMenu.Shutdown"), onclick: function() { this._shutdownNode_handler(node); }.bind(this) } + ] + }) + }) + ] } + ); }, + _nodeIcon_template: function( node ) { + var icon, alt; + if( node.name === "Unassigned" ) { + icon = "fa-exclamation-triangle"; + alt = i18n.text( "NodeType.Unassigned" ); + } else if( node.cluster.settings && "tribe" in node.cluster.settings) { + icon = "fa-sitemap"; + alt = i18n.text("NodeType.Tribe" ); + } else { + icon = "fa-" + (node.master_node ? "star" : "circle") + (node.data_node ? "" : "-o" ); + alt = i18n.text( node.master_node ? ( node.data_node ? "NodeType.Master" : "NodeType.Coord" ) : ( node.data_node ? "NodeType.Worker" : "NodeType.Client" ) ); + } + return { tag: "TD", title: alt, cls: "uiNodesView-icon", children: [ + { tag: "SPAN", cls: "fa fa-2x " + icon } + ] }; + }, + _node_template: function(node) { + return { tag: "TR", cls: "uiNodesView-node" + (node.master_node ? " master": ""), children: [ + this._nodeIcon_template( node ), + { tag: "TH", children: node.name === "Unassigned" ? [ + { tag: "H3", text: node.name } + ] : [ + { tag: "H3", text: node.cluster.name }, + { tag: "DIV", text: node.cluster.hostname }, + this.interactive ? this._nodeControls_template( node ) : null + ] } + ].concat(node.routings.map(this._routing_template, this))}; + }, + _indexHeaderControls_template: function( index ) { return ( + { tag: "DIV", cls: "uiNodesView-controls", children: [ + new ui.MenuButton({ + label: i18n.text("IndexInfoMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("IndexInfoMenu.Status"), onclick: function() { new ui.JsonPanel({ json: index.status, title: index.name }); } }, + { text: i18n.text("IndexInfoMenu.Metadata"), onclick: function() { new ui.JsonPanel({ json: index.metadata, title: index.name }); } } + ] + }) + }), + new ui.MenuButton({ + label: i18n.text("IndexActionsMenu.Title"), + menu: new ui.MenuPanel({ + items: [ + { text: i18n.text("IndexActionsMenu.NewAlias"), onclick: function() { this._newAliasAction_handler(index); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Refresh"), onclick: function() { this._postIndexAction_handler("_refresh", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Flush"), onclick: function() { this._postIndexAction_handler("_flush", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Optimize"), onclick: function () { this._optimizeIndex_handler(index); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Snapshot"), disabled: closed, onclick: function() { this._postIndexAction_handler("_gateway/snapshot", index, false); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Analyser"), onclick: function() { this._testAnalyser_handler(index); }.bind(this) }, + { text: (index.state === "close") ? i18n.text("IndexActionsMenu.Open") : i18n.text("IndexActionsMenu.Close"), onclick: function() { this._postIndexAction_handler((index.state === "close") ? "_open" : "_close", index, true); }.bind(this) }, + { text: i18n.text("IndexActionsMenu.Delete"), onclick: function() { this._deleteIndexAction_handler(index); }.bind(this) } + ] + }) + }) + ] } + ); }, + _indexHeader_template: function( index ) { + var closed = index.state === "close"; + var line1 = closed ? "index: close" : ( "size: " + (index.status && index.status.primaries && index.status.total ? ut.byteSize_template( index.status.primaries.store.size_in_bytes ) + " (" + ut.byteSize_template( index.status.total.store.size_in_bytes ) + ")" : "unknown" ) ); + var line2 = closed ? "\u00A0" : ( "docs: " + (index.status && index.status.primaries && index.status.primaries.docs && index.status.total && index.status.total.docs ? index.status.primaries.docs.count.toLocaleString() + " (" + (index.status.total.docs.count + index.status.total.docs.deleted).toLocaleString() + ")" : "unknown" ) ); + return index.name ? { tag: "TH", cls: (closed ? "close" : ""), children: [ + { tag: "H3", text: index.name }, + { tag: "DIV", text: line1 }, + { tag: "DIV", text: line2 }, + this.interactive ? this._indexHeaderControls_template( index ) : null + ] } : [ { tag: "TD" }, { tag: "TH" } ]; + }, + _aliasRender_template_none: function( cluster, indices ) { + return null; + }, + _aliasRender_template_list: function( cluster, indices ) { + return cluster.aliases.length && { tag: "TBODY", children: [ + { tag: "TR", children: [ + { tag: "TD" } + ].concat( indices.map( function( index ) { + return { tag: "TD", children: index.metadata && index.metadata.aliases.map( function( alias ) { + return { tag: "LI", text: alias }; + } ) }; + })) } + ] }; + }, + _aliasRender_template_full: function( cluster, indices ) { + return cluster.aliases.length && { tag: "TBODY", children: cluster.aliases.map( function(alias, row) { + return { tag: "TR", children: [ { tag: "TD" },{ tag: "TD" } ].concat(alias.indices.map(function(index, i) { + if (index) { + return { + tag: "TD", + css: { background: "#" + "9ce9c7fc9".substr((row+6)%7,3) }, + cls: "uiNodesView-hasAlias" + ( alias.min === i ? " min" : "" ) + ( alias.max === i ? " max" : "" ), + text: alias.name, + children: this.interactive ? [ + { tag: 'SPAN', + text: i18n.text("General.CloseGlyph"), + cls: 'uiNodesView-hasAlias-remove', + onclick: this._deleteAliasAction_handler.bind( this, index, alias ) + } + ]: null + }; + } else { + return { tag: "TD" }; + } + }, this ) ) }; + }, this ) }; + }, + _main_template: function(cluster, indices) { + return { tag: "TABLE", cls: "table uiNodesView", children: [ + this._styleSheetEl, + { tag: "THEAD", children: [ { tag: "TR", children: indices.map(this._indexHeader_template, this) } ] }, + this._aliasRenderFunction( cluster, indices ), + { tag: "TBODY", children: cluster.nodes.map(this._node_template, this) } + ] }; + } + + }); + +})( this.app, this.i18n, this.joey ); diff --git a/elasticsearch/plugins/head/src/app/ui/nodesView/nodesViewDemo.js b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesViewDemo.js new file mode 100644 index 00000000..9049163b --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/nodesView/nodesViewDemo.js @@ -0,0 +1,17 @@ +$( function() { + + var ui = window.app.ns("ui"); + + var data = { + "cluster":{"nodes":[{"name":"cqTmT9GLSlSWx-B7pvM--w","routings":[{"name":"_river","replicas":[],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[null,{"replica":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_01"},"status":{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":10328420,"max_doc":10329720,"deleted_docs":4},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"643ms","total_time_in_millis":643}}}],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[null,{"replica":{"state":"STARTED","primary":false,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":false,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"75ms","total_time_in_millis":75}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[null,{"replica":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_03"},"status":{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"86ms","total_time_in_millis":86}}}],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":false,"stats":{"timestamp":1381790346979,"name":"Elathan","transport_address":"inet[/127.0.0.1:9301]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"277b","size_in_bytes":277,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":0,"index_time":"0s","index_time_in_millis":0,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":6,"query_time":"19ms","query_time_in_millis":19,"query_current":0,"fetch_total":0,"fetch_time":"0s","fetch_time_in_millis":0,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":10,"total_time":"0s","total_time_in_millis":0},"flush":{"total":186,"total_time":"9.1s","total_time_in_millis":9191},"warmer":{"current":0,"total":3,"total_time":"6ms","total_time_in_millis":6},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346979,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4065169408,"used":"4.2gb","used_in_bytes":4524765184,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4426321920,"actual_used":"3.8gb","actual_used_in_bytes":4163612672},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346979,"open_file_descriptors":266,"cpu":{"percent":0,"sys":"2.8m","sys_in_millis":172614,"user":"2.3m","user_in_millis":142295,"total":"5.2m","total_in_millis":314909},"mem":{"resident":"21.6mb","resident_in_bytes":22728704,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3900530688}},"jvm":{"timestamp":1381790346980,"uptime":"4d","uptime_in_millis":349028405,"mem":{"heap_used":"31.3mb","heap_used_in_bytes":32851896,"heap_committed":"265.5mb","heap_committed_in_bytes":278462464,"non_heap_used":"39.3mb","non_heap_used_in_bytes":41210896,"non_heap_committed":"63.7mb","non_heap_committed_in_bytes":66809856,"pools":{"Code Cache":{"used":"2mb","used_in_bytes":2120704,"max":"48mb","max_in_bytes":50331648,"peak_used":"2mb","peak_used_in_bytes":2131520,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"24mb","used_in_bytes":25224136,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"27mb","peak_used_in_bytes":28311552,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"816.5kb","used_in_bytes":836128,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2mb","peak_used_in_bytes":2162688,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"6.4mb","used_in_bytes":6791632,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"8.2mb","peak_used_in_bytes":8653824,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"37.2mb","used_in_bytes":39090192,"max":"82mb","max_in_bytes":85983232,"peak_used":"37.2mb","peak_used_in_bytes":39090192,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":51,"peak_count":56},"gc":{"collection_count":43,"collection_time":"374ms","collection_time_in_millis":374,"collectors":{"ParNew":{"collection_count":41,"collection_time":"374ms","collection_time_in_millis":374},"ConcurrentMarkSweep":{"collection_count":2,"collection_time":"0s","collection_time_in_millis":0}}}},"thread_pool":{"generic":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":3,"completed":7505},"index":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"merge":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":16},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":188},"search":{"threads":6,"queue":0,"active":0,"rejected":0,"largest":6,"completed":6},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":5,"queue":0,"active":1,"rejected":0,"largest":5,"completed":8593},"refresh":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438878,"out_segs":7330495,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790347004,"data":[{"path":"/var/elasticsearch/es2/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231241613312,"available":"215.1gb","available_in_bytes":230979469312,"disk_reads":2641856,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593427456,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":155197,"rx_size":"6.9mb","rx_size_in_bytes":7339947,"tx_count":155195,"tx_size":"11.6mb","tx_size_in_bytes":12251491},"http":{"current_open":0,"total_opened":0}},"cluster":{"name":"Elathan","transport_address":"inet[/127.0.0.1:9301]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9203]"}},{"name":"jw4owU-ZQgOYdM7ElauDTg","routings":[{"name":"_river","replicas":[],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[null,{"replica":{"state":"STARTED","primary":false,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":1,"index":"ag_01"},"status":{"routing":{"state":"STARTED","primary":false,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"194ms","total_time_in_millis":194}}}],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[{"replica":{"state":"STARTED","primary":true,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":0,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":true,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"107ms","total_time_in_millis":107}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":false,"stats":{"timestamp":1381790346963,"name":"Numinus","transport_address":"inet[/127.0.0.1:9303]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"178b","size_in_bytes":178,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":2,"index_time":"50ms","index_time_in_millis":50,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":12,"query_time":"32ms","query_time_in_millis":32,"query_current":0,"fetch_total":0,"fetch_time":"0s","fetch_time_in_millis":0,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":9,"total_time":"66ms","total_time_in_millis":66},"flush":{"total":218,"total_time":"5.6s","total_time_in_millis":5697},"warmer":{"current":0,"total":2,"total_time":"4ms","total_time_in_millis":4},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346963,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4058091520,"used":"4.2gb","used_in_bytes":4531843072,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4419244032,"actual_used":"3.8gb","actual_used_in_bytes":4170690560},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346963,"open_file_descriptors":264,"cpu":{"percent":0,"sys":"2.9m","sys_in_millis":174208,"user":"2.3m","user_in_millis":139440,"total":"5.2m","total_in_millis":313648},"mem":{"resident":"27.2mb","resident_in_bytes":28606464,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3903823872}},"jvm":{"timestamp":1381790346978,"uptime":"4d","uptime_in_millis":349026382,"mem":{"heap_used":"12.8mb","heap_used_in_bytes":13428352,"heap_committed":"265.5mb","heap_committed_in_bytes":278462464,"non_heap_used":"39.8mb","non_heap_used_in_bytes":41791784,"non_heap_committed":"55.2mb","non_heap_committed_in_bytes":57950208,"pools":{"Code Cache":{"used":"1.9mb","used_in_bytes":2065280,"max":"48mb","max_in_bytes":50331648,"peak_used":"1.9mb","peak_used_in_bytes":2074880,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"2.4mb","used_in_bytes":2564176,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"27mb","peak_used_in_bytes":28311552,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"646.5kb","used_in_bytes":662048,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2.8mb","peak_used_in_bytes":3029584,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"9.7mb","used_in_bytes":10202128,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"9.7mb","peak_used_in_bytes":10202128,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"37.8mb","used_in_bytes":39726504,"max":"82mb","max_in_bytes":85983232,"peak_used":"37.8mb","peak_used_in_bytes":39726504,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":60,"peak_count":66},"gc":{"collection_count":42,"collection_time":"512ms","collection_time_in_millis":512,"collectors":{"ParNew":{"collection_count":40,"collection_time":"512ms","collection_time_in_millis":512},"ConcurrentMarkSweep":{"collection_count":2,"collection_time":"0s","collection_time_in_millis":0}}}},"thread_pool":{"generic":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":3,"completed":7490},"index":{"threads":2,"queue":0,"active":0,"rejected":0,"largest":2,"completed":2},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"merge":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":18},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":221},"search":{"threads":12,"queue":0,"active":0,"rejected":0,"largest":12,"completed":12},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":3,"queue":0,"active":1,"rejected":0,"largest":5,"completed":8597},"refresh":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438878,"out_segs":7330495,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790346992,"data":[{"path":"/var/elasticsearch/es4/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231241613312,"available":"215.1gb","available_in_bytes":230979469312,"disk_reads":2641856,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593427456,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":155179,"rx_size":"7mb","rx_size_in_bytes":7341687,"tx_count":155177,"tx_size":"11.6mb","tx_size_in_bytes":12249878},"http":{"current_open":0,"total_opened":0}},"cluster":{"name":"Numinus","transport_address":"inet[/127.0.0.1:9303]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9202]"}},{"name":"SwCmq0QKQuShZcJAbuW8OQ","routings":[{"name":"_river","replicas":[{"replica":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"_river"},"status":{"routing":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"_river"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1380240228838,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":62,"total_time":"1.6s","total_time_in_millis":1632}}}],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[{"replica":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"ag_01"},"status":{"routing":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"216ms","total_time_in_millis":216}}}],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[null,null,{"replica":{"state":"STARTED","primary":true,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":2,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":true,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"17ms","total_time_in_millis":17}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":false,"stats":{"timestamp":1381790346944,"name":"Ameridroid","transport_address":"inet[/127.0.0.1:9305]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"257b","size_in_bytes":257,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":6,"index_time":"64ms","index_time_in_millis":64,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":12,"query_time":"27ms","query_time_in_millis":27,"query_current":0,"fetch_total":0,"fetch_time":"0s","fetch_time_in_millis":0,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":10,"total_time":"113ms","total_time_in_millis":113},"flush":{"total":241,"total_time":"5.5s","total_time_in_millis":5553},"warmer":{"current":0,"total":3,"total_time":"4ms","total_time_in_millis":4},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346944,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4059074560,"used":"4.2gb","used_in_bytes":4530860032,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4419702784,"actual_used":"3.8gb","actual_used_in_bytes":4170231808},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346944,"open_file_descriptors":266,"cpu":{"percent":0,"sys":"2.8m","sys_in_millis":171912,"user":"2.3m","user_in_millis":143905,"total":"5.2m","total_in_millis":315817},"mem":{"resident":"30.4mb","resident_in_bytes":31936512,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3905077248}},"jvm":{"timestamp":1381790346944,"uptime":"4d","uptime_in_millis":349024271,"mem":{"heap_used":"16.6mb","heap_used_in_bytes":17485968,"heap_committed":"253.9mb","heap_committed_in_bytes":266272768,"non_heap_used":"39.7mb","non_heap_used_in_bytes":41733256,"non_heap_committed":"40.4mb","non_heap_committed_in_bytes":42405888,"pools":{"Code Cache":{"used":"1.9mb","used_in_bytes":2018624,"max":"48mb","max_in_bytes":50331648,"peak_used":"1.9mb","peak_used_in_bytes":2029504,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"5.9mb","used_in_bytes":6242912,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"16.6mb","peak_used_in_bytes":17432576,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"489.7kb","used_in_bytes":501496,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2mb","peak_used_in_bytes":2162688,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"10.2mb","used_in_bytes":10741560,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"10.2mb","peak_used_in_bytes":10741560,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"37.8mb","used_in_bytes":39714632,"max":"82mb","max_in_bytes":85983232,"peak_used":"37.8mb","peak_used_in_bytes":39714632,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":63,"peak_count":70},"gc":{"collection_count":61,"collection_time":"533ms","collection_time_in_millis":533,"collectors":{"ParNew":{"collection_count":61,"collection_time":"533ms","collection_time_in_millis":533},"ConcurrentMarkSweep":{"collection_count":0,"collection_time":"0s","collection_time_in_millis":0}}}},"thread_pool":{"generic":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":4,"completed":7504},"index":{"threads":4,"queue":0,"active":0,"rejected":0,"largest":4,"completed":6},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":4},"merge":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":4},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":23},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":241},"search":{"threads":12,"queue":0,"active":0,"rejected":0,"largest":12,"completed":12},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":4,"queue":0,"active":1,"rejected":0,"largest":5,"completed":10284},"refresh":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":4}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438844,"out_segs":7330461,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790346959,"data":[{"path":"/var/elasticsearch/es6/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231240040448,"available":"215.1gb","available_in_bytes":230977896448,"disk_reads":2641849,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593058816,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":156880,"rx_size":"7mb","rx_size_in_bytes":7431874,"tx_count":156877,"tx_size":"11.9mb","tx_size_in_bytes":12480678},"http":{"current_open":0,"total_opened":0}},"cluster":{"name":"Ameridroid","transport_address":"inet[/127.0.0.1:9305]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9205]"}},{"name":"aBuoaKR5QVCfUZHgrJEfVg","routings":[{"name":"_river","replicas":[],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[{"replica":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_01"},"status":{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"382ms","total_time_in_millis":382}}}],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[null,null,{"replica":{"state":"STARTED","primary":false,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":2,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":false,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"15ms","total_time_in_millis":15}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[{"replica":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_03"},"status":{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"101ms","total_time_in_millis":101}}}],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":false,"stats":{"timestamp":1381790346945,"name":"Immortus","transport_address":"inet[/127.0.0.1:9302]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"277b","size_in_bytes":277,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":2,"index_time":"59ms","index_time_in_millis":59,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":12,"query_time":"27ms","query_time_in_millis":27,"query_current":0,"fetch_total":1,"fetch_time":"9ms","fetch_time_in_millis":9,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":9,"total_time":"72ms","total_time_in_millis":72},"flush":{"total":239,"total_time":"6.3s","total_time_in_millis":6363},"warmer":{"current":0,"total":3,"total_time":"8ms","total_time_in_millis":8},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346963,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4058099712,"used":"4.2gb","used_in_bytes":4531834880,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4419252224,"actual_used":"3.8gb","actual_used_in_bytes":4170682368},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346963,"open_file_descriptors":266,"cpu":{"percent":0,"sys":"2.9m","sys_in_millis":174105,"user":"2.4m","user_in_millis":145373,"total":"5.3m","total_in_millis":319478},"mem":{"resident":"29mb","resident_in_bytes":30453760,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3912540160}},"jvm":{"timestamp":1381790346963,"uptime":"4d","uptime_in_millis":349027381,"mem":{"heap_used":"28.5mb","heap_used_in_bytes":29980664,"heap_committed":"265.5mb","heap_committed_in_bytes":278462464,"non_heap_used":"40mb","non_heap_used_in_bytes":42021480,"non_heap_committed":"65.6mb","non_heap_committed_in_bytes":68853760,"pools":{"Code Cache":{"used":"2mb","used_in_bytes":2183808,"max":"48mb","max_in_bytes":50331648,"peak_used":"2mb","peak_used_in_bytes":2193408,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"23.8mb","used_in_bytes":25026800,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"27mb","peak_used_in_bytes":28311552,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"464.1kb","used_in_bytes":475296,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2mb","peak_used_in_bytes":2162688,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"4.2mb","used_in_bytes":4478568,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"12.4mb","peak_used_in_bytes":13054208,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"37.9mb","used_in_bytes":39837672,"max":"82mb","max_in_bytes":85983232,"peak_used":"38.2mb","peak_used_in_bytes":40134632,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":62,"peak_count":67},"gc":{"collection_count":80,"collection_time":"561ms","collection_time_in_millis":561,"collectors":{"ParNew":{"collection_count":78,"collection_time":"557ms","collection_time_in_millis":557},"ConcurrentMarkSweep":{"collection_count":2,"collection_time":"4ms","collection_time_in_millis":4}}}},"thread_pool":{"generic":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":4,"completed":7528},"index":{"threads":2,"queue":0,"active":0,"rejected":0,"largest":2,"completed":2},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"merge":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":26},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":242},"search":{"threads":12,"queue":0,"active":0,"rejected":0,"largest":12,"completed":13},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":5,"queue":0,"active":2,"rejected":0,"largest":5,"completed":10288},"refresh":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438862,"out_segs":7330479,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790346977,"data":[{"path":"/var/elasticsearch/es3/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231241089024,"available":"215.1gb","available_in_bytes":230978945024,"disk_reads":2641854,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593402880,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":156920,"rx_size":"7.1mb","rx_size_in_bytes":7451979,"tx_count":156915,"tx_size":"11.9mb","tx_size_in_bytes":12499329},"http":{"current_open":0,"total_opened":0}},"cluster":{"name":"Immortus","transport_address":"inet[/127.0.0.1:9302]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9201]"}},{"name":"3Rbfod0sS9eZ6VrOkY0JKw","routings":[{"name":"_river","replicas":[{"replica":{"state":"STARTED","primary":true,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"_river"},"status":{"routing":{"state":"STARTED","primary":true,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"_river"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1380240228838,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":62,"total_time":"833ms","total_time_in_millis":833}}}],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[{"replica":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"4ms","total_time_in_millis":4}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[{"replica":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_03"},"status":{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"274ms","total_time_in_millis":274}}}],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":true,"stats":{"timestamp":1381790346938,"name":"Steel Serpent","transport_address":"inet[server/127.0.0.1:9300]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"237b","size_in_bytes":237,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":2,"index_time":"59ms","index_time_in_millis":59,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":12,"query_time":"32ms","query_time_in_millis":32,"query_current":0,"fetch_total":3,"fetch_time":"5ms","fetch_time_in_millis":5,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":6,"total_time":"58ms","total_time_in_millis":58},"flush":{"total":240,"total_time":"4.3s","total_time_in_millis":4339},"warmer":{"current":0,"total":3,"total_time":"8ms","total_time_in_millis":8},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346938,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4062093312,"used":"4.2gb","used_in_bytes":4527841280,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4422721536,"actual_used":"3.8gb","actual_used_in_bytes":4167213056},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346938,"open_file_descriptors":272,"cpu":{"percent":0,"sys":"3.3m","sys_in_millis":200578,"user":"3.2m","user_in_millis":192613,"total":"6.5m","total_in_millis":393191},"mem":{"resident":"43.8mb","resident_in_bytes":46018560,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3910467584}},"jvm":{"timestamp":1381790346938,"uptime":"4d","uptime_in_millis":349029191,"mem":{"heap_used":"28.9mb","heap_used_in_bytes":30381984,"heap_committed":"253.9mb","heap_committed_in_bytes":266272768,"non_heap_used":"42.9mb","non_heap_used_in_bytes":45068896,"non_heap_committed":"43.3mb","non_heap_committed_in_bytes":45420544,"pools":{"Code Cache":{"used":"3.3mb","used_in_bytes":3534656,"max":"48mb","max_in_bytes":50331648,"peak_used":"3.3mb","peak_used_in_bytes":3545408,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"15.6mb","used_in_bytes":16402808,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"16.6mb","peak_used_in_bytes":17432576,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"44.9kb","used_in_bytes":46016,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2mb","peak_used_in_bytes":2162688,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"13.2mb","used_in_bytes":13933160,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"13.2mb","peak_used_in_bytes":13933160,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"39.6mb","used_in_bytes":41534240,"max":"82mb","max_in_bytes":85983232,"peak_used":"39.6mb","peak_used_in_bytes":41534240,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":64,"peak_count":68},"gc":{"collection_count":272,"collection_time":"967ms","collection_time_in_millis":967,"collectors":{"ParNew":{"collection_count":272,"collection_time":"967ms","collection_time_in_millis":967},"ConcurrentMarkSweep":{"collection_count":0,"collection_time":"0s","collection_time_in_millis":0}}}},"thread_pool":{"generic":{"threads":2,"queue":0,"active":0,"rejected":0,"largest":4,"completed":9499},"index":{"threads":2,"queue":0,"active":0,"rejected":0,"largest":2,"completed":2},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"merge":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":21},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":241},"search":{"threads":12,"queue":0,"active":0,"rejected":0,"largest":12,"completed":15},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":5,"queue":0,"active":1,"rejected":0,"largest":5,"completed":15207},"refresh":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438818,"out_segs":7330435,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790346956,"data":[{"path":"/var/elasticsearch/es1/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231240040448,"available":"215.1gb","available_in_bytes":230977896448,"disk_reads":2641849,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593058816,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":780551,"rx_size":"59mb","rx_size_in_bytes":61929023,"tx_count":780563,"tx_size":"35.2mb","tx_size_in_bytes":36961925},"http":{"current_open":6,"total_opened":201}},"cluster":{"name":"Steel Serpent","transport_address":"inet[server/127.0.0.1:9300]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9200]"}},{"name":"PO1wHIidQo-w7sGHaYHvjg","routings":[{"name":"_river","replicas":[],"max_number_of_shards":"1","open":true},{"name":"ag_01","replicas":[],"max_number_of_shards":"2","open":true},{"name":"ag_02","replicas":[null,{"replica":{"state":"STARTED","primary":true,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_02"},"status":{"routing":{"state":"STARTED","primary":true,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"122ms","total_time_in_millis":122}}}],"max_number_of_shards":"3","open":true},{"name":"ag_03","replicas":[null,{"replica":{"state":"STARTED","primary":false,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_03"},"status":{"routing":{"state":"STARTED","primary":false,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"137ms","total_time_in_millis":137}}}],"max_number_of_shards":"2","open":true},{"name":"twitter_river","replicas":[],"max_number_of_shards":"5","open":false}],"master_node":false,"stats":{"timestamp":1381790346964,"name":"Thumbelina","transport_address":"inet[/127.0.0.1:9304]","hostname":"server","indices":{"docs":{"count":0,"deleted":0},"store":{"size":"178b","size_in_bytes":178,"throttle_time":"0s","throttle_time_in_millis":0},"indexing":{"index_total":4,"index_time":"55ms","index_time_in_millis":55,"index_current":0,"delete_total":0,"delete_time":"0s","delete_time_in_millis":0,"delete_current":0},"get":{"total":0,"get_time":"0s","time_in_millis":0,"exists_total":0,"exists_time":"0s","exists_time_in_millis":0,"missing_total":0,"missing_time":"0s","missing_time_in_millis":0,"current":0},"search":{"open_contexts":0,"query_total":6,"query_time":"21ms","query_time_in_millis":21,"query_current":0,"fetch_total":2,"fetch_time":"10ms","fetch_time_in_millis":10,"fetch_current":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":9,"total_time":"81ms","total_time_in_millis":81},"flush":{"total":198,"total_time":"3.6s","total_time_in_millis":3662},"warmer":{"current":0,"total":2,"total_time":"3ms","total_time_in_millis":3},"filter_cache":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"id_cache":{"memory_size":"0b","memory_size_in_bytes":0},"fielddata":{"memory_size":"0b","memory_size_in_bytes":0,"evictions":0},"completion":{"size":"0b","size_in_bytes":0}},"os":{"timestamp":1381790346964,"uptime":"15.5m","uptime_in_millis":932146,"load_average":[1.79736328125,1.95166015625,1.904296875],"cpu":{"sys":22,"user":6,"idle":70,"stolen":0},"mem":{"free":"3.7gb","free_in_bytes":4058308608,"used":"4.2gb","used_in_bytes":4531625984,"free_percent":51,"used_percent":48,"actual_free":"4.1gb","actual_free_in_bytes":4419461120,"actual_used":"3.8gb","actual_used_in_bytes":4170473472},"swap":{"used":"2.8gb","used_in_bytes":3025272832,"free":"1.1gb","free_in_bytes":1269694464}},"process":{"timestamp":1381790346964,"open_file_descriptors":264,"cpu":{"percent":0,"sys":"2.8m","sys_in_millis":172378,"user":"2.3m","user_in_millis":140227,"total":"5.2m","total_in_millis":312605},"mem":{"resident":"28.5mb","resident_in_bytes":29892608,"share":"-1b","share_in_bytes":-1,"total_virtual":"3.6gb","total_virtual_in_bytes":3904798720}},"jvm":{"timestamp":1381790346976,"uptime":"4d","uptime_in_millis":349025339,"mem":{"heap_used":"20.8mb","heap_used_in_bytes":21889120,"heap_committed":"253.9mb","heap_committed_in_bytes":266272768,"non_heap_used":"40.2mb","non_heap_used_in_bytes":42232320,"non_heap_committed":"40.8mb","non_heap_committed_in_bytes":42799104,"pools":{"Code Cache":{"used":"2mb","used_in_bytes":2125312,"max":"48mb","max_in_bytes":50331648,"peak_used":"2mb","peak_used_in_bytes":2134912,"peak_max":"48mb","peak_max_in_bytes":50331648},"Par Eden Space":{"used":"8.4mb","used_in_bytes":8836504,"max":"66.5mb","max_in_bytes":69795840,"peak_used":"16.6mb","peak_used_in_bytes":17432576,"peak_max":"66.5mb","peak_max_in_bytes":69795840},"Par Survivor Space":{"used":"659.3kb","used_in_bytes":675192,"max":"8.3mb","max_in_bytes":8716288,"peak_used":"2mb","peak_used_in_bytes":2162688,"peak_max":"8.3mb","peak_max_in_bytes":8716288},"CMS Old Gen":{"used":"11.8mb","used_in_bytes":12377424,"max":"940.8mb","max_in_bytes":986513408,"peak_used":"11.8mb","peak_used_in_bytes":12377424,"peak_max":"940.8mb","peak_max_in_bytes":986513408},"CMS Perm Gen":{"used":"38.2mb","used_in_bytes":40107008,"max":"82mb","max_in_bytes":85983232,"peak_used":"38.2mb","peak_used_in_bytes":40107008,"peak_max":"82mb","peak_max_in_bytes":85983232}}},"threads":{"count":59,"peak_count":65},"gc":{"collection_count":61,"collection_time":"632ms","collection_time_in_millis":632,"collectors":{"ParNew":{"collection_count":61,"collection_time":"632ms","collection_time_in_millis":632},"ConcurrentMarkSweep":{"collection_count":0,"collection_time":"0s","collection_time_in_millis":0}}}},"thread_pool":{"generic":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":4,"completed":7515},"index":{"threads":4,"queue":0,"active":0,"rejected":0,"largest":4,"completed":4},"get":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"snapshot":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"merge":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2},"suggest":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"bulk":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"optimize":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"warmer":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":22},"flush":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":2,"completed":198},"search":{"threads":8,"queue":0,"active":0,"rejected":0,"largest":8,"completed":8},"percolate":{"threads":0,"queue":0,"active":0,"rejected":0,"largest":0,"completed":0},"management":{"threads":4,"queue":0,"active":1,"rejected":0,"largest":5,"completed":10266},"refresh":{"threads":1,"queue":0,"active":0,"rejected":0,"largest":1,"completed":2}},"network":{"tcp":{"active_opens":91033,"passive_opens":3206,"curr_estab":824,"in_segs":9438872,"out_segs":7330489,"retrans_segs":10420,"estab_resets":4179,"attempt_fails":2304,"in_errs":15,"out_rsts":-1}},"fs":{"timestamp":1381790347016,"data":[{"path":"/var/elasticsearch/es5/data/elasticsearch/nodes/0","mount":"/","dev":"/dev/disk0s2","total":"465.1gb","total_in_bytes":499418034176,"free":"215.3gb","free_in_bytes":231241613312,"available":"215.1gb","available_in_bytes":230979469312,"disk_reads":2641856,"disk_writes":3231915,"disk_read_size":"75.9gb","disk_read_size_in_bytes":81593427456,"disk_write_size":"76.2gb","disk_write_size_in_bytes":81856144384}]},"transport":{"server_open":60,"rx_count":156869,"rx_size":"7mb","rx_size_in_bytes":7444733,"tx_count":156866,"tx_size":"11.9mb","tx_size_in_bytes":12497730},"http":{"current_open":0,"total_opened":2}},"cluster":{"name":"Thumbelina","transport_address":"inet[/127.0.0.1:9304]","hostname":"server","version":"0.90.5","http_address":"inet[server/127.0.0.1:9204]"}}],"aliases":[{"name":"search","max":3,"min":1,"indices":[false,{"name":"ag_01","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"2","index.version.created":"900599"},"mappings":{},"aliases":["search"]},"status":{"index":{"primary_size":"198b","primary_size_in_bytes":198,"size":"356b","size_in_bytes":356},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":2,"total_time":"0s","total_time_in_millis":0},"flush":{"total":27,"total_time":"1.4s","total_time_in_millis":1435},"shards":{"0":[{"routing":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"216ms","total_time_in_millis":216}},{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"382ms","total_time_in_millis":382}}],"1":[{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"643ms","total_time_in_millis":643}},{"routing":{"state":"STARTED","primary":false,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"194ms","total_time_in_millis":194}}]}}},{"name":"ag_02","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"3","index.version.created":"900599"},"mappings":{},"aliases":["search"]},"status":{"index":{"primary_size":"297b","primary_size_in_bytes":297,"size":"534b","size_in_bytes":534},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":3,"total_time":"0s","total_time_in_millis":0},"flush":{"total":20,"total_time":"340ms","total_time_in_millis":340},"shards":{"0":[{"routing":{"state":"STARTED","primary":true,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"107ms","total_time_in_millis":107}},{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"4ms","total_time_in_millis":4}}],"1":[{"routing":{"state":"STARTED","primary":false,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"75ms","total_time_in_millis":75}},{"routing":{"state":"STARTED","primary":true,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"122ms","total_time_in_millis":122}}],"2":[{"routing":{"state":"STARTED","primary":true,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"17ms","total_time_in_millis":17}},{"routing":{"state":"STARTED","primary":false,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"15ms","total_time_in_millis":15}}]}}},{"name":"ag_03","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"2","index.version.created":"900599"},"mappings":{},"aliases":["search","live"]},"status":{"index":{"primary_size":"198b","primary_size_in_bytes":198,"size":"356b","size_in_bytes":356},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":2,"total_time":"0s","total_time_in_millis":0},"flush":{"total":27,"total_time":"598ms","total_time_in_millis":598},"shards":{"0":[{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"101ms","total_time_in_millis":101}},{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"274ms","total_time_in_millis":274}}],"1":[{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"86ms","total_time_in_millis":86}},{"routing":{"state":"STARTED","primary":false,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"137ms","total_time_in_millis":137}}]}}},false]},{"name":"live","max":3,"min":3,"indices":[false,false,false,{"name":"ag_03","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"2","index.version.created":"900599"},"mappings":{},"aliases":["search","live"]},"status":{"index":{"primary_size":"198b","primary_size_in_bytes":198,"size":"356b","size_in_bytes":356},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":2,"total_time":"0s","total_time_in_millis":0},"flush":{"total":27,"total_time":"598ms","total_time_in_millis":598},"shards":{"0":[{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"101ms","total_time_in_millis":101}},{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"274ms","total_time_in_millis":274}}],"1":[{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"86ms","total_time_in_millis":86}},{"routing":{"state":"STARTED","primary":false,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"137ms","total_time_in_millis":137}}]}}},false]}]}, + "indices":[{"name":null},{"name":"_river","state":"open","metadata":{"state":"open","settings":{"index.version.created":"900599","index.number_of_replicas":"1","index.number_of_shards":"1"},"mappings":{},"aliases":[]},"status":{"index":{"primary_size":"79b","primary_size_in_bytes":79,"size":"158b","size_in_bytes":158},"translog":{"operations":0},"docs":{"num_docs":1,"max_doc":1,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":124,"total_time":"2.4s","total_time_in_millis":2465},"shards":{"0":[{"routing":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"_river"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1380240228838,"operations":0},"docs":{"num_docs":187162420,"max_doc":187171320,"deleted_docs":9},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":62,"total_time":"1.6s","total_time_in_millis":1632}},{"routing":{"state":"STARTED","primary":true,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"_river"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1380240228838,"operations":0},"docs":{"num_docs":187162420,"max_doc":187171320,"deleted_docs":9},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":62,"total_time":"833ms","total_time_in_millis":833}}]}}},{"name":"ag_01","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"2","index.version.created":"900599"},"mappings":{},"aliases":["search"]},"status":{"index":{"primary_size":"198b","primary_size_in_bytes":198,"size":"356b","size_in_bytes":356},"translog":{"operations":0},"docs":{"num_docs":18716242,"max_doc":18717132,"deleted_docs":9},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":2,"total_time":"0s","total_time_in_millis":0},"flush":{"total":27,"total_time":"1.4s","total_time_in_millis":1435},"shards":{"0":[{"routing":{"state":"STARTED","primary":false,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"216ms","total_time_in_millis":216}},{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712651,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"382ms","total_time_in_millis":382}}],"1":[{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"643ms","total_time_in_millis":643}},{"routing":{"state":"STARTED","primary":false,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":1,"index":"ag_01"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731712637,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"194ms","total_time_in_millis":194}}]}}},{"name":"ag_02","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"3","index.version.created":"900599"},"mappings":{},"aliases":["search"]},"status":{"index":{"primary_size":"297b","primary_size_in_bytes":297,"size":"534b","size_in_bytes":534},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":3,"total_time":"0s","total_time_in_millis":0},"flush":{"total":20,"total_time":"340ms","total_time_in_millis":340},"shards":{"0":[{"routing":{"state":"STARTED","primary":true,"node":"jw4owU-ZQgOYdM7ElauDTg","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"107ms","total_time_in_millis":107}},{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663369,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"4ms","total_time_in_millis":4}}],"1":[{"routing":{"state":"STARTED","primary":false,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"75ms","total_time_in_millis":75}},{"routing":{"state":"STARTED","primary":true,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663385,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"122ms","total_time_in_millis":122}}],"2":[{"routing":{"state":"STARTED","primary":true,"node":"SwCmq0QKQuShZcJAbuW8OQ","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":4,"total_time":"17ms","total_time_in_millis":17}},{"routing":{"state":"STARTED","primary":false,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":2,"index":"ag_02"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381749663407,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":3,"total_time":"15ms","total_time_in_millis":15}}]}}},{"name":"ag_03","state":"open","metadata":{"state":"open","settings":{"index.number_of_replicas":"1","index.number_of_shards":"2","index.version.created":"900599"},"mappings":{},"aliases":["search","live"]},"status":{"index":{"primary_size":"198b","primary_size_in_bytes":198,"size":"356b","size_in_bytes":356},"translog":{"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":2,"total_time":"0s","total_time_in_millis":0},"flush":{"total":27,"total_time":"598ms","total_time_in_millis":598},"shards":{"0":[{"routing":{"state":"STARTED","primary":true,"node":"aBuoaKR5QVCfUZHgrJEfVg","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":6,"total_time":"101ms","total_time_in_millis":101}},{"routing":{"state":"STARTED","primary":false,"node":"3Rbfod0sS9eZ6VrOkY0JKw","relocating_node":null,"shard":0,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770063,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"274ms","total_time_in_millis":274}}],"1":[{"routing":{"state":"STARTED","primary":true,"node":"cqTmT9GLSlSWx-B7pvM--w","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"99b","size_in_bytes":99},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":1,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"86ms","total_time_in_millis":86}},{"routing":{"state":"STARTED","primary":false,"node":"PO1wHIidQo-w7sGHaYHvjg","relocating_node":null,"shard":1,"index":"ag_03"},"state":"STARTED","index":{"size":"79b","size_in_bytes":79},"translog":{"id":1381731770058,"operations":0},"docs":{"num_docs":0,"max_doc":0,"deleted_docs":0},"merges":{"current":0,"current_docs":0,"current_size":"0b","current_size_in_bytes":0,"total":0,"total_time":"0s","total_time_in_millis":0,"total_docs":0,"total_size":"0b","total_size_in_bytes":0},"refresh":{"total":0,"total_time":"0s","total_time_in_millis":0},"flush":{"total":7,"total_time":"137ms","total_time_in_millis":137}}]}}},{"name":"twitter_river","state":"close","metadata":{"state":"close","settings":{"index.number_of_replicas":"1","index.version.created":"900599","index.number_of_shards":"5"},"mappings":{"status":{"properties":{"text":{"type":"string"},"location":{"properties":{"lon":{"type":"double"},"lat":{"type":"double"}}},"link":{"properties":{"start":{"type":"long"},"expand_url":{"type":"string"},"display_url":{"type":"string"},"url":{"type":"string"},"end":{"type":"long"}}},"hashtag":{"properties":{"text":{"type":"string"},"start":{"type":"long"},"end":{"type":"long"}}},"truncated":{"type":"boolean"},"source":{"type":"string"},"retweet":{"properties":{"id":{"type":"long"},"user_screen_name":{"type":"string"},"retweet_count":{"type":"long"},"user_id":{"type":"long"}}},"created_at":{"format":"dateOptionalTime","type":"date"},"retweet_count":{"type":"long"},"in_reply":{"properties":{"user_screen_name":{"type":"string"},"status":{"type":"long"},"user_id":{"type":"long"}}},"mention":{"properties":{"id":{"type":"long"},"start":{"type":"long"},"name":{"type":"string"},"screen_name":{"type":"string"},"end":{"type":"long"}}},"place":{"properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"country_code":{"type":"string"},"url":{"type":"string"},"full_name":{"type":"string"},"country":{"type":"string"}}},"user":{"properties":{"id":{"type":"long"},"profile_image_url_https":{"type":"string"},"location":{"type":"string"},"description":{"type":"string"},"name":{"type":"string"},"screen_name":{"type":"string"},"profile_image_url":{"type":"string"}}}}}},"aliases":[]},"status":null}] + }; + + window.builder = function() { + return new ui.NodesView({ + interactive: true, + data: data + }); + }; + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/page/page.js b/elasticsearch/plugins/head/src/app/ui/page/page.js new file mode 100644 index 00000000..8c5eb70a --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/page/page.js @@ -0,0 +1,14 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.Page = ui.AbstractWidget.extend({ + show: function() { + this.el.show(); + }, + hide: function() { + this.el.hide(); + } + }); + +})( this.app ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.css b/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.css new file mode 100644 index 00000000..cd4138e2 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.css @@ -0,0 +1,12 @@ +.uiPanelForm-field { + display: block; + padding: 2px 0; + clear: both; +} + +.uiPanelForm-label { + float: left; + width: 200px; + padding: 3px 7px; + text-align: right; +} diff --git a/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.js b/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.js new file mode 100644 index 00000000..8ed4fe7f --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/panelForm/panelForm.js @@ -0,0 +1,26 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.PanelForm = ui.AbstractWidget.extend({ + defaults: { + fields: null // (required) instanceof app.ux.FieldCollection + }, + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + this.attach( parent ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiPanelForm", children: this.config.fields.fields.map(this._field_template, this) }; + }, + _field_template: function(field) { + return { tag: "LABEL", cls: "uiPanelForm-field", children: [ + { tag: "DIV", cls: "uiPanelForm-label", children: [ field.label, ut.require_template(field) ] }, + field + ]}; + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.css b/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.css new file mode 100644 index 00000000..dfe055cd --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.css @@ -0,0 +1,62 @@ +.uiQueryFilter { + width: 350px; + padding: 5px; + background: #d8e7ff; + background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); + background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); +} + +.uiQueryFilter DIV.uiQueryFilter-section { + margin-bottom: 5px; +} + +.uiQueryFilter HEADER { + display: block; + font-variant: small-caps; + font-weight: bold; + margin: 5px 0; +} + +.uiQueryFilter-aliases SELECT { + width: 100%; +} + +.uiQueryFilter-booble { + cursor: pointer; + background: #e8f1ff; + border: 1px solid #e8f1ff; + border-radius: 5px; + padding: 1px 4px; + margin-bottom: 1px; + overflow: hidden; + white-space: nowrap; +} + +.uiQueryFilter-booble.selected { + background: #dae3f0; + border-top: 1px solid #c8d4e6; + border-left: 1px solid #c8d4e6; + border-bottom: 1px solid #ffffff; + border-right: 1px solid #ffffff; +} + +.uiQueryFilter-filterName { + background-color: #cbdfff; + margin-bottom: 4px; + padding: 3px; + cursor: pointer; +} + +.uiQueryFilter-filters INPUT { + width: 300px; +} + +.uiQueryFilter-subMultiFields { + padding-left: 10px; +} + +.uiQueryFilter-rangeHintFrom, +.uiQueryFilter-rangeHintTo { + margin: 0; + opacity: 0.75; +} \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.js b/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.js new file mode 100644 index 00000000..bd449a0b --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/queryFilter/queryFilter.js @@ -0,0 +1,277 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var ut = app.ns("ut"); + + ui.QueryFilter = ui.AbstractWidget.extend({ + defaults: { + metadata: null, // (required) instanceof app.data.MetaData + query: null // (required) instanceof app.data.Query that the filters will act apon + }, + init: function() { + this._super(); + this.metadata = this.config.metadata; + this.query = this.config.query; + this.el = $(this._main_template()); + }, + helpTypeMap: { + "date" : "QueryFilter.DateRangeHelp" + }, + requestUpdate: function(jEv) { + if(jEv && jEv.originalEvent) { // we only want to update on real user interaction not generated events + this.query.setPage(1); + this.query.query(); + } + }, + getSpec: function(fieldName) { + return this.metadata.fields[fieldName]; + }, + _selectAlias_handler: function(jEv) { + var indices = (jEv.target.selectedIndex === 0) ? [] : this.metadata.getIndices($(jEv.target).val()); + $(".uiQueryFilter-index").each(function(i, el) { + var jEl = $(el); + if(indices.contains(jEl.text()) !== jEl.hasClass("selected")) { + jEl.click(); + } + }); + this.requestUpdate(jEv); + }, + _selectIndex_handler: function(jEv) { + var jEl = $(jEv.target).closest(".uiQueryFilter-index"); + jEl.toggleClass("selected"); + var selected = jEl.hasClass("selected"); + this.query.setIndex(jEl.text(), selected); + if(selected) { + var types = this.metadata.getTypes(this.query.indices); + this.el.find("DIV.uiQueryFilter-type.selected").each(function(n, el) { + if(! types.contains($(el).text())) { + $(el).click(); + } + }); + } + this.requestUpdate(jEv); + }, + _selectType_handler: function(jEv) { + var jEl = $(jEv.target).closest(".uiQueryFilter-type"); + jEl.toggleClass("selected"); + var type = jEl.text(), selected = jEl.hasClass("selected"); + this.query.setType(type, selected); + if(selected) { + var indices = this.metadata.types[type].indices; + // es throws a 500 if searching an index for a type it does not contain - so we prevent that + this.el.find("DIV.uiQueryFilter-index.selected").each(function(n, el) { + if(! indices.contains($(el).text())) { + $(el).click(); + } + }); + // es throws a 500 if you specify types from different indices with _all + jEl.siblings(".uiQueryFilter-type.selected").forEach(function(el) { + if(this.metadata.types[$(el).text()].indices.intersection(indices).length === 0) { + $(el).click(); + } + }, this); + } + this.requestUpdate(jEv); + }, + _openFilter_handler: function(section) { + var field_name = section.config.title; + if(! section.loaded) { + var spec = this.getSpec(field_name); + if(spec.core_type === "string") { + section.body.append(this._textFilter_template(spec)); + } else if(spec.core_type === "date") { + section.body.append(this._dateFilter_template(spec)); + section.body.append(new ui.DateHistogram({ printEl: section.body.find("INPUT"), cluster: this.cluster, query: this.query, spec: spec })); + } else if(spec.core_type === "number") { + section.body.append(this._numericFilter_template(spec)); + } else if(spec.core_type === 'boolean') { + section.body.append(this._booleanFilter_template(spec)); + } else if (spec.core_type === 'multi_field') { + section.body.append(this._multiFieldFilter_template(section, spec)); + } + section.loaded = true; + } + section.on("animComplete", function(section) { section.body.find("INPUT").focus(); }); + }, + _textFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqids = jEl.data("uqids") || []; + uqids.forEach(function(uqid) { + uqid && this.query.removeClause(uqid); + }, this); + if(val.length) { + if(jEl[0] === document.activeElement && jEl[0].selectionStart === jEl[0].selectionEnd) { + val = val.replace(new RegExp("(.{"+jEl[0].selectionStart+"})"), "$&*"); + } + uqids = val.split(/\s+/).map(function(term) { + // Figure out the actual field name - needed for multi_field, because + // querying for "field.field" will not work. Simply "field" must be used + // if nothing is aliased. + var fieldNameParts = spec.field_name.split('.'); + var part = fieldNameParts.length - 1; + var name = fieldNameParts[part]; + while (part >= 1) { + if (fieldNameParts[part] !== fieldNameParts[part - 1]) { + name = fieldNameParts[part - 1] + "." + name; + } + part--; + } + return term && this.query.addClause(term, name, "wildcard", "must"); + }, this); + } + jEl.data("uqids", uqids); + this.requestUpdate(jEv); + }, + _dateFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqid = jEl.data("uqid") || null; + var range = window.dateRangeParser.parse(val); + var lastRange = jEl.data("lastRange"); + if(!range || (lastRange && lastRange.start === range.start && lastRange.end === range.end)) { + return; + } + uqid && this.query.removeClause(uqid); + if((range.start && range.end) === null) { + uqid = null; + } else { + var value = {}; + if( range.start ) { + value["gte"] = range.start; + } + if( range.end ) { + value["lte"] = range.end; + } + uqid = this.query.addClause( value, spec.field_name, "range", "must"); + } + jEl.data("lastRange", range); + jEl.siblings(".uiQueryFilter-rangeHintFrom") + .text(i18n.text("QueryFilter.DateRangeHint.from", range.start && new Date(range.start).toUTCString())); + jEl.siblings(".uiQueryFilter-rangeHintTo") + .text(i18n.text("QueryFilter.DateRangeHint.to", range.end && new Date(range.end).toUTCString())); + jEl.data("uqid", uqid); + this.requestUpdate(jEv); + }, + _numericFilterChange_handler: function(jEv) { + var jEl = $(jEv.target).closest("INPUT"); + var val = jEl.val(); + var spec = jEl.data("spec"); + var uqid = jEl.data("uqid") || null; + var lastRange = jEl.data("lastRange"); + var range = (function(val) { + var ops = val.split(/->|<>|/.test(val)) { + return { gte: (ops[0] - ops[1]), lte: (ops[0] + ops[1]) }; + } else if(/->| 0 ) { + this._timer = window.setInterval( this._refresh_handler, this.value ); + } + }, + _click_handler: function() { + this._refresh_handler(); + }, + _select_handler: function( el, event ) { + this.set( event.value ); + this.fire("change", this ); + }, + _refresh_handler: function() { + this.fire("refresh", this ); + }, + _getItems: function() { + return [ + { text: i18n.text("General.ManualRefresh"), value: -1 }, + { text: i18n.text("General.RefreshQuickly"), value: 100 }, + { text: i18n.text("General.Refresh5seconds"), value: 5000 }, + { text: i18n.text("General.Refresh1minute"), value: 60000 } + ]; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonDemo.js b/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonDemo.js new file mode 100644 index 00000000..c37df9c9 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonDemo.js @@ -0,0 +1,12 @@ +$( function() { + + var ui = window.app.ns("ui"); + + window.builder = function() { + return new ui.RefreshButton({ + onRefresh: function() { console.log("-> refresh", arguments ); }, + onChange: function() { console.log("-> change", arguments ); } + }); + }; + +}); diff --git a/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonSpec.js b/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonSpec.js new file mode 100644 index 00000000..8cc325f4 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/refreshButton/refreshButtonSpec.js @@ -0,0 +1,92 @@ +describe("app.ui.RefreshButton", function() { + + var RefreshButton = window.app.ui.RefreshButton; + + var r, refresh_handler, change_handler; + + function openMenuPanel( button, label ) { + button.el.find("BUTTON").eq(1).click(); + $(".uiMenuPanel-label:contains(" + label + ")").click(); + test.clock.tick(); // menuPanel -> bind _close_handler + } + + + beforeEach( function() { + test.clock.steal(); + refresh_handler = jasmine.createSpy("refresh_handler"); + change_handler = jasmine.createSpy("change_handler"); + r = new RefreshButton({ + onRefresh: refresh_handler, + onChange: change_handler + }); + r.attach( document.body ); + }); + + afterEach( function() { + r.remove(); + test.clock.restore(); + }); + + it("should have an initial default value", function() { + expect( r.value ).toBe( -1 ); + }); + + it("should fire a refresh event after clicking the refresh button ", function() { + r.el.find("BUTTON").eq(0).click(); + + expect( refresh_handler ).toHaveBeenCalled(); + }); + + it("should change the refresh rate when set it called", function() { + r.set( 100 ); + expect( r.value ).toBe( 100 ); + }); + + it("should set an interval when rate is set to a positive value", function() { + r.set( 100 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 1 ); + }); + + it("should not set an interval when rate is set to a non positive value", function() { + r.set( -1 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 0 ); + }); + + it("should fire a refresh event on intervals if refresh menu item is set to quickly", function() { + openMenuPanel( r, "quickly" ); + + expect( refresh_handler.calls.count() ).toBe( 0 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 1 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 2 ); + }); + + it("should not fire refresh events when user selects Manual", function() { + + openMenuPanel( r, "quickly" ); + + expect( refresh_handler.calls.count() ).toBe( 0 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 1 ); + + openMenuPanel( r, "Manual" ); + + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 1 ); + test.clock.tick(); + expect( refresh_handler.calls.count() ).toBe( 1 ); + }); + + it("should fire a change event when a new refresh rate is selected", function() { + openMenuPanel( r, "quickly" ); + expect( change_handler.calls.count() ).toBe( 1 ); + expect( r.value ).toBe( 100 ); + openMenuPanel( r, "Manual" ); + expect( change_handler.calls.count() ).toBe( 2 ); + expect( r.value ).toBe( -1 ); + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/ui/resultTable/resultTable.js b/elasticsearch/plugins/head/src/app/ui/resultTable/resultTable.js new file mode 100644 index 00000000..6c7cf558 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/resultTable/resultTable.js @@ -0,0 +1,55 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.ResultTable = ui.Table.extend({ + defaults: { + width: 500, + height: 400 + }, + + init: function() { + this._super(); + this.on("rowClick", this._showPreview_handler); + this.selectedRow = null; + $(document).bind("keydown", this._nav_handler); + }, + remove: function() { + $(document).unbind("keydown", this._nav_handler); + this._super(); + }, + attach: function(parent) { + if(parent) { + var height = parent.height() || ( $(document).height() - parent.offset().top - 41 ); // 41 = height in px of .uiTable-tools + uiTable-header + var width = parent.width(); + this.el.width( width ); + this.body.width( width ).height( height ); + } + this._super(parent); + }, + showPreview: function(row) { + row.addClass("selected"); + this.preview = new app.ui.JsonPanel({ + title: i18n.text("Browser.ResultSourcePanelTitle"), + json: row.data("row")._source, + onClose: function() { row.removeClass("selected"); } + }); + }, + _nav_handler: function(jEv) { + if(jEv.keyCode !== 40 && jEv.keyCode !== 38) { + return; + } + this.selectedRow && this.preview && this.preview.remove(); + if(jEv.keyCode === 40) { // up arrow + this.selectedRow = this.selectedRow ? this.selectedRow.next("TR") : this.body.find("TR:first"); + } else if(jEv.keyCode === 38) { // down arrow + this.selectedRow = this.selectedRow ? this.selectedRow.prev("TR") : this.body.find("TR:last"); + } + this.selectedRow && this.showPreview(this.selectedRow); + }, + _showPreview_handler: function(obj, data) { + this.showPreview(this.selectedRow = data.row); + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.css b/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.css new file mode 100644 index 00000000..7315527d --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.css @@ -0,0 +1,11 @@ +.uiSelectMenuPanel .uiMenuPanel-label { + margin-left: 1em; + padding-left: 4px; +} + +.uiSelectMenuPanel .uiMenuPanel-item.selected .uiMenuPanel-label:before { + content: "\2713"; + width: 12px; + margin-left: -12px; + display: inline-block; +} diff --git a/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.js b/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.js new file mode 100644 index 00000000..8cbecc56 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/selectMenuPanel/selectMenuPanel.js @@ -0,0 +1,33 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.SelectMenuPanel = ui.MenuPanel.extend({ + defaults: { + items: [], // (required) an array of menu items + value: null + }, + _baseCls: "uiSelectMenuPanel uiMenuPanel", + init: function() { + this.value = this.config.value; + this._super(); + }, + _getItems: function() { + return this.config.items.map( function( item ) { + return { + text: item.text, + selected: this.value === item.value, + onclick: function( jEv ) { + var el = $( jEv.target ).closest("LI"); + el.parent().children().removeClass("selected"); + el.addClass("selected"); + this.fire( "select", this, { value: item.value } ); + this.value = item.value; + }.bind(this) + }; + }, this ); + + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.css b/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.css new file mode 100644 index 00000000..654389fc --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.css @@ -0,0 +1,28 @@ +.uiSidebarSection-head { + background-color: #b9cfff; + background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAcCAMAAABifa5OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMAUExURQUCFf///wICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0NDQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///2Oyy/cAAAACdFJOU/8A5bcwSgAAADxJREFUeNq8zzkOACAMA8Hd/3+agiuRcIsrRopIjArOoLK1QAMNNBCRPkhLyzkn35Bjfd7JR1Nr09NoDACnvgDl1zlzoQAAAABJRU5ErkJggg=='); + background-repeat: no-repeat; + background-position: 2px 5px; + margin-bottom: 1px; + padding: 3px 3px 3px 17px; + cursor: pointer; +} + +.shown > .uiSidebarSection-head { + background-position: 2px -13px; +} + +.uiSidebarSection-body { + margin-bottom: 3px; + display: none; +} + +.uiSidebarSection-help { + text-shadow: #228 1px 1px 2px; + color: blue; + cursor: pointer; +} + +.uiSidebarSection-help:hover { + text-decoration: underline; +} diff --git a/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.js b/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.js new file mode 100644 index 00000000..d5ab9844 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/sidebarSection/sidebarSection.js @@ -0,0 +1,41 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + + ui.SidebarSection = ui.AbstractWidget.extend({ + defaults: { + title: "", + help: null, + body: null, + open: false + }, + init: function() { + this._super(); + this.el = $.joey( this._main_template() ); + this.body = this.el.children(".uiSidebarSection-body"); + this.config.open && ( this.el.addClass("shown") && this.body.css("display", "block") ); + }, + _showSection_handler: function( ev ) { + var shown = $( ev.target ).closest(".uiSidebarSection") + .toggleClass("shown") + .children(".uiSidebarSection-body").slideToggle(200, function() { this.fire("animComplete", this); }.bind(this)) + .end() + .hasClass("shown"); + this.fire(shown ? "show" : "hide", this); + }, + _showHelp_handler: function( ev ) { + new ui.HelpPanel({ref: this.config.help}); + ev.stopPropagation(); + }, + _main_template: function() { return ( + { tag: "DIV", cls: "uiSidebarSection", children: [ + (this.config.title && { tag: "DIV", cls: "uiSidebarSection-head", onclick: this._showSection_handler, children: [ + this.config.title, + ( this.config.help && { tag: "SPAN", cls: "uiSidebarSection-help pull-right", onclick: this._showHelp_handler, text: i18n.text("General.HelpGlyph") } ) + ] }), + { tag: "DIV", cls: "uiSidebarSection-body", children: [ this.config.body ] } + ] } + ); } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.css b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.css new file mode 100644 index 00000000..e8d27529 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.css @@ -0,0 +1,33 @@ +.uiSplitButton { + white-space: nowrap; +} + +.uiSplitButton .uiButton:first-child { + margin-right: 0; + display: inline-block; +} + +.uiSplitButton .uiButton:first-child .uiButton-content { + border-right-width: 1; + border-right-color: #5296c7; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.uiSplitButton .uiMenuButton { + margin-left: 0; +} + +.uiSplitButton .uiButton:last-child .uiButton-content { + border-radius: 2px; + border-left-width: 1; + border-left-color: #96c6eb; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + height: 20px; +} + +.uiSplitButton .uiButton:last-child .uiButton-label { + padding: 2px 17px 2px 6px; + margin-left: -8px; +} diff --git a/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.js b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.js new file mode 100644 index 00000000..8785b99c --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButton.js @@ -0,0 +1,54 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.SplitButton = ui.AbstractWidget.extend({ + defaults: { + items: [], + label: "" + }, + _baseCls: "uiSplitButton", + init: function( parent ) { + this._super( parent ); + this.value = null; + this.button = new ui.Button({ + label: this.config.label, + onclick: this._click_handler + }); + this.menu = new ui.SelectMenuPanel({ + value: this.config.value, + items: this._getItems(), + onSelect: this._select_handler + }); + this.menuButton = new ui.MenuButton({ + label: "\u00a0", + menu: this.menu + }); + this.el = $.joey(this._main_template()); + }, + remove: function() { + this.menu.remove(); + }, + disable: function() { + this.button.disable(); + }, + enable: function() { + this.button.enable(); + }, + _click_handler: function() { + this.fire("click", this, { value: this.value } ); + }, + _select_handler: function( panel, event ) { + this.fire( "select", this, event ); + }, + _getItems: function() { + return this.config.items; + }, + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: [ + this.button, this.menuButton + ] }; + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/splitButton/splitButtonDemo.js b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButtonDemo.js new file mode 100644 index 00000000..76b6fa2d --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/splitButton/splitButtonDemo.js @@ -0,0 +1,16 @@ +$( function() { + + var ui = window.app.ns("ui"); + + window.builder = function() { + return new ui.SplitButton({ + label: "Default", + items: [ + { label: "Action" }, + { label: "Another Action" }, + { label: "Selected", selected: true } + ] + }); + }; + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.css b/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.css new file mode 100644 index 00000000..50220458 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.css @@ -0,0 +1,7 @@ +.uiStructuredQuery { + padding: 10px; +} + +.uiStructuredQuery-out { + min-height: 30px; +} diff --git a/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.js b/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.js new file mode 100644 index 00000000..aeb4b6b8 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/structuredQuery/structuredQuery.js @@ -0,0 +1,95 @@ +(function( $, app, i18n ) { + + var ui = app.ns("ui"); + var data = app.ns("data"); + + var StructuredQuery = ui.AbstractWidget.extend({ + defaults: { + cluster: null // (required) instanceof app.services.Cluster + }, + _baseCls: "uiStructuredQuery", + init: function(parent) { + this._super(); + this.selector = new ui.IndexSelector({ + onIndexChanged: this._indexChanged_handler, + cluster: this.config.cluster + }); + this.el = $(this._main_template()); + this.out = this.el.find("DIV.uiStructuredQuery-out"); + this.attach( parent ); + }, + + _indexChanged_handler: function( index ) { + this.filter && this.filter.remove(); + this.filter = new ui.FilterBrowser({ + cluster: this.config.cluster, + index: index, + onStartingSearch: function() { this.el.find("DIV.uiStructuredQuery-out").text( i18n.text("General.Searching") ); this.el.find("DIV.uiStructuredQuery-src").hide(); }.bind(this), + onSearchSource: this._searchSource_handler, + onResults: this._results_handler + }); + this.el.find(".uiStructuredQuery-body").append(this.filter); + }, + + _results_handler: function( filter, event ) { + var typeMap = { + "json": this._jsonResults_handler, + "table": this._tableResults_handler, + "csv": this._csvResults_handler + }; + typeMap[ event.type ].call( this, event.data, event.metadata ); + }, + _jsonResults_handler: function( results ) { + this.el.find("DIV.uiStructuredQuery-out").empty().append( new ui.JsonPretty({ obj: results })); + }, + _csvResults_handler: function( results ) { + this.el.find("DIV.uiStructuredQuery-out").empty().append( new ui.CSVTable({ results: results })); + }, + _tableResults_handler: function( results, metadata ) { + // hack up a QueryDataSourceInterface so that StructuredQuery keeps working without using a Query object + var qdi = new data.QueryDataSourceInterface({ metadata: metadata, query: new data.Query() }); + var tab = new ui.Table( { + store: qdi, + height: 400, + width: this.out.innerWidth() + } ).attach(this.out.empty()); + qdi._results_handler(qdi.config.query, results); + }, + + _showRawJSON : function() { + if($("#rawJsonText").length === 0) { + var hiddenButton = $("#showRawJSON"); + var jsonText = $({tag: "P", type: "p", id: "rawJsonText"}); + jsonText.text(hiddenButton[0].value); + hiddenButton.parent().append(jsonText); + } + }, + + _searchSource_handler: function(src) { + var searchSourceDiv = this.el.find("DIV.uiStructuredQuery-src"); + searchSourceDiv.empty().append(new app.ui.JsonPretty({ obj: src })); + if(typeof JSON !== "undefined") { + var showRawJSON = $({ tag: "BUTTON", type: "button", text: i18n.text("StructuredQuery.ShowRawJson"), id: "showRawJSON", value: JSON.stringify(src), onclick: this._showRawJSON }); + searchSourceDiv.append(showRawJSON); + } + searchSourceDiv.show(); + }, + + _main_template: function() { + return { tag: "DIV", cls: this._baseCls, children: [ + this.selector, + { tag: "DIV", cls: "uiStructuredQuery-body" }, + { tag: "DIV", cls: "uiStructuredQuery-src", css: { display: "none" } }, + { tag: "DIV", cls: "uiStructuredQuery-out" } + ]}; + } + }); + + ui.StructuredQuery = ui.Page.extend({ + init: function() { + this.q = new StructuredQuery( this.config ); + this.el = this.q.el; + } + }); + +})( this.jQuery, this.app, this.i18n ); diff --git a/elasticsearch/plugins/head/src/app/ui/table/table.css b/elasticsearch/plugins/head/src/app/ui/table/table.css new file mode 100644 index 00000000..ea8e0cfd --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/table/table.css @@ -0,0 +1,92 @@ +.uiTable TABLE { + border-collapse: collapse; +} + +.uiTable-body { + overflow-y: scroll; + overflow-x: auto; +} + +.uiTable-headers { + overflow-x: hidden; +} + +.uiTable-body TD { + white-space: nowrap; +} + +.uiTable-body .uiTable-header-row TH, +.uiTable-body .uiTable-header-row TH DIV { + padding-top: 0; + padding-bottom: 0; +} + +.uiTable-body .uiTable-header-cell > DIV { + height: 0; + overflow: hidden; +} + +.uiTable-headercell-menu { + float: right; +} + +.uiTable-tools { + padding: 3px 4px; + height: 14px; +} + +.uiTable-header-row { + background: #ddd; + background: -moz-linear-gradient(top, #eee, #ccc); + background: -webkit-linear-gradient(top, #eee, #ccc); +} + +.uiTable-headercell-text { + margin-right: 20px; +} + +.uiTable-headercell-menu { + display: none; +} + +.uiTable-header-row TH { + border-right: 1px solid #bbb; + padding: 0; + text-align: left; +} + +.uiTable-header-row TH > DIV { + padding: 3px 4px; + border-right: 1px solid #eee; +} + +.uiTable-headerEndCap > DIV { + width: 19px; +} + +.uiTable-header-row .uiTable-sort { + background: #ccc; + background: -moz-linear-gradient(top, #bebebe, #ccc); + background: -webkit-linear-gradient(top, #bebebe, #ccc); +} +.uiTable-header-row TH.uiTable-sort > DIV { + border-right: 1px solid #ccc; +} + +.uiTable-sort .uiTable-headercell-menu { + display: block; +} + +.uiTable TABLE TD { + border-right: 1px solid transparent; + padding: 3px 4px; +} + +.uiTable-body TABLE TR:nth-child(even) { + background: #f3f3f3; +} + +.uiTable-body TABLE TR.selected { + color: white; + background: #6060f1; +} diff --git a/elasticsearch/plugins/head/src/app/ui/table/table.js b/elasticsearch/plugins/head/src/app/ui/table/table.js new file mode 100644 index 00000000..eb847a05 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/table/table.js @@ -0,0 +1,102 @@ +( function( $, app ) { + + var ui = app.ns("ui"); + + ui.Table = ui.AbstractWidget.extend({ + defaults: { + store: null, // (required) implements interface app.data.DataSourceInterface + height: 0, + width: 0 + }, + _baseCls: "uiTable", + init: function(parent) { + this._super(); + this.initElements(parent); + this.config.store.on("data", this._data_handler); + }, + attach: function(parent) { + if(parent) { + this._super(parent); + this._reflow(); + } + }, + initElements: function(parent) { + this.el = $.joey(this._main_template()); + this.body = this.el.find(".uiTable-body"); + this.headers = this.el.find(".uiTable-headers"); + this.tools = this.el.find(".uiTable-tools"); + this.attach( parent ); + }, + _data_handler: function(store) { + this.tools.text(store.summary); + this.headers.empty().append(this._header_template(store.columns)); + this.body.empty().append(this._body_template(store.data, store.columns)); + this._reflow(); + }, + _reflow: function() { + var firstCol = this.body.find("TR:first TH.uiTable-header-cell > DIV"), + headers = this.headers.find("TR:first TH.uiTable-header-cell > DIV"); + for(var i = 0; i < headers.length; i++) { + $(headers[i]).width( $(firstCol[i]).width() ); + } + this._scroll_handler(); + }, + _scroll_handler: function(ev) { + this.el.find(".uiTable-headers").scrollLeft(this.body.scrollLeft()); + }, + _dataClick_handler: function(ev) { + var row = $(ev.target).closest("TR"); + if(row.length) { + this.fire("rowClick", this, { row: row } ); + } + }, + _headerClick_handler: function(ev) { + var header = $(ev.target).closest("TH.uiTable-header-cell"); + if(header.length) { + this.fire("headerClick", this, { header: header, column: header.data("column"), dir: header.data("dir") }); + } + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), css: { width: this.config.width + "px" }, cls: this._baseCls, children: [ + { tag: "DIV", cls: "uiTable-tools" }, + { tag: "DIV", cls: "uiTable-headers", onclick: this._headerClick_handler }, + { tag: "DIV", cls: "uiTable-body", + onclick: this._dataClick_handler, + onscroll: this._scroll_handler, + css: { height: this.config.height + "px", width: this.config.width + "px" } + } + ] }; + }, + _header_template: function(columns) { + var ret = { tag: "TABLE", children: [ this._headerRow_template(columns) ] }; + ret.children[0].children.push(this._headerEndCap_template()); + return ret; + }, + _headerRow_template: function(columns) { + return { tag: "TR", cls: "uiTable-header-row", children: columns.map(function(column) { + var dir = ((this.config.store.sort.column === column) && this.config.store.sort.dir) || "none"; + return { tag: "TH", data: { column: column, dir: dir }, cls: "uiTable-header-cell" + ((dir !== "none") ? " uiTable-sort" : ""), children: [ + { tag: "DIV", children: [ + { tag: "DIV", cls: "uiTable-headercell-menu", text: dir === "asc" ? "\u25b2" : "\u25bc" }, + { tag: "DIV", cls: "uiTable-headercell-text", text: column } + ]} + ]}; + }, this)}; + }, + _headerEndCap_template: function() { + return { tag: "TH", cls: "uiTable-headerEndCap", children: [ { tag: "DIV" } ] }; + }, + _body_template: function(data, columns) { + return { tag: "TABLE", children: [] + .concat(this._headerRow_template(columns)) + .concat(data.map(function(row) { + return { tag: "TR", data: { row: row }, cls: "uiTable-row", children: columns.map(function(column){ + return { tag: "TD", cls: "uiTable-cell", children: [ { tag: "DIV", text: (row[column] || "").toString() } ] }; + })}; + })) + }; + } + + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/textField/textField.js b/elasticsearch/plugins/head/src/app/ui/textField/textField.js new file mode 100644 index 00000000..c7348962 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/textField/textField.js @@ -0,0 +1,24 @@ +(function( app ) { + + var ui = app.ns("ui"); + + ui.TextField = ui.AbstractField.extend({ + init: function() { + this._super(); + }, + _keyup_handler: function() { + this.fire("change", this ); + }, + _main_template: function() { + return { tag: "DIV", id: this.id(), cls: "uiField uiTextField", children: [ + { tag: "INPUT", + type: "text", + name: this.config.name, + placeholder: this.config.placeholder, + onkeyup: this._keyup_handler + } + ]}; + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ui/textField/textFieldDemo.js b/elasticsearch/plugins/head/src/app/ui/textField/textFieldDemo.js new file mode 100644 index 00000000..25790d48 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/textField/textFieldDemo.js @@ -0,0 +1,13 @@ +$( function() { + + var ui = window.app.ns("ui"); + + window.builder = function() { return ( + { tag: "DIV", children: [ + new ui.TextField({}), + new ui.TextField({ placeholder: "placeholder" }), + new ui.TextField({ onchange: function( tf ) { console.log( tf.val() ); } }) + ] } + ); }; + +}); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.css b/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.css new file mode 100644 index 00000000..e5cf72f1 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.css @@ -0,0 +1,24 @@ +.uiToolbar { + height: 28px; + background: #fdfefe; + background: -moz-linear-gradient(top, #fdfefe, #eaedef); + background: -webkit-linear-gradient(top, #fdfefe, #eaedef); + border-bottom: 1px solid #d2d5d7; + padding: 3px 10px; +} + +.uiToolbar H2 { + display: inline-block; + font-size: 120%; + margin: 0; + padding: 5px 20px 5px 0; +} + +.uiToolbar .uiTextField { + display: inline-block; +} + +.uiToolbar .uiTextField INPUT { + padding-top: 2px; + padding-bottom: 5px; +} \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.js b/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.js new file mode 100644 index 00000000..d64eefe3 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ui/toolbar/toolbar.js @@ -0,0 +1,25 @@ +(function( $, app ) { + + var ui = app.ns("ui"); + + ui.Toolbar = ui.AbstractWidget.extend({ + defaults: { + label: "", + left: [], + right: [] + }, + init: function(parent) { + this._super(); + this.el = $.joey(this._main_template()); + }, + _main_template: function() { + return { tag: "DIV", cls: "uiToolbar", children: [ + { tag: "DIV", cls: "pull-left", children: [ + { tag: "H2", text: this.config.label } + ].concat(this.config.left) }, + { tag: "DIV", cls: "pull-right", children: this.config.right } + ]}; + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ux/class.js b/elasticsearch/plugins/head/src/app/ux/class.js new file mode 100644 index 00000000..80142637 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/class.js @@ -0,0 +1,50 @@ +/** + * base class for creating inheritable classes + * based on resigs 'Simple Javascript Inheritance Class' (based on base2 and prototypejs) + * modified with static super and auto config + * @name Class + * @constructor + */ +(function( $, app ){ + + var ux = app.ns("ux"); + + var initializing = false, fnTest = /\b_super\b/; + + ux.Class = function(){}; + + ux.Class.extend = function(prop) { + function Class() { + if(!initializing) { + var args = Array.prototype.slice.call(arguments); + this.config = $.extend( function(t) { // automatically construct a config object based on defaults and last item passed into the constructor + return $.extend(t._proto && t._proto() && arguments.callee(t._proto()) || {}, t.defaults); + } (this) , args.pop() ); + this.init && this.init.apply(this, args); // automatically run the init function when class created + } + } + + initializing = true; + var prototype = new this(); + initializing = false; + + var _super = this.prototype; + prototype._proto = function() { + return _super; + }; + + for(var name in prop) { + prototype[name] = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { this._super = _super[name]; return fn.apply(this, arguments); }; + })(name, prop[name]) : prop[name]; + } + + Class.prototype = prototype; + Class.constructor = Class; + + Class.extend = arguments.callee; // make class extendable + + return Class; + }; +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ux/dragdrop.js b/elasticsearch/plugins/head/src/app/ux/dragdrop.js new file mode 100644 index 00000000..17ca9be1 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/dragdrop.js @@ -0,0 +1,124 @@ +(function( $, app ) { + + var ux = app.ns("ux"); + + /** + * Provides drag and drop functionality
    + * a DragDrop instance is created for each usage pattern and then used over and over again
    + * first a dragObj is defined - this is the jquery node that will be dragged around
    + * second, the event callbacks are defined - these allow you control the ui during dragging and run functions when successfully dropping
    + * thirdly drop targets are defined - this is a list of DOM nodes, the constructor works in one of two modes: + *
  • without targets - objects can be picked up and dragged around, dragStart and dragStop events fire
  • + *
  • with targets - as objects are dragged over targets dragOver, dragOut and DragDrop events fire + * to start dragging call the DragDrop.pickup_handler() function, dragging stops when the mouse is released. + * @constructor + * The following options are supported + *
    targetSelector
    + *
    an argument passed directly to jquery to create a list of targets, as such it can be a CSS style selector, or an array of DOM nodes
    if target selector is null the DragDrop does Drag only and will not fire dragOver dragOut and dragDrop events
    + *
    pickupSelector
    + *
    a jquery selector. The pickup_handler is automatically bound to matched elements (eg clicking on these elements starts the drag). if pickupSelector is null, the pickup_handler must be manually bound $(el).bind("mousedown", dragdrop.pickup_handler)
    + *
    dragObj
    + *
    the jQuery element to drag around when pickup is called. If not defined, dragObj must be set in onDragStart
    + *
    draggingClass
    + *
    the class(es) added to items when they are being dragged
    + * The following observables are supported + *
    dragStart
    + *
    a callback when start to drag
    function(jEv)
    + *
    dragOver
    + *
    a callback when we drag into a target
    function(jEl)
    + *
    dragOut
    + *
    a callback when we drag out of a target, or when we drop over a target
    function(jEl)
    + *
    dragDrop
    + *
    a callback when we drop on a target
    function(jEl)
    + *
    dragStop
    + *
    a callback when we stop dragging
    function(jEv)
    + */ + ux.DragDrop = ux.Observable.extend({ + defaults : { + targetsSelector : null, + pickupSelector: null, + dragObj : null, + draggingClass : "dragging" + }, + + init: function(options) { + this._super(); // call the class initialiser + + this.drag_handler = this.drag.bind(this); + this.drop_handler = this.drop.bind(this); + this.pickup_handler = this.pickup.bind(this); + this.targets = []; + this.dragObj = null; + this.dragObjOffset = null; + this.currentTarget = null; + if(this.config.pickupSelector) { + $(this.config.pickupSelector).bind("mousedown", this.pickup_handler); + } + }, + + drag : function(jEv) { + jEv.preventDefault(); + var mloc = acx.vector( this.lockX || jEv.pageX, this.lockY || jEv.pageY ); + this.dragObj.css(mloc.add(this.dragObjOffset).asOffset()); + if(this.targets.length === 0) { + return; + } + if(this.currentTarget !== null && mloc.within(this.currentTarget[1], this.currentTarget[2])) { + return; + } + if(this.currentTarget !== null) { + this.fire('dragOut', this.currentTarget[0]); + this.currentTarget = null; + } + for(var i = 0; i < this.targets.length; i++) { + if(mloc.within(this.targets[i][1], this.targets[i][2])) { + this.currentTarget = this.targets[i]; + break; + } + } + if(this.currentTarget !== null) { + this.fire('dragOver', this.currentTarget[0]); + } + }, + + drop : function(jEv) { + $(document).unbind("mousemove", this.drag_handler); + $(document).unbind("mouseup", this.drop_handler); + this.dragObj.removeClass(this.config.draggingClass); + if(this.currentTarget !== null) { + this.fire('dragOut', this.currentTarget[0]); + this.fire('dragDrop', this.currentTarget[0]); + } + this.fire('dragStop', jEv); + this.dragObj = null; + }, + + pickup : function(jEv, opts) { + $.extend(this.config, opts); + this.fire('dragStart', jEv); + this.dragObj = this.dragObj || this.config.dragObj; + this.dragObjOffset = this.config.dragObjOffset || acx.vector(this.dragObj.offset()).sub(jEv.pageX, jEv.pageY); + this.lockX = this.config.lockX ? jEv.pageX : 0; + this.lockY = this.config.lockY ? jEv.pageY : 0; + this.dragObj.addClass(this.config.draggingClass); + if(!this.dragObj.get(0).parentNode || this.dragObj.get(0).parentNode.nodeType === 11) { // 11 = document fragment + $(document.body).append(this.dragObj); + } + if(this.config.targetsSelector) { + this.currentTarget = null; + var targets = ( this.targets = [] ); + // create an array of elements optimised for rapid collision detection calculation + $(this.config.targetsSelector).each(function(i, el) { + var jEl = $(el); + var tl = acx.vector(jEl.offset()); + var br = tl.add(jEl.width(), jEl.height()); + targets.push([jEl, tl, br]); + }); + } + $(document).bind("mousemove", this.drag_handler); + $(document).bind("mouseup", this.drop_handler); + this.drag_handler(jEv); + } + }); + +})( this.jQuery, this.app ); diff --git a/elasticsearch/plugins/head/src/app/ux/fieldCollection.js b/elasticsearch/plugins/head/src/app/ux/fieldCollection.js new file mode 100644 index 00000000..ce4064f4 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/fieldCollection.js @@ -0,0 +1,25 @@ +(function( app ) { + + var ux = app.ns("ux"); + + ux.FieldCollection = ux.Observable.extend({ + defaults: { + fields: [] // the collection of fields + }, + init: function() { + this._super(); + this.fields = this.config.fields; + }, + validate: function() { + return this.fields.reduce(function(r, field) { + return r && field.validate(); + }, true); + }, + getData: function(type) { + return this.fields.reduce(function(r, field) { + r[field.name] = field.val(); return r; + }, {}); + } + }); + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ux/observable.js b/elasticsearch/plugins/head/src/app/ux/observable.js new file mode 100644 index 00000000..01303978 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/observable.js @@ -0,0 +1,46 @@ +(function( app ) { + + var ux = app.ns("ux"); + + ux.Observable = ux.Class.extend((function() { + return { + init: function() { + this.observers = {}; + for( var opt in this.config ) { // automatically install observers that are defined in the configuration + if( opt.indexOf( 'on' ) === 0 ) { + this.on( opt.substring(2) , this.config[ opt ] ); + } + } + }, + _getObs: function( type ) { + return ( this.observers[ type.toLowerCase() ] || ( this.observers[ type.toLowerCase() ] = [] ) ); + }, + on: function( type, fn, params, thisp ) { + this._getObs( type ).push( { "cb" : fn, "args" : params || [] , "cx" : thisp || this } ); + return this; + }, + fire: function( type ) { + var params = Array.prototype.slice.call( arguments, 1 ); + this._getObs( type ).slice().forEach( function( ob ) { + ob["cb"].apply( ob["cx"], ob["args"].concat( params ) ); + } ); + return this; + }, + removeAllObservers: function() { + this.observers = {}; + }, + removeObserver: function( type, fn ) { + var obs = this._getObs( type ), + index = obs.reduce( function(p, t, i) { return (t.cb === fn) ? i : p; }, -1 ); + if(index !== -1) { + obs.splice( index, 1 ); + } + return this; // make observable functions chainable + }, + hasObserver: function( type ) { + return !! this._getObs( type ).length; + } + }; + })()); + +})( this.app ); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/app/ux/singleton.js b/elasticsearch/plugins/head/src/app/ux/singleton.js new file mode 100644 index 00000000..953e4a09 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/singleton.js @@ -0,0 +1,21 @@ +(function( app ) { + + var ux = app.ns("ux"); + + var extend = ux.Observable.extend; + var instance = function() { + if( ! ("me" in this) ) { + this.me = new this(); + } + return this.me; + }; + + ux.Singleton = ux.Observable.extend({}); + + ux.Singleton.extend = function() { + var Self = extend.apply( this, arguments ); + Self.instance = instance; + return Self; + }; + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/app/ux/singletonSpec.js b/elasticsearch/plugins/head/src/app/ux/singletonSpec.js new file mode 100644 index 00000000..96ef6a28 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/singletonSpec.js @@ -0,0 +1,41 @@ +describe("app.ux.singleton", function(){ + +var Singleton = window.app.ux.Singleton; + + describe("creating a singleton", function() { + var X = Singleton.extend({ + foo: function() { + return "bar"; + } + }); + + var Y = Singleton.extend({ + bar: function() { + return "baz"; + } + }); + + it("should have properties like a normal class", function() { + var a = X.instance(); + + expect( a instanceof X ).toBe( true ); + expect( a.foo() ).toBe( "bar" ); + }); + + it("should return single instance each time instance() is called", function() { + var a = X.instance(); + var b = X.instance(); + + expect( a ).toBe( b ); + }); + + it("should not share instances with different singletons", function() { + var a = X.instance(); + var c = Y.instance(); + + expect( a ).not.toBe( c ); + }); + + }); + +}); diff --git a/elasticsearch/plugins/head/src/app/ux/table.css b/elasticsearch/plugins/head/src/app/ux/table.css new file mode 100644 index 00000000..7d829c4c --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/table.css @@ -0,0 +1,20 @@ +TABLE.table { + border-collapse: collapse; +} + + +TABLE.table TH { + font-weight: normal; + text-align: left; + vertical-align: middle; +} + +TABLE.table TBODY.striped TR:nth-child(odd) { + background: #eee; +} + +TABLE.table H3 { + margin: 0; + font-weight: bold; + font-size: 140%; +} diff --git a/elasticsearch/plugins/head/src/app/ux/templates/templateSpec.js b/elasticsearch/plugins/head/src/app/ux/templates/templateSpec.js new file mode 100644 index 00000000..db76b132 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/templates/templateSpec.js @@ -0,0 +1,81 @@ +describe("app.ut.byteSize_template", function() { + + describe("byteSize_template()", function() { + var byteSize_template = window.app.ut.byteSize_template; + + it("should postfix with a B and have not decimal for number less than 1000", function() { + expect( byteSize_template( 0 ) ).toBe( "0B" ); + expect( byteSize_template( 1 ) ).toBe( "1B" ); + expect( byteSize_template( 10 ) ).toBe( "10B" ); + expect( byteSize_template( 100 ) ).toBe( "100B" ); + expect( byteSize_template( 999 ) ).toBe( "999B" ); + }); + + it("should have 0.xxX for values between 1000 and 1023", function() { + expect( byteSize_template( 1000 ) ).toBe( "0.98ki" ); + expect( byteSize_template( 1024 * 1000 ) ).toBe( "0.98Mi" ); + }); + + it("should always have three significant digits", function() { + expect( byteSize_template( 1023 ) ).toBe( "1.00ki" ); + expect( byteSize_template( 1024 ) ).toBe( "1.00ki" ); + expect( byteSize_template( 1025 ) ).toBe( "1.00ki" ); + expect( byteSize_template( 1024 * 5 ) ).toBe( "5.00ki" ); + expect( byteSize_template( 1024 * 55 ) ).toBe( "55.0ki" ); + expect( byteSize_template( 1024 * 555 ) ).toBe( "555ki" ); + }); + + it("should have the correct postfix", function() { + expect( byteSize_template( 3 * Math.pow( 1024, 1) ) ).toBe( "3.00ki" ); + expect( byteSize_template( 3 * Math.pow( 1024, 2) ) ).toBe( "3.00Mi" ); + expect( byteSize_template( 3 * Math.pow( 1024, 3) ) ).toBe( "3.00Gi" ); + expect( byteSize_template( 3 * Math.pow( 1024, 4) ) ).toBe( "3.00Ti" ); + expect( byteSize_template( 3 * Math.pow( 1024, 5) ) ).toBe( "3.00Pi" ); + expect( byteSize_template( 3 * Math.pow( 1024, 6) ) ).toBe( "3.00Ei" ); + expect( byteSize_template( 3 * Math.pow( 1024, 7) ) ).toBe( "3.00Zi" ); + expect( byteSize_template( 3 * Math.pow( 1024, 8) ) ).toBe( "3.00Yi" ); + }); + + it("should show an overflow for stupidly big numbers", function() { + expect( byteSize_template( 3 * Math.pow( 1024, 10) ) ).toBe( "3.00..E" ); + }); + }); + + describe("count_template()", function() { + var count_template = window.app.ut.count_template; + + it("should not postfix and not decimal for number less than 1000", function() { + expect( count_template( 0 ) ).toBe( "0" ); + expect( count_template( 1 ) ).toBe( "1" ); + expect( count_template( 10 ) ).toBe( "10" ); + expect( count_template( 100 ) ).toBe( "100" ); + expect( count_template( 999 ) ).toBe( "999" ); + }); + + it("should always have three significant digits", function() { + expect( count_template( 1000 ) ).toBe( "1.00k" ); + expect( count_template( 1005 ) ).toBe( "1.00k" ); + expect( count_template( 1055 ) ).toBe( "1.05k" ); + expect( count_template( 1000 * 5 ) ).toBe( "5.00k" ); + expect( count_template( 1000 * 55 ) ).toBe( "55.0k" ); + expect( count_template( 1000 * 555 ) ).toBe( "555k" ); + }); + + it("should have the correct postfix", function() { + expect( count_template( 3 * Math.pow( 1000, 1) ) ).toBe( "3.00k" ); + expect( count_template( 3 * Math.pow( 1000, 2) ) ).toBe( "3.00M" ); + expect( count_template( 3 * Math.pow( 1000, 3) ) ).toBe( "3.00G" ); + expect( count_template( 3 * Math.pow( 1000, 4) ) ).toBe( "3.00T" ); + expect( count_template( 3 * Math.pow( 1000, 5) ) ).toBe( "3.00P" ); + expect( count_template( 3 * Math.pow( 1000, 6) ) ).toBe( "3.00E" ); + expect( count_template( 3 * Math.pow( 1000, 7) ) ).toBe( "3.00Z" ); + expect( count_template( 3 * Math.pow( 1000, 8) ) ).toBe( "3.00Y" ); + }); + + it("should show an overflow for stupidly big numbers", function() { + expect( count_template( 3 * Math.pow( 1000, 10) ) ).toBe( "3.00..E" ); + }); + }); + + +}); diff --git a/elasticsearch/plugins/head/src/app/ux/templates/templates.js b/elasticsearch/plugins/head/src/app/ux/templates/templates.js new file mode 100644 index 00000000..3fdb5087 --- /dev/null +++ b/elasticsearch/plugins/head/src/app/ux/templates/templates.js @@ -0,0 +1,32 @@ +(function( app ) { + + var ut = app.ns("ut"); + + ut.option_template = function(v) { return { tag: "OPTION", value: v, text: v }; }; + + ut.require_template = function(f) { return f.require ? { tag: "SPAN", cls: "require", text: "*" } : null; }; + + + var sib_prefix = ['B','ki','Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + + ut.byteSize_template = function(n) { + var i = 0; + while( n >= 1000 ) { + i++; + n /= 1024; + } + return (i === 0 ? n.toString() : n.toFixed( 3 - parseInt(n,10).toString().length )) + ( sib_prefix[ i ] || "..E" ); + }; + + var sid_prefix = ['','k','M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + + ut.count_template = function(n) { + var i = 0; + while( n >= 1000 ) { + i++; + n /= 1000; + } + return i === 0 ? n.toString() : ( n.toFixed( 3 - parseInt(n,10).toString().length ) + ( sid_prefix[ i ] || "..E" ) ); + }; + +})( this.app ); diff --git a/elasticsearch/plugins/head/src/vendor/dateRangeParser/date-range-parser.js b/elasticsearch/plugins/head/src/vendor/dateRangeParser/date-range-parser.js new file mode 100644 index 00000000..b8095fd6 --- /dev/null +++ b/elasticsearch/plugins/head/src/vendor/dateRangeParser/date-range-parser.js @@ -0,0 +1,203 @@ +/*! + * date-range-parser.js + * Contributed to the Apache Software Foundation by: + * Ben Birch - Aconex + * fork me at https://github.com/mobz/date-range-parser + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + +*/ + +(function() { + + var drp = window.dateRangeParser = {}; + + drp.defaultRange = 1000 * 60 * 60 * 24; + + drp.now = null; // set a different value for now than the time at function invocation + + drp.parse = function(v) { + try { + var r = drp._parse(v); + r.end && r.end--; // remove 1 millisecond from the final end range + } catch(e) { + r = null; + } + return r; + }; + + drp.print = function(t, p) { + var format = ["", "-", "-", " ", ":", ":", "."]; + var da = makeArray(t); + var str = ""; + for(var i = 0; i <= p; i++) { + str += format[i] + (da[i] < 10 ? "0" : "") + da[i]; + } + return str; + }; + + (function() { + drp._relTokens = {}; + + var values = { + "yr" : 365*24*60*60*1000, + "mon" : 31*24*60*60*1000, + "day" : 24*60*60*1000, + "hr" : 60*60*1000, + "min" : 60*1000, + "sec" : 1000 + }; + + var alias_lu = { + "yr" : "y,yr,yrs,year,years", + "mon" : "mo,mon,mos,mons,month,months", + "day" : "d,dy,dys,day,days", + "hr" : "h,hr,hrs,hour,hours", + "min" : "m,min,mins,minute,minutes", + "sec" : "s,sec,secs,second,seconds" + }; + + for(var key in alias_lu) { + if(alias_lu.hasOwnProperty(key)) { + var aliases = alias_lu[key].split(","); + for(var i = 0; i < aliases.length; i++) { + drp._relTokens[aliases[i]] = values[key]; + } + } + } + })(); + + function makeArray(d) { + var da = new Date(d); + return [ da.getUTCFullYear(), da.getUTCMonth()+1, da.getUTCDate(), da.getUTCHours(), da.getUTCMinutes(), da.getUTCSeconds(), da.getUTCMilliseconds() ]; + } + + function fromArray(a) { + var d = [].concat(a); d[1]--; + return Date.UTC.apply(null, d); + } + + drp._parse = function parse(v) { + var now = this.now || new Date().getTime(); + + function precArray(d, p, offset) { + var tn = makeArray(d); + tn[p] += offset || 0; + for(var i = p+1; i < 7; i++) { + tn[i] = i < 3 ? 1 : 0; + } + return tn; + } + function makePrecRange(dt, p, r) { + var ret = { }; + ret.start = fromArray(dt); + dt[p] += r || 1; + ret.end = fromArray(dt); + return ret; + } + function procTerm(term) { + var m = term.replace(/\s/g, "").toLowerCase().match(/^([a-z ]+)$|^([ 0-9:-]+)$|^(\d+[a-z]+)$/); + if(m[1]) { // matches ([a-z ]+) + function dra(p, o, r) { + var dt = precArray(now, p, o); + if(r) { + dt[2] -= new Date(fromArray(dt)).getUTCDay(); + } + return makePrecRange(dt, p, r); + } + switch( m[1]) { + case "now" : return { start: now, end: now, now: now }; + case "today" : return dra( 2, 0 ); + case "thisweek" : return dra( 2, 0, 7 ); + case "thismonth" : return dra( 1, 0 ); + case "thisyear" : return dra( 0, 0 ); + case "yesterday" : return dra( 2, -1 ); + case "lastweek" : return dra( 2, -7, 7 ); + case "lastmonth" : return dra( 1, -1 ); + case "lastyear" : return dra( 0, -1 ); + case "tomorrow" : return dra( 2, 1 ); + case "nextweek" : return dra( 2, 7, 7 ); + case "nextmonth" : return dra( 1, 1 ); + case "nextyear" : return dra(0, 1 ); + } + throw "unknown token " + m[1]; + } else if(m[2]) { // matches ([ 0-9:-]+) + dn = makeArray(now); + var dt = m[2].match(/^(?:(\d{4})(?:\-(\d\d))?(?:\-(\d\d))?)? ?(?:(\d{1,2})(?:\:(\d\d)(?:\:(\d\d))?)?)?$/); + dt.shift(); + for(var p = 0, z = false, i = 0; i < 7; i++) { + if(dt[i]) { + dn[i] = parseInt(dt[i], 10); + p = i; + z = true; + } else { + if(z) + dn[i] = i < 3 ? 1 : 0; + } + } + return makePrecRange(dn, p); + } else if(m[3]) { // matches (\d+[a-z]{1,4}) + var dr = m[3].match(/(\d+)\s*([a-z]+)/i); + var n = parseInt(dr[1], 10); + return { rel: n * drp._relTokens[dr[2]] }; + } + throw "unknown term " + term; + } + + if(!v) { + return { start: null, end: null }; + } + var terms = v.split(/\s*([^<>]*[^<>-])?\s*(->|<>|<)?\s*([^<>]+)?\s*/); + + var term1 = terms[1] ? procTerm(terms[1]) : null; + var op = terms[2] || ""; + var term2 = terms[3] ? procTerm(terms[3]) : null; + + if(op === "<" || op === "->" ) { + if(term1 && !term2) { + return { start: term1.start, end: null }; + } else if(!term1 && term2) { + return { start: null, end: term2.end }; + } else { + if(term2.rel) { + return { start: term1.start, end: term1.end + term2.rel }; + } else if(term1.rel) { + return { start: term2.start - term1.rel, end: term2.end }; + } else { + return { start: term1.start, end: term2.end }; + } + } + } else if(op === "<>") { + if(!term2) { + return { start: term1.start - drp.defaultRange, end: term1.end + drp.defaultRange } + } else { + if(! ("rel" in term2)) throw "second term did not hav a range"; + return { start: term1.start - term2.rel, end: term1.end + term2.rel }; + } + } else { + if(term1.rel) { + return { start: now - term1.rel, end: now + term1.rel }; + } else if(term1.now) { + return { start: term1.now - drp.defaultRange, end: term1.now + drp.defaultRange }; + } else { + return { start: term1.start, end: term1.end }; + } + } + throw "could not process value " + v; + }; +})(); \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.css b/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.css new file mode 100644 index 00000000..be7e15cb --- /dev/null +++ b/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.css @@ -0,0 +1,1338 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ +/* FONT PATH + * -------------------------- */ +@font-face { + font-family: 'FontAwesome'; + src: url('fonts/fontawesome-webfont.eot?v=4.0.3'); + src: url('fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} +.fa { + display: inline-block; + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +/* makes the font 33% larger relative to the icon container */ +.fa-lg { + font-size: 1.3333333333333333em; + line-height: 0.75em; + vertical-align: -15%; +} +.fa-2x { + font-size: 2em; +} +.fa-3x { + font-size: 3em; +} +.fa-4x { + font-size: 4em; +} +.fa-5x { + font-size: 5em; +} +.fa-fw { + width: 1.2857142857142858em; + text-align: center; +} +.fa-ul { + padding-left: 0; + margin-left: 2.142857142857143em; + list-style-type: none; +} +.fa-ul > li { + position: relative; +} +.fa-li { + position: absolute; + left: -2.142857142857143em; + width: 2.142857142857143em; + top: 0.14285714285714285em; + text-align: center; +} +.fa-li.fa-lg { + left: -1.8571428571428572em; +} +.fa-border { + padding: .2em .25em .15em; + border: solid 0.08em #eeeeee; + border-radius: .1em; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.fa.pull-left { + margin-right: .3em; +} +.fa.pull-right { + margin-left: .3em; +} +.fa-spin { + -webkit-animation: spin 2s infinite linear; + -moz-animation: spin 2s infinite linear; + -o-animation: spin 2s infinite linear; + animation: spin 2s infinite linear; +} +@-moz-keyframes spin { + 0% { + -moz-transform: rotate(0deg); + } + 100% { + -moz-transform: rotate(359deg); + } +} +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + } +} +@-o-keyframes spin { + 0% { + -o-transform: rotate(0deg); + } + 100% { + -o-transform: rotate(359deg); + } +} +@-ms-keyframes spin { + 0% { + -ms-transform: rotate(0deg); + } + 100% { + -ms-transform: rotate(359deg); + } +} +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} +.fa-rotate-90 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); +} +.fa-rotate-180 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} +.fa-rotate-270 { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); + -webkit-transform: rotate(270deg); + -moz-transform: rotate(270deg); + -ms-transform: rotate(270deg); + -o-transform: rotate(270deg); + transform: rotate(270deg); +} +.fa-flip-horizontal { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); + -webkit-transform: scale(-1, 1); + -moz-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + transform: scale(-1, 1); +} +.fa-flip-vertical { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); + -webkit-transform: scale(1, -1); + -moz-transform: scale(1, -1); + -ms-transform: scale(1, -1); + -o-transform: scale(1, -1); + transform: scale(1, -1); +} +.fa-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.fa-stack-1x, +.fa-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.fa-stack-1x { + line-height: inherit; +} +.fa-stack-2x { + font-size: 2em; +} +.fa-inverse { + color: #ffffff; +} +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ +.fa-glass:before { + content: "\f000"; +} +.fa-music:before { + content: "\f001"; +} +.fa-search:before { + content: "\f002"; +} +.fa-envelope-o:before { + content: "\f003"; +} +.fa-heart:before { + content: "\f004"; +} +.fa-star:before { + content: "\f005"; +} +.fa-star-o:before { + content: "\f006"; +} +.fa-user:before { + content: "\f007"; +} +.fa-film:before { + content: "\f008"; +} +.fa-th-large:before { + content: "\f009"; +} +.fa-th:before { + content: "\f00a"; +} +.fa-th-list:before { + content: "\f00b"; +} +.fa-check:before { + content: "\f00c"; +} +.fa-times:before { + content: "\f00d"; +} +.fa-search-plus:before { + content: "\f00e"; +} +.fa-search-minus:before { + content: "\f010"; +} +.fa-power-off:before { + content: "\f011"; +} +.fa-signal:before { + content: "\f012"; +} +.fa-gear:before, +.fa-cog:before { + content: "\f013"; +} +.fa-trash-o:before { + content: "\f014"; +} +.fa-home:before { + content: "\f015"; +} +.fa-file-o:before { + content: "\f016"; +} +.fa-clock-o:before { + content: "\f017"; +} +.fa-road:before { + content: "\f018"; +} +.fa-download:before { + content: "\f019"; +} +.fa-arrow-circle-o-down:before { + content: "\f01a"; +} +.fa-arrow-circle-o-up:before { + content: "\f01b"; +} +.fa-inbox:before { + content: "\f01c"; +} +.fa-play-circle-o:before { + content: "\f01d"; +} +.fa-rotate-right:before, +.fa-repeat:before { + content: "\f01e"; +} +.fa-refresh:before { + content: "\f021"; +} +.fa-list-alt:before { + content: "\f022"; +} +.fa-lock:before { + content: "\f023"; +} +.fa-flag:before { + content: "\f024"; +} +.fa-headphones:before { + content: "\f025"; +} +.fa-volume-off:before { + content: "\f026"; +} +.fa-volume-down:before { + content: "\f027"; +} +.fa-volume-up:before { + content: "\f028"; +} +.fa-qrcode:before { + content: "\f029"; +} +.fa-barcode:before { + content: "\f02a"; +} +.fa-tag:before { + content: "\f02b"; +} +.fa-tags:before { + content: "\f02c"; +} +.fa-book:before { + content: "\f02d"; +} +.fa-bookmark:before { + content: "\f02e"; +} +.fa-print:before { + content: "\f02f"; +} +.fa-camera:before { + content: "\f030"; +} +.fa-font:before { + content: "\f031"; +} +.fa-bold:before { + content: "\f032"; +} +.fa-italic:before { + content: "\f033"; +} +.fa-text-height:before { + content: "\f034"; +} +.fa-text-width:before { + content: "\f035"; +} +.fa-align-left:before { + content: "\f036"; +} +.fa-align-center:before { + content: "\f037"; +} +.fa-align-right:before { + content: "\f038"; +} +.fa-align-justify:before { + content: "\f039"; +} +.fa-list:before { + content: "\f03a"; +} +.fa-dedent:before, +.fa-outdent:before { + content: "\f03b"; +} +.fa-indent:before { + content: "\f03c"; +} +.fa-video-camera:before { + content: "\f03d"; +} +.fa-picture-o:before { + content: "\f03e"; +} +.fa-pencil:before { + content: "\f040"; +} +.fa-map-marker:before { + content: "\f041"; +} +.fa-adjust:before { + content: "\f042"; +} +.fa-tint:before { + content: "\f043"; +} +.fa-edit:before, +.fa-pencil-square-o:before { + content: "\f044"; +} +.fa-share-square-o:before { + content: "\f045"; +} +.fa-check-square-o:before { + content: "\f046"; +} +.fa-arrows:before { + content: "\f047"; +} +.fa-step-backward:before { + content: "\f048"; +} +.fa-fast-backward:before { + content: "\f049"; +} +.fa-backward:before { + content: "\f04a"; +} +.fa-play:before { + content: "\f04b"; +} +.fa-pause:before { + content: "\f04c"; +} +.fa-stop:before { + content: "\f04d"; +} +.fa-forward:before { + content: "\f04e"; +} +.fa-fast-forward:before { + content: "\f050"; +} +.fa-step-forward:before { + content: "\f051"; +} +.fa-eject:before { + content: "\f052"; +} +.fa-chevron-left:before { + content: "\f053"; +} +.fa-chevron-right:before { + content: "\f054"; +} +.fa-plus-circle:before { + content: "\f055"; +} +.fa-minus-circle:before { + content: "\f056"; +} +.fa-times-circle:before { + content: "\f057"; +} +.fa-check-circle:before { + content: "\f058"; +} +.fa-question-circle:before { + content: "\f059"; +} +.fa-info-circle:before { + content: "\f05a"; +} +.fa-crosshairs:before { + content: "\f05b"; +} +.fa-times-circle-o:before { + content: "\f05c"; +} +.fa-check-circle-o:before { + content: "\f05d"; +} +.fa-ban:before { + content: "\f05e"; +} +.fa-arrow-left:before { + content: "\f060"; +} +.fa-arrow-right:before { + content: "\f061"; +} +.fa-arrow-up:before { + content: "\f062"; +} +.fa-arrow-down:before { + content: "\f063"; +} +.fa-mail-forward:before, +.fa-share:before { + content: "\f064"; +} +.fa-expand:before { + content: "\f065"; +} +.fa-compress:before { + content: "\f066"; +} +.fa-plus:before { + content: "\f067"; +} +.fa-minus:before { + content: "\f068"; +} +.fa-asterisk:before { + content: "\f069"; +} +.fa-exclamation-circle:before { + content: "\f06a"; +} +.fa-gift:before { + content: "\f06b"; +} +.fa-leaf:before { + content: "\f06c"; +} +.fa-fire:before { + content: "\f06d"; +} +.fa-eye:before { + content: "\f06e"; +} +.fa-eye-slash:before { + content: "\f070"; +} +.fa-warning:before, +.fa-exclamation-triangle:before { + content: "\f071"; +} +.fa-plane:before { + content: "\f072"; +} +.fa-calendar:before { + content: "\f073"; +} +.fa-random:before { + content: "\f074"; +} +.fa-comment:before { + content: "\f075"; +} +.fa-magnet:before { + content: "\f076"; +} +.fa-chevron-up:before { + content: "\f077"; +} +.fa-chevron-down:before { + content: "\f078"; +} +.fa-retweet:before { + content: "\f079"; +} +.fa-shopping-cart:before { + content: "\f07a"; +} +.fa-folder:before { + content: "\f07b"; +} +.fa-folder-open:before { + content: "\f07c"; +} +.fa-arrows-v:before { + content: "\f07d"; +} +.fa-arrows-h:before { + content: "\f07e"; +} +.fa-bar-chart-o:before { + content: "\f080"; +} +.fa-twitter-square:before { + content: "\f081"; +} +.fa-facebook-square:before { + content: "\f082"; +} +.fa-camera-retro:before { + content: "\f083"; +} +.fa-key:before { + content: "\f084"; +} +.fa-gears:before, +.fa-cogs:before { + content: "\f085"; +} +.fa-comments:before { + content: "\f086"; +} +.fa-thumbs-o-up:before { + content: "\f087"; +} +.fa-thumbs-o-down:before { + content: "\f088"; +} +.fa-star-half:before { + content: "\f089"; +} +.fa-heart-o:before { + content: "\f08a"; +} +.fa-sign-out:before { + content: "\f08b"; +} +.fa-linkedin-square:before { + content: "\f08c"; +} +.fa-thumb-tack:before { + content: "\f08d"; +} +.fa-external-link:before { + content: "\f08e"; +} +.fa-sign-in:before { + content: "\f090"; +} +.fa-trophy:before { + content: "\f091"; +} +.fa-github-square:before { + content: "\f092"; +} +.fa-upload:before { + content: "\f093"; +} +.fa-lemon-o:before { + content: "\f094"; +} +.fa-phone:before { + content: "\f095"; +} +.fa-square-o:before { + content: "\f096"; +} +.fa-bookmark-o:before { + content: "\f097"; +} +.fa-phone-square:before { + content: "\f098"; +} +.fa-twitter:before { + content: "\f099"; +} +.fa-facebook:before { + content: "\f09a"; +} +.fa-github:before { + content: "\f09b"; +} +.fa-unlock:before { + content: "\f09c"; +} +.fa-credit-card:before { + content: "\f09d"; +} +.fa-rss:before { + content: "\f09e"; +} +.fa-hdd-o:before { + content: "\f0a0"; +} +.fa-bullhorn:before { + content: "\f0a1"; +} +.fa-bell:before { + content: "\f0f3"; +} +.fa-certificate:before { + content: "\f0a3"; +} +.fa-hand-o-right:before { + content: "\f0a4"; +} +.fa-hand-o-left:before { + content: "\f0a5"; +} +.fa-hand-o-up:before { + content: "\f0a6"; +} +.fa-hand-o-down:before { + content: "\f0a7"; +} +.fa-arrow-circle-left:before { + content: "\f0a8"; +} +.fa-arrow-circle-right:before { + content: "\f0a9"; +} +.fa-arrow-circle-up:before { + content: "\f0aa"; +} +.fa-arrow-circle-down:before { + content: "\f0ab"; +} +.fa-globe:before { + content: "\f0ac"; +} +.fa-wrench:before { + content: "\f0ad"; +} +.fa-tasks:before { + content: "\f0ae"; +} +.fa-filter:before { + content: "\f0b0"; +} +.fa-briefcase:before { + content: "\f0b1"; +} +.fa-arrows-alt:before { + content: "\f0b2"; +} +.fa-group:before, +.fa-users:before { + content: "\f0c0"; +} +.fa-chain:before, +.fa-link:before { + content: "\f0c1"; +} +.fa-cloud:before { + content: "\f0c2"; +} +.fa-flask:before { + content: "\f0c3"; +} +.fa-cut:before, +.fa-scissors:before { + content: "\f0c4"; +} +.fa-copy:before, +.fa-files-o:before { + content: "\f0c5"; +} +.fa-paperclip:before { + content: "\f0c6"; +} +.fa-save:before, +.fa-floppy-o:before { + content: "\f0c7"; +} +.fa-square:before { + content: "\f0c8"; +} +.fa-bars:before { + content: "\f0c9"; +} +.fa-list-ul:before { + content: "\f0ca"; +} +.fa-list-ol:before { + content: "\f0cb"; +} +.fa-strikethrough:before { + content: "\f0cc"; +} +.fa-underline:before { + content: "\f0cd"; +} +.fa-table:before { + content: "\f0ce"; +} +.fa-magic:before { + content: "\f0d0"; +} +.fa-truck:before { + content: "\f0d1"; +} +.fa-pinterest:before { + content: "\f0d2"; +} +.fa-pinterest-square:before { + content: "\f0d3"; +} +.fa-google-plus-square:before { + content: "\f0d4"; +} +.fa-google-plus:before { + content: "\f0d5"; +} +.fa-money:before { + content: "\f0d6"; +} +.fa-caret-down:before { + content: "\f0d7"; +} +.fa-caret-up:before { + content: "\f0d8"; +} +.fa-caret-left:before { + content: "\f0d9"; +} +.fa-caret-right:before { + content: "\f0da"; +} +.fa-columns:before { + content: "\f0db"; +} +.fa-unsorted:before, +.fa-sort:before { + content: "\f0dc"; +} +.fa-sort-down:before, +.fa-sort-asc:before { + content: "\f0dd"; +} +.fa-sort-up:before, +.fa-sort-desc:before { + content: "\f0de"; +} +.fa-envelope:before { + content: "\f0e0"; +} +.fa-linkedin:before { + content: "\f0e1"; +} +.fa-rotate-left:before, +.fa-undo:before { + content: "\f0e2"; +} +.fa-legal:before, +.fa-gavel:before { + content: "\f0e3"; +} +.fa-dashboard:before, +.fa-tachometer:before { + content: "\f0e4"; +} +.fa-comment-o:before { + content: "\f0e5"; +} +.fa-comments-o:before { + content: "\f0e6"; +} +.fa-flash:before, +.fa-bolt:before { + content: "\f0e7"; +} +.fa-sitemap:before { + content: "\f0e8"; +} +.fa-umbrella:before { + content: "\f0e9"; +} +.fa-paste:before, +.fa-clipboard:before { + content: "\f0ea"; +} +.fa-lightbulb-o:before { + content: "\f0eb"; +} +.fa-exchange:before { + content: "\f0ec"; +} +.fa-cloud-download:before { + content: "\f0ed"; +} +.fa-cloud-upload:before { + content: "\f0ee"; +} +.fa-user-md:before { + content: "\f0f0"; +} +.fa-stethoscope:before { + content: "\f0f1"; +} +.fa-suitcase:before { + content: "\f0f2"; +} +.fa-bell-o:before { + content: "\f0a2"; +} +.fa-coffee:before { + content: "\f0f4"; +} +.fa-cutlery:before { + content: "\f0f5"; +} +.fa-file-text-o:before { + content: "\f0f6"; +} +.fa-building-o:before { + content: "\f0f7"; +} +.fa-hospital-o:before { + content: "\f0f8"; +} +.fa-ambulance:before { + content: "\f0f9"; +} +.fa-medkit:before { + content: "\f0fa"; +} +.fa-fighter-jet:before { + content: "\f0fb"; +} +.fa-beer:before { + content: "\f0fc"; +} +.fa-h-square:before { + content: "\f0fd"; +} +.fa-plus-square:before { + content: "\f0fe"; +} +.fa-angle-double-left:before { + content: "\f100"; +} +.fa-angle-double-right:before { + content: "\f101"; +} +.fa-angle-double-up:before { + content: "\f102"; +} +.fa-angle-double-down:before { + content: "\f103"; +} +.fa-angle-left:before { + content: "\f104"; +} +.fa-angle-right:before { + content: "\f105"; +} +.fa-angle-up:before { + content: "\f106"; +} +.fa-angle-down:before { + content: "\f107"; +} +.fa-desktop:before { + content: "\f108"; +} +.fa-laptop:before { + content: "\f109"; +} +.fa-tablet:before { + content: "\f10a"; +} +.fa-mobile-phone:before, +.fa-mobile:before { + content: "\f10b"; +} +.fa-circle-o:before { + content: "\f10c"; +} +.fa-quote-left:before { + content: "\f10d"; +} +.fa-quote-right:before { + content: "\f10e"; +} +.fa-spinner:before { + content: "\f110"; +} +.fa-circle:before { + content: "\f111"; +} +.fa-mail-reply:before, +.fa-reply:before { + content: "\f112"; +} +.fa-github-alt:before { + content: "\f113"; +} +.fa-folder-o:before { + content: "\f114"; +} +.fa-folder-open-o:before { + content: "\f115"; +} +.fa-smile-o:before { + content: "\f118"; +} +.fa-frown-o:before { + content: "\f119"; +} +.fa-meh-o:before { + content: "\f11a"; +} +.fa-gamepad:before { + content: "\f11b"; +} +.fa-keyboard-o:before { + content: "\f11c"; +} +.fa-flag-o:before { + content: "\f11d"; +} +.fa-flag-checkered:before { + content: "\f11e"; +} +.fa-terminal:before { + content: "\f120"; +} +.fa-code:before { + content: "\f121"; +} +.fa-reply-all:before { + content: "\f122"; +} +.fa-mail-reply-all:before { + content: "\f122"; +} +.fa-star-half-empty:before, +.fa-star-half-full:before, +.fa-star-half-o:before { + content: "\f123"; +} +.fa-location-arrow:before { + content: "\f124"; +} +.fa-crop:before { + content: "\f125"; +} +.fa-code-fork:before { + content: "\f126"; +} +.fa-unlink:before, +.fa-chain-broken:before { + content: "\f127"; +} +.fa-question:before { + content: "\f128"; +} +.fa-info:before { + content: "\f129"; +} +.fa-exclamation:before { + content: "\f12a"; +} +.fa-superscript:before { + content: "\f12b"; +} +.fa-subscript:before { + content: "\f12c"; +} +.fa-eraser:before { + content: "\f12d"; +} +.fa-puzzle-piece:before { + content: "\f12e"; +} +.fa-microphone:before { + content: "\f130"; +} +.fa-microphone-slash:before { + content: "\f131"; +} +.fa-shield:before { + content: "\f132"; +} +.fa-calendar-o:before { + content: "\f133"; +} +.fa-fire-extinguisher:before { + content: "\f134"; +} +.fa-rocket:before { + content: "\f135"; +} +.fa-maxcdn:before { + content: "\f136"; +} +.fa-chevron-circle-left:before { + content: "\f137"; +} +.fa-chevron-circle-right:before { + content: "\f138"; +} +.fa-chevron-circle-up:before { + content: "\f139"; +} +.fa-chevron-circle-down:before { + content: "\f13a"; +} +.fa-html5:before { + content: "\f13b"; +} +.fa-css3:before { + content: "\f13c"; +} +.fa-anchor:before { + content: "\f13d"; +} +.fa-unlock-alt:before { + content: "\f13e"; +} +.fa-bullseye:before { + content: "\f140"; +} +.fa-ellipsis-h:before { + content: "\f141"; +} +.fa-ellipsis-v:before { + content: "\f142"; +} +.fa-rss-square:before { + content: "\f143"; +} +.fa-play-circle:before { + content: "\f144"; +} +.fa-ticket:before { + content: "\f145"; +} +.fa-minus-square:before { + content: "\f146"; +} +.fa-minus-square-o:before { + content: "\f147"; +} +.fa-level-up:before { + content: "\f148"; +} +.fa-level-down:before { + content: "\f149"; +} +.fa-check-square:before { + content: "\f14a"; +} +.fa-pencil-square:before { + content: "\f14b"; +} +.fa-external-link-square:before { + content: "\f14c"; +} +.fa-share-square:before { + content: "\f14d"; +} +.fa-compass:before { + content: "\f14e"; +} +.fa-toggle-down:before, +.fa-caret-square-o-down:before { + content: "\f150"; +} +.fa-toggle-up:before, +.fa-caret-square-o-up:before { + content: "\f151"; +} +.fa-toggle-right:before, +.fa-caret-square-o-right:before { + content: "\f152"; +} +.fa-euro:before, +.fa-eur:before { + content: "\f153"; +} +.fa-gbp:before { + content: "\f154"; +} +.fa-dollar:before, +.fa-usd:before { + content: "\f155"; +} +.fa-rupee:before, +.fa-inr:before { + content: "\f156"; +} +.fa-cny:before, +.fa-rmb:before, +.fa-yen:before, +.fa-jpy:before { + content: "\f157"; +} +.fa-ruble:before, +.fa-rouble:before, +.fa-rub:before { + content: "\f158"; +} +.fa-won:before, +.fa-krw:before { + content: "\f159"; +} +.fa-bitcoin:before, +.fa-btc:before { + content: "\f15a"; +} +.fa-file:before { + content: "\f15b"; +} +.fa-file-text:before { + content: "\f15c"; +} +.fa-sort-alpha-asc:before { + content: "\f15d"; +} +.fa-sort-alpha-desc:before { + content: "\f15e"; +} +.fa-sort-amount-asc:before { + content: "\f160"; +} +.fa-sort-amount-desc:before { + content: "\f161"; +} +.fa-sort-numeric-asc:before { + content: "\f162"; +} +.fa-sort-numeric-desc:before { + content: "\f163"; +} +.fa-thumbs-up:before { + content: "\f164"; +} +.fa-thumbs-down:before { + content: "\f165"; +} +.fa-youtube-square:before { + content: "\f166"; +} +.fa-youtube:before { + content: "\f167"; +} +.fa-xing:before { + content: "\f168"; +} +.fa-xing-square:before { + content: "\f169"; +} +.fa-youtube-play:before { + content: "\f16a"; +} +.fa-dropbox:before { + content: "\f16b"; +} +.fa-stack-overflow:before { + content: "\f16c"; +} +.fa-instagram:before { + content: "\f16d"; +} +.fa-flickr:before { + content: "\f16e"; +} +.fa-adn:before { + content: "\f170"; +} +.fa-bitbucket:before { + content: "\f171"; +} +.fa-bitbucket-square:before { + content: "\f172"; +} +.fa-tumblr:before { + content: "\f173"; +} +.fa-tumblr-square:before { + content: "\f174"; +} +.fa-long-arrow-down:before { + content: "\f175"; +} +.fa-long-arrow-up:before { + content: "\f176"; +} +.fa-long-arrow-left:before { + content: "\f177"; +} +.fa-long-arrow-right:before { + content: "\f178"; +} +.fa-apple:before { + content: "\f179"; +} +.fa-windows:before { + content: "\f17a"; +} +.fa-android:before { + content: "\f17b"; +} +.fa-linux:before { + content: "\f17c"; +} +.fa-dribbble:before { + content: "\f17d"; +} +.fa-skype:before { + content: "\f17e"; +} +.fa-foursquare:before { + content: "\f180"; +} +.fa-trello:before { + content: "\f181"; +} +.fa-female:before { + content: "\f182"; +} +.fa-male:before { + content: "\f183"; +} +.fa-gittip:before { + content: "\f184"; +} +.fa-sun-o:before { + content: "\f185"; +} +.fa-moon-o:before { + content: "\f186"; +} +.fa-archive:before { + content: "\f187"; +} +.fa-bug:before { + content: "\f188"; +} +.fa-vk:before { + content: "\f189"; +} +.fa-weibo:before { + content: "\f18a"; +} +.fa-renren:before { + content: "\f18b"; +} +.fa-pagelines:before { + content: "\f18c"; +} +.fa-stack-exchange:before { + content: "\f18d"; +} +.fa-arrow-circle-o-right:before { + content: "\f18e"; +} +.fa-arrow-circle-o-left:before { + content: "\f190"; +} +.fa-toggle-left:before, +.fa-caret-square-o-left:before { + content: "\f191"; +} +.fa-dot-circle-o:before { + content: "\f192"; +} +.fa-wheelchair:before { + content: "\f193"; +} +.fa-vimeo-square:before { + content: "\f194"; +} +.fa-turkish-lira:before, +.fa-try:before { + content: "\f195"; +} +.fa-plus-square-o:before { + content: "\f196"; +} diff --git a/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.min.css b/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.min.css new file mode 100644 index 00000000..449d6ac5 --- /dev/null +++ b/elasticsearch/plugins/head/src/vendor/font-awesome/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} \ No newline at end of file diff --git a/elasticsearch/plugins/head/src/vendor/font-awesome/fonts/FontAwesome.otf b/elasticsearch/plugins/head/src/vendor/font-awesome/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 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