├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── basebuild.xml ├── build.xml ├── default.properties ├── lib ├── ant-contrib-0.6.jar ├── splunkdevtools-1.1.jar ├── yuicompressor-2.4.7.jar └── yuicompressor-2.4.8.jar ├── related ├── Slideshow.gif ├── Slideshow_Icon_Color.pxm ├── Slideshow_Icon_White.pxm ├── appIcon.pxm ├── appIconAlt.pxm ├── appIcon_2x.pxm ├── presentation_icon.pxm └── screenshots.pxm └── src ├── README.txt ├── appserver └── static │ ├── appIcon.png │ ├── contrib │ ├── bootstrap-duallist │ │ ├── bootstrap-duallistbox.css │ │ ├── bootstrap-duallistbox.min.css │ │ ├── jquery.bootstrap-duallistbox.js │ │ └── jquery.bootstrap-duallistbox.min.js │ ├── kvstore.js │ ├── nprogress │ │ ├── nprogress.css │ │ └── nprogress.js │ ├── store.min.js │ └── text.js │ ├── css │ ├── HideChrome.css │ └── SlideshowSetupView.css │ ├── js │ ├── templates │ │ └── SlideshowSetupPage.html │ └── views │ │ └── SlideshowSetupView.js │ ├── screenshot.png │ └── slideshow_setup.js ├── default ├── app.conf ├── collections.conf ├── data │ └── ui │ │ ├── nav │ │ └── default.xml │ │ └── views │ │ ├── slideshow_setup.xml │ │ └── slideshow_supportability.xml └── savedsearches.conf ├── metadata └── default.meta └── static ├── appIcon.png ├── appIconAlt.png └── appIcon_2x.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [LukeMurphey] 4 | custom: ['https://www.paypal.com/donate?business=MQSKTS3W7LUTY&item_name=Support+continued+development+of+Splunk+apps¤cy_code=USD'] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /local.properties 2 | .project 3 | .pydevproject 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | .DS_Store? 9 | ._* 10 | .Spotlight-V100 11 | .Trashes 12 | Icon? 13 | ehthumbs.db 14 | Thumbs.db 15 | /tmp/ 16 | 17 | .vscode/* 18 | .ropeproject/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Luke Murphey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I support this app in my free-time and at my own expense. Please consider offering a donation in order to promote continued development. [You can donate on Paypal.](https://www.paypal.com/donate?business=MQSKTS3W7LUTY&item_name=Support+continued+development+of+Splunk+apps¤cy_code=USD) 2 | 3 | splunk-slideshow 4 | ================ 5 | 6 | A Splunk app that will rotate between dashboards on a frequency; useful for displaying content on informational big screens. 7 | 8 | Download it from Splunk-base here: https://splunkbase.splunk.com/app/1799/. 9 | 10 | ![alt](related/Slideshow.gif) 11 | -------------------------------------------------------------------------------- /basebuild.xml: -------------------------------------------------------------------------------- 1 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 126 | 127 | 128 | 132 | 133 | 134 | 135 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 152 | 153 | 154 | 155 | 156 | 157 | No app name was provided, cannot proceed 158 | 159 | 160 | 161 | 162 | 163 | ]]> 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | value.build.packageoutput.directory=tmp/packages 172 | value.build.optimize=true 173 | 174 | 175 | 176 | 177 | # Uncomment the file below and define the location of your Splunk installation in local.properties so that you 178 | # can deploy the application to a Splunk installation automatically: 179 | #value.deploy.splunk_home=C:/Program Files/Splunk 180 | 181 | # Change the following to match your install of Splunk if you want the build script to force Splunk to recognize new web-content automatically 182 | value.deploy.splunkd_url=https://127.0.0.1:8089 183 | value.deploy.splunkweb_url=http://127.0.0.1:8000 184 | value.deploy.splunk_username=admin 185 | value.deploy.splunk_password=changeme 186 | 187 | 188 | 189 | 190 | 191 | Success! 192 | 193 | A build script has been setup. To run your build run the following: 194 | 195 | ant 196 | 197 | Place the source-code of your app in the following "src" directory, a.k.a: 198 | 199 | ${absolute_src_path} 200 | 201 | You can customize the build process by placing properties in the default.properties file or the local.properties files. 202 | 203 | 204 | 205 | 208 | 209 | 210 | 211 | 212 | 213 | 217 | 218 | 222 | 223 | 227 | 228 | 232 | 233 | 234 | 235 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 253 | 254 | 255 | 258 | 259 | 260 | 261 | 262 | 265 | 266 | 267 | 271 | 272 | 273 | 274 | 275 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 299 | 300 | 301 | 302 | 303 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 330 | 331 | 332 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 417 | 418 | 419 | 420 | Revision number is: ${value.build.number} (${value.build.date}) 421 | 422 | 423 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 562 | 563 | 564 | 565 | 566 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | App ${value.build.appname} build ${value.build.number} created: ${value.build.package.file} 596 | 597 | 598 | 599 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 615 | 616 | 1 617 | 618 | 619 | 622 | 623 | 624 | 625 | 626 | 629 | 630 | 631 | 632 | "value.deploy.splunk_home" has not been defined 633 | Declare it in the a local.properties file in the following path: 634 | 635 | ${absolute_src_path}/local.properties 636 | 637 | Below is an example of the entry in the file: 638 | 639 | value.deploy.splunk_home=/Applications/Splunk 640 | 641 | 642 | 643 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | App ${value.build.appname} build ${value.build.number} deployed to ${export_dir} 669 | 670 | 671 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 684 | 685 | 686 | 687 | [settings] 688 | minify_js = False 689 | minify_css = False 690 | js_no_cache = True 691 | cacheEntriesLimit = 0 692 | cacheBytesLimit = 0 693 | enableWebDebug = True 694 | 695 | 696 | 697 | 700 | 701 | SPLUNK_FIPS=1 702 | 703 | 704 | 707 | 708 | 709 | 710 | [settings] 711 | root_endpoint=/custom_endpoint 712 | 713 | 714 | 715 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 759 | 760 | 761 | 762 | 763 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 776 | 777 | 778 | 779 | "splunkd_url" has not been defined 780 | Declare it in the a local.properties file in the following path: 781 | 782 | ${absolute_src_path}/local.properties 783 | 784 | Below is an example of the entry in the file: 785 | 786 | value.deploy.splunkd_url=http://splunk.example.com:8000 787 | 788 | 789 | 790 | 793 | 794 | 795 | 796 | "splunkweb_url" has not been defined 797 | Declare it in the a local.properties file in the following path: 798 | 799 | ${absolute_src_path}/local.properties 800 | 801 | Below is an example of the entry in the file: 802 | 803 | value.deploy.splunkweb_url=https://splunk.example.com:8089 804 | 805 | 806 | 807 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 866 | 867 | 868 | 871 | 872 | 873 | 876 | 877 | 878 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 950 | 951 | 952 | 953 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 984 | 985 | 986 | 987 | 988 | 991 | 992 | 993 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1026 | 1027 | 1028 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1047 | 1048 | 1049 | 1052 | 1053 | 1054 | 1057 | 1058 | 1059 | 1062 | 1063 | 1064 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1083 | 1084 | 1085 | 1086 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1116 | 1117 | 1118 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | appinspect was not found. 1135 | Install appinspect from http://dev.splunk.com/view/appinspect/SP-CAAAFAK if you haven't already. 1136 | 1137 | You can declare the "value.test.appinspect_path" parameter with the path where appinspect is installed if isn't on your system path. 1138 | Declare it in a local.properties file in the following path: 1139 | ${absolute_src_path}/local.properties 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | 2 | value.build.packageoutput.directory=tmp/packages 3 | value.build.optimize=true 4 | -------------------------------------------------------------------------------- /lib/ant-contrib-0.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/lib/ant-contrib-0.6.jar -------------------------------------------------------------------------------- /lib/splunkdevtools-1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/lib/splunkdevtools-1.1.jar -------------------------------------------------------------------------------- /lib/yuicompressor-2.4.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/lib/yuicompressor-2.4.7.jar -------------------------------------------------------------------------------- /lib/yuicompressor-2.4.8.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/lib/yuicompressor-2.4.8.jar -------------------------------------------------------------------------------- /related/Slideshow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/Slideshow.gif -------------------------------------------------------------------------------- /related/Slideshow_Icon_Color.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/Slideshow_Icon_Color.pxm -------------------------------------------------------------------------------- /related/Slideshow_Icon_White.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/Slideshow_Icon_White.pxm -------------------------------------------------------------------------------- /related/appIcon.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/appIcon.pxm -------------------------------------------------------------------------------- /related/appIconAlt.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/appIconAlt.pxm -------------------------------------------------------------------------------- /related/appIcon_2x.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/appIcon_2x.pxm -------------------------------------------------------------------------------- /related/presentation_icon.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/presentation_icon.pxm -------------------------------------------------------------------------------- /related/screenshots.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/related/screenshots.pxm -------------------------------------------------------------------------------- /src/README.txt: -------------------------------------------------------------------------------- 1 | ================================================ 2 | Overview 3 | ================================================ 4 | 5 | A Splunk app that will flip between dashboards on an interval; useful for displaying content on informational big screens. 6 | 7 | 8 | ================================================ 9 | Configuring Splunk 10 | ================================================ 11 | 12 | Install this app into Splunk by doing the following: 13 | 14 | 1. Log in to Splunk Web and navigate to "Apps » Manage Apps" via the app dropdown at the top left of Splunk's user interface 15 | 2. Click the "install app from file" button 16 | 3. Upload the file by clicking "Choose file" and selecting the app 17 | 4. Click upload 18 | 5. Restart Splunk if a dialog asks you to 19 | 20 | 21 | ================================================ 22 | Configuring Slideshow 23 | ================================================ 24 | 25 | Once the app is installed, you can use the app by configuring a new slideshow: 26 | 27 | 1. Open the "Slideshow" app from the main launcher. 28 | 2. Select the views you want to show in the slideshow 29 | 3. Press play to begin the show 30 | 31 | ================================================ 32 | Getting Support 33 | ================================================ 34 | 35 | Go to the following website if you need support: 36 | 37 | http://lukemurphey.net/projects/splunk-slideshow/wiki/Start_here 38 | 39 | 40 | ================================================ 41 | Change History 42 | ================================================ 43 | 44 | +---------+------------------------------------------------------------------------------------------------------------------+ 45 | | Version | Changes | 46 | +---------+------------------------------------------------------------------------------------------------------------------+ 47 | | 0.5 | Initial release | 48 | |---------|------------------------------------------------------------------------------------------------------------------| 49 | | 0.6 | Fixed issue where the dual-list plugin would not load sometimes | 50 | |---------|------------------------------------------------------------------------------------------------------------------| 51 | | 0.7 | Added option that will hide content that is unnecessary for viewing dashboards (controls, footer, etc) | 52 | | | Fixed issue that prevented the app from working on Splunk 6.0 | 53 | | | Added help that described why some views were not available in the list of available views | 54 | |---------|------------------------------------------------------------------------------------------------------------------| 55 | | 0.8 | Improved styling on the filtered text on the setup view | 56 | | | Added controls for pausing, stopping, fast forwarding or rewinding a show | 57 | | | Added support for IE 8 and IE 9 | 58 | |---------|------------------------------------------------------------------------------------------------------------------| 59 | | 1.0 | Fixed grammar issue on the setup view | 60 | |---------|------------------------------------------------------------------------------------------------------------------| 61 | | 1.0.1 | Updated icon for Splunk 6.2 | 62 | |---------|------------------------------------------------------------------------------------------------------------------| 63 | | 2.0 | Completely updated backend that adds support for more views and apps (such as Enterprise Security) | 64 | | | Added ability to enter interval with units and use float values (like 1.5m for 1.5 minutes) | 65 | | | Added predefined selectable intervals | 66 | |---------|------------------------------------------------------------------------------------------------------------------| 67 | | 2.1 | Added the ability to invert colors of the view in the show (to provide a dark theme) | 68 | | | Fixed exception the occurred when the show was stopped in Internet Explorer | 69 | | | Scrollbars are now hidden when the "Hide controls" setting is enabled | 70 | | | Fixed issue where the slideshow always opened in a new window | 71 | | | Removed support for the progress bar on Internet Explorer due to several issues when the window is closed | 72 | |---------|------------------------------------------------------------------------------------------------------------------| 73 | | 2.2 | Employs a new approach to rendering the show views which: | 74 | | | 1) Eliminates the flashing that happens when a view is being switched | 75 | | | 2) Eliminates the moving of controls, header and footer when controls are to be hidden | 76 | | | 3) Adds the ability to stop a show within the slide-show window | 77 | |---------|------------------------------------------------------------------------------------------------------------------| 78 | | 2.2.1 | Added view and information to help explain why some views are not listed | 79 | |---------|------------------------------------------------------------------------------------------------------------------| 80 | | 2.2.2 | Fixed issue where the bottom of some screens was not black even when the invert colors mode was on | 81 | | | CSS and JS files are now minified to decrease load times | 82 | |---------|------------------------------------------------------------------------------------------------------------------| 83 | | 2.3 | Added controls for switching to the next or previous views | 84 | |---------|------------------------------------------------------------------------------------------------------------------| 85 | | 2.3.1 | Fixing memory leak | 86 | |---------|------------------------------------------------------------------------------------------------------------------| 87 | | 2.4 | Added ability to save slide-shows | 88 | |---------|------------------------------------------------------------------------------------------------------------------| 89 | | 2.4.1 | Fixing error where the "save new show" button was disabled preventing users from making a new show | 90 | |---------|------------------------------------------------------------------------------------------------------------------| 91 | | 2.4.2 | Fixing back button that failed to work | 92 | |---------|------------------------------------------------------------------------------------------------------------------| 93 | | 2.4.2 | Making the app work on Splunk 6.5 | 94 | | | Updating icon to work with newer version of Splunk-base | 95 | |---------|------------------------------------------------------------------------------------------------------------------| 96 | | 2.5.0 | Updated the icon | 97 | | | Added support for loading custom URLs | 98 | |---------|------------------------------------------------------------------------------------------------------------------| 99 | | 2.5.1 | Fixed issue where the slideshow did not execute correctly | 100 | |---------|------------------------------------------------------------------------------------------------------------------| 101 | | 2.6 | Added ability to hide the progress-bar (useful for remote displays such as X11 over SSH) | 102 | |---------|------------------------------------------------------------------------------------------------------------------| 103 | | 2.6.1 | Fixed App inspect issues | 104 | |---------|------------------------------------------------------------------------------------------------------------------| 105 | | 2.6.2 | Fixed App inspect issues | 106 | +---------+------------------------------------------------------------------------------------------------------------------+ 107 | -------------------------------------------------------------------------------- /src/appserver/static/appIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/src/appserver/static/appIcon.png -------------------------------------------------------------------------------- /src/appserver/static/contrib/bootstrap-duallist/bootstrap-duallistbox.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Duallistbox - v3.0.1 3 | * A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices. 4 | * http://www.virtuosoft.eu/code/bootstrap-duallistbox/ 5 | * 6 | * Made by István Ujj-Mészáros 7 | * Under Apache License v2.0 License 8 | */ 9 | .bootstrap-duallistbox-container .buttons { 10 | width: 100%; 11 | margin-bottom: -1px; 12 | } 13 | 14 | .bootstrap-duallistbox-container label { 15 | display: block; 16 | } 17 | 18 | .bootstrap-duallistbox-container .info { 19 | display: inline-block; 20 | margin-bottom: 5px; 21 | font-size: 11px; 22 | } 23 | 24 | .bootstrap-duallistbox-container .clear1, 25 | .bootstrap-duallistbox-container .clear2 { 26 | display: none; 27 | font-size: 10px; 28 | } 29 | 30 | .bootstrap-duallistbox-container .box1.filtered .clear1, 31 | .bootstrap-duallistbox-container .box2.filtered .clear2 { 32 | display: inline-block; 33 | } 34 | 35 | .bootstrap-duallistbox-container .move, 36 | .bootstrap-duallistbox-container .remove { 37 | width: 60%; 38 | } 39 | 40 | .bootstrap-duallistbox-container .btn-group .btn { 41 | border-bottom-left-radius: 0; 42 | border-bottom-right-radius: 0; 43 | } 44 | .bootstrap-duallistbox-container select { 45 | border-top-left-radius: 0; 46 | border-top-right-radius: 0; 47 | } 48 | 49 | .bootstrap-duallistbox-container .moveall, 50 | .bootstrap-duallistbox-container .removeall { 51 | width: 40%; 52 | } 53 | 54 | .bootstrap-duallistbox-container.bs2compatible .btn-group > .btn + .btn { 55 | margin-left: 0; 56 | } 57 | 58 | .bootstrap-duallistbox-container select { 59 | width: 100%; 60 | height: 300px; 61 | padding: 0; 62 | } 63 | 64 | .bootstrap-duallistbox-container .filter { 65 | display: inline-block; 66 | width: 100%; 67 | height: 31px; 68 | margin: 0 0 5px 0; 69 | -webkit-box-sizing: border-box; 70 | -moz-box-sizing: border-box; 71 | box-sizing: border-box; 72 | } 73 | 74 | .bootstrap-duallistbox-container .filter.placeholder { 75 | color: #aaa; 76 | } 77 | 78 | .bootstrap-duallistbox-container.moveonselect .move, 79 | .bootstrap-duallistbox-container.moveonselect .remove { 80 | display:none; 81 | } 82 | 83 | .bootstrap-duallistbox-container.moveonselect .moveall, 84 | .bootstrap-duallistbox-container.moveonselect .removeall { 85 | width: 100%; 86 | } 87 | -------------------------------------------------------------------------------- /src/appserver/static/contrib/bootstrap-duallist/bootstrap-duallistbox.min.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Duallistbox - v3.0.1 3 | * A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices. 4 | * http://www.virtuosoft.eu/code/bootstrap-duallistbox/ 5 | * 6 | * Made by István Ujj-Mészáros 7 | * Under Apache License v2.0 License 8 | */ 9 | 10 | .bootstrap-duallistbox-container .buttons{width:100%;margin-bottom:-1px}.bootstrap-duallistbox-container label{display:block}.bootstrap-duallistbox-container .info{display:inline-block;margin-bottom:5px;font-size:11px}.bootstrap-duallistbox-container .clear1,.bootstrap-duallistbox-container .clear2{display:none;font-size:10px}.bootstrap-duallistbox-container .box1.filtered .clear1,.bootstrap-duallistbox-container .box2.filtered .clear2{display:inline-block}.bootstrap-duallistbox-container .move,.bootstrap-duallistbox-container .remove{width:60%}.bootstrap-duallistbox-container .btn-group .btn{border-bottom-left-radius:0;border-bottom-right-radius:0}.bootstrap-duallistbox-container select{border-top-left-radius:0;border-top-right-radius:0}.bootstrap-duallistbox-container .moveall,.bootstrap-duallistbox-container .removeall{width:40%}.bootstrap-duallistbox-container.bs2compatible .btn-group>.btn+.btn{margin-left:0}.bootstrap-duallistbox-container select{width:100%;height:300px;padding:0}.bootstrap-duallistbox-container .filter{display:inline-block;width:100%;height:31px;margin:0 0 5px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-duallistbox-container .filter.placeholder{color:#aaa}.bootstrap-duallistbox-container.moveonselect .move,.bootstrap-duallistbox-container.moveonselect .remove{display:none}.bootstrap-duallistbox-container.moveonselect .moveall,.bootstrap-duallistbox-container.moveonselect .removeall{width:100%} -------------------------------------------------------------------------------- /src/appserver/static/contrib/bootstrap-duallist/jquery.bootstrap-duallistbox.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Duallistbox - v3.0.1 3 | * A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices. 4 | * http://www.virtuosoft.eu/code/bootstrap-duallistbox/ 5 | * 6 | * Made by István Ujj-Mészáros 7 | * Under Apache License v2.0 License 8 | */ 9 | ;(function ($, window, document, undefined) { 10 | // Create the defaults once 11 | var pluginName = 'bootstrapDualListbox', 12 | defaults = { 13 | bootstrap2Compatible: false, 14 | filterTextClear: 'show all', 15 | filterPlaceHolder: 'Filter', 16 | moveSelectedLabel: 'Move selected', 17 | moveAllLabel: 'Move all', 18 | removeSelectedLabel: 'Remove selected', 19 | removeAllLabel: 'Remove all', 20 | moveOnSelect: true, // true/false (forced true on androids, see the comment later) 21 | preserveSelectionOnMove: false, // 'all' / 'moved' / false 22 | selectedListLabel: false, // 'string', false 23 | nonSelectedListLabel: false, // 'string', false 24 | helperSelectNamePostfix: '_helper', // 'string_of_postfix' / false 25 | selectOrMinimalHeight: 100, 26 | showFilterInputs: true, // whether to show filter inputs 27 | nonSelectedFilter: '', // string, filter the non selected options 28 | selectedFilter: '', // string, filter the selected options 29 | infoText: 'Showing all {0}', // text when all options are visible / false for no info text 30 | infoTextFiltered: 'Filtered {0} from {1}', // when not all of the options are visible due to the filter 31 | infoTextEmpty: 'Empty list', // when there are no options present in the list 32 | filterOnValues: false // filter by selector's values, boolean 33 | }, 34 | // Selections are invisible on android if the containing select is styled with CSS 35 | // http://code.google.com/p/android/issues/detail?id=16922 36 | isBuggyAndroid = /android/i.test(navigator.userAgent.toLowerCase()); 37 | 38 | // The actual plugin constructor 39 | function BootstrapDualListbox(element, options) { 40 | this.element = $(element); 41 | // jQuery has an extend method which merges the contents of two or 42 | // more objects, storing the result in the first object. The first object 43 | // is generally empty as we don't want to alter the default options for 44 | // future instances of the plugin 45 | this.settings = $.extend({}, defaults, options); 46 | this._defaults = defaults; 47 | this._name = pluginName; 48 | this.init(); 49 | } 50 | 51 | function triggerChangeEvent(dualListbox) { 52 | dualListbox.element.trigger('change'); 53 | } 54 | 55 | function updateSelectionStates(dualListbox) { 56 | dualListbox.element.find('option').each(function(index, item) { 57 | var $item = $(item); 58 | if (typeof($item.data('original-index')) === 'undefined') { 59 | $item.data('original-index', dualListbox.elementCount++); 60 | } 61 | if (typeof($item.data('_selected')) === 'undefined') { 62 | $item.data('_selected', false); 63 | } 64 | }); 65 | } 66 | 67 | function changeSelectionState(dualListbox, original_index, selected) { 68 | dualListbox.element.find('option').each(function(index, item) { 69 | var $item = $(item); 70 | if ($item.data('original-index') === original_index) { 71 | $item.prop('selected', selected); 72 | } 73 | }); 74 | } 75 | 76 | function formatString(s, args) { 77 | return s.replace(/\{(\d+)\}/g, function(match, number) { 78 | return typeof args[number] !== 'undefined' ? args[number] : match; 79 | }); 80 | } 81 | 82 | function refreshInfo(dualListbox) { 83 | if (!dualListbox.settings.infoText) { 84 | return; 85 | } 86 | 87 | var visible1 = dualListbox.elements.select1.find('option').length, 88 | visible2 = dualListbox.elements.select2.find('option').length, 89 | all1 = dualListbox.element.find('option').length - dualListbox.selectedElements, 90 | all2 = dualListbox.selectedElements, 91 | content = ''; 92 | 93 | if (all1 === 0) { 94 | content = dualListbox.settings.infoTextEmpty; 95 | } else if (visible1 === all1) { 96 | content = formatString(dualListbox.settings.infoText, [visible1, all1]); 97 | } else { 98 | content = formatString(dualListbox.settings.infoTextFiltered, [visible1, all1]); 99 | } 100 | 101 | dualListbox.elements.info1.html(content); 102 | dualListbox.elements.box1.toggleClass('filtered', !(visible1 === all1 || all1 === 0)); 103 | 104 | if (all2 === 0) { 105 | content = dualListbox.settings.infoTextEmpty; 106 | } else if (visible2 === all2) { 107 | content = formatString(dualListbox.settings.infoText, [visible2, all2]); 108 | } else { 109 | content = formatString(dualListbox.settings.infoTextFiltered, [visible2, all2]); 110 | } 111 | 112 | dualListbox.elements.info2.html(content); 113 | dualListbox.elements.box2.toggleClass('filtered', !(visible2 === all2 || all2 === 0)); 114 | } 115 | 116 | function refreshSelects(dualListbox) { 117 | dualListbox.selectedElements = 0; 118 | 119 | dualListbox.elements.select1.empty(); 120 | dualListbox.elements.select2.empty(); 121 | 122 | dualListbox.element.find('option').each(function(index, item) { 123 | var $item = $(item); 124 | if ($item.prop('selected')) { 125 | dualListbox.selectedElements++; 126 | dualListbox.elements.select2.append($item.clone(true).prop('selected', $item.data('_selected'))); 127 | } else { 128 | dualListbox.elements.select1.append($item.clone(true).prop('selected', $item.data('_selected'))); 129 | } 130 | }); 131 | 132 | if (dualListbox.settings.showFilterInputs) { 133 | filter(dualListbox, 1); 134 | filter(dualListbox, 2); 135 | } 136 | refreshInfo(dualListbox); 137 | } 138 | 139 | function filter(dualListbox, selectIndex) { 140 | if (!dualListbox.settings.showFilterInputs) { 141 | return; 142 | } 143 | 144 | saveSelections(dualListbox, selectIndex); 145 | 146 | dualListbox.elements['select'+selectIndex].empty().scrollTop(0); 147 | var regex = new RegExp($.trim(dualListbox.elements['filterInput'+selectIndex].val()), 'gi'), 148 | options = dualListbox.element; 149 | 150 | if (selectIndex === 1) { 151 | options = options.find('option').not(':selected'); 152 | } else { 153 | options = options.find('option:selected'); 154 | } 155 | 156 | options.each(function(index, item) { 157 | var $item = $(item), 158 | isFiltered = true; 159 | if (item.text.match(regex) || (dualListbox.settings.filterOnValues && $item.attr('value').match(regex) ) ) { 160 | isFiltered = false; 161 | dualListbox.elements['select'+selectIndex].append($item.clone(true).prop('selected', $item.data('_selected'))); 162 | } 163 | dualListbox.element.find('option').eq($item.data('original-index')).data('filtered'+selectIndex, isFiltered); 164 | }); 165 | 166 | refreshInfo(dualListbox); 167 | } 168 | 169 | function saveSelections(dualListbox, selectIndex) { 170 | dualListbox.elements['select'+selectIndex].find('option').each(function(index, item) { 171 | var $item = $(item); 172 | dualListbox.element.find('option').eq($item.data('original-index')).data('_selected', $item.prop('selected')); 173 | }); 174 | } 175 | 176 | function sortOptions(select) { 177 | select.find('option').sort(function(a, b) { 178 | return ($(a).data('original-index') > $(b).data('original-index')) ? 1 : -1; 179 | }).appendTo(select); 180 | } 181 | 182 | function clearSelections(dualListbox) { 183 | dualListbox.elements.select1.find('option').each(function() { 184 | dualListbox.element.find('option').data('_selected', false); 185 | }); 186 | } 187 | 188 | function move(dualListbox) { 189 | if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) { 190 | saveSelections(dualListbox, 1); 191 | saveSelections(dualListbox, 2); 192 | } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) { 193 | saveSelections(dualListbox, 1); 194 | } 195 | 196 | dualListbox.elements.select1.find('option:selected').each(function(index, item) { 197 | var $item = $(item); 198 | if (!$item.data('filtered1')) { 199 | changeSelectionState(dualListbox, $item.data('original-index'), true); 200 | } 201 | }); 202 | 203 | refreshSelects(dualListbox); 204 | triggerChangeEvent(dualListbox); 205 | sortOptions(dualListbox.elements.select2); 206 | } 207 | 208 | function remove(dualListbox) { 209 | if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) { 210 | saveSelections(dualListbox, 1); 211 | saveSelections(dualListbox, 2); 212 | } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) { 213 | saveSelections(dualListbox, 2); 214 | } 215 | 216 | dualListbox.elements.select2.find('option:selected').each(function(index, item) { 217 | var $item = $(item); 218 | if (!$item.data('filtered2')) { 219 | changeSelectionState(dualListbox, $item.data('original-index'), false); 220 | } 221 | }); 222 | 223 | refreshSelects(dualListbox); 224 | triggerChangeEvent(dualListbox); 225 | sortOptions(dualListbox.elements.select1); 226 | } 227 | 228 | function moveAll(dualListbox) { 229 | if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) { 230 | saveSelections(dualListbox, 1); 231 | saveSelections(dualListbox, 2); 232 | } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) { 233 | saveSelections(dualListbox, 1); 234 | } 235 | 236 | dualListbox.element.find('option').each(function(index, item) { 237 | var $item = $(item); 238 | if (!$item.data('filtered1')) { 239 | $item.prop('selected', true); 240 | } 241 | }); 242 | 243 | refreshSelects(dualListbox); 244 | triggerChangeEvent(dualListbox); 245 | } 246 | 247 | function removeAll(dualListbox) { 248 | if (dualListbox.settings.preserveSelectionOnMove === 'all' && !dualListbox.settings.moveOnSelect) { 249 | saveSelections(dualListbox, 1); 250 | saveSelections(dualListbox, 2); 251 | } else if (dualListbox.settings.preserveSelectionOnMove === 'moved' && !dualListbox.settings.moveOnSelect) { 252 | saveSelections(dualListbox, 2); 253 | } 254 | 255 | dualListbox.element.find('option').each(function(index, item) { 256 | var $item = $(item); 257 | if (!$item.data('filtered2')) { 258 | $item.prop('selected', false); 259 | } 260 | }); 261 | 262 | refreshSelects(dualListbox); 263 | triggerChangeEvent(dualListbox); 264 | } 265 | 266 | function bindEvents(dualListbox) { 267 | dualListbox.elements.form.submit(function(e) { 268 | if (dualListbox.elements.filterInput1.is(':focus')) { 269 | e.preventDefault(); 270 | dualListbox.elements.filterInput1.focusout(); 271 | } else if (dualListbox.elements.filterInput2.is(':focus')) { 272 | e.preventDefault(); 273 | dualListbox.elements.filterInput2.focusout(); 274 | } 275 | }); 276 | 277 | dualListbox.element.on('bootstrapDualListbox.refresh', function(e, mustClearSelections){ 278 | dualListbox.refresh(mustClearSelections); 279 | }); 280 | 281 | dualListbox.elements.filterClear1.on('click', function() { 282 | dualListbox.setNonSelectedFilter('', true); 283 | }); 284 | 285 | dualListbox.elements.filterClear2.on('click', function() { 286 | dualListbox.setSelectedFilter('', true); 287 | }); 288 | 289 | dualListbox.elements.moveButton.on('click', function() { 290 | move(dualListbox); 291 | }); 292 | 293 | dualListbox.elements.moveAllButton.on('click', function() { 294 | moveAll(dualListbox); 295 | }); 296 | 297 | dualListbox.elements.removeButton.on('click', function() { 298 | remove(dualListbox); 299 | }); 300 | 301 | dualListbox.elements.removeAllButton.on('click', function() { 302 | removeAll(dualListbox); 303 | }); 304 | 305 | dualListbox.elements.filterInput1.on('change keyup', function() { 306 | filter(dualListbox, 1); 307 | }); 308 | 309 | dualListbox.elements.filterInput2.on('change keyup', function() { 310 | filter(dualListbox, 2); 311 | }); 312 | } 313 | 314 | BootstrapDualListbox.prototype = { 315 | init: function () { 316 | // Add the custom HTML template 317 | this.container = $('' + 318 | '
' + 319 | '
' + 320 | ' ' + 321 | ' ' + 322 | ' ' + 323 | ' ' + 324 | ' ' + 325 | ' ' + 326 | '
' + 327 | ' ' + 331 | ' ' + 334 | '
' + 335 | ' ' + 336 | '
' + 337 | '
' + 338 | ' ' + 339 | ' ' + 340 | ' ' + 341 | ' ' + 342 | ' ' + 343 | ' ' + 344 | '
' + 345 | ' ' + 348 | ' ' + 352 | '
' + 353 | ' ' + 354 | '
' + 355 | '
') 356 | .insertBefore(this.element); 357 | 358 | // Cache the inner elements 359 | this.elements = { 360 | originalSelect: this.element, 361 | box1: $('.box1', this.container), 362 | box2: $('.box2', this.container), 363 | filterInput1: $('.box1 .filter', this.container), 364 | filterInput2: $('.box2 .filter', this.container), 365 | filterClear1: $('.box1 .clear1', this.container), 366 | filterClear2: $('.box2 .clear2', this.container), 367 | label1: $('.box1 > label', this.container), 368 | label2: $('.box2 > label', this.container), 369 | info1: $('.box1 .info', this.container), 370 | info2: $('.box2 .info', this.container), 371 | select1: $('.box1 select', this.container), 372 | select2: $('.box2 select', this.container), 373 | moveButton: $('.box1 .move', this.container), 374 | removeButton: $('.box2 .remove', this.container), 375 | moveAllButton: $('.box1 .moveall', this.container), 376 | removeAllButton: $('.box2 .removeall', this.container), 377 | form: $($('.box1 .filter', this.container)[0].form) 378 | }; 379 | 380 | // Set select IDs 381 | this.originalSelectName = this.element.attr('name') || ''; 382 | var select1Id = 'bootstrap-duallistbox-nonselected-list_' + this.originalSelectName, 383 | select2Id = 'bootstrap-duallistbox-selected-list_' + this.originalSelectName; 384 | this.elements.select1.attr('id', select1Id); 385 | this.elements.select2.attr('id', select2Id); 386 | this.elements.label1.attr('for', select1Id); 387 | this.elements.label2.attr('for', select2Id); 388 | 389 | // Apply all settings 390 | this.selectedElements = 0; 391 | this.elementCount = 0; 392 | this.setBootstrap2Compatible(this.settings.bootstrap2Compatible); 393 | this.setFilterTextClear(this.settings.filterTextClear); 394 | this.setFilterPlaceHolder(this.settings.filterPlaceHolder); 395 | this.setMoveSelectedLabel(this.settings.moveSelectedLabel); 396 | this.setMoveAllLabel(this.settings.moveAllLabel); 397 | this.setRemoveSelectedLabel(this.settings.removeSelectedLabel); 398 | this.setRemoveAllLabel(this.settings.removeAllLabel); 399 | this.setMoveOnSelect(this.settings.moveOnSelect); 400 | this.setPreserveSelectionOnMove(this.settings.preserveSelectionOnMove); 401 | this.setSelectedListLabel(this.settings.selectedListLabel); 402 | this.setNonSelectedListLabel(this.settings.nonSelectedListLabel); 403 | this.setHelperSelectNamePostfix(this.settings.helperSelectNamePostfix); 404 | this.setSelectOrMinimalHeight(this.settings.selectOrMinimalHeight); 405 | 406 | updateSelectionStates(this); 407 | 408 | this.setShowFilterInputs(this.settings.showFilterInputs); 409 | this.setNonSelectedFilter(this.settings.nonSelectedFilter); 410 | this.setSelectedFilter(this.settings.selectedFilter); 411 | this.setInfoText(this.settings.infoText); 412 | this.setInfoTextFiltered(this.settings.infoTextFiltered); 413 | this.setInfoTextEmpty(this.settings.infoTextEmpty); 414 | this.setFilterOnValues(this.settings.filterOnValues); 415 | 416 | // Hide the original select 417 | this.element.hide(); 418 | 419 | bindEvents(this); 420 | refreshSelects(this); 421 | 422 | return this.element; 423 | }, 424 | setBootstrap2Compatible: function(value, refresh) { 425 | this.settings.bootstrap2Compatible = value; 426 | if (value) { 427 | this.container.removeClass('row').addClass('row-fluid bs2compatible'); 428 | this.container.find('.box1, .box2').removeClass('col-md-6').addClass('span6'); 429 | this.container.find('.clear1, .clear2').removeClass('btn-default btn-xs').addClass('btn-mini'); 430 | this.container.find('input, select').removeClass('form-control'); 431 | this.container.find('.btn').removeClass('btn-default'); 432 | this.container.find('.moveall > i, .move > i').removeClass('glyphicon glyphicon-arrow-right').addClass('icon-arrow-right'); 433 | this.container.find('.removeall > i, .remove > i').removeClass('glyphicon glyphicon-arrow-left').addClass('icon-arrow-left'); 434 | } else { 435 | this.container.removeClass('row-fluid bs2compatible').addClass('row'); 436 | this.container.find('.box1, .box2').removeClass('span6').addClass('col-md-6'); 437 | this.container.find('.clear1, .clear2').removeClass('btn-mini').addClass('btn-default btn-xs'); 438 | this.container.find('input, select').addClass('form-control'); 439 | this.container.find('.btn').addClass('btn-default'); 440 | this.container.find('.moveall > i, .move > i').removeClass('icon-arrow-right').addClass('glyphicon glyphicon-arrow-right'); 441 | this.container.find('.removeall > i, .remove > i').removeClass('icon-arrow-left').addClass('glyphicon glyphicon-arrow-left'); 442 | } 443 | if (refresh) { 444 | refreshSelects(this); 445 | } 446 | return this.element; 447 | }, 448 | setFilterTextClear: function(value, refresh) { 449 | this.settings.filterTextClear = value; 450 | this.elements.filterClear1.html(value); 451 | this.elements.filterClear2.html(value); 452 | if (refresh) { 453 | refreshSelects(this); 454 | } 455 | return this.element; 456 | }, 457 | setFilterPlaceHolder: function(value, refresh) { 458 | this.settings.filterPlaceHolder = value; 459 | this.elements.filterInput1.attr('placeholder', value); 460 | this.elements.filterInput2.attr('placeholder', value); 461 | if (refresh) { 462 | refreshSelects(this); 463 | } 464 | return this.element; 465 | }, 466 | setMoveSelectedLabel: function(value, refresh) { 467 | this.settings.moveSelectedLabel = value; 468 | this.elements.moveButton.attr('title', value); 469 | if (refresh) { 470 | refreshSelects(this); 471 | } 472 | return this.element; 473 | }, 474 | setMoveAllLabel: function(value, refresh) { 475 | this.settings.moveAllLabel = value; 476 | this.elements.moveAllButton.attr('title', value); 477 | if (refresh) { 478 | refreshSelects(this); 479 | } 480 | return this.element; 481 | }, 482 | setRemoveSelectedLabel: function(value, refresh) { 483 | this.settings.removeSelectedLabel = value; 484 | this.elements.removeButton.attr('title', value); 485 | if (refresh) { 486 | refreshSelects(this); 487 | } 488 | return this.element; 489 | }, 490 | setRemoveAllLabel: function(value, refresh) { 491 | this.settings.removeAllLabel = value; 492 | this.elements.removeAllButton.attr('title', value); 493 | if (refresh) { 494 | refreshSelects(this); 495 | } 496 | return this.element; 497 | }, 498 | setMoveOnSelect: function(value, refresh) { 499 | if (isBuggyAndroid) { 500 | value = true; 501 | } 502 | this.settings.moveOnSelect = value; 503 | if (this.settings.moveOnSelect) { 504 | this.container.addClass('moveonselect'); 505 | var self = this; 506 | this.elements.select1.on('change', function() { 507 | move(self); 508 | }); 509 | this.elements.select2.on('change', function() { 510 | remove(self); 511 | }); 512 | } else { 513 | this.container.removeClass('moveonselect'); 514 | this.elements.select1.off('change'); 515 | this.elements.select2.off('change'); 516 | } 517 | if (refresh) { 518 | refreshSelects(this); 519 | } 520 | return this.element; 521 | }, 522 | setPreserveSelectionOnMove: function(value, refresh) { 523 | // We are forcing to move on select and disabling preserveSelectionOnMove on Android 524 | if (isBuggyAndroid) { 525 | value = false; 526 | } 527 | this.settings.preserveSelectionOnMove = value; 528 | if (refresh) { 529 | refreshSelects(this); 530 | } 531 | return this.element; 532 | }, 533 | setSelectedListLabel: function(value, refresh) { 534 | this.settings.selectedListLabel = value; 535 | if (value) { 536 | this.elements.label2.show().html(value); 537 | } else { 538 | this.elements.label2.hide().html(value); 539 | } 540 | if (refresh) { 541 | refreshSelects(this); 542 | } 543 | return this.element; 544 | }, 545 | setNonSelectedListLabel: function(value, refresh) { 546 | this.settings.nonSelectedListLabel = value; 547 | if (value) { 548 | this.elements.label1.show().html(value); 549 | } else { 550 | this.elements.label1.hide().html(value); 551 | } 552 | if (refresh) { 553 | refreshSelects(this); 554 | } 555 | return this.element; 556 | }, 557 | setHelperSelectNamePostfix: function(value, refresh) { 558 | this.settings.helperSelectNamePostfix = value; 559 | if (value) { 560 | this.elements.select1.attr('name', this.originalSelectName + value + '1'); 561 | this.elements.select2.attr('name', this.originalSelectName + value + '2'); 562 | } else { 563 | this.elements.select1.removeAttr('name'); 564 | this.elements.select2.removeAttr('name'); 565 | } 566 | if (refresh) { 567 | refreshSelects(this); 568 | } 569 | return this.element; 570 | }, 571 | setSelectOrMinimalHeight: function(value, refresh) { 572 | this.settings.selectOrMinimalHeight = value; 573 | var height = this.element.height(); 574 | if (this.element.height() < value) { 575 | height = value; 576 | } 577 | this.elements.select1.height(height); 578 | this.elements.select2.height(height); 579 | if (refresh) { 580 | refreshSelects(this); 581 | } 582 | return this.element; 583 | }, 584 | setShowFilterInputs: function(value, refresh) { 585 | if (!value) { 586 | this.setNonSelectedFilter(''); 587 | this.setSelectedFilter(''); 588 | refreshSelects(this); 589 | this.elements.filterInput1.hide(); 590 | this.elements.filterInput2.hide(); 591 | } else { 592 | this.elements.filterInput1.show(); 593 | this.elements.filterInput2.show(); 594 | } 595 | this.settings.showFilterInputs = value; 596 | if (refresh) { 597 | refreshSelects(this); 598 | } 599 | return this.element; 600 | }, 601 | setNonSelectedFilter: function(value, refresh) { 602 | if (this.settings.showFilterInputs) { 603 | this.settings.nonSelectedFilter = value; 604 | this.elements.filterInput1.val(value); 605 | if (refresh) { 606 | refreshSelects(this); 607 | } 608 | return this.element; 609 | } 610 | }, 611 | setSelectedFilter: function(value, refresh) { 612 | if (this.settings.showFilterInputs) { 613 | this.settings.selectedFilter = value; 614 | this.elements.filterInput2.val(value); 615 | if (refresh) { 616 | refreshSelects(this); 617 | } 618 | return this.element; 619 | } 620 | }, 621 | setInfoText: function(value, refresh) { 622 | this.settings.infoText = value; 623 | if (refresh) { 624 | refreshSelects(this); 625 | } 626 | return this.element; 627 | }, 628 | setInfoTextFiltered: function(value, refresh) { 629 | this.settings.infoTextFiltered = value; 630 | if (refresh) { 631 | refreshSelects(this); 632 | } 633 | return this.element; 634 | }, 635 | setInfoTextEmpty: function(value, refresh) { 636 | this.settings.infoTextEmpty = value; 637 | if (refresh) { 638 | refreshSelects(this); 639 | } 640 | return this.element; 641 | }, 642 | setFilterOnValues: function(value, refresh) { 643 | this.settings.filterOnValues = value; 644 | if (refresh) { 645 | refreshSelects(this); 646 | } 647 | return this.element; 648 | }, 649 | getContainer: function() { 650 | return this.container; 651 | }, 652 | refresh: function(mustClearSelections) { 653 | updateSelectionStates(this); 654 | 655 | if (!mustClearSelections) { 656 | saveSelections(this, 1); 657 | saveSelections(this, 2); 658 | } else { 659 | clearSelections(this); 660 | } 661 | 662 | refreshSelects(this); 663 | }, 664 | destroy: function() { 665 | this.container.remove(); 666 | this.element.show(); 667 | $.data(this, 'plugin_' + pluginName, null); 668 | return this.element; 669 | } 670 | }; 671 | 672 | // A really lightweight plugin wrapper around the constructor, 673 | // preventing against multiple instantiations 674 | $.fn[ pluginName ] = function (options) { 675 | var args = arguments; 676 | 677 | // Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin. 678 | if (options === undefined || typeof options === 'object') { 679 | return this.each(function () { 680 | // If this is not a select 681 | if (!$(this).is('select')) { 682 | $(this).find('select').each(function(index, item) { 683 | // For each nested select, instantiate the Dual List Box 684 | $(item).bootstrapDualListbox(options); 685 | }); 686 | } else if (!$.data(this, 'plugin_' + pluginName)) { 687 | // Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet 688 | 689 | // if it has no instance, create a new one, pass options to our plugin constructor, 690 | // and store the plugin instance in the elements jQuery data object. 691 | $.data(this, 'plugin_' + pluginName, new BootstrapDualListbox(this, options)); 692 | } 693 | }); 694 | // If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function, 695 | // treat this as a call to a public method. 696 | } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 697 | 698 | // Cache the method call to make it possible to return a value 699 | var returns; 700 | 701 | this.each(function () { 702 | var instance = $.data(this, 'plugin_' + pluginName); 703 | // Tests that there's already a plugin-instance and checks that the requested public method exists 704 | if (instance instanceof BootstrapDualListbox && typeof instance[options] === 'function') { 705 | // Call the method of our plugin instance, and pass it the supplied arguments. 706 | returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1)); 707 | } 708 | }); 709 | 710 | // If the earlier cached method gives a value back return the value, 711 | // otherwise return this to preserve chainability. 712 | return returns !== undefined ? returns : this; 713 | } 714 | 715 | }; 716 | 717 | })(jQuery, window, document); 718 | -------------------------------------------------------------------------------- /src/appserver/static/contrib/bootstrap-duallist/jquery.bootstrap-duallistbox.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootstrap Duallistbox - v3.0.1 3 | * A responsive dual listbox widget optimized for Twitter Bootstrap. It works on all modern browsers and on touch devices. 4 | * http://www.virtuosoft.eu/code/bootstrap-duallistbox/ 5 | * 6 | * Made by István Ujj-Mészáros 7 | * Under Apache License v2.0 License 8 | */ 9 | !function(a,b,c,d){function e(b,c){this.element=a(b),this.settings=a.extend({},v,c),this._defaults=v,this._name=u,this.init()}function f(a){a.element.trigger("change")}function g(b){b.element.find("option").each(function(c,d){var e=a(d);"undefined"==typeof e.data("original-index")&&e.data("original-index",b.elementCount++),"undefined"==typeof e.data("_selected")&&e.data("_selected",!1)})}function h(b,c,d){b.element.find("option").each(function(b,e){var f=a(e);f.data("original-index")===c&&f.prop("selected",d)})}function i(a,b){return a.replace(/\{(\d+)\}/g,function(a,c){return"undefined"!=typeof b[c]?b[c]:a})}function j(a){if(a.settings.infoText){var b=a.elements.select1.find("option").length,c=a.elements.select2.find("option").length,d=a.element.find("option").length-a.selectedElements,e=a.selectedElements,f="";f=0===d?a.settings.infoTextEmpty:b===d?i(a.settings.infoText,[b,d]):i(a.settings.infoTextFiltered,[b,d]),a.elements.info1.html(f),a.elements.box1.toggleClass("filtered",!(b===d||0===d)),f=0===e?a.settings.infoTextEmpty:c===e?i(a.settings.infoText,[c,e]):i(a.settings.infoTextFiltered,[c,e]),a.elements.info2.html(f),a.elements.box2.toggleClass("filtered",!(c===e||0===e))}}function k(b){b.selectedElements=0,b.elements.select1.empty(),b.elements.select2.empty(),b.element.find("option").each(function(c,d){var e=a(d);e.prop("selected")?(b.selectedElements++,b.elements.select2.append(e.clone(!0).prop("selected",e.data("_selected")))):b.elements.select1.append(e.clone(!0).prop("selected",e.data("_selected")))}),b.settings.showFilterInputs&&(l(b,1),l(b,2)),j(b)}function l(b,c){if(b.settings.showFilterInputs){m(b,c),b.elements["select"+c].empty().scrollTop(0);var d=new RegExp(a.trim(b.elements["filterInput"+c].val()),"gi"),e=b.element;e=1===c?e.find("option").not(":selected"):e.find("option:selected"),e.each(function(e,f){var g=a(f),h=!0;(f.text.match(d)||b.settings.filterOnValues&&g.attr("value").match(d))&&(h=!1,b.elements["select"+c].append(g.clone(!0).prop("selected",g.data("_selected")))),b.element.find("option").eq(g.data("original-index")).data("filtered"+c,h)}),j(b)}}function m(b,c){b.elements["select"+c].find("option").each(function(c,d){var e=a(d);b.element.find("option").eq(e.data("original-index")).data("_selected",e.prop("selected"))})}function n(b){b.find("option").sort(function(b,c){return a(b).data("original-index")>a(c).data("original-index")?1:-1}).appendTo(b)}function o(a){a.elements.select1.find("option").each(function(){a.element.find("option").data("_selected",!1)})}function p(b){"all"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect?"moved"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect||m(b,1):(m(b,1),m(b,2)),b.elements.select1.find("option:selected").each(function(c,d){var e=a(d);e.data("filtered1")||h(b,e.data("original-index"),!0)}),k(b),f(b),n(b.elements.select2)}function q(b){"all"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect?"moved"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect||m(b,2):(m(b,1),m(b,2)),b.elements.select2.find("option:selected").each(function(c,d){var e=a(d);e.data("filtered2")||h(b,e.data("original-index"),!1)}),k(b),f(b),n(b.elements.select1)}function r(b){"all"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect?"moved"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect||m(b,1):(m(b,1),m(b,2)),b.element.find("option").each(function(b,c){var d=a(c);d.data("filtered1")||d.prop("selected",!0)}),k(b),f(b)}function s(b){"all"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect?"moved"!==b.settings.preserveSelectionOnMove||b.settings.moveOnSelect||m(b,2):(m(b,1),m(b,2)),b.element.find("option").each(function(b,c){var d=a(c);d.data("filtered2")||d.prop("selected",!1)}),k(b),f(b)}function t(a){a.elements.form.submit(function(b){a.elements.filterInput1.is(":focus")?(b.preventDefault(),a.elements.filterInput1.focusout()):a.elements.filterInput2.is(":focus")&&(b.preventDefault(),a.elements.filterInput2.focusout())}),a.element.on("bootstrapDualListbox.refresh",function(b,c){a.refresh(c)}),a.elements.filterClear1.on("click",function(){a.setNonSelectedFilter("",!0)}),a.elements.filterClear2.on("click",function(){a.setSelectedFilter("",!0)}),a.elements.moveButton.on("click",function(){p(a)}),a.elements.moveAllButton.on("click",function(){r(a)}),a.elements.removeButton.on("click",function(){q(a)}),a.elements.removeAllButton.on("click",function(){s(a)}),a.elements.filterInput1.on("change keyup",function(){l(a,1)}),a.elements.filterInput2.on("change keyup",function(){l(a,2)})}var u="bootstrapDualListbox",v={bootstrap2Compatible:!1,filterTextClear:"show all",filterPlaceHolder:"Filter",moveSelectedLabel:"Move selected",moveAllLabel:"Move all",removeSelectedLabel:"Remove selected",removeAllLabel:"Remove all",moveOnSelect:!0,preserveSelectionOnMove:!1,selectedListLabel:!1,nonSelectedListLabel:!1,helperSelectNamePostfix:"_helper",selectOrMinimalHeight:100,showFilterInputs:!0,nonSelectedFilter:"",selectedFilter:"",infoText:"Showing all {0}",infoTextFiltered:'Filtered {0} from {1}',infoTextEmpty:"Empty list",filterOnValues:!1},w=/android/i.test(navigator.userAgent.toLowerCase());e.prototype={init:function(){this.container=a('
').insertBefore(this.element),this.elements={originalSelect:this.element,box1:a(".box1",this.container),box2:a(".box2",this.container),filterInput1:a(".box1 .filter",this.container),filterInput2:a(".box2 .filter",this.container),filterClear1:a(".box1 .clear1",this.container),filterClear2:a(".box2 .clear2",this.container),label1:a(".box1 > label",this.container),label2:a(".box2 > label",this.container),info1:a(".box1 .info",this.container),info2:a(".box2 .info",this.container),select1:a(".box1 select",this.container),select2:a(".box2 select",this.container),moveButton:a(".box1 .move",this.container),removeButton:a(".box2 .remove",this.container),moveAllButton:a(".box1 .moveall",this.container),removeAllButton:a(".box2 .removeall",this.container),form:a(a(".box1 .filter",this.container)[0].form)},this.originalSelectName=this.element.attr("name")||"";var b="bootstrap-duallistbox-nonselected-list_"+this.originalSelectName,c="bootstrap-duallistbox-selected-list_"+this.originalSelectName;return this.elements.select1.attr("id",b),this.elements.select2.attr("id",c),this.elements.label1.attr("for",b),this.elements.label2.attr("for",c),this.selectedElements=0,this.elementCount=0,this.setBootstrap2Compatible(this.settings.bootstrap2Compatible),this.setFilterTextClear(this.settings.filterTextClear),this.setFilterPlaceHolder(this.settings.filterPlaceHolder),this.setMoveSelectedLabel(this.settings.moveSelectedLabel),this.setMoveAllLabel(this.settings.moveAllLabel),this.setRemoveSelectedLabel(this.settings.removeSelectedLabel),this.setRemoveAllLabel(this.settings.removeAllLabel),this.setMoveOnSelect(this.settings.moveOnSelect),this.setPreserveSelectionOnMove(this.settings.preserveSelectionOnMove),this.setSelectedListLabel(this.settings.selectedListLabel),this.setNonSelectedListLabel(this.settings.nonSelectedListLabel),this.setHelperSelectNamePostfix(this.settings.helperSelectNamePostfix),this.setSelectOrMinimalHeight(this.settings.selectOrMinimalHeight),g(this),this.setShowFilterInputs(this.settings.showFilterInputs),this.setNonSelectedFilter(this.settings.nonSelectedFilter),this.setSelectedFilter(this.settings.selectedFilter),this.setInfoText(this.settings.infoText),this.setInfoTextFiltered(this.settings.infoTextFiltered),this.setInfoTextEmpty(this.settings.infoTextEmpty),this.setFilterOnValues(this.settings.filterOnValues),this.element.hide(),t(this),k(this),this.element},setBootstrap2Compatible:function(a,b){return this.settings.bootstrap2Compatible=a,a?(this.container.removeClass("row").addClass("row-fluid bs2compatible"),this.container.find(".box1, .box2").removeClass("col-md-6").addClass("span6"),this.container.find(".clear1, .clear2").removeClass("btn-default btn-xs").addClass("btn-mini"),this.container.find("input, select").removeClass("form-control"),this.container.find(".btn").removeClass("btn-default"),this.container.find(".moveall > i, .move > i").removeClass("glyphicon glyphicon-arrow-right").addClass("icon-arrow-right"),this.container.find(".removeall > i, .remove > i").removeClass("glyphicon glyphicon-arrow-left").addClass("icon-arrow-left")):(this.container.removeClass("row-fluid bs2compatible").addClass("row"),this.container.find(".box1, .box2").removeClass("span6").addClass("col-md-6"),this.container.find(".clear1, .clear2").removeClass("btn-mini").addClass("btn-default btn-xs"),this.container.find("input, select").addClass("form-control"),this.container.find(".btn").addClass("btn-default"),this.container.find(".moveall > i, .move > i").removeClass("icon-arrow-right").addClass("glyphicon glyphicon-arrow-right"),this.container.find(".removeall > i, .remove > i").removeClass("icon-arrow-left").addClass("glyphicon glyphicon-arrow-left")),b&&k(this),this.element},setFilterTextClear:function(a,b){return this.settings.filterTextClear=a,this.elements.filterClear1.html(a),this.elements.filterClear2.html(a),b&&k(this),this.element},setFilterPlaceHolder:function(a,b){return this.settings.filterPlaceHolder=a,this.elements.filterInput1.attr("placeholder",a),this.elements.filterInput2.attr("placeholder",a),b&&k(this),this.element},setMoveSelectedLabel:function(a,b){return this.settings.moveSelectedLabel=a,this.elements.moveButton.attr("title",a),b&&k(this),this.element},setMoveAllLabel:function(a,b){return this.settings.moveAllLabel=a,this.elements.moveAllButton.attr("title",a),b&&k(this),this.element},setRemoveSelectedLabel:function(a,b){return this.settings.removeSelectedLabel=a,this.elements.removeButton.attr("title",a),b&&k(this),this.element},setRemoveAllLabel:function(a,b){return this.settings.removeAllLabel=a,this.elements.removeAllButton.attr("title",a),b&&k(this),this.element},setMoveOnSelect:function(a,b){if(w&&(a=!0),this.settings.moveOnSelect=a,this.settings.moveOnSelect){this.container.addClass("moveonselect");var c=this;this.elements.select1.on("change",function(){p(c)}),this.elements.select2.on("change",function(){q(c)})}else this.container.removeClass("moveonselect"),this.elements.select1.off("change"),this.elements.select2.off("change");return b&&k(this),this.element},setPreserveSelectionOnMove:function(a,b){return w&&(a=!1),this.settings.preserveSelectionOnMove=a,b&&k(this),this.element},setSelectedListLabel:function(a,b){return this.settings.selectedListLabel=a,a?this.elements.label2.show().html(a):this.elements.label2.hide().html(a),b&&k(this),this.element},setNonSelectedListLabel:function(a,b){return this.settings.nonSelectedListLabel=a,a?this.elements.label1.show().html(a):this.elements.label1.hide().html(a),b&&k(this),this.element},setHelperSelectNamePostfix:function(a,b){return this.settings.helperSelectNamePostfix=a,a?(this.elements.select1.attr("name",this.originalSelectName+a+"1"),this.elements.select2.attr("name",this.originalSelectName+a+"2")):(this.elements.select1.removeAttr("name"),this.elements.select2.removeAttr("name")),b&&k(this),this.element},setSelectOrMinimalHeight:function(a,b){this.settings.selectOrMinimalHeight=a;var c=this.element.height();return this.element.height() 168 | * If you need to save new model with custom _key you can specify { isNew: true } in options to treat save this model as new.
169 | * Rejects operation when {@link http://backbonejs.org/#Model-id|Model.prototype.id} is empty string. 170 | * @method 171 | * @param [key] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.save} 172 | * @param [value] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.save} 173 | * @param [options] {object} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.save} 174 | * @return {object} see {@link http://api.jquery.com/category/deferred-object/|jQuery Deferred Object} 175 | */ 176 | save: function(key, value, options) { 177 | if (this.id === '') { 178 | return $.Deferred().reject(null, null, new Error('Model with empty string as a _key cannot be saved')); 179 | } 180 | 181 | // Support all kind of syntax 182 | // (see default Backbone.Model.save implementation) 183 | /*jshint -W041 */ 184 | var attrs; 185 | if (key == null || typeof key === 'object') { 186 | attrs = key; 187 | options = value; 188 | } else { 189 | (attrs = {})[key] = value; 190 | } 191 | /*jshint +W041 */ 192 | 193 | if (!this.isNew() && !this._isNew) { 194 | // Send save request as POST not PUT 195 | options = _.extend({type: 'POST'}, options); 196 | } 197 | 198 | if (this._isNew) { 199 | this._isNew = true; 200 | options = _.clone(options) || {}; 201 | var model = this; 202 | var success = options.success; 203 | options.success = function() { 204 | model.isNew(false); 205 | if (success) { 206 | success.apply(this, arguments); 207 | } 208 | }; 209 | } 210 | 211 | return Backbone.Model.prototype.save.call(this, attrs, options); 212 | }, 213 | 214 | /** 215 | * Overrides {@link http://backbonejs.org/#Model-destroy|Backbone.Model.prototype.destroy} method to support 216 | * empty KVStore response on success.
217 | * Rejects operation when {@link http://backbonejs.org/#Model-id|Model.prototype.id} is empty string. 218 | * @method 219 | * @param [key] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.destroy} 220 | * @param [value] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.destroy} 221 | * @param [options] {object} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.destroy} 222 | * @return {object} see {@link http://api.jquery.com/category/deferred-object/|jQuery Deferred Object} 223 | */ 224 | destroy: function(options) { 225 | if (this.id === '') { 226 | return $.Deferred().reject(null, null, new Error('Model with empty string as a _key cannot be destroyed')); 227 | } 228 | 229 | // KVStore does not return anything when model 230 | // is destroyed on server side, let's force Backbone to 231 | // not parse response as JSON. 232 | var opts; 233 | if (!options || !options.dataType) { 234 | opts = { dataType: 'text' }; 235 | if (options) { 236 | opts = _.extend(opts, options); 237 | } 238 | } else { 239 | opts = options; 240 | } 241 | return Backbone.Model.prototype.destroy.call(this, opts); 242 | }, 243 | 244 | /** 245 | * Overrides {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.fetch}.
246 | * Rejects operation when {@link http://backbonejs.org/#Model-id|Model.prototype.id} is empty string. 247 | * @method 248 | * @param [key] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.fetch} 249 | * @param [value] {(string|object)} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.fetch} 250 | * @param [options] {object} see {@link http://backbonejs.org/#Model-save|Backbone.Model.prototype.fetch} 251 | * @return {object} see {@link http://api.jquery.com/category/deferred-object/|jQuery Deferred Object} 252 | */ 253 | fetch: function() { 254 | if (this.id === '') { 255 | return $.Deferred().reject(null, null, new Error('Model with empty string as a _key cannot be fetched')); 256 | } 257 | return Backbone.Model.prototype.fetch.apply(this, arguments); 258 | } 259 | } 260 | ); 261 | 262 | 263 | exports.Collection = Backbone.Collection.extend( 264 | /** @lends module:kvstore~Collection.prototype */ 265 | { 266 | /** 267 | * KVStore collection name. 268 | * @type {string} 269 | * @abstract 270 | * @default 271 | */ 272 | collectionName: null, 273 | 274 | /** 275 | * Splunk namespace { owner: 'x', app: 'y' }. 276 | * @type {(object|function)} 277 | * @default function returns namespace { owner: 'nobody', app: '{current application}' } 278 | */ 279 | namespace: getDefaultNamespace, 280 | 281 | /** 282 | * Constructs backbone collection compatible with KVStore REST API. 283 | * @constructs 284 | * @augments Backbone.Collection 285 | * @param {array} [models] see {@link http://backbonejs.org/#Collection-constructor|Backbone Collection constructor} 286 | * @param {object} [options] see {@link http://backbonejs.org/#Collection-constructor|Backbone Collection constructor}, 287 | * you may specify namespace {app: 'x', owner: 'y'} as an option 288 | * @example 289 | * // Define Model and Collection for specific collection 290 | * var MyModel = kvstore.Model.extend({ 291 | * collectionName: 'mycollection' 292 | * }); 293 | * var MyCollection = kvstore.Collection.extend({ 294 | * collectionName: 'mycollection', 295 | * model: MyModel 296 | * }); 297 | * 298 | * // Fetch collection with parameters 299 | * var collection = new MyCollection(); 300 | * collection.fetch({ 301 | * data: $.param({ 302 | * skip: 10, 303 | * limit: 10, 304 | * sort: 'name', 305 | * query: JSON.stringify({ name: { '$gte': 'A' } }) 306 | * }) 307 | * }) 308 | * .done(function() { 309 | * console.log('Collection loaded, length = ' + collection.length); 310 | * }); 311 | */ 312 | initialize: function(models, options) { 313 | if (options) { 314 | if (!_.isUndefined(options.namespace)) { 315 | this.namespace = _.clone(options.namespace); 316 | } 317 | } 318 | return Backbone.Collection.prototype.initialize.apply(this, arguments); 319 | }, 320 | 321 | /** 322 | * Overrides {@link http://backbonejs.org/#Collection-url}. 323 | * @return {string} url to KVStore collection constructed using 324 | * {@link module:kvstore~Collection#collectionName|collectionName} and 325 | * {@link module:kvstore~Collection#namespace|namespace} 326 | */ 327 | url: function() { 328 | return constructUrl(this.collectionName, _.result(this, 'namespace')); 329 | } 330 | } 331 | ); 332 | 333 | 334 | return exports; 335 | 336 | }); -------------------------------------------------------------------------------- /src/appserver/static/contrib/nprogress/nprogress.css: -------------------------------------------------------------------------------- 1 | /* Make clicks pass-through */ 2 | #nprogress { 3 | pointer-events: none; 4 | -webkit-pointer-events: none; 5 | } 6 | 7 | #nprogress .bar { 8 | background: #29d; 9 | 10 | position: fixed; 11 | z-index: 100; 12 | top: 0; 13 | left: 0; 14 | 15 | width: 100%; 16 | height: 2px; 17 | } 18 | 19 | /* Fancy blur effect */ 20 | #nprogress .peg { 21 | display: block; 22 | position: absolute; 23 | right: 0px; 24 | width: 100px; 25 | height: 100%; 26 | box-shadow: 0 0 10px #29d, 0 0 5px #29d; 27 | opacity: 1.0; 28 | 29 | -webkit-transform: rotate(3deg) translate(0px, -4px); 30 | -moz-transform: rotate(3deg) translate(0px, -4px); 31 | -ms-transform: rotate(3deg) translate(0px, -4px); 32 | -o-transform: rotate(3deg) translate(0px, -4px); 33 | transform: rotate(3deg) translate(0px, -4px); 34 | } 35 | 36 | /* Remove these to get rid of the spinner */ 37 | #nprogress .spinner { 38 | display: block; 39 | position: fixed; 40 | z-index: 100; 41 | top: 15px; 42 | right: 15px; 43 | } 44 | 45 | #nprogress .spinner-icon { 46 | width: 14px; 47 | height: 14px; 48 | 49 | border: solid 2px transparent; 50 | border-top-color: #29d; 51 | border-left-color: #29d; 52 | border-radius: 10px; 53 | 54 | -webkit-animation: nprogress-spinner 400ms linear infinite; 55 | -moz-animation: nprogress-spinner 400ms linear infinite; 56 | -ms-animation: nprogress-spinner 400ms linear infinite; 57 | -o-animation: nprogress-spinner 400ms linear infinite; 58 | animation: nprogress-spinner 400ms linear infinite; 59 | } 60 | 61 | @-webkit-keyframes nprogress-spinner { 62 | 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 63 | 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } 64 | } 65 | @-moz-keyframes nprogress-spinner { 66 | 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); } 67 | 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); } 68 | } 69 | @-o-keyframes nprogress-spinner { 70 | 0% { -o-transform: rotate(0deg); transform: rotate(0deg); } 71 | 100% { -o-transform: rotate(360deg); transform: rotate(360deg); } 72 | } 73 | @-ms-keyframes nprogress-spinner { 74 | 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); } 75 | 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); } 76 | } 77 | @keyframes nprogress-spinner { 78 | 0% { transform: rotate(0deg); transform: rotate(0deg); } 79 | 100% { transform: rotate(360deg); transform: rotate(360deg); } 80 | } 81 | -------------------------------------------------------------------------------- /src/appserver/static/contrib/nprogress/nprogress.js: -------------------------------------------------------------------------------- 1 | /*! NProgress (c) 2013, Rico Sta. Cruz 2 | * http://ricostacruz.com/nprogress */ 3 | 4 | ;(function(factory) { 5 | 6 | if (typeof module === 'function') { 7 | module.exports = factory(this.jQuery || require('jquery')); 8 | } else { 9 | this.NProgress = factory(this.jQuery); 10 | } 11 | 12 | })(function($) { 13 | var NProgress = {}; 14 | 15 | NProgress.version = '0.1.2'; 16 | 17 | var Settings = NProgress.settings = { 18 | minimum: 0.08, 19 | easing: 'ease', 20 | positionUsing: '', 21 | speed: 200, 22 | trickle: true, 23 | trickleRate: 0.02, 24 | trickleSpeed: 800, 25 | document: window.document, 26 | showSpinner: true, 27 | template: '
' 28 | }; 29 | 30 | /** 31 | * Updates configuration. 32 | * 33 | * NProgress.configure({ 34 | * minimum: 0.1 35 | * }); 36 | */ 37 | NProgress.configure = function(options) { 38 | $.extend(Settings, options); 39 | return this; 40 | }; 41 | 42 | /** 43 | * Last number. 44 | */ 45 | 46 | NProgress.status = null; 47 | 48 | /** 49 | * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. 50 | * 51 | * NProgress.set(0.4); 52 | * NProgress.set(1.0); 53 | */ 54 | 55 | NProgress.set = function(n) { 56 | var started = NProgress.isStarted(); 57 | n = clamp(n, Settings.minimum, 1); 58 | NProgress.status = (n === 1 ? null : n); 59 | 60 | var $progress = NProgress.render(!started), 61 | $bar = $progress.find('[role="bar"]', Settings.document), 62 | speed = Settings.speed, 63 | ease = Settings.easing; 64 | 65 | $progress[0].offsetWidth; /* Repaint */ 66 | 67 | $progress.queue(function(next) { 68 | // Set positionUsing if it hasn't already been set 69 | if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); 70 | 71 | // Add transition 72 | $bar.css(barPositionCSS(n, speed, ease)); 73 | 74 | if (n === 1) { 75 | // Fade out 76 | $progress.css({ transition: 'none', opacity: 1 }); 77 | $progress[0].offsetWidth; /* Repaint */ 78 | 79 | setTimeout(function() { 80 | $progress.css({ transition: 'all '+speed+'ms linear', opacity: 0 }); 81 | setTimeout(function() { 82 | NProgress.remove(); 83 | next(); 84 | }, speed); 85 | }, speed); 86 | } else { 87 | setTimeout(next, speed); 88 | } 89 | }); 90 | 91 | return this; 92 | }; 93 | 94 | NProgress.isStarted = function() { 95 | return typeof NProgress.status === 'number'; 96 | }; 97 | 98 | /** 99 | * Shows the progress bar. 100 | * This is the same as setting the status to 0%, except that it doesn't go backwards. 101 | * 102 | * NProgress.start(); 103 | * 104 | */ 105 | NProgress.start = function() { 106 | if (!NProgress.status) NProgress.set(0); 107 | 108 | var work = function() { 109 | setTimeout(function() { 110 | if (!NProgress.status) return; 111 | NProgress.trickle(); 112 | work(); 113 | }, Settings.trickleSpeed); 114 | }; 115 | 116 | if (Settings.trickle) work(); 117 | 118 | return this; 119 | }; 120 | 121 | /** 122 | * Hides the progress bar. 123 | * This is the *sort of* the same as setting the status to 100%, with the 124 | * difference being `done()` makes some placebo effect of some realistic motion. 125 | * 126 | * NProgress.done(); 127 | * 128 | * If `true` is passed, it will show the progress bar even if its hidden. 129 | * 130 | * NProgress.done(true); 131 | */ 132 | 133 | NProgress.done = function(force) { 134 | if (!force && !NProgress.status) return this; 135 | 136 | return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); 137 | }; 138 | 139 | /** 140 | * Increments by a random amount. 141 | */ 142 | 143 | NProgress.inc = function(amount) { 144 | var n = NProgress.status; 145 | 146 | if (!n) { 147 | return NProgress.start(); 148 | } else { 149 | if (typeof amount !== 'number') { 150 | amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95); 151 | } 152 | 153 | n = clamp(n + amount, 0, 0.994); 154 | return NProgress.set(n); 155 | } 156 | }; 157 | 158 | NProgress.trickle = function() { 159 | return NProgress.inc(Math.random() * Settings.trickleRate); 160 | }; 161 | 162 | /** 163 | * (Internal) renders the progress bar markup based on the `template` 164 | * setting. 165 | */ 166 | 167 | NProgress.render = function(fromStart) { 168 | if (NProgress.isRendered()) return $("#nprogress", Settings.document); 169 | $('html', Settings.document).addClass('nprogress-busy'); 170 | 171 | var $el = $("
", Settings.document) 172 | .html(Settings.template); 173 | 174 | var perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0); 175 | 176 | $el.find('[role="bar"]').css({ 177 | transition: 'all 0 linear', 178 | transform: 'translate3d('+perc+'%,0,0)' 179 | }); 180 | 181 | if (!Settings.showSpinner) 182 | $el.find('[role="spinner"]').remove(); 183 | 184 | $el.appendTo(Settings.document.body); 185 | 186 | return $el; 187 | }; 188 | 189 | /** 190 | * Removes the element. Opposite of render(). 191 | */ 192 | 193 | NProgress.remove = function() { 194 | $('html', Settings.document).removeClass('nprogress-busy'); 195 | $('#nprogress', Settings.document).remove(); 196 | }; 197 | 198 | /** 199 | * Checks if the progress bar is rendered. 200 | */ 201 | 202 | NProgress.isRendered = function() { 203 | return ($("#nprogress", Settings.document).length > 0); 204 | }; 205 | 206 | /** 207 | * Determine which positioning CSS rule to use. 208 | */ 209 | 210 | NProgress.getPositioningCSS = function() { 211 | // Sniff on document.body.style 212 | var bodyStyle = document.body.style; 213 | 214 | // Sniff prefixes 215 | var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : 216 | ('MozTransform' in bodyStyle) ? 'Moz' : 217 | ('msTransform' in bodyStyle) ? 'ms' : 218 | ('OTransform' in bodyStyle) ? 'O' : ''; 219 | 220 | if (vendorPrefix + 'Perspective' in bodyStyle) { 221 | // Modern browsers with 3D support, e.g. Webkit, IE10 222 | return 'translate3d'; 223 | } else if (vendorPrefix + 'Transform' in bodyStyle) { 224 | // Browsers without 3D support, e.g. IE9 225 | return 'translate'; 226 | } else { 227 | // Browsers without translate() support, e.g. IE7-8 228 | return 'margin'; 229 | } 230 | }; 231 | 232 | /** 233 | * Helpers 234 | */ 235 | 236 | function clamp(n, min, max) { 237 | if (n < min) return min; 238 | if (n > max) return max; 239 | return n; 240 | } 241 | 242 | /** 243 | * (Internal) converts a percentage (`0..1`) to a bar translateX 244 | * percentage (`-100%..0%`). 245 | */ 246 | 247 | function toBarPerc(n) { 248 | return (-1 + n) * 100; 249 | } 250 | 251 | 252 | /** 253 | * (Internal) returns the correct CSS for changing the bar's 254 | * position given an n percentage, and speed and ease from Settings 255 | */ 256 | 257 | function barPositionCSS(n, speed, ease) { 258 | var barCSS; 259 | 260 | if (Settings.positionUsing === 'translate3d') { 261 | barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; 262 | } else if (Settings.positionUsing === 'translate') { 263 | barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; 264 | } else { 265 | barCSS = { 'margin-left': toBarPerc(n)+'%' }; 266 | } 267 | 268 | barCSS.transition = 'all '+speed+'ms '+ease; 269 | 270 | return barCSS; 271 | } 272 | 273 | return NProgress; 274 | }); 275 | -------------------------------------------------------------------------------- /src/appserver/static/contrib/store.min.js: -------------------------------------------------------------------------------- 1 | /*jsl:ignoreall*/ 2 | /* Copyright (c) 2010-2012 Marcus Westin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | (function(){function o(){try{return r in t&&t[r]}catch(e){return!1}}var e={},t=window,n=t.document,r="localStorage",i="__storejs__",s;e.disabled=!1,e.set=function(e,t){},e.get=function(e){},e.remove=function(e){},e.clear=function(){},e.transact=function(t,n,r){var i=e.get(t);r==null&&(r=n,n=null),typeof i=="undefined"&&(i=n||{}),r(i),e.set(t,i)},e.getAll=function(){},e.serialize=function(e){return JSON.stringify(e)},e.deserialize=function(e){if(typeof e!="string")return undefined;try{return JSON.parse(e)}catch(t){return e||undefined}};if(o())s=t[r],e.set=function(t,n){return n===undefined?e.remove(t):(s.setItem(t,e.serialize(n)),n)},e.get=function(t){return e.deserialize(s.getItem(t))},e.remove=function(e){s.removeItem(e)},e.clear=function(){s.clear()},e.getAll=function(){var t={};for(var n=0;ndocument.w=window'),a.close(),u=a.w.frames[0].document,s=u.createElement("div")}catch(f){s=n.createElement("div"),u=n.body}function l(t){return function(){var n=Array.prototype.slice.call(arguments,0);n.unshift(s),u.appendChild(s),s.addBehavior("#default#userData"),s.load(r);var i=t.apply(e,n);return u.removeChild(s),i}}var c=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function h(e){return e.replace(c,"___")}e.set=l(function(t,n,i){return n=h(n),i===undefined?e.remove(n):(t.setAttribute(n,e.serialize(i)),t.save(r),i)}),e.get=l(function(t,n){return n=h(n),e.deserialize(t.getAttribute(n))}),e.remove=l(function(e,t){t=h(t),e.removeAttribute(t),e.save(r)}),e.clear=l(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(r);for(var n=0,i;i=t[n];n++)e.removeAttribute(i.name);e.save(r)}),e.getAll=l(function(t){var n=t.XMLDocument.documentElement.attributes,r={};for(var i=0,s;s=n[i];++i){var o=h(s.name);r[s.name]=e.deserialize(t.getAttribute(o))}return r})}try{e.set(i,i),e.get(i)!=i&&(e.disabled=!0),e.remove(i)}catch(f){e.disabled=!0}e.enabled=!e.disabled,typeof module!="undefined"&&module.exports?module.exports=e:this.store=e})() -------------------------------------------------------------------------------- /src/appserver/static/contrib/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 2.0.5+ Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/requirejs/text for details 5 | */ 6 | /*jslint regexp: true */ 7 | /*global require, XMLHttpRequest, ActiveXObject, 8 | define, window, process, Packages, 9 | java, location, Components, FileUtils */ 10 | 11 | define(['module'], function (module) { 12 | 'use strict'; 13 | 14 | var text, fs, Cc, Ci, 15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 18 | hasLocation = typeof location !== 'undefined' && location.href, 19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 20 | defaultHostName = hasLocation && location.hostname, 21 | defaultPort = hasLocation && (location.port || undefined), 22 | buildMap = [], 23 | masterConfig = (module.config && module.config()) || {}; 24 | 25 | text = { 26 | version: '2.0.5+', 27 | 28 | strip: function (content) { 29 | //Strips declarations so that external SVG and XML 30 | //documents can be added to a document without worry. Also, if the string 31 | //is an HTML document, only the part inside the body tag is returned. 32 | if (content) { 33 | content = content.replace(xmlRegExp, ""); 34 | var matches = content.match(bodyRegExp); 35 | if (matches) { 36 | content = matches[1]; 37 | } 38 | } else { 39 | content = ""; 40 | } 41 | return content; 42 | }, 43 | 44 | jsEscape: function (content) { 45 | return content.replace(/(['\\])/g, '\\$1') 46 | .replace(/[\f]/g, "\\f") 47 | .replace(/[\b]/g, "\\b") 48 | .replace(/[\n]/g, "\\n") 49 | .replace(/[\t]/g, "\\t") 50 | .replace(/[\r]/g, "\\r") 51 | .replace(/[\u2028]/g, "\\u2028") 52 | .replace(/[\u2029]/g, "\\u2029"); 53 | }, 54 | 55 | createXhr: masterConfig.createXhr || function () { 56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 57 | var xhr, i, progId; 58 | if (typeof XMLHttpRequest !== "undefined") { 59 | return new XMLHttpRequest(); 60 | } else if (typeof ActiveXObject !== "undefined") { 61 | for (i = 0; i < 3; i += 1) { 62 | progId = progIds[i]; 63 | try { 64 | xhr = new ActiveXObject(progId); 65 | } catch (e) {} 66 | 67 | if (xhr) { 68 | progIds = [progId]; // so faster next time 69 | break; 70 | } 71 | } 72 | } 73 | 74 | return xhr; 75 | }, 76 | 77 | /** 78 | * Parses a resource name into its component parts. Resource names 79 | * look like: module/name.ext!strip, where the !strip part is 80 | * optional. 81 | * @param {String} name the resource name 82 | * @returns {Object} with properties "moduleName", "ext" and "strip" 83 | * where strip is a boolean. 84 | */ 85 | parseName: function (name) { 86 | var modName, ext, temp, 87 | strip = false, 88 | index = name.indexOf("."), 89 | isRelative = name.indexOf('./') === 0 || 90 | name.indexOf('../') === 0; 91 | 92 | if (index !== -1 && (!isRelative || index > 1)) { 93 | modName = name.substring(0, index); 94 | ext = name.substring(index + 1, name.length); 95 | } else { 96 | modName = name; 97 | } 98 | 99 | temp = ext || modName; 100 | index = temp.indexOf("!"); 101 | if (index !== -1) { 102 | //Pull off the strip arg. 103 | strip = temp.substring(index + 1) === "strip"; 104 | temp = temp.substring(0, index); 105 | if (ext) { 106 | ext = temp; 107 | } else { 108 | modName = temp; 109 | } 110 | } 111 | 112 | return { 113 | moduleName: modName, 114 | ext: ext, 115 | strip: strip 116 | }; 117 | }, 118 | 119 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 120 | 121 | /** 122 | * Is an URL on another domain. Only works for browser use, returns 123 | * false in non-browser environments. Only used to know if an 124 | * optimized .js version of a text resource should be loaded 125 | * instead. 126 | * @param {String} url 127 | * @returns Boolean 128 | */ 129 | useXhr: function (url, protocol, hostname, port) { 130 | var uProtocol, uHostName, uPort, 131 | match = text.xdRegExp.exec(url); 132 | if (!match) { 133 | return true; 134 | } 135 | uProtocol = match[2]; 136 | uHostName = match[3]; 137 | 138 | uHostName = uHostName.split(':'); 139 | uPort = uHostName[1]; 140 | uHostName = uHostName[0]; 141 | 142 | return (!uProtocol || uProtocol === protocol) && 143 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && 144 | ((!uPort && !uHostName) || uPort === port); 145 | }, 146 | 147 | finishLoad: function (name, strip, content, onLoad) { 148 | content = strip ? text.strip(content) : content; 149 | if (masterConfig.isBuild) { 150 | buildMap[name] = content; 151 | } 152 | onLoad(content); 153 | }, 154 | 155 | load: function (name, req, onLoad, config) { 156 | //Name has format: some.module.filext!strip 157 | //The strip part is optional. 158 | //if strip is present, then that means only get the string contents 159 | //inside a body tag in an HTML string. For XML/SVG content it means 160 | //removing the declarations so the content can be inserted 161 | //into the current doc without problems. 162 | 163 | // Do not bother with the work if a build and text will 164 | // not be inlined. 165 | if (config.isBuild && !config.inlineText) { 166 | onLoad(); 167 | return; 168 | } 169 | 170 | masterConfig.isBuild = config.isBuild; 171 | 172 | var parsed = text.parseName(name), 173 | nonStripName = parsed.moduleName + 174 | (parsed.ext ? '.' + parsed.ext : ''), 175 | url = req.toUrl(nonStripName), 176 | useXhr = (masterConfig.useXhr) || 177 | text.useXhr; 178 | 179 | //Load the text. Use XHR if possible and in a browser. 180 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 181 | text.get(url, function (content) { 182 | text.finishLoad(name, parsed.strip, content, onLoad); 183 | }, function (err) { 184 | if (onLoad.error) { 185 | onLoad.error(err); 186 | } 187 | }); 188 | } else { 189 | //Need to fetch the resource across domains. Assume 190 | //the resource has been optimized into a JS module. Fetch 191 | //by the module name + extension, but do not include the 192 | //!strip part to avoid file system issues. 193 | req([nonStripName], function (content) { 194 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 195 | parsed.strip, content, onLoad); 196 | }); 197 | } 198 | }, 199 | 200 | write: function (pluginName, moduleName, write, config) { 201 | if (buildMap.hasOwnProperty(moduleName)) { 202 | var content = text.jsEscape(buildMap[moduleName]); 203 | write.asModule(pluginName + "!" + moduleName, 204 | "define(function () { return '" + 205 | content + 206 | "';});\n"); 207 | } 208 | }, 209 | 210 | writeFile: function (pluginName, moduleName, req, write, config) { 211 | var parsed = text.parseName(moduleName), 212 | extPart = parsed.ext ? '.' + parsed.ext : '', 213 | nonStripName = parsed.moduleName + extPart, 214 | //Use a '.js' file name so that it indicates it is a 215 | //script that can be loaded across domains. 216 | fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; 217 | 218 | //Leverage own load() method to load plugin value, but only 219 | //write out values that do not have the strip argument, 220 | //to avoid any potential issues with ! in file names. 221 | text.load(nonStripName, req, function (value) { 222 | //Use own write() method to construct full module value. 223 | //But need to create shell that translates writeFile's 224 | //write() to the right interface. 225 | var textWrite = function (contents) { 226 | return write(fileName, contents); 227 | }; 228 | textWrite.asModule = function (moduleName, contents) { 229 | return write.asModule(moduleName, fileName, contents); 230 | }; 231 | 232 | text.write(pluginName, nonStripName, textWrite, config); 233 | }, config); 234 | } 235 | }; 236 | 237 | if (masterConfig.env === 'node' || (!masterConfig.env && 238 | typeof process !== "undefined" && 239 | process.versions && 240 | !!process.versions.node)) { 241 | //Using special require.nodeRequire, something added by r.js. 242 | fs = require.nodeRequire('fs'); 243 | 244 | text.get = function (url, callback) { 245 | var file = fs.readFileSync(url, 'utf8'); 246 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 247 | if (file.indexOf('\uFEFF') === 0) { 248 | file = file.substring(1); 249 | } 250 | callback(file); 251 | }; 252 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env && 253 | text.createXhr())) { 254 | text.get = function (url, callback, errback, headers) { 255 | var xhr = text.createXhr(), header; 256 | xhr.open('GET', url, true); 257 | 258 | //Allow plugins direct access to xhr headers 259 | if (headers) { 260 | for (header in headers) { 261 | if (headers.hasOwnProperty(header)) { 262 | xhr.setRequestHeader(header.toLowerCase(), headers[header]); 263 | } 264 | } 265 | } 266 | 267 | //Allow overrides specified in config 268 | if (masterConfig.onXhr) { 269 | masterConfig.onXhr(xhr, url); 270 | } 271 | 272 | xhr.onreadystatechange = function (evt) { 273 | var status, err; 274 | //Do not explicitly handle errors, those should be 275 | //visible via console output in the browser. 276 | if (xhr.readyState === 4) { 277 | status = xhr.status; 278 | if (status > 399 && status < 600) { 279 | //An http 4xx or 5xx error. Signal an error. 280 | err = new Error(url + ' HTTP status: ' + status); 281 | err.xhr = xhr; 282 | errback(err); 283 | } else { 284 | callback(xhr.responseText); 285 | } 286 | } 287 | }; 288 | xhr.send(null); 289 | }; 290 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env && 291 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) { 292 | //Why Java, why is this so awkward? 293 | text.get = function (url, callback) { 294 | var stringBuffer, line, 295 | encoding = "utf-8", 296 | file = new java.io.File(url), 297 | lineSeparator = java.lang.System.getProperty("line.separator"), 298 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 299 | content = ''; 300 | try { 301 | stringBuffer = new java.lang.StringBuffer(); 302 | line = input.readLine(); 303 | 304 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 305 | // http://www.unicode.org/faq/utf_bom.html 306 | 307 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 308 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 309 | if (line && line.length() && line.charAt(0) === 0xfeff) { 310 | // Eat the BOM, since we've already found the encoding on this file, 311 | // and we plan to concatenating this buffer with others; the BOM should 312 | // only appear at the top of a file. 313 | line = line.substring(1); 314 | } 315 | 316 | stringBuffer.append(line); 317 | 318 | while ((line = input.readLine()) !== null) { 319 | stringBuffer.append(lineSeparator); 320 | stringBuffer.append(line); 321 | } 322 | //Make sure we return a JavaScript string and not a Java string. 323 | content = String(stringBuffer.toString()); //String 324 | } finally { 325 | input.close(); 326 | } 327 | callback(content); 328 | }; 329 | } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && 330 | typeof Components !== 'undefined' && Components.classes && 331 | Components.interfaces)) { 332 | //Avert your gaze! 333 | Cc = Components.classes, 334 | Ci = Components.interfaces; 335 | Components.utils['import']('resource://gre/modules/FileUtils.jsm'); 336 | 337 | text.get = function (url, callback) { 338 | var inStream, convertStream, 339 | readData = {}, 340 | fileObj = new FileUtils.File(url); 341 | 342 | //XPCOM, you so crazy 343 | try { 344 | inStream = Cc['@mozilla.org/network/file-input-stream;1'] 345 | .createInstance(Ci.nsIFileInputStream); 346 | inStream.init(fileObj, 1, 0, false); 347 | 348 | convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] 349 | .createInstance(Ci.nsIConverterInputStream); 350 | convertStream.init(inStream, "utf-8", inStream.available(), 351 | Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); 352 | 353 | convertStream.readString(inStream.available(), readData); 354 | convertStream.close(); 355 | inStream.close(); 356 | callback(readData.value); 357 | } catch (e) { 358 | throw new Error((fileObj && fileObj.path || '') + ': ' + e); 359 | } 360 | }; 361 | } 362 | return text; 363 | }); -------------------------------------------------------------------------------- /src/appserver/static/css/HideChrome.css: -------------------------------------------------------------------------------- 1 | 2 | .fieldset{ 3 | display: none !important; 4 | } 5 | 6 | .splunk-dashboard-controls, .shared-splunkbar, #footer, .appHeader, .app-bar, .appHeaderWrapper, .btn, input{ 7 | display: none; 8 | } 9 | 10 | body { 11 | overflow:hidden; 12 | } -------------------------------------------------------------------------------- /src/appserver/static/css/SlideshowSetupView.css: -------------------------------------------------------------------------------- 1 | 2 | #views_list{ 3 | height: 320px; 4 | } 5 | 6 | input[type="checkbox"]{ 7 | float: left; 8 | margin-right: 6px; 9 | margin-top: 2px; 10 | } 11 | 12 | .bootstrap-duallistbox-container .label-warning{ 13 | border-radius: .25em; 14 | background-color: #f7902b; 15 | line-height: 1.3; 16 | color: #fff; 17 | font-size: 75%; 18 | text-transform: none; 19 | } 20 | 21 | .running-icon{ 22 | display: inline-block; 23 | margin-bottom: -4px; 24 | margin-right: 1px; 25 | width: 16px; 26 | height: 16px; 27 | background-repeat: no-repeat; 28 | background-image: url(data:image/gif;base64,R0lGODlhEAAQAPQAAMk6J////8tEMuGTic9RQO/HwuSflv///+u6s/bi39p5bNZrXfnu7d2Fef36+vLU0OitpQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFdyAgAgIJIeWoAkRCCMdBkKtIHIngyMKsErPBYbADpkSCwhDmQCBethRB6Vj4kFCkQPG4IlWDgrNRIwnO4UKBXDufzQvDMaoSDBgFb886MiQadgNABAokfCwzBA8LCg0Egl8jAggGAA1kBIA1BAYzlyILczULC2UhACH5BAkKAAAALAAAAAAQABAAAAV2ICACAmlAZTmOREEIyUEQjLKKxPHADhEvqxlgcGgkGI1DYSVAIAWMx+lwSKkICJ0QsHi9RgKBwnVTiRQQgwF4I4UFDQQEwi6/3YSGWRRmjhEETAJfIgMFCnAKM0KDV4EEEAQLiF18TAYNXDaSe3x6mjidN1s3IQAh+QQJCgAAACwAAAAAEAAQAAAFeCAgAgLZDGU5jgRECEUiCI+yioSDwDJyLKsXoHFQxBSHAoAAFBhqtMJg8DgQBgfrEsJAEAg4YhZIEiwgKtHiMBgtpg3wbUZXGO7kOb1MUKRFMysCChAoggJCIg0GC2aNe4gqQldfL4l/Ag1AXySJgn5LcoE3QXI3IQAh+QQJCgAAACwAAAAAEAAQAAAFdiAgAgLZNGU5joQhCEjxIssqEo8bC9BRjy9Ag7GILQ4QEoE0gBAEBcOpcBA0DoxSK/e8LRIHn+i1cK0IyKdg0VAoljYIg+GgnRrwVS/8IAkICyosBIQpBAMoKy9dImxPhS+GKkFrkX+TigtLlIyKXUF+NjagNiEAIfkECQoAAAAsAAAAABAAEAAABWwgIAICaRhlOY4EIgjH8R7LKhKHGwsMvb4AAy3WODBIBBKCsYA9TjuhDNDKEVSERezQEL0WrhXucRUQGuik7bFlngzqVW9LMl9XWvLdjFaJtDFqZ1cEZUB0dUgvL3dgP4WJZn4jkomWNpSTIyEAIfkECQoAAAAsAAAAABAAEAAABX4gIAICuSxlOY6CIgiD8RrEKgqGOwxwUrMlAoSwIzAGpJpgoSDAGifDY5kopBYDlEpAQBwevxfBtRIUGi8xwWkDNBCIwmC9Vq0aiQQDQuK+VgQPDXV9hCJjBwcFYU5pLwwHXQcMKSmNLQcIAExlbH8JBwttaX0ABAcNbWVbKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICSRBlOY7CIghN8zbEKsKoIjdFzZaEgUBHKChMJtRwcWpAWoWnifm6ESAMhO8lQK0EEAV3rFopIBCEcGwDKAqPh4HUrY4ICHH1dSoTFgcHUiZjBhAJB2AHDykpKAwHAwdzf19KkASIPl9cDgcnDkdtNwiMJCshACH5BAkKAAAALAAAAAAQABAAAAV3ICACAkkQZTmOAiosiyAoxCq+KPxCNVsSMRgBsiClWrLTSWFoIQZHl6pleBh6suxKMIhlvzbAwkBWfFWrBQTxNLq2RG2yhSUkDs2b63AYDAoJXAcFRwADeAkJDX0AQCsEfAQMDAIPBz0rCgcxky0JRWE1AmwpKyEAIfkECQoAAAAsAAAAABAAEAAABXkgIAICKZzkqJ4nQZxLqZKv4NqNLKK2/Q4Ek4lFXChsg5ypJjs1II3gEDUSRInEGYAw6B6zM4JhrDAtEosVkLUtHA7RHaHAGJQEjsODcEg0FBAFVgkQJQ1pAwcDDw8KcFtSInwJAowCCA6RIwqZAgkPNgVpWndjdyohACH5BAkKAAAALAAAAAAQABAAAAV5ICACAimc5KieLEuUKvm2xAKLqDCfC2GaO9eL0LABWTiBYmA06W6kHgvCqEJiAIJiu3gcvgUsscHUERm+kaCxyxa+zRPk0SgJEgfIvbAdIAQLCAYlCj4DBw0IBQsMCjIqBAcPAooCBg9pKgsJLwUFOhCZKyQDA3YqIQAh+QQJCgAAACwAAAAAEAAQAAAFdSAgAgIpnOSonmxbqiThCrJKEHFbo8JxDDOZYFFb+A41E4H4OhkOipXwBElYITDAckFEOBgMQ3arkMkUBdxIUGZpEb7kaQBRlASPg0FQQHAbEEMGDSVEAA1QBhAED1E0NgwFAooCDWljaQIQCE5qMHcNhCkjIQAh+QQJCgAAACwAAAAAEAAQAAAFeSAgAgIpnOSoLgxxvqgKLEcCC65KEAByKK8cSpA4DAiHQ/DkKhGKh4ZCtCyZGo6F6iYYPAqFgYy02xkSaLEMV34tELyRYNEsCQyHlvWkGCzsPgMCEAY7Cg04Uk48LAsDhRA8MVQPEF0GAgqYYwSRlycNcWskCkApIyEAOwAAAAAAAAAAAA==); 29 | } 30 | 31 | .btn-stop{ 32 | text-shadow: 0 -1px 0 rgba(0,0,0,0.25); 33 | background-color: #c93a27; 34 | background-image: -moz-linear-gradient(top, #c93a27, #af3321); 35 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#c93a27), to(#af3321)); 36 | background-image: -webkit-linear-gradient(top, #c93a27, #af3321); 37 | background-image: -o-linear-gradient(top, #c93a27, #af3321); 38 | background-image: linear-gradient(to bottom, #c93a27, #af3321); 39 | background-repeat: repeat-x; 40 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffc93a27', endColorstr='#ffaf3321', GradientType=0); 41 | background-color: #c93a27; 42 | border: 1px solid #914e1e; 43 | border-bottom-color: #914e1e; 44 | border-top-color: #914e1e; 45 | color: #fff; 46 | -webkit-box-shadow: inset 0 1px 0 #c93a27; 47 | -moz-box-shadow: inset 0 1px 0 #c93a27; 48 | box-shadow: inset 0 1px 0 #c93a27; 49 | } 50 | 51 | 52 | .btn-stop:hover, 53 | .btn-stop:focus, 54 | .btn-stop:active, 55 | .btn-stop.active, 56 | .btn-stop.disabled, 57 | .btn-stop[disabled] { 58 | background-color: #e60d10; 59 | background-image: -moz-linear-gradient(top, #e60d10, #c9140f); 60 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#e60d10), to(#c9140f)); 61 | background-image: -webkit-linear-gradient(top, #e60d10, #c9140f); 62 | background-image: -o-linear-gradient(top, #e60d10, #c9140f); 63 | background-image: linear-gradient(to bottom, #e60d10, #c9140f); 64 | background-repeat: repeat-x; 65 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe60d10', endColorstr='#ffc9140f', GradientType=0); 66 | background-color: #e60d10; 67 | border: 1px solid #914e1e; 68 | border-bottom-color: #914e1e; 69 | border-top-color: #914e1e; 70 | color: #fff; 71 | -webkit-box-shadow: inset 0 1px 0 #e60d10; 72 | -moz-box-shadow: inset 0 1px 0 #e60d10; 73 | box-shadow: inset 0 1px 0 #e60d10; 74 | } 75 | 76 | #stop_show:hover{ 77 | background-color: #c93a27; 78 | } 79 | 80 | /* 81 | .btn-stop:active, 82 | .btn-stop.active { 83 | background-color: #942a25 \9; 84 | } 85 | */ 86 | 87 | #showframe, #loadingframe, #overlayframe{ 88 | width: 100%; 89 | border: 0px; 90 | position: fixed; 91 | top: 0px; 92 | left: 0px; 93 | } 94 | 95 | #loadingframe{ 96 | height:1024px; 97 | z-index: 100; 98 | } 99 | 100 | #overlayframe{ 101 | height:164px; 102 | z-index: 101; 103 | top: -100px; 104 | } 105 | 106 | #overlay-controls{ 107 | background-color: white; 108 | border-radius: 5px; 109 | border: 1px solid rgb(158, 158, 158); 110 | width: 350px; 111 | margin-left: auto; 112 | margin-right: auto; 113 | padding: 4px; 114 | position: fixed; 115 | top: -100px; 116 | left: 50%; 117 | margin-left: -125px; 118 | margin-top: 20px; 119 | z-index: 101; 120 | padding: 18px; 121 | display: none; 122 | } 123 | 124 | #overlay-controls > button{ 125 | margin-left: 12px; 126 | } 127 | 128 | .inline-controls{ 129 | float:left; 130 | } 131 | 132 | .saved-slideshow-controls{ 133 | padding-top: 30px; 134 | } 135 | 136 | .saved-slideshow-controls > .btn{ 137 | padding: 8px; 138 | } 139 | 140 | #link, #autoplay-link{ 141 | width: 370px; 142 | background-color: white; 143 | } 144 | 145 | #show_links{ 146 | margin-left: 8px; 147 | } 148 | 149 | #message_outer_holder{ 150 | display: inline-block; 151 | } 152 | 153 | #message_holder > .alert{ 154 | margin-bottom: 0px; 155 | margin-top: 12px; 156 | } 157 | 158 | input[name=delay]{ 159 | margin-top: 10px; 160 | } 161 | 162 | /* Make sure that the show frame has a background color. Otherwise, a page that doesn't declare will allow the underlying view to show through. */ 163 | #showframe{ 164 | background-color: white; 165 | } -------------------------------------------------------------------------------- /src/appserver/static/js/templates/SlideshowSetupPage.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 53 | 54 |
55 |
Slideshow is running
56 | 57 | 58 | 59 | 60 |
61 | 62 | Select the views and configure how you want to display the views below. 63 | When the show is started, the selected pages will be displayed for the amount of time specified automatically. 64 | 65 |
66 |
67 | Saved Shows 68 |
69 | 70 |
71 |
72 | 79 |
80 |
81 |
82 | 83 | 84 |
85 |
86 |
87 |
88 |
89 | 90 |
91 |
92 | Views to Display 93 | 104 | 105 | Why aren't some dashboards listed here? 106 | Add custom URL... 107 |
108 |
109 | 112 | Options 113 |
114 |
115 | 116 |
117 | 118 | 119 | 120 | 121 | 122 |
123 | Defines how long to show each view before changing to the next one 124 | 125 |
126 |
127 | 128 |
129 |
130 | checked<% } %>> 131 | 132 | Hides controls and other content that are not necessary for viewing content read-only (simplifies the views and makes them fit smaller screens) 133 | 134 |
135 |
136 | 137 |
138 |
139 | checked<% } %>> 140 | 141 | Invert the colors to darken the page 142 | 143 |
144 |
145 | 146 |
147 |
148 | checked<% } %>> 149 | 150 | Hide progress-bar in order to reduce bandwidth consumption on slow or remote displays 151 | 152 |
153 |
154 | 155 |
156 | 157 | 158 |
159 | -------------------------------------------------------------------------------- /src/appserver/static/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/src/appserver/static/screenshot.png -------------------------------------------------------------------------------- /src/appserver/static/slideshow_setup.js: -------------------------------------------------------------------------------- 1 | 2 | //Translations for en_US 3 | i18n_register({"plural": function(n) { return n == 1 ? 0 : 1; }, "catalog": {}}); 4 | 5 | require.config({ 6 | paths: { 7 | slideshow_setup_view: '../app/slideshow/js/views/SlideshowSetupView' 8 | } 9 | }); 10 | 11 | require(['jquery','underscore','splunkjs/mvc', 'slideshow_setup_view', 'splunkjs/mvc/simplexml/ready!'], 12 | function($, _, mvc, SlideshowSetupView){ 13 | 14 | // Render the slideshow setup page 15 | var slideshowSetupView = new SlideshowSetupView({ 16 | el: $('#slideshow_setup_screen') 17 | }); 18 | 19 | // Render the page 20 | slideshowSetupView.render(); 21 | 22 | }); -------------------------------------------------------------------------------- /src/default/app.conf: -------------------------------------------------------------------------------- 1 | [launcher] 2 | version = 2.6.2 3 | description = An app that will rotate between dashboards on a frequency; useful for displaying content on informational big screens. 4 | author = LukeMurphey 5 | 6 | [package] 7 | id = slideshow 8 | 9 | [install] 10 | build = ${value.build.number} 11 | # Last commit: ${value.build.date} 12 | 13 | [ui] 14 | is_visible = true 15 | label = Slideshow -------------------------------------------------------------------------------- /src/default/collections.conf: -------------------------------------------------------------------------------- 1 | [saved_slideshows] 2 | field.name = string 3 | field.configuration = string 4 | accelerated_fields.name = {"name": 1} 5 | -------------------------------------------------------------------------------- /src/default/data/ui/nav/default.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/default/data/ui/views/slideshow_setup.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |
-------------------------------------------------------------------------------- /src/default/data/ui/views/slideshow_supportability.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Indicates if views are likely supported within slideshows 4 | 5 | 6 | 7 | Views 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /src/default/savedsearches.conf: -------------------------------------------------------------------------------- 1 | [Slideshow Views Supportability] 2 | display.general.enablePreview = 1 3 | display.general.timeRangePicker.show = false 4 | display.general.type = statistics 5 | display.visualizations.show = 1 6 | enableSched = 0 7 | search = | rest /services/data/ui/views | eval sharing='eai:acl.sharing' | eval possible_issue="" | eval possible_issue=if(sharing=="app", "View is not shared", possible_issue) | eval possible_issue=if('eai:acl.perms.read' == "*", possible_issue, "View is not readable by all users") | eval possible_issue=if(isVisible=0,"View is set to invisible", possible_issue) | eval possible_issue=if(like('eai:data', "%type=\"redirect\"%"),"View is just a redirect", possible_issue) | eval warnings=if(possible_issue=="","no", "yes") | eval possible_issue=if(possible_issue == "", "No issues noted", possible_issue) | table title warnings possible_issue -------------------------------------------------------------------------------- /src/metadata/default.meta: -------------------------------------------------------------------------------- 1 | [] 2 | access = read : [ * ], write : [ admin ] 3 | export = system -------------------------------------------------------------------------------- /src/static/appIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/src/static/appIcon.png -------------------------------------------------------------------------------- /src/static/appIconAlt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/src/static/appIconAlt.png -------------------------------------------------------------------------------- /src/static/appIcon_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMurphey/splunk-slideshow/0b767f7b79aba1cf304cabfdcf1ce3e2d7c23506/src/static/appIcon_2x.png --------------------------------------------------------------------------------