├── .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 | 
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;n
document.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 |
15 |
19 |
20 |
21 |
22 | Copy the link that you want to share.
23 |
24 |
25 | Link that will auto-play the show when opened:
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Link that doesn't auto-play:
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
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 |
88 |
89 |
90 |
91 |
108 |
109 |
112 |
113 |
114 |
126 |
127 |
128 |
129 |
135 |
136 |
137 |
138 |
144 |
145 |
146 |
147 |
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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------