aboutsummaryrefslogtreecommitdiff
path: root/subsonic-main/src/main/webapp
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
committerScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
commita1a18f77a50804e0127dfa4b0f5240c49c541184 (patch)
tree19a38880afe505beddb5590379a8134d7730a277 /subsonic-main/src/main/webapp
parentb61d787706979e7e20f4c3c4f93c1f129d92273f (diff)
downloaddsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.gz
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.bz2
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.zip
Initial Commit
Diffstat (limited to 'subsonic-main/src/main/webapp')
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/applicationContext-cache.xml22
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml234
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml245
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/dwr.xml62
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/accessDenied.jsp22
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/advancedSettings.jsp142
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/allmusic.jsp16
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/avatarUploadResult.jsp35
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp206
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp86
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/createShare.jsp51
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/db.jsp45
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/donate.jsp147
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/editTags.jsp164
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/externalPlayer.jsp99
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/generalSettings.jsp165
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/gettingStarted.jsp53
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/head.jsp11
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/help.jsp70
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/helpToolTip.jsp18
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/home.jsp189
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/importPlaylist.jsp37
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/include.jsp8
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/index.jsp26
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/internetRadioSettings.jsp62
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/jquery.jsp3
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/left.jsp168
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/login.jsp64
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/lyrics.jsp79
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/main.jsp479
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/more.jsp159
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/musicFolderSettings.jsp114
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/networkSettings.jsp107
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/notFound.jsp21
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/passwordSettings.jsp45
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/personalSettings.jsp228
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/playAddDownload.jsp64
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp617
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/playerSettings.jsp177
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/playlist.jsp235
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/podcast.jsp26
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/podcastReceiver.jsp269
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/podcastSettings.jsp88
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/rating.jsp51
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/recover.jsp34
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/reload.jsp11
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/rest/videoPlayer.jsp142
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/right.jsp191
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/search.jsp150
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/settingsHeader.jsp32
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/shareSettings.jsp72
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/starred.jsp131
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/status.jsp93
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/test.jsp20
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/top.jsp98
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/transcodingSettings.jsp70
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/upload.jsp29
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/userSettings.jsp201
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/videoPlayer.jsp190
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/browse.jsp56
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/head.jsp10
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/index.jsp62
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/loadPlaylist.jsp23
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/playlist.jsp56
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/search.jsp19
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/searchResult.jsp30
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/wap/settings.jsp47
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/jsp/xspfPlaylist.jsp30
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/sub.tld114
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/subsonic-servlet.xml479
-rw-r--r--subsonic-main/src/main/webapp/WEB-INF/web.xml207
-rw-r--r--subsonic-main/src/main/webapp/ad/omakasa.html5
-rw-r--r--subsonic-main/src/main/webapp/crossdomain.xml6
-rw-r--r--subsonic-main/src/main/webapp/error.jsp48
-rw-r--r--subsonic-main/src/main/webapp/flash/jw-player-5.6.swfbin0 -> 104032 bytes
-rw-r--r--subsonic-main/src/main/webapp/flash/whotube.zipbin0 -> 21255 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/add.gifbin0 -> 69 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/android.pngbin0 -> 3154 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/back.gifbin0 -> 890 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/add.pngbin0 -> 1048 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/android.pngbin0 -> 988 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/back.pngbin0 -> 794 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/background_main.pngbin0 -> 158474 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/clear_rating.pngbin0 -> 976 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/donate.pngbin0 -> 2541 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/donate_small.pngbin0 -> 715 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/down.pngbin0 -> 1009 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/download.pngbin0 -> 1020 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/error.pngbin0 -> 2276 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/forward.pngbin0 -> 788 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/gpl.pngbin0 -> 4555 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/help.pngbin0 -> 2850 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/help_small.pngbin0 -> 1107 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/home.pngbin0 -> 2711 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/home_hover.pngbin0 -> 633 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/list_heading.pngbin0 -> 330 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/log.pngbin0 -> 1090 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/logo.pngbin0 -> 14018 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/more.pngbin0 -> 2629 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/now_playing.pngbin0 -> 1038 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/paypal.gifbin0 -> 7515 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/phone.pngbin0 -> 884 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/play.pngbin0 -> 1038 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/playing.pngbin0 -> 2372 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/podcast.pngbin0 -> 2902 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/podcast_small.pngbin0 -> 1064 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/progress.pngbin0 -> 155 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/random.pngbin0 -> 1110 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/rating_half.pngbin0 -> 843 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/rating_off.pngbin0 -> 605 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/rating_on.pngbin0 -> 776 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/remove.pngbin0 -> 952 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/search.pngbin0 -> 2446 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/settings.pngbin0 -> 2709 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/status.pngbin0 -> 2214 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/subair.pngbin0 -> 744 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/up.pngbin0 -> 961 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/upload.pngbin0 -> 1009 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/buuftheme/wap.pngbin0 -> 967 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/clearRating.pngbin0 -> 545 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/add.pngbin0 -> 3247 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/back.pngbin0 -> 500 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/background.pngbin0 -> 3214 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/background_main.pngbin0 -> 221782 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/clear_rating.pngbin0 -> 656 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/down.pngbin0 -> 503 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/download.pngbin0 -> 3330 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/error.pngbin0 -> 1183 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/forward.pngbin0 -> 499 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/help.pngbin0 -> 4110 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/help_small.pngbin0 -> 3322 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/home.pngbin0 -> 4105 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/home_hover.pngbin0 -> 4665 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/list_heading.pngbin0 -> 316 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/log.pngbin0 -> 651 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/logo.pngbin0 -> 10261 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/more.pngbin0 -> 4297 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/now_playing.pngbin0 -> 879 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/phone.pngbin0 -> 3434 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/play.pngbin0 -> 3318 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/playing.pngbin0 -> 3879 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/podcast.pngbin0 -> 4696 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/podcast_small.pngbin0 -> 3473 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/progress.pngbin0 -> 155 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/random.pngbin0 -> 819 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/rating_half.pngbin0 -> 511 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/rating_on.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/remove.pngbin0 -> 3153 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/search.pngbin0 -> 4339 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/settings.pngbin0 -> 4401 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/status.pngbin0 -> 3999 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/up.pngbin0 -> 623 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/coolandclean/upload.pngbin0 -> 3355 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/current.gifbin0 -> 348 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/add.pngbin0 -> 644 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/back.pngbin0 -> 713 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/clear_rating.pngbin0 -> 2937 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/down.pngbin0 -> 609 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/download.pngbin0 -> 649 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/forward.pngbin0 -> 723 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/help.pngbin0 -> 1238 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/help_small.pngbin0 -> 812 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/home.pngbin0 -> 1064 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/log.pngbin0 -> 657 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/logo.pngbin0 -> 5494 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/more.pngbin0 -> 784 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/now_playing.pngbin0 -> 761 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/phone.pngbin0 -> 722 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/play.pngbin0 -> 610 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/playing.pngbin0 -> 1317 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/podcast.pngbin0 -> 1489 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/podcast_small.pngbin0 -> 876 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/random.pngbin0 -> 3099 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/remove.pngbin0 -> 633 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/search.pngbin0 -> 1198 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/settings.pngbin0 -> 1111 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/status.pngbin0 -> 1675 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/up.pngbin0 -> 610 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/denim/upload.pngbin0 -> 645 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/donate.pngbin0 -> 1036 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/donate_small.pngbin0 -> 585 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/down.gifbin0 -> 856 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/download.gifbin0 -> 855 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/error.pngbin0 -> 954 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/forward.gifbin0 -> 889 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/gpl.pngbin0 -> 2986 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/add.pngbin0 -> 2927 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/back.pngbin0 -> 2945 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/background_main.pngbin0 -> 230821 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/background_main_blank.pngbin0 -> 2303 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/clear_rating.pngbin0 -> 2976 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/down.pngbin0 -> 2970 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/download.pngbin0 -> 2970 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/error.pngbin0 -> 3322 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/forward.pngbin0 -> 2903 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/help.pngbin0 -> 2338 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/help_small.pngbin0 -> 1098 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/home.pngbin0 -> 1244 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/log.pngbin0 -> 792 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/logo.pngbin0 -> 10523 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/more.pngbin0 -> 1798 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/now_playing.pngbin0 -> 1359 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/phone.pngbin0 -> 722 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/play.pngbin0 -> 2970 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/playing.pngbin0 -> 1686 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/podcast.pngbin0 -> 1813 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/podcast_small.pngbin0 -> 897 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/random.pngbin0 -> 3161 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/rating_half.pngbin0 -> 762 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/rating_on.pngbin0 -> 830 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/remove.pngbin0 -> 2862 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/search.pngbin0 -> 1071 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/settings.pngbin0 -> 1840 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/status.pngbin0 -> 1181 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/up.pngbin0 -> 2941 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/groove/upload.pngbin0 -> 2941 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hd/background.pngbin0 -> 519958 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/help.pngbin0 -> 1484 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/help_small.pngbin0 -> 932 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/favicon.icobin0 -> 61798 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/help.pngbin0 -> 3136 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/home.pngbin0 -> 1300 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/more.pngbin0 -> 3166 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/now_playing.pngbin0 -> 3090 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/podcast_large.pngbin0 -> 3142 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/settings.pngbin0 -> 3244 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/status.pngbin0 -> 3101 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hicon/subsonic.pngbin0 -> 8114 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/Untitled-1.icobin0 -> 61798 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/help.pngbin0 -> 3169 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/home.pngbin0 -> 1314 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/more.pngbin0 -> 3200 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/now_playing.pngbin0 -> 3121 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/podcast_large.pngbin0 -> 3166 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/settings.pngbin0 -> 3280 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/status.pngbin0 -> 3113 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hiconi/subsonic.pngbin0 -> 7596 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/bg.jpgbin0 -> 11283 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/bg2.jpgbin0 -> 67101 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/favicon.icobin0 -> 61798 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/help.pngbin0 -> 3668 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/home.pngbin0 -> 3698 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/more.pngbin0 -> 3871 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/now_playing.pngbin0 -> 3624 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/podcast_large.pngbin0 -> 3898 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/settings.pngbin0 -> 3692 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/status.pngbin0 -> 3441 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/hitech/subsonic.pngbin0 -> 6984 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/home.pngbin0 -> 800 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/log.pngbin0 -> 992 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnight/back.pngbin0 -> 1251 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnight/forward.pngbin0 -> 1251 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/error.pngbin0 -> 1671 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_Now_Playing.pngbin0 -> 879 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_add.pngbin0 -> 371 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_back.pngbin0 -> 694 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_background.gifbin0 -> 9367 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_clear_Rating.pngbin0 -> 1139 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_donate.pngbin0 -> 5752 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_down.pngbin0 -> 630 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_download.pngbin0 -> 749 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls.jpgbin0 -> 9672 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls_hover.jpgbin0 -> 24098 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_forward.pngbin0 -> 693 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help.pngbin0 -> 1641 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help_small.pngbin0 -> 694 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home.pngbin0 -> 3453 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home_hover.jpgbin0 -> 14915 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_log.pngbin0 -> 820 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo.pngbin0 -> 9472 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo_favicon.icobin0 -> 894 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_more.pngbin0 -> 793 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_phone.pngbin0 -> 587 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_play.pngbin0 -> 934 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_playing.pngbin0 -> 1852 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast.pngbin0 -> 1369 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast_small.pngbin0 -> 666 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_random.pngbin0 -> 739 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_remove.pngbin0 -> 391 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_search.pngbin0 -> 2991 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_settings.pngbin0 -> 2148 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_status.pngbin0 -> 2007 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_table.jpgbin0 -> 1648 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_text_back.jpgbin0 -> 1921 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_up.pngbin0 -> 584 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_upload.pngbin0 -> 639 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/progress.pngbin0 -> 155 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/ratingHalf.pngbin0 -> 1092 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/ratingOff.pngbin0 -> 1029 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/midnightfun/ratingOn.pngbin0 -> 1230 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/monochrome/subdot.pngbin0 -> 6900 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/more.pngbin0 -> 1418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/now_playing.pngbin0 -> 1131 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/paypal.gifbin0 -> 2127 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/add.pngbin0 -> 644 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/back.pngbin0 -> 713 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/clear_rating.pngbin0 -> 2937 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/down.pngbin0 -> 609 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/download.pngbin0 -> 649 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/forward.pngbin0 -> 723 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/help.pngbin0 -> 1238 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/help_small.pngbin0 -> 812 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/home.pngbin0 -> 1064 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/log.pngbin0 -> 657 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/logo.pngbin0 -> 9827 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/more.pngbin0 -> 784 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/now_playing.pngbin0 -> 761 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/phone.pngbin0 -> 722 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/play.pngbin0 -> 610 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/playing.pngbin0 -> 1317 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/podcast.pngbin0 -> 1489 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/podcast_small.pngbin0 -> 876 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/random.pngbin0 -> 3099 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/remove.pngbin0 -> 633 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/search.pngbin0 -> 1198 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/settings.pngbin0 -> 1111 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/status.pngbin0 -> 1675 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/up.pngbin0 -> 610 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/pinkpanther/upload.pngbin0 -> 645 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/play.gifbin0 -> 70 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/podcast.pngbin0 -> 815 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/podcast_large.pngbin0 -> 1192 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/progress.pngbin0 -> 155 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/random.pngbin0 -> 932 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ratingHalf.pngbin0 -> 511 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ratingOff.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ratingOn.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/remove.gifbin0 -> 855 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/add.gifbin0 -> 13222 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/back.gifbin0 -> 13202 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/background.pngbin0 -> 707425 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/clearRating.pngbin0 -> 48184 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/current.gifbin0 -> 551 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/donate.pngbin0 -> 1095 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/down.gifbin0 -> 13265 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/download.gifbin0 -> 13258 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/error.pngbin0 -> 1102 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/favicon.icobin0 -> 3638 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/forward.gifbin0 -> 13261 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/help.pngbin0 -> 50596 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/help_small.pngbin0 -> 48608 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/home.pngbin0 -> 906 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/log.pngbin0 -> 992 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/more.pngbin0 -> 50720 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/now_playing.pngbin0 -> 51823 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/paypal.gifbin0 -> 2127 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/play.gifbin0 -> 13198 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/podcast.pngbin0 -> 1133 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/podcast_large.pngbin0 -> 1133 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/progress.pngbin0 -> 155 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/random.pngbin0 -> 48117 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/ratingHalf.pngbin0 -> 1092 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/ratingOff.pngbin0 -> 1029 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/ratingOn.pngbin0 -> 1230 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/remove.gifbin0 -> 13221 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/search.pngbin0 -> 1232 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/settings.pngbin0 -> 51387 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/status.pngbin0 -> 50439 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/subsonic_black.pngbin0 -> 62131 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/subsonic_white.pngbin0 -> 62273 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/up.gifbin0 -> 13265 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/upload.gifbin0 -> 13222 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/ripserver/wap.pngbin0 -> 48214 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/search.pngbin0 -> 1356 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/settings.pngbin0 -> 1327 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/share_facebook.pngbin0 -> 786 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/share_googleplus.pngbin0 -> 3340 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/share_twitter.pngbin0 -> 854 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/add.pngbin0 -> 807 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/back.pngbin0 -> 952 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/clear_rating.pngbin0 -> 2925 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/down.pngbin0 -> 738 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/download.pngbin0 -> 607 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/forward.pngbin0 -> 964 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/help.pngbin0 -> 1190 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/help_small.pngbin0 -> 791 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/home.pngbin0 -> 1022 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/log.pngbin0 -> 622 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/logo.pngbin0 -> 9096 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/more.pngbin0 -> 760 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/now_playing.pngbin0 -> 1180 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/phone.pngbin0 -> 722 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/play.pngbin0 -> 755 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/playing.pngbin0 -> 1168 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/podcast.pngbin0 -> 1283 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/podcast_small.pngbin0 -> 835 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/random.pngbin0 -> 3049 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/remove.pngbin0 -> 633 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/search.pngbin0 -> 1166 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/settings.pngbin0 -> 898 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/status.pngbin0 -> 1455 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/up.pngbin0 -> 749 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/simplify/upload.pngbin0 -> 605 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/favicon.icobin0 -> 61798 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/help.pngbin0 -> 3915 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/home.pngbin0 -> 1488 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/more.pngbin0 -> 3855 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/now_playing.pngbin0 -> 3667 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/podcast_large.pngbin0 -> 3693 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/settings.pngbin0 -> 3946 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/status.pngbin0 -> 3534 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/subsonic.pngbin0 -> 7194 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/slick/top_bg.jpgbin0 -> 10806 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/add.pngbin0 -> 6409 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/back.pngbin0 -> 986 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/clear_rating.pngbin0 -> 2927 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/down.pngbin0 -> 6474 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/download.pngbin0 -> 6488 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/forward.pngbin0 -> 998 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/help.pngbin0 -> 7241 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/help_small.pngbin0 -> 1076 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/home.pngbin0 -> 6836 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/log.pngbin0 -> 641 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/logo.pngbin0 -> 5145 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/more.pngbin0 -> 7009 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/now_playing.pngbin0 -> 914 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/phone.pngbin0 -> 769 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/play.pngbin0 -> 6458 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/playing.pngbin0 -> 7636 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/podcast.pngbin0 -> 7233 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/podcast_small.pngbin0 -> 837 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/random.pngbin0 -> 3050 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/remove.pngbin0 -> 6414 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/search.pngbin0 -> 7134 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/settings.pngbin0 -> 7413 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/status.pngbin0 -> 6935 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/up.pngbin0 -> 6459 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic/upload.pngbin0 -> 6487 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/add.pngbin0 -> 6645 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/back.pngbin0 -> 6767 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/clear_rating.pngbin0 -> 2937 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/down.pngbin0 -> 6684 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/download.pngbin0 -> 6688 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/forward.pngbin0 -> 6765 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/help.pngbin0 -> 7377 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/help_small.pngbin0 -> 589 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/home.pngbin0 -> 6983 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/log.pngbin0 -> 657 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/logo.pngbin0 -> 4904 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/more.pngbin0 -> 7178 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/now_playing.pngbin0 -> 761 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/phone.pngbin0 -> 769 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/play.pngbin0 -> 6658 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/playing.pngbin0 -> 8314 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/podcast.pngbin0 -> 7400 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/podcast_small.pngbin0 -> 876 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/random.pngbin0 -> 3099 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/remove.pngbin0 -> 6631 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/search.pngbin0 -> 7320 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/settings.pngbin0 -> 7483 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/status.pngbin0 -> 7089 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/up.pngbin0 -> 6681 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_blue/upload.pngbin0 -> 6668 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/add.pngbin0 -> 6395 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/back.pngbin0 -> 969 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/clear_rating.pngbin0 -> 2927 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/donate.pngbin0 -> 1899 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/down.pngbin0 -> 6467 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/download.pngbin0 -> 6413 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/error.pngbin0 -> 3334 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/favicon.icobin0 -> 1150 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/forward.pngbin0 -> 961 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/help.pngbin0 -> 7220 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/help_small.pngbin0 -> 1076 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/home.pngbin0 -> 6812 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/log.pngbin0 -> 641 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/logo.pngbin0 -> 5145 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/more.pngbin0 -> 6989 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/now_playing.pngbin0 -> 914 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/paypal.gifbin0 -> 956 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/phone.pngbin0 -> 769 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/play.pngbin0 -> 6418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/playing.pngbin0 -> 7612 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/podcast.pngbin0 -> 7196 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/podcast_small.pngbin0 -> 837 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/progress.pngbin0 -> 502 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/random.pngbin0 -> 3050 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/rating_half.pngbin0 -> 777 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/rating_off.pngbin0 -> 418 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/rating_on.pngbin0 -> 845 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/remove.pngbin0 -> 6392 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/search.pngbin0 -> 7103 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/settings.pngbin0 -> 7388 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/status.pngbin0 -> 6893 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/up.pngbin0 -> 6446 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/sonic_white/upload.pngbin0 -> 6444 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/spinner.gifbin0 -> 847 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/star_off.pngbin0 -> 594 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/starred.pngbin0 -> 699 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/status.pngbin0 -> 869 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/subair.pngbin0 -> 744 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/subsonic_black.pngbin0 -> 4797 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/subsonic_white.pngbin0 -> 4799 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/up.gifbin0 -> 856 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/upload.gifbin0 -> 311 bytes
-rw-r--r--subsonic-main/src/main/webapp/icons/wap.pngbin0 -> 488 bytes
-rw-r--r--subsonic-main/src/main/webapp/index.html10
-rw-r--r--subsonic-main/src/main/webapp/index.jsp10
-rw-r--r--subsonic-main/src/main/webapp/script/AC_OETags.js269
-rw-r--r--subsonic-main/src/main/webapp/script/builder.js136
-rw-r--r--subsonic-main/src/main/webapp/script/controls.js965
-rw-r--r--subsonic-main/src/main/webapp/script/effects.js1130
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/FancyZoom.js761
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/FancyZoomHTML.js318
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/closebox.pngbin0 -> 1910 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/spacer.gifbin0 -> 43 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-fill.pngbin0 -> 134 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-l.pngbin0 -> 310 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-r.pngbin0 -> 290 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow1.pngbin0 -> 310 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow2.pngbin0 -> 164 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow3.pngbin0 -> 368 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow4.pngbin0 -> 178 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow5.pngbin0 -> 180 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow6.pngbin0 -> 428 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow7.pngbin0 -> 186 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow8.pngbin0 -> 426 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-1.pngbin0 -> 1882 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-10.pngbin0 -> 1892 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-11.pngbin0 -> 1901 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-12.pngbin0 -> 1902 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-2.pngbin0 -> 1893 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-3.pngbin0 -> 1922 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-4.pngbin0 -> 1890 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-5.pngbin0 -> 1938 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-6.pngbin0 -> 1927 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-7.pngbin0 -> 1898 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-8.pngbin0 -> 1910 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-9.pngbin0 -> 1901 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/jquery-1.7.1.min.js4
-rw-r--r--subsonic-main/src/main/webapp/script/jquery-ui-1.8.18.custom.min.js356
-rw-r--r--subsonic-main/src/main/webapp/script/pngfix.js39
-rw-r--r--subsonic-main/src/main/webapp/script/prototype.js4320
-rw-r--r--subsonic-main/src/main/webapp/script/scripts.js21
-rw-r--r--subsonic-main/src/main/webapp/script/smooth-scroll.js102
-rw-r--r--subsonic-main/src/main/webapp/script/swfobject.js4
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon.js221
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/b.gifbin0 -> 46 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/background.gifbin0 -> 43 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/l.gifbin0 -> 46 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/lb.gifbin0 -> 85 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/lt.gifbin0 -> 86 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/r.gifbin0 -> 46 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/rb.gifbin0 -> 86 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/rt.gifbin0 -> 85 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/stemb.gifbin0 -> 165 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/stemt.gifbin0 -> 167 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/tip_balloon/t.gifbin0 -> 46 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/boxsizing.htc157
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/handle.horizontal.hover.pngbin0 -> 440 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/handle.horizontal.pngbin0 -> 443 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/handle.vertical.hover.pngbin0 -> 395 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/handle.vertical.pngbin0 -> 398 bytes
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/luna.css75
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/range.js132
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/slider.js489
-rw-r--r--subsonic-main/src/main/webapp/script/webfx/timer.js62
-rw-r--r--subsonic-main/src/main/webapp/script/wz_tooltip.js1301
-rw-r--r--subsonic-main/src/main/webapp/style/barents.css30
-rw-r--r--subsonic-main/src/main/webapp/style/black.css17
-rw-r--r--subsonic-main/src/main/webapp/style/buuftheme.css120
-rw-r--r--subsonic-main/src/main/webapp/style/coolandclean.css116
-rw-r--r--subsonic-main/src/main/webapp/style/default.css245
-rw-r--r--subsonic-main/src/main/webapp/style/denim.css90
-rw-r--r--subsonic-main/src/main/webapp/style/groove.css102
-rw-r--r--subsonic-main/src/main/webapp/style/groove_simple.css32
-rw-r--r--subsonic-main/src/main/webapp/style/hd.css93
-rw-r--r--subsonic-main/src/main/webapp/style/hd1080.css17
-rw-r--r--subsonic-main/src/main/webapp/style/hd720.css16
-rw-r--r--subsonic-main/src/main/webapp/style/hd768.css16
-rw-r--r--subsonic-main/src/main/webapp/style/hicon.css85
-rw-r--r--subsonic-main/src/main/webapp/style/hiconi.css85
-rw-r--r--subsonic-main/src/main/webapp/style/hitech.css96
-rw-r--r--subsonic-main/src/main/webapp/style/lowerleftfade.pngbin0 -> 274 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/midnight.css77
-rw-r--r--subsonic-main/src/main/webapp/style/midnightfun.css158
-rw-r--r--subsonic-main/src/main/webapp/style/monochrome.css111
-rw-r--r--subsonic-main/src/main/webapp/style/monochrome_black.css115
-rw-r--r--subsonic-main/src/main/webapp/style/pinkpanther.css91
-rw-r--r--subsonic-main/src/main/webapp/style/ripserver.css44
-rw-r--r--subsonic-main/src/main/webapp/style/sandstorm.css39
-rw-r--r--subsonic-main/src/main/webapp/style/shadow.css107
-rw-r--r--subsonic-main/src/main/webapp/style/shadow.pngbin0 -> 3576 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/simplify.css90
-rw-r--r--subsonic-main/src/main/webapp/style/slick.css89
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_75_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_454545_256x240.pngbin0 -> 4369 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_888888_256x240.pngbin0 -> 4369 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--subsonic-main/src/main/webapp/style/smoothness/jquery-ui-1.8.18.custom.css565
-rw-r--r--subsonic-main/src/main/webapp/style/sonic.css88
-rw-r--r--subsonic-main/src/main/webapp/style/sonic_blue.css77
-rw-r--r--subsonic-main/src/main/webapp/style/sonic_white.css78
-rw-r--r--subsonic-main/src/main/webapp/style/upperrightfade.pngbin0 -> 275 bytes
-rw-r--r--subsonic-main/src/main/webapp/xsd/albumList2_example_1.xml16
-rw-r--r--subsonic-main/src/main/webapp/xsd/albumList_example_1.xml12
-rw-r--r--subsonic-main/src/main/webapp/xsd/album_example_1.xml17
-rw-r--r--subsonic-main/src/main/webapp/xsd/artist_example_1.xml24
-rw-r--r--subsonic-main/src/main/webapp/xsd/artists_example_1.xml14
-rw-r--r--subsonic-main/src/main/webapp/xsd/chatMessages_example_1.xml12
-rw-r--r--subsonic-main/src/main/webapp/xsd/directory_example_1.xml11
-rw-r--r--subsonic-main/src/main/webapp/xsd/directory_example_2.xml20
-rw-r--r--subsonic-main/src/main/webapp/xsd/error_example_1.xml8
-rw-r--r--subsonic-main/src/main/webapp/xsd/indexes_example_1.xml30
-rw-r--r--subsonic-main/src/main/webapp/xsd/jukeboxPlaylist_example_1.xml20
-rw-r--r--subsonic-main/src/main/webapp/xsd/jukeboxStatus_example_1.xml9
-rw-r--r--subsonic-main/src/main/webapp/xsd/license_example_1.xml8
-rw-r--r--subsonic-main/src/main/webapp/xsd/lyrics_example_1.xml39
-rw-r--r--subsonic-main/src/main/webapp/xsd/musicFolders_example_1.xml12
-rw-r--r--subsonic-main/src/main/webapp/xsd/nowPlaying_example_1.xml19
-rw-r--r--subsonic-main/src/main/webapp/xsd/ping_example_1.xml5
-rw-r--r--subsonic-main/src/main/webapp/xsd/playlist_example_1.xml37
-rw-r--r--subsonic-main/src/main/webapp/xsd/playlists_example_1.xml12
-rw-r--r--subsonic-main/src/main/webapp/xsd/podcasts_example_1.xml43
-rw-r--r--subsonic-main/src/main/webapp/xsd/randomSongs_example_1.xml20
-rw-r--r--subsonic-main/src/main/webapp/xsd/searchResult2_example_1.xml18
-rw-r--r--subsonic-main/src/main/webapp/xsd/searchResult3_example_1.xml54
-rw-r--r--subsonic-main/src/main/webapp/xsd/searchResult_example_1.xml20
-rw-r--r--subsonic-main/src/main/webapp/xsd/shares_example_1.xml23
-rw-r--r--subsonic-main/src/main/webapp/xsd/song_example_1.xml11
-rw-r--r--subsonic-main/src/main/webapp/xsd/starred2_example_1.xml30
-rw-r--r--subsonic-main/src/main/webapp/xsd/starred_example_1.xml30
-rw-r--r--subsonic-main/src/main/webapp/xsd/subsonic-rest-api.xsd434
-rw-r--r--subsonic-main/src/main/webapp/xsd/user_example_1.xml10
686 files changed, 22654 insertions, 0 deletions
diff --git a/subsonic-main/src/main/webapp/WEB-INF/applicationContext-cache.xml b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-cache.xml
new file mode 100644
index 00000000..a8692d08
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-cache.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+
+ <bean id="cacheFactory" class="net.sourceforge.subsonic.cache.CacheFactory"/>
+
+ <bean id="userCache" factory-bean="cacheFactory" factory-method="getCache">
+ <constructor-arg value="userCache"/>
+ </bean>
+
+ <bean id="mediaFileMemoryCache" factory-bean="cacheFactory" factory-method="getCache">
+ <constructor-arg value="mediaFileMemoryCache"/>
+ </bean>
+
+ <bean id="musicFileMemoryCache" factory-bean="cacheFactory" factory-method="getCache">
+ <constructor-arg value="musicFileMemoryCache"/>
+ </bean>
+
+</beans>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml
new file mode 100644
index 00000000..8cf5e9c2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
+ <property name="filterInvocationDefinitionSource">
+ <value>
+ PATTERN_TYPE_APACHE_ANT
+ /wap**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
+ /podcastReceiver**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
+ /podcast**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
+ /rest/**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,restRequestParameterProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
+ /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
+ </value>
+ </property>
+ </bean>
+
+ <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
+
+ <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
+ <constructor-arg value="/login.view?logout"/>
+ <!-- URL redirected to after logout -->
+ <constructor-arg>
+ <list>
+ <ref bean="rememberMeServices"/>
+ <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
+ </list>
+ </constructor-arg>
+ </bean>
+
+ <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="authenticationFailureUrl" value="/login.view?error"/>
+ <property name="defaultTargetUrl" value="/"/>
+ <property name="alwaysUseDefaultTargetUrl" value="true"/>
+ <property name="filterProcessesUrl" value="/j_acegi_security_check"/>
+ <property name="rememberMeServices" ref="rememberMeServices"/>
+ </bean>
+
+ <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint"/>
+ </bean>
+
+ <bean id="restRequestParameterProcessingFilter" class="net.sourceforge.subsonic.security.RESTRequestParameterProcessingFilter">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
+ <property name="realmName" value="Subsonic"/>
+ </bean>
+
+ <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
+
+ <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="rememberMeServices" ref="rememberMeServices"/>
+ </bean>
+
+ <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
+ <property name="key" value="subsonic"/>
+ <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
+ </bean>
+
+ <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
+ <property name="authenticationEntryPoint">
+ <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
+ <property name="loginFormUrl" value="/login.view?"/>
+ <property name="forceHttps" value="false"/>
+ </bean>
+ </property>
+ <property name="accessDeniedHandler">
+ <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
+ <property name="errorPage" value="/accessDenied.view"/>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="basicExceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
+ <property name="authenticationEntryPoint">
+ <bean class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
+ <property name="realmName" value="Subsonic"/>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="alwaysReauthenticate" value="true"/>
+ <property name="accessDecisionManager" ref="accessDecisionManager"/>
+ <property name="objectDefinitionSource">
+ <value>
+ PATTERN_TYPE_APACHE_ANT
+
+ /login.view=IS_AUTHENTICATED_ANONYMOUSLY
+ /recover.view=IS_AUTHENTICATED_ANONYMOUSLY
+ /accessDenied.view=IS_AUTHENTICATED_ANONYMOUSLY
+ /videoPlayer.view=IS_AUTHENTICATED_ANONYMOUSLY
+ /coverArt.view=IS_AUTHENTICATED_ANONYMOUSLY
+ /stream/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /share/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /style/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /icons/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /flash/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /script/**=IS_AUTHENTICATED_ANONYMOUSLY
+ /crossdomain.xml=IS_AUTHENTICATED_ANONYMOUSLY
+
+ /personalSettings.view=ROLE_SETTINGS
+ /passwordSettings.view=ROLE_SETTINGS
+ /playerSettings.view=ROLE_SETTINGS
+ /shareSettings.view=ROLE_SETTINGS
+
+ /generalSettings.view=ROLE_ADMIN
+ /advancedSettings.view=ROLE_ADMIN
+ /userSettings.view=ROLE_ADMIN
+ /musicFolderSettings.view=ROLE_ADMIN
+ /networkSettings.view=ROLE_ADMIN
+ /transcodingSettings.view=ROLE_ADMIN
+ /internetRadioSettings.view=ROLE_ADMIN
+ /podcastSettings.view=ROLE_ADMIN
+ /db.view=ROLE_ADMIN
+
+ /deletePlaylist.view=ROLE_PLAYLIST
+ /savePlaylist.view=ROLE_PLAYLIST
+
+ /download.view=ROLE_DOWNLOAD
+
+ /upload.view=ROLE_UPLOAD
+
+ /createShare.view=ROLE_SHARE
+
+ /changeCoverArt.view=ROLE_COVERART
+ /editTags.view=ROLE_COVERART
+
+ /setMusicFileInfo.view=ROLE_COMMENT
+
+ /podcastReceiverAdmin.view=ROLE_PODCAST
+
+ /**=IS_AUTHENTICATED_REMEMBERED
+ </value>
+ </property>
+ </bean>
+
+ <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
+ <property name="allowIfAllAbstainDecisions" value="false"/>
+ <property name="decisionVoters">
+ <list>
+ <bean class="org.acegisecurity.vote.RoleVoter"/>
+ <bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
+ </list>
+ </property>
+ </bean>
+
+ <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
+ <property name="userDetailsService" ref="securityService"/>
+ <property name="tokenValiditySeconds" value="31536000"/>
+ <!-- One year -->
+ <property name="key" value="subsonic"/>
+ </bean>
+
+ <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
+ <property name="providers">
+ <list>
+ <ref local="daoAuthenticationProvider"/>
+ <ref local="ldapAuthenticationProvider"/>
+ <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
+ <property name="key" value="subsonic"/>
+ </bean>
+ <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
+ <property name="key" value="subsonic"/>
+ </bean>
+ </list>
+ </property>
+ </bean>
+
+ <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
+ <property name="userDetailsService" ref="securityService"/>
+ <property name="userCache" ref="userCacheWrapper"/>
+ </bean>
+
+ <bean id="userCacheWrapper" class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
+ <property name="cache" ref="userCache"/>
+ </bean>
+
+ <bean id="ldapAuthenticationProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
+ <constructor-arg ref="bindAuthenticator"/>
+ <constructor-arg ref="userDetailsServiceBasedAuthoritiesPopulator"/>
+ <property name="userCache" ref="userCacheWrapper"/>
+ </bean>
+
+ <bean id="bindAuthenticator" class="net.sourceforge.subsonic.ldap.SubsonicLdapBindAuthenticator">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="userDetailsServiceBasedAuthoritiesPopulator"
+ class="net.sourceforge.subsonic.ldap.UserDetailsServiceBasedAuthoritiesPopulator">
+ <property name="userDetailsService" ref="securityService"/>
+ </bean>
+
+ <!-- Authorization of AJAX services. -->
+ <bean id="ajaxServiceInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
+ <property name="authenticationManager" ref="authenticationManager"/>
+ <property name="accessDecisionManager" ref="accessDecisionManager"/>
+ <property name="objectDefinitionSource">
+ <value>
+ net.sourceforge.subsonic.ajax.TagService.setTags=ROLE_COVERART
+ net.sourceforge.subsonic.ajax.TransferService.getUploadInfo=ROLE_UPLOAD
+ </value>
+ </property>
+ </bean>
+
+ <bean id="ajaxTagServiceSecure" class="org.springframework.aop.framework.ProxyFactoryBean">
+ <property name="target" ref="ajaxTagService"/>
+ <property name="interceptorNames">
+ <list>
+ <idref local="ajaxServiceInterceptor"/>
+ </list>
+ </property>
+ </bean>
+
+ <bean id="ajaxTransferServiceSecure" class="org.springframework.aop.framework.ProxyFactoryBean">
+ <property name="target" ref="ajaxTransferService"/>
+ <property name="interceptorNames">
+ <list>
+ <idref local="ajaxServiceInterceptor"/>
+ </list>
+ </property>
+ </bean>
+
+</beans> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml
new file mode 100644
index 00000000..95f76359
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <!-- DAO's -->
+
+ <bean id="playerDao" class="net.sourceforge.subsonic.dao.PlayerDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="mediaFileDao" class="net.sourceforge.subsonic.dao.MediaFileDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="artistDao" class="net.sourceforge.subsonic.dao.ArtistDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="albumDao" class="net.sourceforge.subsonic.dao.AlbumDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="playlistDao" class="net.sourceforge.subsonic.dao.PlaylistDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="internetRadioDao" class="net.sourceforge.subsonic.dao.InternetRadioDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="musicFileInfoDao" class="net.sourceforge.subsonic.dao.RatingDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="musicFolderDao" class="net.sourceforge.subsonic.dao.MusicFolderDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="userDao" class="net.sourceforge.subsonic.dao.UserDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="transcodingDao" class="net.sourceforge.subsonic.dao.TranscodingDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="podcastDao" class="net.sourceforge.subsonic.dao.PodcastDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="avatarDao" class="net.sourceforge.subsonic.dao.AvatarDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="shareDao" class="net.sourceforge.subsonic.dao.ShareDao">
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+
+ <bean id="daoHelper" class="net.sourceforge.subsonic.dao.DaoHelper"/>
+
+
+ <!-- Services -->
+
+ <bean id="mediaFileService" class="net.sourceforge.subsonic.service.MediaFileService">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileMemoryCache" ref="mediaFileMemoryCache"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ <property name="metaDataParserFactory" ref="metaDataParserFactory"/>
+ </bean>
+
+ <bean id="securityService" class="net.sourceforge.subsonic.service.SecurityService">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="userDao" ref="userDao"/>
+ <property name="userCache" ref="userCache"/>
+ </bean>
+
+ <bean id="settingsService" class="net.sourceforge.subsonic.service.SettingsService" init-method="init">
+ <property name="internetRadioDao" ref="internetRadioDao"/>
+ <property name="musicFolderDao" ref="musicFolderDao"/>
+ <property name="userDao" ref="userDao"/>
+ <property name="avatarDao" ref="avatarDao"/>
+ <property name="versionService" ref="versionService"/>
+ </bean>
+
+ <bean id="mediaScannerService" class="net.sourceforge.subsonic.service.MediaScannerService" init-method="init" depends-on="metaDataParserFactory">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="artistDao" ref="artistDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ <property name="searchService" ref="searchService"/>
+ </bean>
+
+ <bean id="searchService" class="net.sourceforge.subsonic.service.SearchService">
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="artistDao" ref="artistDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ </bean>
+
+ <bean id="networkService" class="net.sourceforge.subsonic.service.NetworkService" init-method="init">
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="playerService" class="net.sourceforge.subsonic.service.PlayerService" init-method="init">
+ <property name="playerDao" ref="playerDao"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ </bean>
+
+ <bean id="playlistService" class="net.sourceforge.subsonic.service.PlaylistService" init-method="init">
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="playlistDao" ref="playlistDao"/>
+ </bean>
+
+ <bean id="versionService" class="net.sourceforge.subsonic.service.VersionService"/>
+
+ <bean id="statusService" class="net.sourceforge.subsonic.service.StatusService"/>
+
+ <bean id="musicInfoService" class="net.sourceforge.subsonic.service.RatingService">
+ <property name="ratingDao" ref="musicFileInfoDao"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+
+ <bean id="musicIndexService" class="net.sourceforge.subsonic.service.MusicIndexService">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ </bean>
+
+ <bean id="audioScrobblerService" class="net.sourceforge.subsonic.service.AudioScrobblerService">
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="transcodingService" class="net.sourceforge.subsonic.service.TranscodingService">
+ <property name="transcodingDao" ref="transcodingDao"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="playerService" ref="playerService"/>
+ </bean>
+
+ <bean id="shareService" class="net.sourceforge.subsonic.service.ShareService">
+ <property name="shareDao" ref="shareDao"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+
+ <bean id="podcastService" class="net.sourceforge.subsonic.service.PodcastService" init-method="init">
+ <property name="podcastDao" ref="podcastDao"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+
+ <bean id="adService" class="net.sourceforge.subsonic.service.AdService">
+ <property name="adInterval" value="4"/>
+ </bean>
+
+ <bean id="jukeboxService" class="net.sourceforge.subsonic.service.JukeboxService">
+ <property name="statusService" ref="statusService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ <property name="audioScrobblerService" ref="audioScrobblerService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+
+ <bean id="metaDataParserFactory" class="net.sourceforge.subsonic.service.metadata.MetaDataParserFactory">
+ <property name="parsers">
+ <list>
+ <bean class="net.sourceforge.subsonic.service.metadata.JaudiotaggerParser"/>
+ <bean class="net.sourceforge.subsonic.service.metadata.FFmpegParser">
+ <property name="transcodingService" ref="transcodingService"/>
+ </bean>
+ <bean class="net.sourceforge.subsonic.service.metadata.DefaultMetaDataParser"/>
+ </list>
+ </property>
+ </bean>
+
+ <!-- AJAX services -->
+
+ <bean id="ajaxMultiService" class="net.sourceforge.subsonic.ajax.MultiService">
+ <property name="networkService" ref="networkService"/>
+ </bean>
+
+ <bean id="ajaxNowPlayingService" class="net.sourceforge.subsonic.ajax.NowPlayingService">
+ <property name="playerService" ref="playerService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaScannerService" ref="mediaScannerService"/>
+ </bean>
+
+ <bean id="ajaxPlayQueueService" class="net.sourceforge.subsonic.ajax.PlayQueueService">
+ <property name="playerService" ref="playerService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="jukeboxService" ref="jukeboxService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+
+ <bean id="ajaxPlaylistService" class="net.sourceforge.subsonic.ajax.PlaylistService">
+ <property name="playlistService" ref="playlistService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ </bean>
+
+ <bean id="ajaxLyricsService" class="net.sourceforge.subsonic.ajax.LyricsService"/>
+
+ <bean id="ajaxCoverArtService" class="net.sourceforge.subsonic.ajax.CoverArtService">
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+
+ <bean id="ajaxStarService" class="net.sourceforge.subsonic.ajax.StarService">
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ </bean>
+
+ <bean id="ajaxTagService" class="net.sourceforge.subsonic.ajax.TagService">
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="metaDataParserFactory" ref="metaDataParserFactory"/>
+ </bean>
+
+ <bean id="ajaxTransferService" class="net.sourceforge.subsonic.ajax.TransferService"/>
+
+ <bean id="ajaxChatService" class="net.sourceforge.subsonic.ajax.ChatService" init-method="init">
+ <property name="securityService" ref="securityService"/>
+ </bean>
+
+</beans>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/dwr.xml b/subsonic-main/src/main/webapp/WEB-INF/dwr.xml
new file mode 100644
index 00000000..e7ea3187
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/dwr.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">
+
+<dwr>
+ <allow>
+
+ <create creator="spring" javascript="multiService">
+ <param name="beanName" value="ajaxMultiService"/>
+ </create>
+
+ <create creator="spring" javascript="nowPlayingService">
+ <param name="beanName" value="ajaxNowPlayingService"/>
+ </create>
+
+ <create creator="spring" javascript="playQueueService">
+ <param name="beanName" value="ajaxPlayQueueService"/>
+ </create>
+
+ <create creator="spring" javascript="playlistService">
+ <param name="beanName" value="ajaxPlaylistService"/>
+ </create>
+
+ <create creator="spring" javascript="lyricsService">
+ <param name="beanName" value="ajaxLyricsService"/>
+ </create>
+
+ <create creator="spring" javascript="coverArtService">
+ <param name="beanName" value="ajaxCoverArtService"/>
+ </create>
+
+ <create creator="spring" javascript="starService">
+ <param name="beanName" value="ajaxStarService"/>
+ </create>
+
+ <create creator="spring" javascript="tagService">
+ <param name="beanName" value="ajaxTagService"/>
+ </create>
+
+ <create creator="spring" javascript="transferService">
+ <param name="beanName" value="ajaxTransferService"/>
+ </create>
+
+ <create creator="spring" javascript="chatService">
+ <param name="beanName" value="ajaxChatService"/>
+ </create>
+
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.NetworkStatus"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.NowPlayingInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.ScanInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.PlayQueueInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.PlayQueueInfo$Entry"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.PlaylistInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.PlaylistInfo$Entry"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.domain.Playlist"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.UploadInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.LyricsInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.CoverArtInfo"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.ChatService$Message"/>
+ <convert converter="bean" match="net.sourceforge.subsonic.ajax.ChatService$Messages"/>
+
+ </allow>
+</dwr> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/accessDenied.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/accessDenied.jsp
new file mode 100644
index 00000000..78d2b910
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/accessDenied.jsp
@@ -0,0 +1,22 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+</head>
+
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="errorImage"/>" alt=""/>
+ <fmt:message key="accessDenied.title"/>
+</h1>
+
+<p>
+ <fmt:message key="accessDenied.text"/>
+</p>
+
+<div class="back"><a href="javascript:history.go(-1)"><fmt:message key="common.back"/></a></div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/advancedSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/advancedSettings.jsp
new file mode 100644
index 00000000..9b3a95b1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/advancedSettings.jsp
@@ -0,0 +1,142 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+ function enableLdapFields() {
+ var checkbox = $("ldap");
+ var table = $("ldapTable");
+
+ if (checkbox && checkbox.checked) {
+ table.show();
+ } else {
+ table.hide();
+ }
+ }
+ </script>
+</head>
+
+<body class="mainframe bgcolor1" onload="enableLdapFields()">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="advanced"/>
+</c:import>
+
+<form:form method="post" action="advancedSettings.view" commandName="command">
+
+ <table style="white-space:nowrap" class="indent">
+
+ <tr>
+ <td><fmt:message key="advancedsettings.downsamplecommand"/></td>
+ <td>
+ <form:input path="downsampleCommand" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="downsamplecommand"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.coverartlimit"/></td>
+ <td>
+ <form:input path="coverArtLimit" size="8"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="coverartlimit"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.downloadlimit"/></td>
+ <td>
+ <form:input path="downloadLimit" size="8"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="downloadlimit"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.uploadlimit"/></td>
+ <td>
+ <form:input path="uploadLimit" size="8"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="uploadlimit"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.streamport"/></td>
+ <td>
+ <form:input path="streamPort" size="8"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="streamport"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td colspan="2">
+ <form:checkbox path="ldapEnabled" id="ldap" cssClass="checkbox" onclick="javascript:enableLdapFields()"/>
+ <label for="ldap"><fmt:message key="advancedsettings.ldapenabled"/></label>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ldap"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">
+ <table class="indent" id="ldapTable" style="padding-left:2em">
+ <tr>
+ <td><fmt:message key="advancedsettings.ldapurl"/></td>
+ <td colspan="3">
+ <form:input path="ldapUrl" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapurl"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.ldapsearchfilter"/></td>
+ <td colspan="3">
+ <form:input path="ldapSearchFilter" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapsearchfilter"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="advancedsettings.ldapmanagerdn"/></td>
+ <td>
+ <form:input path="ldapManagerDn" size="20"/>
+ </td>
+ <td><fmt:message key="advancedsettings.ldapmanagerpassword"/></td>
+ <td>
+ <form:password path="ldapManagerPassword" size="20"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapmanagerdn"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="5">
+ <form:checkbox path="ldapAutoShadowing" id="ldapAutoShadowing" cssClass="checkbox"/>
+ <label for="ldapAutoShadowing"><fmt:message key="advancedsettings.ldapautoshadowing"><fmt:param value="${command.brand}"/></fmt:message></label>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ldapautoshadowing"/></c:import>
+ </td>
+ </tr>
+ </table>
+ </td></tr>
+
+ <tr>
+ <td colspan="2" style="padding-top:1.5em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+
+ </table>
+</form:form>
+
+<c:if test="${command.reloadNeeded}">
+ <script language="javascript" type="text/javascript">
+ parent.frames.left.location.href="left.view?";
+ parent.frames.playQueue.location.href="playQueue.view?";
+ </script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/allmusic.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/allmusic.jsp
new file mode 100644
index 00000000..1af611fb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/allmusic.jsp
@@ -0,0 +1,16 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+
+<body onload="document.allmusic.submit();" class="mainframe bgcolor1">
+<h2><fmt:message key="allmusic.text"><fmt:param value="${album}"/></fmt:message></h2>
+
+<form name="allmusic" action="http://www.allmusic.com/search" method="POST" accept-charset="iso-8859-1">
+ <input type="hidden" name="search_term" value="${album}"/>
+ <input type="hidden" name="search_type" value="album"/>
+</form>
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/avatarUploadResult.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/avatarUploadResult.jsp
new file mode 100644
index 00000000..6c3b1010
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/avatarUploadResult.jsp
@@ -0,0 +1,35 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="settingsImage"/>" alt=""/>
+ <fmt:message key="avataruploadresult.title"/>
+</h1>
+
+<c:choose>
+ <c:when test="${empty model.error}">
+ <p>
+ <fmt:message key="avataruploadresult.success"><fmt:param value="${model.avatar.name}"/></fmt:message>
+ <sub:url value="avatar.view" var="avatarUrl">
+ <sub:param name="username" value="${model.username}"/>
+ </sub:url>
+ <img src="${avatarUrl}" alt="${model.avatar.name}" width="${model.avatar.width}"
+ height="${model.avatar.height}" style="padding-left:2em"/>
+ </p>
+ </c:when>
+ <c:otherwise>
+ <p class="warning">
+ <fmt:message key="avataruploadresult.failure"/>
+ </p>
+ </c:otherwise>
+</c:choose>
+
+<div class="back"><a href="personalSettings.view?"><fmt:message key="common.back"/></a></div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp
new file mode 100644
index 00000000..598f12ca
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp
@@ -0,0 +1,206 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/coverArtService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+
+ <script type="text/javascript" language="javascript">
+
+ dwr.engine.setErrorHandler(null);
+ google.load('search', '1');
+ var imageSearch;
+
+ function setImage(imageUrl) {
+ $("wait").show();
+ $("result").hide();
+ $("success").hide();
+ $("error").hide();
+ $("errorDetails").hide();
+ $("noImagesFound").hide();
+ var id = dwr.util.getValue("id");
+ coverArtService.setCoverArtImage(id, imageUrl, setImageComplete);
+ }
+
+ function setImageComplete(errorDetails) {
+ $("wait").hide();
+ if (errorDetails != null) {
+ dwr.util.setValue("errorDetails", "<br/>" + errorDetails, { escapeHtml:false });
+ $("error").show();
+ $("errorDetails").show();
+ } else {
+ $("success").show();
+ }
+ }
+
+ function searchComplete() {
+
+ $("wait").hide();
+
+ if (imageSearch.results && imageSearch.results.length > 0) {
+
+ var images = $("images");
+ images.innerHTML = "";
+
+ var results = imageSearch.results;
+ for (var i = 0; i < results.length; i++) {
+ var result = results[i];
+ var node = $("template").cloneNode(true);
+
+ var link = node.getElementsByClassName("search-result-link")[0];
+ link.href = "javascript:setImage('" + result.url + "');";
+
+ var thumbnail = node.getElementsByClassName("search-result-thumbnail")[0];
+ thumbnail.src = result.tbUrl;
+
+ var title = node.getElementsByClassName("search-result-title")[0];
+ title.innerHTML = result.contentNoFormatting.truncate(30);
+
+ var dimension = node.getElementsByClassName("search-result-dimension")[0];
+ dimension.innerHTML = result.width + " × " + result.height;
+
+ var url = node.getElementsByClassName("search-result-url")[0];
+ url.innerHTML = result.visibleUrl;
+
+ node.show();
+ images.appendChild(node);
+ }
+
+ $("result").show();
+
+ addPaginationLinks(imageSearch);
+
+ } else {
+ $("noImagesFound").show();
+ }
+ }
+
+ function addPaginationLinks() {
+
+ // To paginate search results, use the cursor function.
+ var cursor = imageSearch.cursor;
+ var curPage = cursor.currentPageIndex; // check what page the app is on
+ var pagesDiv = document.createElement("div");
+ for (var i = 0; i < cursor.pages.length; i++) {
+ var page = cursor.pages[i];
+ var label;
+ if (curPage == i) {
+ // If we are on the current page, then don"t make a link.
+ label = document.createElement("b");
+ } else {
+
+ // Create links to other pages using gotoPage() on the searcher.
+ label = document.createElement("a");
+ label.href = "javascript:imageSearch.gotoPage(" + i + ");";
+ }
+ label.innerHTML = page.label;
+ label.style.marginRight = "1em";
+ pagesDiv.appendChild(label);
+ }
+
+ // Create link to next page.
+ if (curPage < cursor.pages.length - 1) {
+ var next = document.createElement("a");
+ next.href = "javascript:imageSearch.gotoPage(" + (curPage + 1) + ");";
+ next.innerHTML = "<fmt:message key="common.next"/>";
+ next.style.marginLeft = "1em";
+ pagesDiv.appendChild(next);
+ }
+
+ var pages = $("pages");
+ pages.innerHTML = "";
+ pages.appendChild(pagesDiv);
+ }
+
+ function search() {
+
+ $("wait").show();
+ $("result").hide();
+ $("success").hide();
+ $("error").hide();
+ $("errorDetails").hide();
+ $("noImagesFound").hide();
+
+ var query = dwr.util.getValue("query");
+ imageSearch.execute(query);
+ }
+
+ function onLoad() {
+
+ imageSearch = new google.search.ImageSearch();
+ imageSearch.setSearchCompleteCallback(this, searchComplete, null);
+ imageSearch.setNoHtmlGeneration();
+ imageSearch.setResultSetSize(8);
+
+ google.search.Search.getBranding("branding");
+
+ $("template").hide();
+
+ search();
+ }
+ google.setOnLoadCallback(onLoad);
+
+
+ </script>
+</head>
+<body class="mainframe bgcolor1">
+<h1><fmt:message key="changecoverart.title"/></h1>
+<form action="javascript:search()">
+ <table class="indent"><tr>
+ <td><input id="query" name="query" size="70" type="text" value="${model.artist} ${model.album}" onclick="select()"/></td>
+ <td style="padding-left:0.5em"><input type="submit" value="<fmt:message key="changecoverart.search"/>"/></td>
+ </tr></table>
+</form>
+
+<form action="javascript:setImage(dwr.util.getValue('url'))">
+ <table><tr>
+ <input id="id" type="hidden" name="id" value="${model.id}"/>
+ <td><label for="url"><fmt:message key="changecoverart.address"/></label></td>
+ <td style="padding-left:0.5em"><input type="text" name="url" size="50" id="url" value="http://" onclick="select()"/></td>
+ <td style="padding-left:0.5em"><input type="submit" value="<fmt:message key="common.ok"/>"></td>
+ </tr></table>
+</form>
+<sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.id}"/></sub:url>
+<div style="padding-top:0.5em;padding-bottom:0.5em">
+ <div class="back"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
+</div>
+
+<h2 id="wait" style="display:none"><fmt:message key="changecoverart.wait"/></h2>
+<h2 id="noImagesFound" style="display:none"><fmt:message key="changecoverart.noimagesfound"/></h2>
+<h2 id="success" style="display:none"><fmt:message key="changecoverart.success"/></h2>
+<h2 id="error" style="display:none"><fmt:message key="changecoverart.error"/></h2>
+<div id="errorDetails" class="warning" style="display:none">
+</div>
+
+<div id="result">
+
+ <div id="pages" style="float:left;padding-left:0.5em;padding-top:0.5em">
+ </div>
+
+ <div id="branding" style="float:right;padding-right:1em;padding-top:0.5em">
+ </div>
+
+ <div style="clear:both;">
+ </div>
+
+ <div id="images" style="width:100%;padding-bottom:2em">
+ </div>
+
+ <div style="clear:both;">
+ </div>
+
+</div>
+
+<div id="template" style="float:left; height:190px; width:220px;padding:0.5em;position:relative">
+ <div style="position:absolute;bottom:0">
+ <a class="search-result-link"><img class="search-result-thumbnail" style="padding:1px; border:1px solid #021a40; background-color:white;"></a>
+ <div class="search-result-title"></div>
+ <div class="search-result-dimension detail"></div>
+ <div class="search-result-url detail"></div>
+ </div>
+</div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp
new file mode 100644
index 00000000..a5030499
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp
@@ -0,0 +1,86 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%@ include file="include.jsp" %>
+
+<%--
+PARAMETERS
+ albumId: ID of album.
+ coverArtSize: Height and width of cover art.
+ albumName: Album name to display as caption and img alt.
+ showLink: Whether to make the cover art image link to the album page.
+ showZoom: Whether to display a link for zooming the cover art.
+ showChange: Whether to display a link for changing the cover art.
+ showCaption: Whether to display the album name as a caption below the image.
+ appearAfter: Fade in after this many milliseconds, or nil if no fading in should happen.
+--%>
+<c:choose>
+ <c:when test="${empty param.coverArtSize}">
+ <c:set var="size" value="auto"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="size" value="${param.coverArtSize + 8}px"/>
+ </c:otherwise>
+</c:choose>
+
+<c:set var="opacity" value="${empty param.appearAfter ? 1 : 0}"/>
+
+<div style="width:${size}; max-width:${size}; height:${size}; max-height:${size}" title="${param.albumName}">
+ <sub:url value="main.view" var="mainUrl">
+ <sub:param name="id" value="${param.albumId}"/>
+ </sub:url>
+
+ <sub:url value="/coverArt.view" var="coverArtUrl">
+ <c:if test="${not empty param.coverArtSize}">
+ <sub:param name="size" value="${param.coverArtSize}"/>
+ </c:if>
+ <sub:param name="id" value="${param.albumId}"/>
+ </sub:url>
+ <sub:url value="/coverArt.view" var="zoomCoverArtUrl">
+ <sub:param name="id" value="${param.albumId}"/>
+ </sub:url>
+
+ <str:randomString count="5" type="alphabet" var="divId"/>
+ <div class="outerpair1" id="${divId}" style="display:none">
+ <div class="outerpair2">
+ <div class="shadowbox">
+ <div class="innerbox">
+ <c:choose>
+ <c:when test="${param.showLink}"><a href="${mainUrl}" title="${param.albumName}"></c:when>
+ <c:when test="${param.showZoom}"><a href="${zoomCoverArtUrl}" rel="zoom" title="${param.albumName}"></c:when>
+ </c:choose>
+ <img src="${coverArtUrl}" alt="${param.albumName}">
+ <c:if test="${param.showLink or param.showZoom}"></a></c:if>
+ </div>
+ </div>
+ </div>
+ </div>
+ <c:if test="${not empty param.appearAfter}">
+ <script type="text/javascript">
+ if (window.addEventListener) {
+ window.addEventListener('load', function() {
+ setTimeout("$('#${divId}').fadeIn(500)", ${param.appearAfter});
+ }, false);
+ }
+ </script>
+ </c:if>
+</div>
+
+<div style="text-align:right; padding-right: 8px;">
+ <c:if test="${param.showChange}">
+ <sub:url value="/changeCoverArt.view" var="changeCoverArtUrl">
+ <sub:param name="id" value="${param.albumId}"/>
+ </sub:url>
+ <a class="detail" href="${changeCoverArtUrl}"><fmt:message key="coverart.change"/></a>
+ </c:if>
+
+ <c:if test="${param.showZoom and param.showChange}">
+ |
+ </c:if>
+
+ <c:if test="${param.showZoom}">
+ <a class="detail" rel="zoom" title="${param.albumName}" href="${zoomCoverArtUrl}"><fmt:message key="coverart.zoom"/></a>
+ </c:if>
+
+ <c:if test="${not param.showZoom and not param.showChange and param.showCaption}">
+ <span class="detail"><str:truncateNicely upper="17">${param.albumName}</str:truncateNicely></span>
+ </c:if>
+</div> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/createShare.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/createShare.jsp
new file mode 100644
index 00000000..3f739077
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/createShare.jsp
@@ -0,0 +1,51 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1><fmt:message key="share.title"/></h1>
+
+<c:choose>
+ <c:when test="${model.urlRedirectionEnabled}">
+ <fmt:message key="share.warning"/>
+ <p>
+ <a href="http://www.facebook.com/sharer.php?u=${model.playUrl}" target="_blank"><img src="<spring:theme code="shareFacebookImage"/>" alt=""></a>&nbsp;
+ <a href="http://www.facebook.com/sharer.php?u=${model.playUrl}" target="_blank"><fmt:message key="share.facebook"/></a>
+ </p>
+
+ <p>
+ <a href="http://twitter.com/?status=Listening to ${model.playUrl}" target="_blank"><img src="<spring:theme code="shareTwitterImage"/>" alt=""></a>&nbsp;
+ <a href="http://twitter.com/?status=Listening to ${model.playUrl}" target="_blank"><fmt:message key="share.twitter"/></a>
+ </p>
+ <p>
+ <g:plusone size="small" annotation="none" href="${model.playUrl}"></g:plusone>&nbsp;<fmt:message key="share.googleplus"/>
+ </p>
+ <p>
+ <fmt:message key="share.link">
+ <fmt:param>${model.playUrl}</fmt:param>
+ </fmt:message>
+ </p>
+ </c:when>
+ <c:otherwise>
+ <p>
+ <fmt:message key="share.disabled"/>
+ </p>
+ </c:otherwise>
+</c:choose>
+
+
+<div style="padding-top:1em">
+ <c:if test="${not empty model.dir}">
+ <sub:url value="main.view" var="backUrl"><sub:param name="path" value="${model.dir.path}"/></sub:url>
+ <div class="back" style="float:left;padding-right:10pt"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
+ </c:if>
+ <c:if test="${model.user.settingsRole}">
+ <div class="forward" style="float:left"><a href="shareSettings.view"><fmt:message key="share.manage"/></a></div>
+ </c:if>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/db.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/db.jsp
new file mode 100644
index 00000000..52918ab1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/db.jsp
@@ -0,0 +1,45 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head><body class="mainframe bgcolor1">
+
+<h1>Database query</h1>
+
+<form method="post" action="db.view">
+ <textarea rows="10" cols="80" name="query" style="margin-top:1em">${model.query}</textarea>
+ <input type="submit" value="<fmt:message key="common.ok"/>">
+</form>
+
+<c:if test="${not empty model.result}">
+ <h1 style="margin-top:2em">Result</h1>
+
+ <table class="indent ruleTable">
+ <c:forEach items="${model.result}" var="row" varStatus="loopStatus">
+
+ <c:if test="${loopStatus.count == 1}">
+ <tr>
+ <c:forEach items="${row}" var="entry">
+ <td class="ruleTableHeader">${entry.key}</td>
+ </c:forEach>
+ </tr>
+ </c:if>
+ <tr>
+ <c:forEach items="${row}" var="entry">
+ <td class="ruleTableCell">${entry.value}</td>
+ </c:forEach>
+ </tr>
+ </c:forEach>
+
+ </table>
+</c:if>
+
+<c:if test="${not empty model.error}">
+ <h1 style="margin-top:2em">Error</h1>
+
+ <p class="warning">
+ ${model.error}
+ </p>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/donate.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/donate.jsp
new file mode 100644
index 00000000..77906cb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/donate.jsp
@@ -0,0 +1,147 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.DonateCommand"--%>
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="donateImage"/>" alt=""/>
+ <fmt:message key="donate.title"/>
+</h1>
+<c:if test="${not empty command.path}">
+ <sub:url value="main.view" var="backUrl">
+ <sub:param name="path" value="${command.path}"/>
+ </sub:url>
+ <div class="back"><a href="${backUrl}">
+ <fmt:message key="common.back"/>
+ </a></div>
+ <br/>
+</c:if>
+
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E5RNJMDJ7C862" var="donate10Url"/>
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CKRS9A4J99TFN" var="donate15Url"/>
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H79PAZLVHFT6E" var="donate20Url"/>
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2TGXFN7AVREEN" var="donate25Url"/>
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=BXJVAQALLFREC" var="donate30Url"/>
+<c:url value="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=M5PX55AC4ER9Y" var="donate50Url"/>
+
+<div style="width:50em; max-width:50em">
+
+<fmt:message key="donate.textbefore"><fmt:param value="${command.brand}"/></fmt:message>
+
+<table cellpadding="10">
+ <tr>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate10Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;10"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate15Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;15"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate20Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;20"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate25Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;25"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate30Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;30"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ <td>
+ <table>
+ <tr>
+ <td><a href="${donate50Url}" target="_blank"><img src="<spring:theme code="paypalImage"/>" alt=""/></a> </td>
+ </tr>
+ <tr>
+ <td class="detail" style="text-align:center;"><fmt:message key="donate.amount"><fmt:param value="&euro;50"/></fmt:message></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+</table>
+
+<fmt:message key="donate.textafter"/>
+
+<c:choose>
+ <c:when test="${command.licenseValid}">
+ <p>
+ <b>
+ <fmt:formatDate value="${command.licenseDate}" dateStyle="long" var="licenseDate"/>
+ <fmt:message key="donate.licensed">
+ <fmt:param value="${command.emailAddress}"/>
+ <fmt:param value="${licenseDate}"/>
+ <fmt:param value="${command.brand}"/>
+ </fmt:message>
+ </p>
+ </c:when>
+ <c:otherwise>
+
+ <p><fmt:message key="donate.register"/></p>
+
+ <form:form commandName="command" method="post" action="donate.view">
+ <form:hidden path="path"/>
+ <table>
+ <tr>
+ <td><fmt:message key="donate.register.email"/></td>
+ <td>
+ <form:input path="emailAddress" size="40"/>
+ </td>
+ </tr>
+ <tr>
+ <td><fmt:message key="donate.register.license"/></td>
+ <td>
+ <form:input path="license" size="40"/>
+ </td>
+ <td><input type="submit" value="<fmt:message key="common.ok"/>"/></td>
+ </tr>
+ <tr>
+ <td/>
+ <td class="warning"><form:errors path="license"/></td>
+ </tr>
+ </table>
+ </form:form>
+
+ <p><fmt:message key="donate.resend"/></p>
+
+ </c:otherwise>
+</c:choose>
+
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/editTags.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/editTags.jsp
new file mode 100644
index 00000000..7d95edd9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/editTags.jsp
@@ -0,0 +1,164 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/tagService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+</head>
+<body class="mainframe bgcolor1">
+
+<script type="text/javascript" language="javascript">
+ var index = 0;
+ var fileCount = ${fn:length(model.songs)};
+ function setArtist() {
+ var artist = dwr.util.getValue("artistAll");
+ for (i = 0; i < fileCount; i++) {
+ dwr.util.setValue("artist" + i, artist);
+ }
+ }
+ function setAlbum() {
+ var album = dwr.util.getValue("albumAll");
+ for (i = 0; i < fileCount; i++) {
+ dwr.util.setValue("album" + i, album);
+ }
+ }
+ function setYear() {
+ var year = dwr.util.getValue("yearAll");
+ for (i = 0; i < fileCount; i++) {
+ dwr.util.setValue("year" + i, year);
+ }
+ }
+ function setGenre() {
+ var genre = dwr.util.getValue("genreAll");
+ for (i = 0; i < fileCount; i++) {
+ dwr.util.setValue("genre" + i, genre);
+ }
+ }
+ function suggestTitle() {
+ for (i = 0; i < fileCount; i++) {
+ var title = dwr.util.getValue("suggestedTitle" + i);
+ dwr.util.setValue("title" + i, title);
+ }
+ }
+ function resetTitle() {
+ for (i = 0; i < fileCount; i++) {
+ var title = dwr.util.getValue("originalTitle" + i);
+ dwr.util.setValue("title" + i, title);
+ }
+ }
+ function suggestTrack() {
+ for (i = 0; i < fileCount; i++) {
+ var track = dwr.util.getValue("suggestedTrack" + i);
+ dwr.util.setValue("track" + i, track);
+ }
+ }
+ function resetTrack() {
+ for (i = 0; i < fileCount; i++) {
+ var track = dwr.util.getValue("originalTrack" + i);
+ dwr.util.setValue("track" + i, track);
+ }
+ }
+ function updateTags() {
+ document.getElementById("save").disabled = true;
+ index = 0;
+ dwr.util.setValue("errors", "");
+ for (i = 0; i < fileCount; i++) {
+ dwr.util.setValue("status" + i, "");
+ }
+ updateNextTag();
+ }
+ function updateNextTag() {
+ var id = dwr.util.getValue("id" + index);
+ var artist = dwr.util.getValue("artist" + index);
+ var track = dwr.util.getValue("track" + index);
+ var album = dwr.util.getValue("album" + index);
+ var title = dwr.util.getValue("title" + index);
+ var year = dwr.util.getValue("year" + index);
+ var genre = dwr.util.getValue("genre" + index);
+ dwr.util.setValue("status" + index, "<fmt:message key="edittags.working"/>");
+ tagService.setTags(id, track, artist, album, title, year, genre, setTagsCallback);
+ }
+ function setTagsCallback(result) {
+ var message;
+ if (result == "SKIPPED") {
+ message = "<fmt:message key="edittags.skipped"/>";
+ } else if (result == "UPDATED") {
+ message = "<b><fmt:message key="edittags.updated"/></b>";
+ } else {
+ message = "<div class='warning'><fmt:message key="edittags.error"/></div>"
+ var errors = dwr.util.getValue("errors");
+ errors += result + "<br/>";
+ dwr.util.setValue("errors", errors, { escapeHtml:false });
+ }
+ dwr.util.setValue("status" + index, message, { escapeHtml:false });
+ index++;
+ if (index < fileCount) {
+ updateNextTag();
+ } else {
+ document.getElementById("save").disabled = false;
+ }
+ }
+</script>
+
+<h1><fmt:message key="edittags.title"/></h1>
+<sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.id}"/></sub:url>
+<div class="back"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
+
+<table class="ruleTable indent">
+ <tr>
+ <th class="ruleTableHeader"><fmt:message key="edittags.file"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.track"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.songtitle"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.artist"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.album"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.year"/></th>
+ <th class="ruleTableHeader"><fmt:message key="edittags.genre"/></th>
+ <th class="ruleTableHeader" width="60pt"><fmt:message key="edittags.status"/></th>
+ </tr>
+ <tr>
+ <th class="ruleTableHeader"/>
+ <th class="ruleTableHeader"><a href="javascript:suggestTrack()"><fmt:message key="edittags.suggest.short"/></a> |
+ <a href="javascript:resetTrack()"><fmt:message key="edittags.reset.short"/></a></th>
+ <th class="ruleTableHeader"><a href="javascript:suggestTitle()"><fmt:message key="edittags.suggest"/></a> |
+ <a href="javascript:resetTitle()"><fmt:message key="edittags.reset"/></a></th>
+ <th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="artistAll" size="15" onkeypress="dwr.util.onReturn(event, setArtist)" value="${model.defaultArtist}"/>&nbsp;<a href="javascript:setArtist()"><fmt:message key="edittags.set"/></a></th>
+ <th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="albumAll" size="15" onkeypress="dwr.util.onReturn(event, setAlbum)" value="${model.defaultAlbum}"/>&nbsp;<a href="javascript:setAlbum()"><fmt:message key="edittags.set"/></a></th>
+ <th class="ruleTableHeader" style="white-space: nowrap"><input type="text" name="yearAll" size="5" onkeypress="dwr.util.onReturn(event, setYear)" value="${model.defaultYear}"/>&nbsp;<a href="javascript:setYear()"><fmt:message key="edittags.set"/></a></th>
+ <th class="ruleTableHeader" style="white-space: nowrap">
+ <select name="genreAll" style="width:7em">
+ <option value=""/>
+ <c:forEach items="${model.allGenres}" var="genre">
+ <option ${genre eq model.defaultGenre ? "selected" : ""} value="${genre}">${genre}</option>
+ </c:forEach>
+ </select>
+
+ <a href="javascript:setGenre()"><fmt:message key="edittags.set"/></a>
+ </th>
+ <th class="ruleTableHeader"/>
+ </tr>
+
+ <c:forEach items="${model.songs}" var="song" varStatus="loopStatus">
+ <tr>
+ <str:truncateNicely lower="25" upper="25" var="fileName">${song.fileName}</str:truncateNicely>
+ <input type="hidden" name="id${loopStatus.count - 1}" value="${song.id}"/>
+ <input type="hidden" name="suggestedTitle${loopStatus.count - 1}" value="${song.suggestedTitle}"/>
+ <input type="hidden" name="originalTitle${loopStatus.count - 1}" value="${song.title}"/>
+ <input type="hidden" name="suggestedTrack${loopStatus.count - 1}" value="${song.suggestedTrack}"/>
+ <input type="hidden" name="originalTrack${loopStatus.count - 1}" value="${song.track}"/>
+ <td class="ruleTableCell" title="${song.fileName}">${fileName}</td>
+ <td class="ruleTableCell"><input type="text" size="5" name="track${loopStatus.count - 1}" value="${song.track}"/></td>
+ <td class="ruleTableCell"><input type="text" size="30" name="title${loopStatus.count - 1}" value="${song.title}"/></td>
+ <td class="ruleTableCell"><input type="text" size="15" name="artist${loopStatus.count - 1}" value="${song.artist}"/></td>
+ <td class="ruleTableCell"><input type="text" size="15" name="album${loopStatus.count - 1}" value="${song.album}"/></td>
+ <td class="ruleTableCell"><input type="text" size="5" name="year${loopStatus.count - 1}" value="${song.year}"/></td>
+ <td class="ruleTableCell"><input type="text" name="genre${loopStatus.count - 1}" value="${song.genre}" style="width:7em"/></td>
+ <td class="ruleTableCell"><div id="status${loopStatus.count - 1}"/></td>
+ </tr>
+ </c:forEach>
+
+</table>
+
+<p><input type="submit" id="save" value="<fmt:message key="common.save"/>" onclick="javascript:updateTags()"/></p>
+<div class="warning" id="errors"/>
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/externalPlayer.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/externalPlayer.jsp
new file mode 100644
index 00000000..31077d85
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/externalPlayer.jsp
@@ -0,0 +1,99 @@
+<%--@elvariable id="model" type="java.util.Map"--%>
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+
+ <sub:url value="/coverArt.view" var="coverArtUrl">
+ <c:if test="${not empty model.coverArt}">
+ <sub:param name="path" value="${model.coverArt.path}"/>
+ </c:if>
+ <sub:param name="size" value="200"/>
+ </sub:url>
+
+ <meta name="og:title" content="${fn:escapeXml(model.songs[0].artist)} &mdash; ${fn:escapeXml(model.songs[0].albumName)}"/>
+ <meta name="og:type" content="album"/>
+ <meta name="og:image" content="http://${model.redirectFrom}.subsonic.org${coverArtUrl}"/>
+
+ <script type="text/javascript">
+ function init() {
+ var flashvars = {
+ id:"player1",
+ screencolor:"000000",
+ frontcolor:"<spring:theme code="textColor"/>",
+ backcolor:"<spring:theme code="backgroundColor"/>",
+ stretching: "fill",
+ "playlist.position": "bottom",
+ "playlist.size": 200
+ };
+ var params = {
+ allowfullscreen:"true",
+ allowscriptaccess:"always"
+ };
+ var attributes = {
+ id:"player1",
+ name:"player1"
+ };
+ swfobject.embedSWF("<c:url value="/flash/jw-player-5.6.swf"/>", "placeholder", "500", "500", "9.0.0", false, flashvars, params, attributes);
+ }
+
+ function playerReady(thePlayer) {
+ var player = $("player1");
+ var list = new Array();
+
+ <c:forEach items="${model.songs}" var="song" varStatus="loopStatus">
+ <%--@elvariable id="song" type="net.sourceforge.subsonic.domain.MediaFile"--%>
+ <sub:url value="/stream" var="streamUrl">
+ <sub:param name="path" value="${song.path}"/>
+ <sub:param name="player" value="${model.player}"/>
+ </sub:url>
+ <sub:url value="/coverArt.view" var="coverUrl">
+ <sub:param name="size" value="500"/>
+ <c:if test="${not empty model.coverArts[loopStatus.count - 1]}">
+ <sub:param name="path" value="${model.coverArts[loopStatus.count - 1].path}"/>
+ </c:if>
+ </sub:url>
+
+ <!-- TODO: Use video provider for aac, m4a -->
+ list[${loopStatus.count - 1}] = {
+ file: "${streamUrl}",
+ image: "${coverUrl}",
+ title: "${fn:escapeXml(song.title)}",
+ provider: "${song.video ? "video" : "sound"}",
+ description: "${fn:escapeXml(song.artist)}"
+ };
+
+ <c:if test="${not empty song.durationSeconds}">
+ list[${loopStatus.count-1}].duration = ${song.durationSeconds};
+ </c:if>
+
+ </c:forEach>
+
+ player.sendEvent("LOAD", list);
+ player.sendEvent("PLAY");
+ }
+
+ </script>
+</head>
+
+<body class="mainframe bgcolor1" style="padding-top:2em" onload="init();">
+
+<div style="margin:auto;width:500px">
+ <h1 >${model.songs[0].artist}</h1>
+ <div style="float:left;padding-right:1.5em">
+ <h2 style="margin:0;">${model.songs[0].albumName}</h2>
+ </div>
+ <div class="detail" style="float:right">Streaming by <a href="http://subsonic.org/" target="_blank"><b>Subsonic</b></a></div>
+
+ <div style="clear:both;padding-top:1em">
+ <div id="placeholder">
+ <a href="http://www.adobe.com/go/getflashplayer" target="_blank"><fmt:message key="playlist.getflash"/></a>
+ </div>
+ </div>
+ <div style="padding-top: 2em">${fn:escapeXml(model.share.description)}</div>
+</div>
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/generalSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/generalSettings.jsp
new file mode 100644
index 00000000..f5cffe35
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/generalSettings.jsp
@@ -0,0 +1,165 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.GeneralSettingsCommand"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+</head>
+
+<body class="mainframe bgcolor1">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="general"/>
+</c:import>
+
+<form:form method="post" action="generalSettings.view" commandName="command">
+
+ <table style="white-space:nowrap" class="indent">
+
+ <tr>
+ <td><fmt:message key="generalsettings.musicmask"/></td>
+ <td>
+ <form:input path="musicFileTypes" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="musicmask"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.videomask"/></td>
+ <td>
+ <form:input path="videoFileTypes" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="videomask"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.coverartmask"/></td>
+ <td>
+ <form:input path="coverArtFileTypes" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="coverartmask"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.index"/></td>
+ <td>
+ <form:input path="index" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="index"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.ignoredarticles"/></td>
+ <td>
+ <form:input path="ignoredArticles" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="ignoredarticles"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.shortcuts"/></td>
+ <td>
+ <form:input path="shortcuts" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="shortcuts"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.language"/></td>
+ <td>
+ <form:select path="localeIndex" cssStyle="width:15em">
+ <c:forEach items="${command.locales}" var="locale" varStatus="loopStatus">
+ <form:option value="${loopStatus.count - 1}" label="${locale}"/>
+ </c:forEach>
+ </form:select>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="language"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.theme"/></td>
+ <td>
+ <form:select path="themeIndex" cssStyle="width:15em">
+ <c:forEach items="${command.themes}" var="theme" varStatus="loopStatus">
+ <form:option value="${loopStatus.count - 1}" label="${theme.name}"/>
+ </c:forEach>
+ </form:select>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="theme"/></c:import>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td>
+ </td>
+ <td>
+ <form:checkbox path="sortAlbumsByYear" id="sortAlbumsByYear"/>
+ <label for="sortAlbumsByYear"><fmt:message key="generalsettings.sortalbumsbyyear"/></label>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ </td>
+ <td>
+ <form:checkbox path="gettingStartedEnabled" id="gettingStartedEnabled"/>
+ <label for="gettingStartedEnabled"><fmt:message key="generalsettings.showgettingstarted"/></label>
+ </td>
+ </tr>
+
+ <tr><td colspan="2">&nbsp;</td></tr>
+
+ <tr>
+ <td><fmt:message key="generalsettings.welcometitle"/></td>
+ <td>
+ <form:input path="welcomeTitle" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
+ </td>
+ </tr>
+ <tr>
+ <td><fmt:message key="generalsettings.welcomesubtitle"/></td>
+ <td>
+ <form:input path="welcomeSubtitle" size="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align:top;"><fmt:message key="generalsettings.welcomemessage"/></td>
+ <td>
+ <form:textarea path="welcomeMessage" rows="5" cols="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="welcomemessage"/></c:import>
+ </td>
+ </tr>
+ <tr>
+ <td style="vertical-align:top;"><fmt:message key="generalsettings.loginmessage"/></td>
+ <td>
+ <form:textarea path="loginMessage" rows="5" cols="70"/>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="loginmessage"/></c:import>
+ <fmt:message key="main.wiki"/>
+ </td>
+ </tr>
+
+ <tr>
+ <td colspan="2" style="padding-top:1.5em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+
+ </table>
+</form:form>
+
+<c:if test="${command.reloadNeeded}">
+ <script language="javascript" type="text/javascript">
+ parent.frames.left.location.href="left.view?";
+ parent.frames.playQueue.location.href="playQueue.view?";
+ </script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/gettingStarted.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/gettingStarted.jsp
new file mode 100644
index 00000000..ad7b2a77
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/gettingStarted.jsp
@@ -0,0 +1,53 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" language="javascript">
+ function hideGettingStarted() {
+ alert("<fmt:message key="gettingStarted.hidealert"/>");
+ location.href = "gettingStarted.view?hide";
+ }
+ </script>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1 style="padding-bottom:0.5em">
+ <img src="<spring:theme code="homeImage"/>" alt="">
+ <fmt:message key="gettingStarted.title"/>
+</h1>
+
+<fmt:message key="gettingStarted.text"/>
+
+<c:if test="${model.runningAsRoot}">
+ <h2 class="warning"><fmt:message key="gettingStarted.root"/></h2>
+</c:if>
+
+<table style="padding-top:1em;padding-bottom:2em;width:60%">
+ <tr>
+ <td style="font-size:26pt;padding:20pt">1</td>
+ <td>
+ <div style="font-size:14pt"><a href="userSettings.view?userIndex=0"><fmt:message key="gettingStarted.step1.title"/></a></div>
+ <div style="padding-top:5pt"><fmt:message key="gettingStarted.step1.text"/></div>
+ </td>
+ </tr>
+ <tr>
+ <td style="font-size:26pt;padding:20pt">2</td>
+ <td>
+ <div style="font-size:14pt"><a href="musicFolderSettings.view"><fmt:message key="gettingStarted.step2.title"/></a></div>
+ <div style="padding-top:5pt"><fmt:message key="gettingStarted.step2.text"/></div>
+ </td>
+ </tr>
+ <tr>
+ <td style="font-size:26pt;padding:20pt">3</td>
+ <td>
+ <div style="font-size:14pt"><a href="networkSettings.view"><fmt:message key="gettingStarted.step3.title"/></a></div>
+ <div style="padding-top:5pt"><fmt:message key="gettingStarted.step3.text"/></div>
+ </td>
+ </tr>
+
+</table>
+
+<div class="forward"><a href="javascript:hideGettingStarted()"><fmt:message key="gettingStarted.hide"/></a></div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/head.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/head.jsp
new file mode 100644
index 00000000..cedadd8d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/head.jsp
@@ -0,0 +1,11 @@
+<%@ include file="include.jsp" %>
+
+<!--[if lt IE 7.]>
+<script defer type="text/javascript" src="<c:url value="/script/pngfix.js"/>"></script>
+<![endif]-->
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<c:set var="styleSheet"><spring:theme code="styleSheet"/></c:set>
+<c:set var="faviconImage"><spring:theme code="faviconImage"/></c:set>
+<link rel="stylesheet" href="<c:url value="/${styleSheet}"/>" type="text/css">
+<link rel="shortcut icon" href="<c:url value="/${faviconImage}"/>" type="text/css">
+<title>Subsonic</title>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/help.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/help.jsp
new file mode 100644
index 00000000..58421828
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/help.jsp
@@ -0,0 +1,70 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:choose>
+ <c:when test="${empty model.buildDate}">
+ <fmt:message key="common.unknown" var="buildDateString"/>
+ </c:when>
+ <c:otherwise>
+ <fmt:formatDate value="${model.buildDate}" dateStyle="long" var="buildDateString"/>
+ </c:otherwise>
+</c:choose>
+
+<c:choose>
+ <c:when test="${empty model.localVersion}">
+ <fmt:message key="common.unknown" var="versionString"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="versionString" value="${model.localVersion} (build ${model.buildNumber})"/>
+ </c:otherwise>
+</c:choose>
+
+<h1>
+ <img src="<spring:theme code="helpImage"/>" alt="">
+ <fmt:message key="help.title"><fmt:param value="${model.brand}"/></fmt:message>
+</h1>
+
+<c:if test="${model.newVersionAvailable}">
+ <p class="warning"><fmt:message key="help.upgrade"><fmt:param value="${model.brand}"/><fmt:param value="${model.latestVersion}"/></fmt:message></p>
+</c:if>
+
+<table width="75%" class="ruleTable indent">
+ <tr><td class="ruleTableHeader"><fmt:message key="help.version.title"/></td><td class="ruleTableCell">${versionString} &ndash; ${buildDateString}</td></tr>
+ <tr><td class="ruleTableHeader"><fmt:message key="help.server.title"/></td><td class="ruleTableCell">${model.serverInfo} (<sub:formatBytes bytes="${model.usedMemory}"/> / <sub:formatBytes bytes="${model.totalMemory}"/>)</td></tr>
+ <tr><td class="ruleTableHeader"><fmt:message key="help.license.title"/></td><td class="ruleTableCell">
+ <a href="http://www.gnu.org/copyleft/gpl.html" target="_blank"><img style="float:right;margin-left: 10px" alt="GPL 3.0" src="<c:url value="/icons/gpl.png"/>"></a>
+ <fmt:message key="help.license.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
+ <tr><td class="ruleTableHeader"><fmt:message key="help.homepage.title"/></td><td class="ruleTableCell"><a target="_blank" href="http://www.subsonic.org/">subsonic.org</a></td></tr>
+ <tr><td class="ruleTableHeader"><fmt:message key="help.forum.title"/></td><td class="ruleTableCell"><a target="_blank" href="http://forum.subsonic.org/">forum.subsonic.org</a></td></tr>
+ <tr><td class="ruleTableHeader"><fmt:message key="help.contact.title"/></td><td class="ruleTableCell"><fmt:message key="help.contact.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
+</table>
+
+<p></p>
+
+<table width="75%"><tr>
+ <td><a href="<c:url value="/donate.view"/>"><img src="<spring:theme code="paypalImage"/>" alt=""></a></td>
+ <td><fmt:message key="help.donate"><fmt:param value="${model.brand}"/></fmt:message></td>
+</tr></table>
+
+<h2><img src="<spring:theme code="logImage"/>" alt="">&nbsp;<fmt:message key="help.log"/></h2>
+
+<table cellpadding="2" class="log indent">
+ <c:forEach items="${model.logEntries}" var="entry">
+ <tr>
+ <td>[<fmt:formatDate value="${entry.date}" dateStyle="short" timeStyle="long" type="both"/>]</td>
+ <td>${entry.level}</td><td>${entry.category}</td><td>${entry.message}</td>
+ </tr>
+ </c:forEach>
+</table>
+
+<p><fmt:message key="help.logfile"><fmt:param value="${model.logFile}"/></fmt:message> </p>
+
+<div class="forward"><a href="help.view?"><fmt:message key="common.refresh"/></a></div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/helpToolTip.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/helpToolTip.jsp
new file mode 100644
index 00000000..04de8ad0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/helpToolTip.jsp
@@ -0,0 +1,18 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<%@ include file="include.jsp" %>
+
+<%--
+ Shows online help as a balloon tool tip.
+
+PARAMETERS
+ topic: Refers to a key in the resource bundle containing the text to display in the tool tip.
+--%>
+
+<spring:theme code="helpPopupImage" var="imageUrl"/>
+<fmt:message key="common.help" var="help"/>
+
+<div id="placeholder-${param.topic}" style="display:none">
+ <div style="font-weight:bold;"><fmt:message key="helppopup.${param.topic}.title"><fmt:param value="Subsonic"/></fmt:message></div>
+ <div><fmt:message key="helppopup.${param.topic}.text"><fmt:param value="Subsonic"/></fmt:message></div>
+</div>
+<img src="${imageUrl}" alt="${help}" title="${help}" onmouseover="TagToTip('placeholder-${param.topic}', BALLOON, true, ABOVE, true, OFFSETX, -17, PADDING, 8, WIDTH, -240, CLICKSTICKY, true, CLICKCLOSE, true)" onmouseout="UnTip()"/>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/home.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/home.jsp
new file mode 100644
index 00000000..8792d60d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/home.jsp
@@ -0,0 +1,189 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <link href="<c:url value="/style/shadow.css"/>" rel="stylesheet">
+
+ <script type="text/javascript" language="javascript">
+ function init() {
+ <c:if test="${model.listType eq 'random'}">
+ setTimeout("refresh()", 20000);
+ </c:if>
+ }
+
+ function refresh() {
+ top.main.location.href = top.main.location.href;
+ }
+ </script>
+</head>
+<body class="mainframe bgcolor1" onload="init();">
+<h1>
+ <img src="<spring:theme code="homeImage"/>" alt="">
+ ${model.welcomeTitle}
+</h1>
+
+<c:if test="${not empty model.welcomeSubtitle}">
+ <h2>${model.welcomeSubtitle}</h2>
+</c:if>
+
+<h2>
+ <c:forTokens items="random newest starred highest frequent recent alphabetical users" delims=" " var="cat" varStatus="loopStatus">
+ <c:if test="${loopStatus.count > 1}">&nbsp;|&nbsp;</c:if>
+ <sub:url var="url" value="home.view">
+ <sub:param name="listSize" value="${model.listSize}"/>
+ <sub:param name="listType" value="${cat}"/>
+ </sub:url>
+
+ <c:choose>
+ <c:when test="${model.listType eq cat}">
+ <span class="headerSelected"><fmt:message key="home.${cat}.title"/></span>
+ </c:when>
+ <c:otherwise>
+ <a href="${url}"><fmt:message key="home.${cat}.title"/></a>
+ </c:otherwise>
+ </c:choose>
+
+ </c:forTokens>
+</h2>
+
+<c:if test="${model.isIndexBeingCreated}">
+ <p class="warning"><fmt:message key="home.scan"/></p>
+</c:if>
+
+<h2><fmt:message key="home.${model.listType}.text"/></h2>
+
+<table width="100%">
+ <tr>
+ <td style="vertical-align:top;">
+<c:choose>
+<c:when test="${model.listType eq 'users'}">
+ <table>
+ <tr>
+ <th><fmt:message key="home.chart.total"/></th>
+ <th><fmt:message key="home.chart.stream"/></th>
+ </tr>
+ <tr>
+ <td><img src="<c:url value="/userChart.view"><c:param name="type" value="total"/></c:url>" alt=""></td>
+ <td><img src="<c:url value="/userChart.view"><c:param name="type" value="stream"/></c:url>" alt=""></td>
+ </tr>
+ <tr>
+ <th><fmt:message key="home.chart.download"/></th>
+ <th><fmt:message key="home.chart.upload"/></th>
+ </tr>
+ <tr>
+ <td><img src="<c:url value="/userChart.view"><c:param name="type" value="download"/></c:url>" alt=""></td>
+ <td><img src="<c:url value="/userChart.view"><c:param name="type" value="upload"/></c:url>" alt=""></td>
+ </tr>
+</table>
+
+</c:when>
+<c:otherwise>
+
+ <table>
+ <c:forEach items="${model.albums}" var="album" varStatus="loopStatus">
+ <c:if test="${loopStatus.count % 5 == 1}">
+ <tr>
+ </c:if>
+
+ <td style="vertical-align:top">
+ <table>
+ <tr><td>
+ <c:import url="coverArt.jsp">
+ <c:param name="albumId" value="${album.id}"/>
+ <c:param name="albumName" value="${album.albumTitle}"/>
+ <c:param name="coverArtSize" value="110"/>
+ <c:param name="showLink" value="true"/>
+ <c:param name="showZoom" value="false"/>
+ <c:param name="showChange" value="false"/>
+ <c:param name="appearAfter" value="${loopStatus.count * 30}"/>
+ </c:import>
+
+ <div class="detail">
+ <c:if test="${not empty album.playCount}">
+ <fmt:message key="home.playcount"><fmt:param value="${album.playCount}"/></fmt:message>
+ </c:if>
+ <c:if test="${not empty album.lastPlayed}">
+ <fmt:formatDate value="${album.lastPlayed}" dateStyle="short" var="lastPlayedDate"/>
+ <fmt:message key="home.lastplayed"><fmt:param value="${lastPlayedDate}"/></fmt:message>
+ </c:if>
+ <c:if test="${not empty album.created}">
+ <fmt:formatDate value="${album.created}" dateStyle="short" var="creationDate"/>
+ <fmt:message key="home.created"><fmt:param value="${creationDate}"/></fmt:message>
+ </c:if>
+ <c:if test="${not empty album.rating}">
+ <c:import url="rating.jsp">
+ <c:param name="readonly" value="true"/>
+ <c:param name="rating" value="${album.rating}"/>
+ </c:import>
+ </c:if>
+ </div>
+
+ <c:choose>
+ <c:when test="${empty album.artist and empty album.albumTitle}">
+ <div class="detail"><fmt:message key="common.unknown"/></div>
+ </c:when>
+ <c:otherwise>
+ <div class="detail"><em><str:truncateNicely lower="15" upper="15">${album.artist}</str:truncateNicely></em></div>
+ <div class="detail"><str:truncateNicely lower="15" upper="15">${album.albumTitle}</str:truncateNicely></div>
+ </c:otherwise>
+ </c:choose>
+
+ </td></tr>
+ </table>
+ </td>
+ <c:if test="${loopStatus.count % 5 == 0}">
+ </tr>
+ </c:if>
+ </c:forEach>
+ </table>
+
+<table>
+ <tr>
+ <td style="padding-right:1.5em">
+ <select name="listSize" onchange="location='home.view?listType=${model.listType}&amp;listOffset=${model.listOffset}&amp;listSize=' + options[selectedIndex].value;">
+ <c:forTokens items="5 10 15 20 30 40 50" delims=" " var="size">
+ <option ${size eq model.listSize ? "selected" : ""} value="${size}"><fmt:message key="home.listsize"><fmt:param value="${size}"/></fmt:message></option>
+ </c:forTokens>
+ </select>
+ </td>
+
+ <c:choose>
+ <c:when test="${model.listType eq 'random'}">
+ <td><div class="forward"><a href="home.view?listType=random&amp;listSize=${model.listSize}"><fmt:message key="common.more"/></a></div></td>
+ </c:when>
+
+ <c:otherwise>
+ <sub:url value="home.view" var="previousUrl">
+ <sub:param name="listType" value="${model.listType}"/>
+ <sub:param name="listOffset" value="${model.listOffset - model.listSize}"/>
+ <sub:param name="listSize" value="${model.listSize}"/>
+ </sub:url>
+ <sub:url value="home.view" var="nextUrl">
+ <sub:param name="listType" value="${model.listType}"/>
+ <sub:param name="listOffset" value="${model.listOffset + model.listSize}"/>
+ <sub:param name="listSize" value="${model.listSize}"/>
+ </sub:url>
+
+ <td style="padding-right:1.5em"><fmt:message key="home.albums"><fmt:param value="${model.listOffset + 1}"/><fmt:param value="${model.listOffset + model.listSize}"/></fmt:message></td>
+ <td style="padding-right:1.5em"><div class="back"><a href="${previousUrl}"><fmt:message key="common.previous"/></a></div></td>
+ <td><div class="forward"><a href="${nextUrl}"><fmt:message key="common.next"/></a></div></td>
+ </c:otherwise>
+ </c:choose>
+ </tr>
+ </table>
+</c:otherwise>
+</c:choose>
+ </td>
+ <c:if test="${not empty model.welcomeMessage}">
+ <td style="vertical-align:top;width:20em">
+ <div style="padding:0 1em 0 1em;border-left:1px solid #<spring:theme code="detailColor"/>">
+ <sub:wiki text="${model.welcomeMessage}"/>
+ </div>
+ </td>
+ </c:if>
+ </tr>
+ </table>
+
+</body></html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/importPlaylist.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/importPlaylist.jsp
new file mode 100644
index 00000000..4bc09b88
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/importPlaylist.jsp
@@ -0,0 +1,37 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1 style="padding-bottom:0.5em">
+ <fmt:message key="importPlaylist.title"/>
+</h1>
+
+<c:if test="${not empty model.playlist}">
+ <p>
+ <fmt:message key="importPlaylist.success"><fmt:param value="${model.playlist.name}"/></fmt:message>
+ <script type="text/javascript" language="javascript">
+ top.left.updatePlaylists();
+ </script>
+ </p>
+</c:if>
+
+<c:if test="${not empty model.error}">
+ <p class="warning">
+ <fmt:message key="importPlaylist.error"><fmt:param value="${model.error}"/></fmt:message>
+ </p>
+</c:if>
+
+<div style="padding-bottom: 0.25em">
+ <fmt:message key="importPlaylist.text"/>
+</div>
+<form method="post" enctype="multipart/form-data" action="importPlaylist.view">
+ <input type="file" id="file" name="file" size="40"/>
+ <input type="submit" value="<fmt:message key="common.ok"/>"/>
+</form>
+
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/include.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/include.jsp
new file mode 100644
index 00000000..41c0aa2d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/include.jsp
@@ -0,0 +1,8 @@
+<%@ page session="false"%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
+<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
+<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@ taglib prefix="sub" uri="http://subsonic.org/taglib/sub" %>
+<%@ taglib prefix="str" uri="http://jakarta.apache.org/taglibs/string-1.1" %>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/index.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/index.jsp
new file mode 100644
index 00000000..4ec7e2b0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/index.jsp
@@ -0,0 +1,26 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <link rel="alternate" type="application/rss+xml" title="Subsonic Podcast" href="podcast.view?suffix=.rss">
+</head>
+
+<frameset rows="70,*,0" border="0" framespacing="0" frameborder="0">
+ <frame name="upper" src="top.view?">
+ <frameset cols="15%,85%" border="0" framespacing="0" frameborder="0">
+ <frame name="left" src="left.view?" marginwidth="0" marginheight="0">
+
+ <frameset rows="70%,30%" border="0" framespacing="0" frameborder="0">
+ <frameset cols="*,${model.showRight ? 230 : 0}" border="0" framespacing="0" frameborder="0">
+ <frame name="main" src="nowPlaying.view?" marginwidth="0" marginheight="0">
+ <frame name="right" src="right.view?">
+ </frameset>
+ <frame name="playQueue" src="playQueue.view?">
+ </frameset>
+ </frameset>
+ <frame name="hidden" frameborder="0" noresize="noresize">
+
+</frameset>
+
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/internetRadioSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/internetRadioSettings.jsp
new file mode 100644
index 00000000..ba3fbe1b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/internetRadioSettings.jsp
@@ -0,0 +1,62 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="internetRadio"/>
+</c:import>
+
+<form method="post" action="internetRadioSettings.view">
+<table class="indent">
+ <tr>
+ <th><fmt:message key="internetradiosettings.name"/></th>
+ <th><fmt:message key="internetradiosettings.streamurl"/></th>
+ <th><fmt:message key="internetradiosettings.homepageurl"/></th>
+ <th style="padding-left:1em"><fmt:message key="internetradiosettings.enabled"/></th>
+ <th style="padding-left:1em"><fmt:message key="common.delete"/></th>
+ </tr>
+
+ <c:forEach items="${model.internetRadios}" var="radio">
+ <tr>
+ <td><input type="text" name="name[${radio.id}]" size="20" value="${radio.name}"/></td>
+ <td><input type="text" name="streamUrl[${radio.id}]" size="40" value="${radio.streamUrl}"/></td>
+ <td><input type="text" name="homepageUrl[${radio.id}]" size="40" value="${radio.homepageUrl}"/></td>
+ <td align="center" style="padding-left:1em"><input type="checkbox" ${radio.enabled ? "checked" : ""} name="enabled[${radio.id}]" class="checkbox"/></td>
+ <td align="center" style="padding-left:1em"><input type="checkbox" name="delete[${radio.id}]" class="checkbox"/></td>
+ </tr>
+ </c:forEach>
+
+ <tr>
+ <th colspan="5" align="left" style="padding-top:1em"><fmt:message key="internetradiosettings.add"/></th>
+ </tr>
+
+ <tr>
+ <td><input type="text" name="name" size="20"/></td>
+ <td><input type="text" name="streamUrl" size="40"/></td>
+ <td><input type="text" name="homepageUrl" size="40"/></td>
+ <td align="center" style="padding-left:1em"><input name="enabled" checked type="checkbox" class="checkbox"/></td>
+ <td/>
+ </tr>
+
+ <tr>
+ <td style="padding-top:1.5em" colspan="5">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+</table>
+</form>
+
+
+<c:if test="${not empty model.error}">
+ <p class="warning"><fmt:message key="${model.error}"/></p>
+</c:if>
+
+<c:if test="${model.reload}">
+ <script language="javascript" type="text/javascript">parent.frames.left.location.href="left.view?"</script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/jquery.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/jquery.jsp
new file mode 100644
index 00000000..094b9bcc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/jquery.jsp
@@ -0,0 +1,3 @@
+<link rel="stylesheet" href="<c:url value="/style/smoothness/jquery-ui-1.8.18.custom.css"/>" type="text/css">
+<script type="text/javascript" src="<c:url value='/script/jquery-1.7.1.min.js'/>"></script>
+<script type="text/javascript" src="<c:url value='/script/jquery-ui-1.8.18.custom.min.js'/>"></script>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/left.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/left.jsp
new file mode 100644
index 00000000..d47c25f6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/left.jsp
@@ -0,0 +1,168 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/smooth-scroll.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ var playlists;
+
+ function init() {
+ dwr.engine.setErrorHandler(null);
+ updatePlaylists();
+ }
+
+ function updatePlaylists() {
+ playlistService.getReadablePlaylists(playlistCallback);
+ }
+
+ function createEmptyPlaylist() {
+ playlistService.createEmptyPlaylist(playlistCallback);
+ }
+
+ function playlistCallback(playlists) {
+ this.playlists = playlists;
+
+ $("#playlists").empty();
+ for (var i = 0; i < playlists.length; i++) {
+ var playlist = playlists[i];
+ $("<p class='dense'><a target='main' href='playlist.view?id=" +
+ playlist.id + "'>" + playlist.name + "&nbsp;(" + playlist.fileCount + ")</a></p>").appendTo("#playlists");
+ }
+ }
+ </script>
+</head>
+
+<body class="bgcolor2 leftframe" onload="init()">
+<a name="top"></a>
+
+<c:if test="${model.scanning}">
+ <div style="padding-bottom:1.0em">
+ <div class="warning"><fmt:message key="left.scanning"/></div>
+ <div class="forward"><a href="left.view"><fmt:message key="common.refresh"/></a></div>
+ </div>
+</c:if>
+
+<div style="padding-bottom:0.5em">
+ <c:forEach items="${model.indexes}" var="index">
+ <a href="#${index.index}" accesskey="${index.index}">${index.index}</a>
+ </c:forEach>
+</div>
+
+<c:if test="${model.statistics.songCount gt 0}">
+ <div class="detail">
+ <fmt:message key="left.statistics">
+ <fmt:param value="${model.statistics.artistCount}"/>
+ <fmt:param value="${model.statistics.albumCount}"/>
+ <fmt:param value="${model.statistics.songCount}"/>
+ <fmt:param value="${model.bytes}"/>
+ <fmt:param value="${model.hours}"/>
+ </fmt:message>
+ </div>
+</c:if>
+
+<c:if test="${fn:length(model.musicFolders) > 1}">
+ <div style="padding-top:1em">
+ <select name="musicFolderId" style="width:100%" onchange="location='left.view?musicFolderId=' + options[selectedIndex].value;" >
+ <option value="-1"><fmt:message key="left.allfolders"/></option>
+ <c:forEach items="${model.musicFolders}" var="musicFolder">
+ <option ${model.selectedMusicFolder.id == musicFolder.id ? "selected" : ""} value="${musicFolder.id}">${musicFolder.name}</option>
+ </c:forEach>
+ </select>
+ </div>
+</c:if>
+
+<c:if test="${not empty model.shortcuts}">
+ <h2 class="bgcolor1"><fmt:message key="left.shortcut"/></h2>
+ <c:forEach items="${model.shortcuts}" var="shortcut">
+ <p class="dense" style="padding-left:0.5em">
+ <sub:url value="main.view" var="mainUrl">
+ <sub:param name="id" value="${shortcut.id}"/>
+ </sub:url>
+ <a target="main" href="${mainUrl}">${shortcut.name}</a>
+ </p>
+ </c:forEach>
+</c:if>
+
+<h2 class="bgcolor1"><fmt:message key="left.playlists"/></h2>
+<div style='padding-left:0.5em'>
+ <div id="playlists"></div>
+ <div style="margin-top: 0.3em"><a href="javascript:noop()" onclick="createEmptyPlaylist()"><fmt:message key="left.createplaylist"/></a></div>
+ <div><a href="importPlaylist.view" target="main"><fmt:message key="left.importplaylist"/></a></div>
+</div>
+
+<c:if test="${not empty model.radios}">
+ <h2 class="bgcolor1"><fmt:message key="left.radio"/></h2>
+ <c:forEach items="${model.radios}" var="radio">
+ <p class="dense">
+ <a target="hidden" href="${radio.streamUrl}">
+ <img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"></a>
+ <c:choose>
+ <c:when test="${empty radio.homepageUrl}">
+ ${radio.name}
+ </c:when>
+ <c:otherwise>
+ <a target="main" href="${radio.homepageUrl}">${radio.name}</a>
+ </c:otherwise>
+ </c:choose>
+ </p>
+ </c:forEach>
+</c:if>
+
+<c:forEach items="${model.indexedArtists}" var="entry">
+ <table class="bgcolor1" style="width:100%;padding:0;margin:1em 0 0 0;border:0">
+ <tr style="padding:0;margin:0;border:0">
+ <th style="text-align:left;padding:0;margin:0;border:0"><a name="${entry.key.index}"></a>
+ <h2 style="padding:0;margin:0;border:0">${entry.key.index}</h2>
+ </th>
+ <th style="text-align:right;">
+ <a href="#top"><img src="<spring:theme code="upImage"/>" alt=""></a>
+ </th>
+ </tr>
+ </table>
+
+ <c:forEach items="${entry.value}" var="artist">
+ <p class="dense" style="padding-left:0.5em">
+ <span title="${artist.name}">
+ <sub:url value="main.view" var="mainUrl">
+ <c:forEach items="${artist.mediaFiles}" var="mediaFile">
+ <sub:param name="id" value="${mediaFile.id}"/>
+ </c:forEach>
+ </sub:url>
+ <a target="main" href="${mainUrl}"><str:truncateNicely upper="${model.captionCutoff}">${artist.name}</str:truncateNicely></a>
+ </span>
+ </p>
+ </c:forEach>
+</c:forEach>
+
+<div style="padding-top:1em"></div>
+
+<c:forEach items="${model.singleSongs}" var="song">
+ <p class="dense" style="padding-left:0.5em">
+ <span title="${song.title}">
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${song.id}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole}"/>
+ <c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyMode}"/>
+ <c:param name="video" value="${song.video and model.player.web}"/>
+ </c:import>
+ <str:truncateNicely upper="${model.captionCutoff}">${song.title}</str:truncateNicely>
+ </span>
+ </p>
+</c:forEach>
+
+<div style="height:5em"></div>
+
+<div class="bgcolor2" style="opacity: 1.0; clear: both; position: fixed; bottom: 0; right: 0; left: 0;
+ padding: 0.25em 0.75em 0.25em 0.75em; border-top:1px solid black; max-width: 850px;">
+ <c:forEach items="${model.indexes}" var="index">
+ <a href="#${index.index}">${index.index}</a>
+ </c:forEach>
+</div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/login.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
new file mode 100644
index 00000000..0173ca8c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/login.jsp
@@ -0,0 +1,64 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript">
+ if (window != window.top) {
+ top.location.href = location.href;
+ }
+ </script>
+
+</head>
+<body class="mainframe bgcolor1" onload="document.getElementById('j_username').focus()">
+
+<form action="<c:url value="/j_acegi_security_check"/>" method="POST">
+ <div class="bgcolor2" align="center" style="border:1px solid black; padding:20px 50px 20px 50px; margin-top:100px">
+
+ <div style="margin-bottom:1em;max-width:50em;text-align:left;"><sub:wiki text="${model.loginMessage}"/></div>
+
+ <table>
+ <tr>
+ <td colspan="2" align="left" style="padding-bottom:10px">
+ <img src="<spring:theme code="logoImage"/>" alt="">
+ </td>
+ </tr>
+ <tr>
+ <td align="left" style="padding-right:10px"><fmt:message key="login.username"/></td>
+ <td align="left"><input type="text" id="j_username" name="j_username" style="width:12em" tabindex="1"></td>
+ </tr>
+
+ <tr>
+ <td align="left" style="padding-bottom:10px"><fmt:message key="login.password"/></td>
+ <td align="left" style="padding-bottom:10px"><input type="password" name="j_password" style="width:12em" tabindex="2"></td>
+ </tr>
+
+ <tr>
+ <td align="left"><input name="submit" type="submit" value="<fmt:message key="login.login"/>" tabindex="4"></td>
+ <td align="left" class="detail">
+ <input type="checkbox" name="_acegi_security_remember_me" id="remember" class="checkbox" tabindex="3">
+ <label for="remember"><fmt:message key="login.remember"/></label>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td align="left" class="detail"><a href="recover.view"><fmt:message key="login.recover"/></a></td>
+ </tr>
+
+ <c:if test="${model.logout}">
+ <tr><td colspan="2" style="padding-top:10px"><b><fmt:message key="login.logout"/></b></td></tr>
+ </c:if>
+ <c:if test="${model.error}">
+ <tr><td colspan="2" style="padding-top:10px"><b class="warning"><fmt:message key="login.error"/></b></td></tr>
+ </c:if>
+
+ </table>
+
+ <c:if test="${model.insecure}">
+ <p><b class="warning"><fmt:message key="login.insecure"><fmt:param value="${model.brand}"/></fmt:message></b></p>
+ </c:if>
+
+ </div>
+</form>
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/lyrics.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/lyrics.jsp
new file mode 100644
index 00000000..82d5ce37
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/lyrics.jsp
@@ -0,0 +1,79 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <title><fmt:message key="lyrics.title"/></title>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/lyricsService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+
+ <script type="text/javascript" language="javascript">
+
+ dwr.engine.setErrorHandler(null);
+
+ function init() {
+ getLyrics('${model.artist}', '${model.song}');
+ }
+
+ function getLyrics(artist, song) {
+ $("wait").style.display = "inline";
+ $("lyrics").style.display = "none";
+ $("noLyricsFound").style.display = "none";
+ lyricsService.getLyrics(artist, song, getLyricsCallback);
+ }
+
+ function getLyricsCallback(lyricsInfo) {
+ dwr.util.setValue("lyricsHeader", lyricsInfo.artist + " - " + lyricsInfo.title);
+ var lyrics;
+ if (lyricsInfo.lyrics != null) {
+ lyrics = lyricsInfo.lyrics.replace(/\n/g, "<br>");
+ }
+ dwr.util.setValue("lyricsText", lyrics, { escapeHtml:false });
+ $("wait").style.display = "none";
+ if (lyrics != null) {
+ $("lyrics").style.display = "inline";
+ } else {
+ $("noLyricsFound").style.display = "inline";
+ }
+ }
+ </script>
+
+</head>
+<body class="mainframe bgcolor1" onload="init();">
+
+<table>
+ <tr>
+ <td><fmt:message key="lyrics.artist"/></td>
+ <td style="padding-left:0.50em"><input id="artist" type="text" size="40" value="${model.artist}" tabindex="1"/></td>
+ <td style="padding-left:0.75em"><input type="submit" value="<fmt:message key="lyrics.search"/>" style="width:6em"
+ onclick="getLyrics(dwr.util.getValue('artist'), dwr.util.getValue('song'))" tabindex="3"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="lyrics.song"/></td>
+ <td style="padding-left:0.50em"><input id="song" type="text" size="40" value="${model.song}" tabindex="2"/></td>
+ <td style="padding-left:0.75em"><input type="submit" value="<fmt:message key="common.close"/>" style="width:6em"
+ onclick="self.close()" tabindex="4"/></td>
+ </tr>
+</table>
+
+<hr/>
+<h2 id="wait"><fmt:message key="lyrics.wait"/></h2>
+<h2 id="noLyricsFound" style="display:none"><fmt:message key="lyrics.nolyricsfound"/></h2>
+
+<div id="lyrics" style="display:none;">
+ <h2 id="lyricsHeader" style="text-align:center;margin-bottom:1em"></h2>
+
+ <div id="lyricsText"></div>
+
+ <p class="detail" style="text-align:right">
+ <fmt:message key="lyrics.courtesy"/>
+ </p>
+</div>
+
+<hr/>
+<p style="text-align:center">
+ <a href="javascript:self.close()">[<fmt:message key="common.close"/>]</a>
+</p>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/main.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/main.jsp
new file mode 100644
index 00000000..fbbd553d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/main.jsp
@@ -0,0 +1,479 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<%--@elvariable id="model" type="java.util.Map"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <link href="<c:url value="/style/shadow.css"/>" rel="stylesheet">
+ <c:if test="${not model.updateNowPlaying}">
+ <meta http-equiv="refresh" content="180;URL=nowPlaying.view?">
+ </c:if>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoom.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoomHTML.js"/>"></script>
+</head><body class="mainframe bgcolor1" onload="init();">
+
+<sub:url value="createShare.view" var="shareUrl">
+ <sub:param name="dir" value="${model.dir.path}"/>
+</sub:url>
+<sub:url value="download.view" var="downloadUrl">
+ <sub:param name="dir" value="${model.dir.path}"/>
+</sub:url>
+<sub:url value="appendPlaylist.view" var="appendPlaylistUrl">
+ <sub:param name="dir" value="${model.dir.path}"/>
+</sub:url>
+
+<script type="text/javascript" language="javascript">
+ function init() {
+ setupZoom('<c:url value="/"/>');
+
+ $("#dialog-select-playlist").dialog({resizable: true, height: 220, position: 'top', modal: true, autoOpen: false,
+ buttons: {
+ "<fmt:message key="common.cancel"/>": function() {
+ $(this).dialog("close");
+ }
+ }});
+ }
+
+ <!-- actionSelected() is invoked when the users selects from the "More actions..." combo box. -->
+ function actionSelected(id) {
+
+ if (id == "top") {
+ return;
+ } else if (id == "selectAll") {
+ selectAll(true);
+ } else if (id == "selectNone") {
+ selectAll(false);
+ } else if (id == "share") {
+ parent.frames.main.location.href = "${shareUrl}&" + getSelectedIndexes();
+ } else if (id == "download") {
+ location.href = "${downloadUrl}&" + getSelectedIndexes();
+ } else if (id == "appendPlaylist") {
+ onAppendPlaylist();
+ }
+ $("#moreActions").prop("selectedIndex", 0);
+ }
+
+ function getSelectedIndexes() {
+ var result = "";
+ for (var i = 0; i < ${fn:length(model.children)}; i++) {
+ var checkbox = $("#songIndex" + i);
+ if (checkbox != null && checkbox.is(":checked")) {
+ result += "i=" + i + "&";
+ }
+ }
+ return result;
+ }
+
+ function selectAll(b) {
+ for (var i = 0; i < ${fn:length(model.children)}; i++) {
+ var checkbox = $("#songIndex" + i);
+ if (checkbox != null) {
+ if (b) {
+ checkbox.attr("checked", "checked");
+ } else {
+ checkbox.removeAttr("checked");
+ }
+ }
+ }
+ }
+
+ function toggleStar(mediaFileId, imageId) {
+ if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
+ $(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
+ starService.unstar(mediaFileId);
+ }
+ else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
+ $(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
+ starService.star(mediaFileId);
+ }
+ }
+
+ function onAppendPlaylist() {
+ playlistService.getWritablePlaylists(playlistCallback);
+ }
+ function playlistCallback(playlists) {
+ $("#dialog-select-playlist-list").empty();
+ for (var i = 0; i < playlists.length; i++) {
+ var playlist = playlists[i];
+ $("<p class='dense'><b><a href='#' onclick='appendPlaylist(" + playlist.id + ")'>" + playlist.name + "</a></b></p>").appendTo("#dialog-select-playlist-list");
+ }
+ $("#dialog-select-playlist").dialog("open");
+ }
+ function appendPlaylist(playlistId) {
+ $("#dialog-select-playlist").dialog("close");
+
+ var mediaFileIds = new Array();
+ for (var i = 0; i < ${fn:length(model.children)}; i++) {
+ var checkbox = $("#songIndex" + i);
+ if (checkbox && checkbox.is(":checked")) {
+ mediaFileIds.push($("#songId" + i).html());
+ }
+ }
+ playlistService.appendToPlaylist(playlistId, mediaFileIds, function (){top.left.updatePlaylists();});
+ }
+
+</script>
+
+<c:if test="${model.updateNowPlaying}">
+
+ <script type="text/javascript" language="javascript">
+ // Variable used by javascript in playlist.jsp
+ var updateNowPlaying = true;
+ </script>
+</c:if>
+
+<h1>
+ <a href="#" onclick="toggleStar(${model.dir.id}, '#starImage'); return false;">
+ <c:choose>
+ <c:when test="${not empty model.dir.starredDate}">
+ <img id="starImage" src="<spring:theme code="ratingOnImage"/>" alt="">
+ </c:when>
+ <c:otherwise>
+ <img id="starImage" src="<spring:theme code="ratingOffImage"/>" alt="">
+ </c:otherwise>
+ </c:choose>
+ </a>
+
+ <c:forEach items="${model.ancestors}" var="ancestor">
+ <sub:url value="main.view" var="ancestorUrl">
+ <sub:param name="id" value="${ancestor.id}"/>
+ </sub:url>
+ <a href="${ancestorUrl}">${ancestor.name}</a> &raquo;
+ </c:forEach>
+ ${model.dir.name}
+
+ <c:if test="${model.dir.album and model.averageRating gt 0}">
+ &nbsp;&nbsp;
+ <c:import url="rating.jsp">
+ <c:param name="path" value="${model.dir.path}"/>
+ <c:param name="readonly" value="true"/>
+ <c:param name="rating" value="${model.averageRating}"/>
+ </c:import>
+ </c:if>
+</h1>
+
+<c:if test="${not model.partyMode}">
+<h2>
+ <c:if test="${model.navigateUpAllowed}">
+ <sub:url value="main.view" var="upUrl">
+ <sub:param name="id" value="${model.parent.id}"/>
+ </sub:url>
+ <a href="${upUrl}"><fmt:message key="main.up"/></a>
+ <c:set var="needSep" value="true"/>
+ </c:if>
+
+ <c:if test="${model.user.streamRole}">
+ <c:if test="${needSep}">|</c:if>
+ <a href="#" onclick="top.playQueue.onPlay(${model.dir.id});"><fmt:message key="main.playall"/></a> |
+ <a href="#" onclick="top.playQueue.onPlayRandom(${model.dir.id}, 10);"><fmt:message key="main.playrandom"/></a> |
+ <a href="#" onclick="top.playQueue.onAdd(${model.dir.id});"><fmt:message key="main.addall"/></a>
+ <c:set var="needSep" value="true"/>
+ </c:if>
+
+ <c:if test="${model.dir.album}">
+
+ <c:if test="${model.user.downloadRole}">
+ <sub:url value="download.view" var="downloadUrl">
+ <sub:param name="id" value="${model.dir.id}"/>
+ </sub:url>
+ <c:if test="${needSep}">|</c:if>
+ <a href="${downloadUrl}"><fmt:message key="common.download"/></a>
+ <c:set var="needSep" value="true"/>
+ </c:if>
+
+ <c:if test="${model.user.coverArtRole}">
+ <sub:url value="editTags.view" var="editTagsUrl">
+ <sub:param name="id" value="${model.dir.id}"/>
+ </sub:url>
+ <c:if test="${needSep}">|</c:if>
+ <a href="${editTagsUrl}"><fmt:message key="main.tags"/></a>
+ <c:set var="needSep" value="true"/>
+ </c:if>
+
+ </c:if>
+
+ <c:if test="${model.user.commentRole}">
+ <c:if test="${needSep}">|</c:if>
+ <a href="javascript:toggleComment()"><fmt:message key="main.comment"/></a>
+ </c:if>
+</h2>
+</c:if>
+
+<c:if test="${model.dir.album}">
+
+ <div class="detail">
+ <c:if test="${model.user.commentRole}">
+ <c:import url="rating.jsp">
+ <c:param name="path" value="${model.dir.path}"/>
+ <c:param name="readonly" value="false"/>
+ <c:param name="rating" value="${model.userRating}"/>
+ </c:import>
+ </c:if>
+
+ <c:if test="${model.user.shareRole}">
+ <a href="${shareUrl}"><img src="<spring:theme code="shareFacebookImage"/>" alt=""></a>
+ <a href="${shareUrl}"><img src="<spring:theme code="shareTwitterImage"/>" alt=""></a>
+ <a href="${shareUrl}"><img src="<spring:theme code="shareGooglePlusImage"/>" alt=""></a>
+ <a href="${shareUrl}"><span class="detail"><fmt:message key="main.sharealbum"/></span></a> |
+ </c:if>
+
+ <c:if test="${not empty model.artist and not empty model.album}">
+ <sub:url value="http://www.google.com/search" var="googleUrl" encoding="UTF-8">
+ <sub:param name="q" value="\"${model.artist}\" \"${model.album}\""/>
+ </sub:url>
+ <sub:url value="http://en.wikipedia.org/wiki/Special:Search" var="wikipediaUrl" encoding="UTF-8">
+ <sub:param name="search" value="\"${model.album}\""/>
+ <sub:param name="go" value="Go"/>
+ </sub:url>
+ <sub:url value="allmusic.view" var="allmusicUrl">
+ <sub:param name="album" value="${model.album}"/>
+ </sub:url>
+ <sub:url value="http://www.last.fm/search" var="lastFmUrl" encoding="UTF-8">
+ <sub:param name="q" value="\"${model.artist}\" \"${model.album}\""/>
+ <sub:param name="type" value="album"/>
+ </sub:url>
+ <fmt:message key="top.search"/> <a target="_blank" href="${googleUrl}">Google</a> |
+ <a target="_blank" href="${wikipediaUrl}">Wikipedia</a> |
+ <a target="_blank" href="${allmusicUrl}">allmusic</a> |
+ <a target="_blank" href="${lastFmUrl}">Last.fm</a>
+ </c:if>
+ </div>
+ <div class="detail" style="padding-top:0.2em">
+ <fmt:message key="main.playcount"><fmt:param value="${model.dir.playCount}"/></fmt:message>
+ <c:if test="${not empty model.dir.lastPlayed}">
+ <fmt:message key="main.lastplayed">
+ <fmt:param><fmt:formatDate type="date" dateStyle="long" value="${model.dir.lastPlayed}"/></fmt:param>
+ </fmt:message>
+ </c:if>
+ </div>
+
+</c:if>
+
+<div id="comment" class="albumComment"><sub:wiki text="${model.dir.comment}"/></div>
+
+<div id="commentForm" style="display:none">
+ <form method="post" action="setMusicFileInfo.view">
+ <input type="hidden" name="action" value="comment">
+ <input type="hidden" name="path" value="${model.dir.path}">
+ <textarea name="comment" rows="6" cols="70">${model.dir.comment}</textarea>
+ <input type="submit" value="<fmt:message key="common.save"/>">
+ </form>
+ <fmt:message key="main.wiki"/>
+</div>
+
+<script type='text/javascript'>
+ function toggleComment() {
+ $("#commentForm").toggle();
+ $("#comment").toggle();
+ }
+</script>
+
+
+<table cellpadding="10" style="width:100%">
+<tr style="vertical-align:top;">
+ <td style="vertical-align:top;">
+ <table style="border-collapse:collapse;white-space:nowrap">
+ <c:set var="cutoff" value="${model.visibility.captionCutoff}"/>
+ <c:forEach items="${model.children}" var="child" varStatus="loopStatus">
+ <%--@elvariable id="child" type="net.sourceforge.subsonic.domain.MediaFile"--%>
+ <c:choose>
+ <c:when test="${loopStatus.count % 2 == 1}">
+ <c:set var="class" value="class='bgcolor2'"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="class" value=""/>
+ </c:otherwise>
+ </c:choose>
+
+ <tr style="margin:0;padding:0;border:0">
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${child.id}"/>
+ <c:param name="video" value="${child.video and model.player.web}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole and (not model.partyMode or not child.directory)}"/>
+ <c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyMode}"/>
+ <c:param name="starEnabled" value="true"/>
+ <c:param name="starred" value="${not empty child.starredDate}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+
+ <c:choose>
+ <c:when test="${child.directory}">
+ <sub:url value="main.view" var="childUrl">
+ <sub:param name="id" value="${child.id}"/>
+ </sub:url>
+ <td style="padding-left:0.25em" colspan="3">
+ <a href="${childUrl}" title="${child.name}"><span style="white-space:nowrap;"><str:truncateNicely upper="${cutoff}">${child.name}</str:truncateNicely></span></a>
+ </td>
+ <td style="padding-left:1.25em"><c:if test="${model.showAlbumYear and not empty child.year}"><span class="detail">${child.year}</span></c:if></td>
+ </c:when>
+
+ <c:otherwise>
+ <td ${class} style="padding-left:0.25em"><input type="checkbox" class="checkbox" id="songIndex${loopStatus.count - 1}">
+ <span id="songId${loopStatus.count - 1}" style="display: none">${child.id}</span></td>
+
+ <c:if test="${model.visibility.trackNumberVisible}">
+ <td ${class} style="padding-right:0.5em;text-align:right">
+ <span class="detail">${child.trackNumber}</span>
+ </td>
+ </c:if>
+
+ <td ${class} style="padding-right:1.25em;white-space:nowrap">
+ <span title="${child.title}"><str:truncateNicely upper="${cutoff}">${fn:escapeXml(child.title)}</str:truncateNicely></span>
+ </td>
+
+ <c:if test="${model.visibility.albumVisible}">
+ <td ${class} style="padding-right:1.25em;white-space:nowrap">
+ <span class="detail" title="${child.albumName}"><str:truncateNicely upper="${cutoff}">${fn:escapeXml(child.albumName)}</str:truncateNicely></span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.artistVisible and model.multipleArtists}">
+ <td ${class} style="padding-right:1.25em;white-space:nowrap">
+ <span class="detail" title="${child.artist}"><str:truncateNicely upper="${cutoff}">${fn:escapeXml(child.artist)}</str:truncateNicely></span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.genreVisible}">
+ <td ${class} style="padding-right:1.25em;white-space:nowrap">
+ <span class="detail">${child.genre}</span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.yearVisible}">
+ <td ${class} style="padding-right:1.25em">
+ <span class="detail">${child.year}</span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.formatVisible}">
+ <td ${class} style="padding-right:1.25em">
+ <span class="detail">${fn:toLowerCase(child.format)}</span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.fileSizeVisible}">
+ <td ${class} style="padding-right:1.25em;text-align:right">
+ <span class="detail"><sub:formatBytes bytes="${child.fileSize}"/></span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.durationVisible}">
+ <td ${class} style="padding-right:1.25em;text-align:right">
+ <span class="detail">${child.durationString}</span>
+ </td>
+ </c:if>
+
+ <c:if test="${model.visibility.bitRateVisible}">
+ <td ${class} style="padding-right:0.25em">
+ <span class="detail">
+ <c:if test="${not empty child.bitRate}">
+ ${child.bitRate} Kbps ${child.variableBitRate ? "vbr" : ""}
+ </c:if>
+ <c:if test="${child.video and not empty child.width and not empty child.height}">
+ (${child.width}x${child.height})
+ </c:if>
+ </span>
+ </td>
+ </c:if>
+
+
+ </c:otherwise>
+ </c:choose>
+ </tr>
+ </c:forEach>
+ </table>
+ </td>
+
+ <td style="vertical-align:top;width:100%">
+ <c:forEach items="${model.coverArts}" var="coverArt" varStatus="loopStatus">
+ <div style="float:left; padding:5px">
+ <c:import url="coverArt.jsp">
+ <c:param name="albumId" value="${coverArt.id}"/>
+ <c:param name="albumName" value="${coverArt.name}"/>
+ <c:param name="coverArtSize" value="${model.coverArtSize}"/>
+ <c:param name="showLink" value="${coverArt ne model.dir}"/>
+ <c:param name="showZoom" value="${coverArt eq model.dir}"/>
+ <c:param name="showChange" value="${(coverArt eq model.dir) and model.user.coverArtRole}"/>
+ <c:param name="showCaption" value="true"/>
+ <c:param name="appearAfter" value="${loopStatus.count * 30}"/>
+ </c:import>
+ </div>
+ </c:forEach>
+
+ <c:if test="${model.showGenericCoverArt}">
+ <div style="float:left; padding:5px">
+ <c:import url="coverArt.jsp">
+ <c:param name="albumId" value="${model.dir.id}"/>
+ <c:param name="coverArtSize" value="${model.coverArtSize}"/>
+ <c:param name="showLink" value="false"/>
+ <c:param name="showZoom" value="false"/>
+ <c:param name="showChange" value="${model.user.coverArtRole}"/>
+ <c:param name="appearAfter" value="0"/>
+ </c:import>
+ </div>
+ </c:if>
+ </td>
+
+ <td style="vertical-align:top;">
+ <div style="padding:0 1em 0 1em;">
+ <c:if test="${not empty model.ad}">
+ <div class="detail" style="text-align:center">
+ ${model.ad}
+ <br/>
+ <br/>
+ <sub:url value="donate.view" var="donateUrl">
+ <sub:param name="path" value="${model.dir.path}"/>
+ </sub:url>
+ <fmt:message key="main.donate"><fmt:param value="${donateUrl}"/><fmt:param value="${model.brand}"/></fmt:message>
+ </div>
+ </c:if>
+ </div>
+ </td>
+</tr>
+</table>
+
+<select id="moreActions" onchange="actionSelected(this.options[selectedIndex].id);" style="margin-bottom:1.0em">
+ <option id="top" selected="selected"><fmt:message key="main.more"/></option>
+ <option style="color:blue;"><fmt:message key="main.more.selection"/></option>
+ <option id="selectAll">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.selectall"/></option>
+ <option id="selectNone">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.selectnone"/></option>
+ <c:if test="${model.user.shareRole}">
+ <option id="share">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="main.more.share"/></option>
+ </c:if>
+ <c:if test="${model.user.downloadRole}">
+ <option id="download">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="common.download"/></option>
+ </c:if>
+ <option id="appendPlaylist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.append"/></option>
+</select>
+
+<div style="padding-bottom: 1em">
+ <c:if test="${not empty model.previousAlbum}">
+ <sub:url value="main.view" var="previousUrl">
+ <sub:param name="id" value="${model.previousAlbum.id}"/>
+ </sub:url>
+ <div class="back" style="float:left;padding-right:10pt"><a href="${previousUrl}" title="${model.previousAlbum.name}">
+ <str:truncateNicely upper="30">${fn:escapeXml(model.previousAlbum.name)}</str:truncateNicely>
+ </a></div>
+ </c:if>
+ <c:if test="${not empty model.nextAlbum}">
+ <sub:url value="main.view" var="nextUrl">
+ <sub:param name="id" value="${model.nextAlbum.id}"/>
+ </sub:url>
+ <div class="forward" style="float:left"><a href="${nextUrl}" title="${model.nextAlbum.name}">
+ <str:truncateNicely upper="30">${fn:escapeXml(model.nextAlbum.name)}</str:truncateNicely>
+ </a></div>
+ </c:if>
+</div>
+
+<div id="dialog-select-playlist" title="<fmt:message key="main.addtoplaylist.title"/>" style="display: none;">
+ <p><fmt:message key="main.addtoplaylist.text"/></p>
+ <div id="dialog-select-playlist-list"></div>
+</div>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
new file mode 100644
index 00000000..18be91fe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
@@ -0,0 +1,159 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <style type="text/css">
+ #progressBar {width: 350px; height: 10px; border: 1px solid black; display:none;}
+ #progressBarContent {width: 0; height: 10px; background: url("<c:url value="/icons/progress.png"/>") repeat;}
+ </style>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/transferService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+
+ <script type="text/javascript">
+ function refreshProgress() {
+ transferService.getUploadInfo(updateProgress);
+ }
+
+ function updateProgress(uploadInfo) {
+
+ var progressBar = document.getElementById("progressBar");
+ var progressBarContent = document.getElementById("progressBarContent");
+ var progressText = document.getElementById("progressText");
+
+
+ if (uploadInfo.bytesTotal > 0) {
+ var percent = Math.ceil((uploadInfo.bytesUploaded / uploadInfo.bytesTotal) * 100);
+ var width = parseInt(percent * 3.5) + 'px';
+ progressBarContent.style.width = width;
+ progressText.innerHTML = percent + "<fmt:message key="more.upload.progress"/>";
+ progressBar.style.display = "block";
+ progressText.style.display = "block";
+ window.setTimeout("refreshProgress()", 1000);
+ } else {
+ progressBar.style.display = "none";
+ progressText.style.display = "none";
+ window.setTimeout("refreshProgress()", 5000);
+ }
+ }
+ </script>
+
+</head>
+<body class="mainframe bgcolor1" onload="${model.user.uploadRole ? "refreshProgress()" : ""}">
+
+<h1>
+ <img src="<spring:theme code="moreImage"/>" alt=""/>
+ <fmt:message key="more.title"/>
+</h1>
+
+<c:if test="${model.user.streamRole}">
+ <h2><img src="<spring:theme code="randomImage"/>" alt=""/>&nbsp;<fmt:message key="more.random.title"/></h2>
+
+ <form method="post" action="randomPlayQueue.view?">
+ <table>
+ <tr>
+ <td><fmt:message key="more.random.text"/></td>
+ <td>
+ <select name="size">
+ <option value="5"><fmt:message key="more.random.songs"><fmt:param value="5"/></fmt:message></option>
+ <option value="10" selected="true"><fmt:message key="more.random.songs"><fmt:param value="10"/></fmt:message></option>
+ <option value="20"><fmt:message key="more.random.songs"><fmt:param value="20"/></fmt:message></option>
+ <option value="50"><fmt:message key="more.random.songs"><fmt:param value="50"/></fmt:message></option>
+ </select>
+ </td>
+ <td><fmt:message key="more.random.genre"/></td>
+ <td>
+ <select name="genre">
+ <option value="any"><fmt:message key="more.random.anygenre"/></option>
+ <c:forEach items="${model.genres}" var="genre">
+ <option value="${genre}"><str:truncateNicely upper="20">${genre}</str:truncateNicely></option>
+ </c:forEach>
+ </select>
+ </td>
+ <td><fmt:message key="more.random.year"/></td>
+ <td>
+ <select name="year">
+ <option value="any"><fmt:message key="more.random.anyyear"/></option>
+
+ <c:forEach begin="0" end="${model.currentYear - 2006}" var="yearOffset">
+ <c:set var="year" value="${model.currentYear - yearOffset}"/>
+ <option value="${year} ${year}">${year}</option>
+ </c:forEach>
+
+ <option value="2005 2010">2005 &ndash; 2010</option>
+ <option value="2000 2005">2000 &ndash; 2005</option>
+ <option value="1990 2000">1990 &ndash; 2000</option>
+ <option value="1980 1990">1980 &ndash; 1990</option>
+ <option value="1970 1980">1970 &ndash; 1980</option>
+ <option value="1960 1970">1960 &ndash; 1970</option>
+ <option value="1950 1960">1950 &ndash; 1960</option>
+ <option value="0 1949">&lt; 1950</option>
+ </select>
+ </td>
+ <td><fmt:message key="more.random.folder"/></td>
+ <td>
+ <select name="musicFolderId">
+ <option value="-1"><fmt:message key="more.random.anyfolder"/></option>
+ <c:forEach items="${model.musicFolders}" var="musicFolder">
+ <option value="${musicFolder.id}">${musicFolder.name}</option>
+ </c:forEach>
+ </select>
+ </td>
+ <td>
+ <input type="submit" value="<fmt:message key="more.random.ok"/>">
+ </td>
+ </tr>
+ <c:if test="${not model.clientSidePlaylist}">
+ <tr>
+ <td colspan="9">
+ <input type="checkbox" name="autoRandom" id="autoRandom" class="checkbox"/>
+ <label for="autoRandom"><fmt:message key="more.random.auto"/></label>
+ </td>
+ </tr>
+ </c:if>
+ </table>
+ </form>
+</c:if>
+<h2><img src="<spring:theme code="androidImage"/>" alt=""/>&nbsp;<fmt:message key="more.apps.title"/></h2>
+<fmt:message key="more.apps.text"/>
+
+<h2><img src="<spring:theme code="wapImage"/>" alt=""/>&nbsp;<fmt:message key="more.mobile.title"/></h2>
+<fmt:message key="more.mobile.text"><fmt:param value="${model.brand}"/></fmt:message>
+
+<h2><img src="<spring:theme code="podcastImage"/>" alt=""/>&nbsp;<fmt:message key="more.podcast.title"/></h2>
+<fmt:message key="more.podcast.text"/>
+
+<c:if test="${model.user.uploadRole}">
+
+ <h2><img src="<spring:theme code="uploadImage"/>" alt=""/>&nbsp;<fmt:message key="more.upload.title"/></h2>
+
+ <form method="post" enctype="multipart/form-data" action="upload.view">
+ <table>
+ <tr>
+ <td><fmt:message key="more.upload.source"/></td>
+ <td colspan="2"><input type="file" id="file" name="file" size="40"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="more.upload.target"/></td>
+ <td><input type="text" id="dir" name="dir" size="37" value="${model.uploadDirectory}"/></td>
+ <td><input type="submit" value="<fmt:message key="more.upload.ok"/>"/></td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <input type="checkbox" checked name="unzip" id="unzip" class="checkbox"/>
+ <label for="unzip"><fmt:message key="more.upload.unzip"/></label>
+ </td>
+ </tr>
+ </table>
+ </form>
+
+
+ <p class="detail" id="progressText"/>
+
+ <div id="progressBar">
+ <div id="progressBarContent"/>
+ </div>
+
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/musicFolderSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/musicFolderSettings.jsp
new file mode 100644
index 00000000..283e6878
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/musicFolderSettings.jsp
@@ -0,0 +1,114 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.MusicFolderSettingsCommand"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="musicFolder"/>
+</c:import>
+
+<form:form commandName="command" action="musicFolderSettings.view" method="post">
+
+<table class="indent">
+ <tr>
+ <th><fmt:message key="musicfoldersettings.name"/></th>
+ <th><fmt:message key="musicfoldersettings.path"/></th>
+ <th style="padding-left:1em"><fmt:message key="musicfoldersettings.enabled"/></th>
+ <th style="padding-left:1em"><fmt:message key="common.delete"/></th>
+ <th></th>
+ </tr>
+
+ <c:forEach items="${command.musicFolders}" var="folder" varStatus="loopStatus">
+ <tr>
+ <td><form:input path="musicFolders[${loopStatus.count-1}].name" size="20"/></td>
+ <td><form:input path="musicFolders[${loopStatus.count-1}].path" size="40"/></td>
+ <td align="center" style="padding-left:1em"><form:checkbox path="musicFolders[${loopStatus.count-1}].enabled" cssClass="checkbox"/></td>
+ <td align="center" style="padding-left:1em"><form:checkbox path="musicFolders[${loopStatus.count-1}].delete" cssClass="checkbox"/></td>
+ <td><c:if test="${not folder.existing}"><span class="warning"><fmt:message key="musicfoldersettings.notfound"/></span></c:if></td>
+ </tr>
+ </c:forEach>
+
+ <tr>
+ <th colspan="4" align="left" style="padding-top:1em"><fmt:message key="musicfoldersettings.add"/></th>
+ </tr>
+
+ <tr>
+ <td><form:input path="newMusicFolder.name" size="20"/></td>
+ <td><form:input path="newMusicFolder.path" size="40"/></td>
+ <td align="center" style="padding-left:1em"><form:checkbox path="newMusicFolder.enabled" cssClass="checkbox"/></td>
+ <td></td>
+ </tr>
+
+</table>
+
+ <div style="padding-top: 1.2em;padding-bottom: 0.3em">
+ <span style="white-space: nowrap">
+ <fmt:message key="musicfoldersettings.scan"/>
+ <form:select path="interval">
+ <fmt:message key="musicfoldersettings.interval.never" var="never"/>
+ <fmt:message key="musicfoldersettings.interval.one" var="one"/>
+ <form:option value="-1" label="${never}"/>
+ <form:option value="1" label="${one}"/>
+
+ <c:forTokens items="2 3 7 14 30 60" delims=" " var="interval">
+ <fmt:message key="musicfoldersettings.interval.many" var="many"><fmt:param value="${interval}"/></fmt:message>
+ <form:option value="${interval}" label="${many}"/>
+ </c:forTokens>
+ </form:select>
+ <form:select path="hour">
+ <c:forEach begin="0" end="23" var="hour">
+ <fmt:message key="musicfoldersettings.hour" var="hourLabel"><fmt:param value="${hour}"/></fmt:message>
+ <form:option value="${hour}" label="${hourLabel}"/>
+ </c:forEach>
+ </form:select>
+ </span>
+ </div>
+
+ <p class="forward"><a href="musicFolderSettings.view?scanNow"><fmt:message key="musicfoldersettings.scannow"/></a></p>
+
+ <c:if test="${command.scanning}">
+ <p style="width:60%"><b><fmt:message key="musicfoldersettings.nowscanning"/></b></p>
+ </c:if>
+
+ <div>
+ <form:checkbox path="fastCache" cssClass="checkbox" id="fastCache"/>
+ <form:label path="fastCache"><fmt:message key="musicfoldersettings.fastcache"/></form:label>
+ </div>
+
+ <p class="detail" style="width:60%;white-space:normal;">
+ <fmt:message key="musicfoldersettings.fastcache.description"/>
+ </p>
+
+ <p class="forward"><a href="musicFolderSettings.view?expunge"><fmt:message key="musicfoldersettings.expunge"/></a></p>
+ <p class="detail" style="width:60%;white-space:normal;margin-top:-10px;">
+ <fmt:message key="musicfoldersettings.expunge.description"/>
+ </p>
+
+ <%--<div>--%>
+ <%--<form:checkbox path="organizeByFolderStructure" cssClass="checkbox" id="organizeByFolderStructure"/>--%>
+ <%--<form:label path="organizeByFolderStructure"><fmt:message key="musicfoldersettings.organizebyfolderstructure"/></form:label>--%>
+ <%--</div>--%>
+
+ <%--<p class="detail" style="width:60%;white-space:normal;">--%>
+ <%--<fmt:message key="musicfoldersettings.organizebyfolderstructure.description"/>--%>
+ <%--</p>--%>
+
+ <p >
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </p>
+
+</form:form>
+
+<c:if test="${command.reload}">
+ <script type="text/javascript">
+ parent.frames.upper.location.href="top.view?";
+ parent.frames.left.location.href="left.view?";
+ parent.frames.right.location.href="right.view?";
+ </script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/networkSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/networkSettings.jsp
new file mode 100644
index 00000000..c59c16c9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/networkSettings.jsp
@@ -0,0 +1,107 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.NetworkSettingsCommand"--%>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/multiService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ function init() {
+ enableUrlRedirectionFields();
+ refreshStatus();
+ }
+
+ function refreshStatus() {
+ multiService.getNetworkStatus(updateStatus);
+ }
+
+ function updateStatus(networkStatus) {
+ dwr.util.setValue("portForwardingStatus", networkStatus.portForwardingStatusText);
+ dwr.util.setValue("urlRedirectionStatus", networkStatus.urlRedirectionStatusText);
+ window.setTimeout("refreshStatus()", 1000);
+ }
+
+ function enableUrlRedirectionFields() {
+ var checkbox = $("urlRedirectionEnabled");
+ var field = $("urlRedirectFrom");
+
+ if (checkbox && checkbox.checked) {
+ field.enable();
+ } else {
+ field.disable();
+ }
+ }
+
+ </script>
+</head>
+<body class="mainframe bgcolor1" onload="init()">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="network"/>
+</c:import>
+
+<p style="padding-top:1em"><fmt:message key="networksettings.text"/></p>
+
+<form:form commandName="command" action="networkSettings.view" method="post">
+ <p style="padding-top:1em">
+ <form:checkbox id="portForwardingEnabled" path="portForwardingEnabled"/>
+ <label for="portForwardingEnabled"><fmt:message key="networksettings.portforwardingenabled"/></label>
+ </p>
+
+ <div style="padding-left:2em;max-width:60em">
+ <p>
+ <fmt:message key="networksettings.portforwardinghelp"><fmt:param>${command.port}</fmt:param></fmt:message>
+ </p>
+
+ <p class="detail">
+ <fmt:message key="networksettings.status"/>
+ <span id="portForwardingStatus" style="margin-left:0.25em"></span>
+ </p>
+ </div>
+
+ <p style="padding-top:1em"><form:checkbox id="urlRedirectionEnabled" path="urlRedirectionEnabled"
+ onclick="enableUrlRedirectionFields()"/>
+ <label for="urlRedirectionEnabled"><fmt:message key="networksettings.urlredirectionenabled"/></label>
+ </p>
+
+ <div style="padding-left:2em">
+
+ <p>http://<form:input id="urlRedirectFrom" path="urlRedirectFrom" size="16" cssStyle="margin-left:0.25em"/>.subsonic.org</p>
+
+ <p class="detail">
+ <fmt:message key="networksettings.status"/>
+ <span id="urlRedirectionStatus" style="margin-left:0.25em"></span>
+ <span id="urlRedirectionTestStatus" style="margin-left:0.25em"></span>
+ </p>
+ </div>
+
+ <c:if test="${command.trial}">
+ <fmt:formatDate value="${command.trialExpires}" dateStyle="long" var="expiryDate"/>
+
+ <p class="warning" style="padding-top:1em">
+ <c:choose>
+ <c:when test="${command.trialExpired}">
+ <fmt:message key="networksettings.trialexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
+ </c:when>
+ <c:otherwise>
+ <fmt:message
+ key="networksettings.trialnotexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
+ </c:otherwise>
+ </c:choose>
+ </p>
+ </c:if>
+
+ <p style="padding-top:1em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </p>
+
+</form:form>
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/notFound.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/notFound.jsp
new file mode 100644
index 00000000..84e666af
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/notFound.jsp
@@ -0,0 +1,21 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+</head>
+
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="errorImage"/>" alt=""/>
+ <fmt:message key="notFound.title"/>
+</h1>
+
+<fmt:message key="notFound.text"/>
+
+<div class="forward" style="float:left;padding-right:10pt"><a href="javascript:top.location.reload(true)"><fmt:message key="notFound.reload"/></a></div>
+<div class="forward" style="float:left"><a href="musicFolderSettings.view"><fmt:message key="notFound.scan"/></a></div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/passwordSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/passwordSettings.jsp
new file mode 100644
index 00000000..75fd3e01
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/passwordSettings.jsp
@@ -0,0 +1,45 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="password"/>
+ <c:param name="restricted" value="true"/>
+</c:import>
+
+<c:choose>
+
+ <c:when test="${command.ldapAuthenticated}">
+ <p><fmt:message key="usersettings.passwordnotsupportedforldap"/></p>
+ </c:when>
+
+ <c:otherwise>
+ <h2><fmt:message key="passwordsettings.title"><fmt:param>${command.username}</fmt:param></fmt:message></h2>
+ <form:form method="post" action="passwordSettings.view" commandName="command">
+ <table class="indent">
+ <tr>
+ <td><fmt:message key="usersettings.newpassword"/></td>
+ <td><form:password path="password"/></td>
+ <td class="warning"><form:errors path="password"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="usersettings.confirmpassword"/></td>
+ <td><form:password path="confirmPassword"/></td>
+ <td/>
+ </tr>
+ <tr>
+ <td colspan="3" style="padding-top:1.5em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+
+ </table>
+ </form:form>
+ </c:otherwise>
+</c:choose>
+
+</body></html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/personalSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/personalSettings.jsp
new file mode 100644
index 00000000..2942d195
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/personalSettings.jsp
@@ -0,0 +1,228 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.PersonalSettingsCommand"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+
+ <script type="text/javascript" language="javascript">
+ function enableLastFmFields() {
+ var checkbox = $("lastFm");
+ var table = $("lastFmTable");
+
+ if (checkbox && checkbox.checked) {
+ table.show();
+ } else {
+ table.hide();
+ }
+ }
+ </script>
+</head>
+
+<body class="mainframe bgcolor1" onload="enableLastFmFields()">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="personal"/>
+ <c:param name="restricted" value="${not command.user.adminRole}"/>
+</c:import>
+
+<h2><fmt:message key="personalsettings.title"><fmt:param>${command.user.username}</fmt:param></fmt:message></h2>
+
+<fmt:message key="common.default" var="default"/>
+<form:form method="post" action="personalSettings.view" commandName="command">
+
+ <table style="white-space:nowrap" class="indent">
+
+ <tr>
+ <td><fmt:message key="personalsettings.language"/></td>
+ <td>
+ <form:select path="localeIndex" cssStyle="width:15em">
+ <form:option value="-1" label="${default}"/>
+ <c:forEach items="${command.locales}" var="locale" varStatus="loopStatus">
+ <form:option value="${loopStatus.count - 1}" label="${locale}"/>
+ </c:forEach>
+ </form:select>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="language"/></c:import>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="personalsettings.theme"/></td>
+ <td>
+ <form:select path="themeIndex" cssStyle="width:15em">
+ <form:option value="-1" label="${default}"/>
+ <c:forEach items="${command.themes}" var="theme" varStatus="loopStatus">
+ <form:option value="${loopStatus.count - 1}" label="${theme.name}"/>
+ </c:forEach>
+ </form:select>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="theme"/></c:import>
+ </td>
+ </tr>
+ </table>
+
+ <table class="indent">
+ <tr>
+ <th style="padding:0 0.5em 0.5em 0;text-align:left;"><fmt:message key="personalsettings.display"/></th>
+ <th style="padding:0 0.5em 0.5em 0.5em;text-align:center;"><fmt:message key="personalsettings.browse"/></th>
+ <th style="padding:0 0 0.5em 0.5em;text-align:center;"><fmt:message key="personalsettings.playlist"/></th>
+ <th style="padding:0 0 0.5em 0.5em">
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="visibility"/></c:import>
+ </th>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.tracknumber"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.trackNumberVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.trackNumberVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.artist"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.artistVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.artistVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.album"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.albumVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.albumVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.genre"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.genreVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.genreVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.year"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.yearVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.yearVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.bitrate"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.bitRateVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.bitRateVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.duration"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.durationVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.durationVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.format"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.formatVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.formatVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.filesize"/></td>
+ <td style="text-align:center"><form:checkbox path="mainVisibility.fileSizeVisible" cssClass="checkbox"/></td>
+ <td style="text-align:center"><form:checkbox path="playlistVisibility.fileSizeVisible" cssClass="checkbox"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.captioncutoff"/></td>
+ <td style="text-align:center"><form:input path="mainVisibility.captionCutoff" size="3"/></td>
+ <td style="text-align:center"><form:input path="playlistVisibility.captionCutoff" size="3"/></td>
+ </tr>
+ </table>
+
+ <table class="indent">
+ <tr>
+ <td><form:checkbox path="showNowPlayingEnabled" id="nowPlaying" cssClass="checkbox"/></td>
+ <td><label for="nowPlaying"><fmt:message key="personalsettings.shownowplaying"/></label></td>
+ <td style="padding-left:2em"><form:checkbox path="showChatEnabled" id="chat" cssClass="checkbox"/></td>
+ <td><label for="chat"><fmt:message key="personalsettings.showchat"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="nowPlayingAllowed" id="nowPlayingAllowed" cssClass="checkbox"/></td>
+ <td><label for="nowPlayingAllowed"><fmt:message key="personalsettings.nowplayingallowed"/></label></td>
+ <td style="padding-left:2em"><form:checkbox path="partyModeEnabled" id="partyModeEnabled" cssClass="checkbox"/></td>
+ <td><label for="partyModeEnabled"><fmt:message key="personalsettings.partymode"/></label>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="partymode"/></c:import>
+ </td>
+ </tr>
+ </table>
+
+ <table class="indent">
+ <tr>
+ <td><form:checkbox path="finalVersionNotificationEnabled" id="final" cssClass="checkbox"/></td>
+ <td><label for="final"><fmt:message key="personalsettings.finalversionnotification"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="betaVersionNotificationEnabled" id="beta" cssClass="checkbox"/></td>
+ <td><label for="beta"><fmt:message key="personalsettings.betaversionnotification"/></label></td>
+ </tr>
+ </table>
+
+ <table class="indent">
+ <tr>
+ <td><form:checkbox path="lastFmEnabled" id="lastFm" cssClass="checkbox" onclick="javascript:enableLastFmFields()"/></td>
+ <td><label for="lastFm"><fmt:message key="personalsettings.lastfmenabled"/></label></td>
+ </tr>
+ </table>
+
+ <table id="lastFmTable" style="padding-left:2em">
+ <tr>
+ <td><fmt:message key="personalsettings.lastfmusername"/></td>
+ <td><form:input path="lastFmUsername" size="24"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="personalsettings.lastfmpassword"/></td>
+ <td><form:password path="lastFmPassword" size="24"/></td>
+ </tr>
+ </table>
+
+ <p style="padding-top:1em;padding-bottom:1em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em"/>
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </p>
+
+ <h2><fmt:message key="personalsettings.avatar.title"/></h2>
+
+ <p style="padding-top:1em">
+ <c:forEach items="${command.avatars}" var="avatar">
+ <c:url value="avatar.view" var="avatarUrl">
+ <c:param name="id" value="${avatar.id}"/>
+ </c:url>
+ <span style="white-space:nowrap;">
+ <form:radiobutton id="avatar-${avatar.id}" path="avatarId" value="${avatar.id}"/>
+ <label for="avatar-${avatar.id}"><img src="${avatarUrl}" alt="${avatar.name}" width="${avatar.width}" height="${avatar.height}" style="padding-right:2em;padding-bottom:1em"/></label>
+ </span>
+ </c:forEach>
+ </p>
+ <p>
+ <form:radiobutton id="noAvatar" path="avatarId" value="-1"/>
+ <label for="noAvatar"><fmt:message key="personalsettings.avatar.none"/></label>
+ </p>
+ <p>
+ <form:radiobutton id="customAvatar" path="avatarId" value="-2"/>
+ <label for="customAvatar"><fmt:message key="personalsettings.avatar.custom"/>
+ <c:if test="${not empty command.customAvatar}">
+ <sub:url value="avatar.view" var="avatarUrl">
+ <sub:param name="username" value="${command.user.username}"/>
+ </sub:url>
+ <img src="${avatarUrl}" alt="${command.customAvatar.name}" width="${command.customAvatar.width}" height="${command.customAvatar.height}" style="padding-right:2em"/>
+ </c:if>
+ </label>
+ </p>
+</form:form>
+
+<form method="post" enctype="multipart/form-data" action="avatarUpload.view">
+ <table>
+ <tr>
+ <td style="padding-right:1em"><fmt:message key="personalsettings.avatar.changecustom"/></td>
+ <td style="padding-right:1em"><input type="file" id="file" name="file" size="40"/></td>
+ <td style="padding-right:1em"><input type="submit" value="<fmt:message key="personalsettings.avatar.upload"/>"/></td>
+ </tr>
+ </table>
+</form>
+
+<p class="detail" style="text-align:right">
+ <fmt:message key="personalsettings.avatar.courtesy"/>
+</p>
+
+<c:if test="${command.reloadNeeded}">
+ <script language="javascript" type="text/javascript">
+ parent.location.href="index.view?";
+ </script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/playAddDownload.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/playAddDownload.jsp
new file mode 100644
index 00000000..e0852182
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/playAddDownload.jsp
@@ -0,0 +1,64 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<%@ include file="include.jsp" %>
+
+<%--
+PARAMETERS
+ id: ID of file.
+ video: Whether the file is a video (default false).
+ playEnabled: Whether the current user is allowed to play songs (default true).
+ addEnabled: Whether the current user is allowed to add songs to the playlist (default true).
+ downloadEnabled: Whether the current user is allowed to download songs (default false).
+ starEnabled: Whether to show star/unstar controls (default false).
+ starred: Whether the file is currently starred.
+ asTable: Whether to put the images in td tags.
+--%>
+
+<sub:url value="/download.view" var="downloadUrl">
+ <sub:param name="id" value="${param.id}"/>
+</sub:url>
+<c:if test="${param.starEnabled}">
+ <c:if test="${param.asTable}"><td></c:if>
+ <a href="#" onclick="toggleStar(${param.id}, '#starImage${param.id}'); return false;">
+ <c:choose>
+ <c:when test="${param.starred}">
+ <img id="starImage${param.id}" src="<spring:theme code="ratingOnImage"/>" alt="">
+ </c:when>
+ <c:otherwise>
+ <img id="starImage${param.id}" src="<spring:theme code="ratingOffImage"/>" alt="">
+ </c:otherwise>
+ </c:choose>
+ </a>
+ <c:if test="${param.asTable}"></td></c:if>
+</c:if>
+
+<c:if test="${param.asTable}"><td></c:if>
+<c:if test="${empty param.playEnabled or param.playEnabled}">
+ <c:choose>
+ <c:when test="${param.video}">
+ <sub:url value="/videoPlayer.view" var="videoUrl">
+ <sub:param name="id" value="${param.id}"/>
+ </sub:url>
+ <a href="${videoUrl}" target="main">
+ <img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"></a>
+ </c:when>
+ <c:otherwise>
+ <a href="javascript:noop()" onclick="top.playQueue.onPlay(${param.id});">
+ <img src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"></a>
+ </c:otherwise>
+ </c:choose>
+</c:if>
+<c:if test="${param.asTable}"></td></c:if>
+
+<c:if test="${param.asTable}"><td></c:if>
+<c:if test="${(empty param.addEnabled or param.addEnabled) and not param.video}">
+ <a href="javascript:noop()" onclick="top.playQueue.onAdd(${param.id});">
+ <img src="<spring:theme code="addImage"/>" alt="<fmt:message key="common.add"/>" title="<fmt:message key="common.add"/>"></a>
+</c:if>
+<c:if test="${param.asTable}"></td></c:if>
+
+<c:if test="${param.asTable}"><td></c:if>
+<c:if test="${param.downloadEnabled}">
+ <a href="${downloadUrl}">
+ <img src="<spring:theme code="downloadImage"/>" alt="<fmt:message key="common.download"/>" title="<fmt:message key="common.download"/>"></a>
+</c:if>
+<c:if test="${param.asTable}"></td></c:if>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
new file mode 100644
index 00000000..5ac4ac46
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/playQueue.jsp
@@ -0,0 +1,617 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/nowPlayingService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/playQueueService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/webfx/range.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/webfx/timer.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/webfx/slider.js"/>"></script>
+ <link type="text/css" rel="stylesheet" href="<c:url value="/script/webfx/luna.css"/>">
+</head>
+
+<body class="bgcolor2 playlistframe" onload="init()">
+
+<script type="text/javascript" language="javascript">
+ var player = null;
+ var songs = null;
+ var currentAlbumUrl = null;
+ var currentStreamUrl = null;
+ var startPlayer = false;
+ var repeatEnabled = false;
+ var slider = null;
+
+ function init() {
+ dwr.engine.setErrorHandler(null);
+ startTimer();
+
+ $("#dialog-select-playlist").dialog({resizable: true, height: 220, position: 'top', modal: true, autoOpen: false,
+ buttons: {
+ "<fmt:message key="common.cancel"/>": function() {
+ $(this).dialog("close");
+ }
+ }});
+
+ <c:choose>
+ <c:when test="${model.player.web}">
+ createPlayer();
+ </c:when>
+ <c:otherwise>
+ getPlayQueue();
+ </c:otherwise>
+ </c:choose>
+ }
+
+ function startTimer() {
+ <!-- Periodically check if the current song has changed. -->
+ nowPlayingService.getNowPlayingForCurrentPlayer(nowPlayingCallback);
+ setTimeout("startTimer()", 10000);
+ }
+
+ function nowPlayingCallback(nowPlayingInfo) {
+ if (nowPlayingInfo != null && nowPlayingInfo.streamUrl != currentStreamUrl) {
+ getPlayQueue();
+ if (currentAlbumUrl != nowPlayingInfo.albumUrl && top.main.updateNowPlaying) {
+ top.main.location.replace("nowPlaying.view?");
+ currentAlbumUrl = nowPlayingInfo.albumUrl;
+ }
+ <c:if test="${not model.player.web}">
+ currentStreamUrl = nowPlayingInfo.streamUrl;
+ updateCurrentImage();
+ </c:if>
+ }
+ }
+
+ function createPlayer() {
+ var flashvars = {
+ backcolor:"<spring:theme code="backgroundColor"/>",
+ frontcolor:"<spring:theme code="textColor"/>",
+ id:"player1"
+ };
+ var params = {
+ allowfullscreen:"true",
+ allowscriptaccess:"always"
+ };
+ var attributes = {
+ id:"player1",
+ name:"player1"
+ };
+ swfobject.embedSWF("<c:url value="/flash/jw-player-5.6.swf"/>", "placeholder", "340", "24", "9.0.0", false, flashvars, params, attributes);
+ }
+
+ function playerReady(thePlayer) {
+ player = document.getElementById("player1");
+ player.addModelListener("STATE", "stateListener");
+ getPlayQueue();
+ }
+
+ function stateListener(obj) { // IDLE, BUFFERING, PLAYING, PAUSED, COMPLETED
+ if (obj.newstate == "COMPLETED") {
+ onNext(repeatEnabled);
+ }
+ }
+
+ function getPlayQueue() {
+ playQueueService.getPlayQueue(playQueueCallback);
+ }
+
+ function onClear() {
+ var ok = true;
+ <c:if test="${model.partyMode}">
+ ok = confirm("<fmt:message key="playlist.confirmclear"/>");
+ </c:if>
+ if (ok) {
+ playQueueService.clear(playQueueCallback);
+ }
+ }
+ function onStart() {
+ playQueueService.start(playQueueCallback);
+ }
+ function onStop() {
+ playQueueService.stop(playQueueCallback);
+ }
+ function onGain(gain) {
+ playQueueService.setGain(gain);
+ }
+ function onSkip(index) {
+ <c:choose>
+ <c:when test="${model.player.web}">
+ skip(index);
+ </c:when>
+ <c:otherwise>
+ currentStreamUrl = songs[index].streamUrl;
+ playQueueService.skip(index, playQueueCallback);
+ </c:otherwise>
+ </c:choose>
+ }
+ function onNext(wrap) {
+ var index = parseInt(getCurrentSongIndex()) + 1;
+ if (wrap) {
+ index = index % songs.length;
+ }
+ skip(index);
+ }
+ function onPrevious() {
+ skip(parseInt(getCurrentSongIndex()) - 1);
+ }
+ function onPlay(id) {
+ startPlayer = true;
+ playQueueService.play(id, playQueueCallback);
+ }
+ function onPlayPlaylist(id) {
+ startPlayer = true;
+ playQueueService.playPlaylist(id, playQueueCallback);
+ }
+ function onPlayRandom(id, count) {
+ startPlayer = true;
+ playQueueService.playRandom(id, count, playQueueCallback);
+ }
+ function onAdd(id) {
+ startPlayer = false;
+ playQueueService.add(id, playQueueCallback);
+ }
+ function onShuffle() {
+ playQueueService.shuffle(playQueueCallback);
+ }
+ function onStar(index) {
+ playQueueService.toggleStar(index, playQueueCallback);
+ }
+ function onRemove(index) {
+ playQueueService.remove(index, playQueueCallback);
+ }
+ function onRemoveSelected() {
+ var indexes = new Array();
+ var counter = 0;
+ for (var i = 0; i < songs.length; i++) {
+ var index = i + 1;
+ if ($("#songIndex" + index).is(":checked")) {
+ indexes[counter++] = i;
+ }
+ }
+ playQueueService.removeMany(indexes, playQueueCallback);
+ }
+
+ function onUp(index) {
+ playQueueService.up(index, playQueueCallback);
+ }
+ function onDown(index) {
+ playQueueService.down(index, playQueueCallback);
+ }
+ function onToggleRepeat() {
+ playQueueService.toggleRepeat(playQueueCallback);
+ }
+ function onUndo() {
+ playQueueService.undo(playQueueCallback);
+ }
+ function onSortByTrack() {
+ playQueueService.sortByTrack(playQueueCallback);
+ }
+ function onSortByArtist() {
+ playQueueService.sortByArtist(playQueueCallback);
+ }
+ function onSortByAlbum() {
+ playQueueService.sortByAlbum(playQueueCallback);
+ }
+ function onSavePlaylist() {
+ playQueueService.savePlaylist(function () {top.left.updatePlaylists();});
+ }
+ function onAppendPlaylist() {
+ playlistService.getWritablePlaylists(playlistCallback);
+ }
+ function playlistCallback(playlists) {
+ $("#dialog-select-playlist-list").empty();
+ for (var i = 0; i < playlists.length; i++) {
+ var playlist = playlists[i];
+ $("<p class='dense'><b><a href='#' onclick='appendPlaylist(" + playlist.id + ")'>" + playlist.name + "</a></b></p>").appendTo("#dialog-select-playlist-list");
+ }
+ $("#dialog-select-playlist").dialog("open");
+ }
+ function appendPlaylist(playlistId) {
+ $("#dialog-select-playlist").dialog("close");
+
+ var mediaFileIds = new Array();
+ for (var i = 0; i < songs.length; i++) {
+ if ($("#songIndex" + (i + 1)).is(":checked")) {
+ mediaFileIds.push(songs[i].id);
+ }
+ }
+ playlistService.appendToPlaylist(playlistId, mediaFileIds, function (){top.left.updatePlaylists();});
+ }
+
+ function playQueueCallback(playQueue) {
+ songs = playQueue.entries;
+ repeatEnabled = playQueue.repeatEnabled;
+ if ($("#start")) {
+ if (playQueue.stopEnabled) {
+ $("#start").hide();
+ $("#stop").show();
+ } else {
+ $("#start").show();
+ $("#stop").hide();
+ }
+ }
+
+ if ($("#toggleRepeat")) {
+ var text = repeatEnabled ? "<fmt:message key="playlist.repeat_on"/>" : "<fmt:message key="playlist.repeat_off"/>";
+ $("#toggleRepeat").html(text);
+ }
+
+ if (songs.length == 0) {
+ $("#empty").show();
+ } else {
+ $("#empty").hide();
+ }
+
+ // Delete all the rows except for the "pattern" row
+ dwr.util.removeAllRows("playlistBody", { filter:function(tr) {
+ return (tr.id != "pattern");
+ }});
+
+ // Create a new set cloned from the pattern row
+ for (var i = 0; i < songs.length; i++) {
+ var song = songs[i];
+ var id = i + 1;
+ dwr.util.cloneNode("pattern", { idSuffix:id });
+ if ($("#trackNumber" + id)) {
+ $("#trackNumber" + id).html(song.trackNumber);
+ }
+ if (song.starred) {
+ $("#starSong" + id).attr("src", "<spring:theme code='ratingOnImage'/>");
+ } else {
+ $("#starSong" + id).attr("src", "<spring:theme code='ratingOffImage'/>");
+ }
+ if ($("#currentImage" + id) && song.streamUrl == currentStreamUrl) {
+ $("#currentImage" + id).show();
+ }
+ if ($("#title" + id)) {
+ $("#title" + id).html(truncate(song.title));
+ $("#title" + id).attr("title", song.title);
+ }
+ if ($("#titleUrl" + id)) {
+ $("#titleUrl" + id).html(truncate(song.title));
+ $("#titleUrl" + id).attr("title", song.title);
+ $("#titleUrl" + id).click(function () {onSkip(this.id.substring(8) - 1)});
+ }
+ if ($("#album" + id)) {
+ $("#album" + id).html(truncate(song.album));
+ $("#album" + id).attr("title", song.album);
+ $("#albumUrl" + id).attr("href", song.albumUrl);
+ }
+ if ($("#artist" + id)) {
+ $("#artist" + id).html(truncate(song.artist));
+ $("#artist" + id).attr("title", song.artist);
+ }
+ if ($("#genre" + id)) {
+ $("#genre" + id).html(song.genre);
+ }
+ if ($("#year" + id)) {
+ $("#year" + id).html(song.year);
+ }
+ if ($("#bitRate" + id)) {
+ $("#bitRate" + id).html(song.bitRate);
+ }
+ if ($("#duration" + id)) {
+ $("#duration" + id).html(song.durationAsString);
+ }
+ if ($("#format" + id)) {
+ $("#format" + id).html(song.format);
+ }
+ if ($("#fileSize" + id)) {
+ $("#fileSize" + id).html(song.fileSize);
+ }
+
+ $("#pattern" + id).show();
+ $("#pattern" + id).addClass((i % 2 == 0) ? "bgcolor1" : "bgcolor2");
+ }
+
+ if (playQueue.sendM3U) {
+ parent.frames.main.location.href="play.m3u?";
+ }
+
+ if (slider) {
+ slider.setValue(playQueue.gain * 100);
+ }
+
+ <c:if test="${model.player.web}">
+ triggerPlayer();
+ </c:if>
+ }
+
+ function triggerPlayer() {
+ if (startPlayer) {
+ startPlayer = false;
+ if (songs.length > 0) {
+ skip(0);
+ }
+ }
+ updateCurrentImage();
+ if (songs.length == 0) {
+ player.sendEvent("LOAD", new Array());
+ player.sendEvent("STOP");
+ }
+ }
+
+ function skip(index) {
+ if (index < 0 || index >= songs.length) {
+ return;
+ }
+
+ var song = songs[index];
+ currentStreamUrl = song.streamUrl;
+ updateCurrentImage();
+ var list = new Array();
+ list[0] = {
+ file:song.streamUrl,
+ title:song.title,
+ provider:"sound"
+ };
+
+ if (song.duration != null) {
+ list[0].duration = song.duration;
+ }
+ if (song.format == "aac" || song.format == "m4a") {
+ list[0].provider = "video";
+ }
+
+ player.sendEvent("LOAD", list);
+ player.sendEvent("PLAY");
+ }
+
+ function updateCurrentImage() {
+ for (var i = 0; i < songs.length; i++) {
+ var song = songs[i];
+ var id = i + 1;
+ var image = $("#currentImage" + id);
+
+ if (image) {
+ if (song.streamUrl == currentStreamUrl) {
+ image.show();
+ } else {
+ image.hide();
+ }
+ }
+ }
+ }
+
+ function getCurrentSongIndex() {
+ for (var i = 0; i < songs.length; i++) {
+ if (songs[i].streamUrl == currentStreamUrl) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ function truncate(s) {
+ if (s == null) {
+ return s;
+ }
+ var cutoff = ${model.visibility.captionCutoff};
+
+ if (s.length > cutoff) {
+ return s.substring(0, cutoff) + "...";
+ }
+ return s;
+ }
+
+ <!-- actionSelected() is invoked when the users selects from the "More actions..." combo box. -->
+ function actionSelected(id) {
+ if (id == "top") {
+ return;
+ } else if (id == "savePlaylist") {
+ onSavePlaylist();
+ } else if (id == "downloadPlaylist") {
+ location.href = "download.view?player=${model.player.id}";
+ } else if (id == "sharePlaylist") {
+ parent.frames.main.location.href = "createShare.view?player=${model.player.id}&" + getSelectedIndexes();
+ } else if (id == "sortByTrack") {
+ onSortByTrack();
+ } else if (id == "sortByArtist") {
+ onSortByArtist();
+ } else if (id == "sortByAlbum") {
+ onSortByAlbum();
+ } else if (id == "selectAll") {
+ selectAll(true);
+ } else if (id == "selectNone") {
+ selectAll(false);
+ } else if (id == "removeSelected") {
+ onRemoveSelected();
+ } else if (id == "download") {
+ location.href = "download.view?player=${model.player.id}&" + getSelectedIndexes();
+ } else if (id == "appendPlaylist") {
+ onAppendPlaylist();
+ }
+ $("#moreActions").prop("selectedIndex", 0);
+ }
+
+ function getSelectedIndexes() {
+ var result = "";
+ for (var i = 0; i < songs.length; i++) {
+ if ($("#songIndex" + (i + 1)).is(":checked")) {
+ result += "i=" + i + "&";
+ }
+ }
+ return result;
+ }
+
+ function selectAll(b) {
+ for (var i = 0; i < songs.length; i++) {
+ if (b) {
+ $("#songIndex" + (i + 1)).attr("checked", "checked");
+ } else {
+ $("#songIndex" + (i + 1)).removeAttr("checked");
+ }
+ }
+ }
+
+</script>
+
+<div class="bgcolor2" style="position:fixed; top:0; width:100%;padding-top:0.5em">
+ <table style="white-space:nowrap;">
+ <tr style="white-space:nowrap;">
+ <c:if test="${model.user.settingsRole}">
+ <td><select name="player" onchange="location='playQueue.view?player=' + options[selectedIndex].value;">
+ <c:forEach items="${model.players}" var="player">
+ <option ${player.id eq model.player.id ? "selected" : ""} value="${player.id}">${player.shortDescription}</option>
+ </c:forEach>
+ </select></td>
+ </c:if>
+ <c:if test="${model.player.web}">
+ <td style="width:340px; height:24px;padding-left:10px;padding-right:10px"><div id="placeholder">
+ <a href="http://www.adobe.com/go/getflashplayer" target="_blank"><fmt:message key="playlist.getflash"/></a>
+ </div></td>
+ </c:if>
+
+ <c:if test="${model.user.streamRole and not model.player.web}">
+ <td style="white-space:nowrap;" id="stop"><b><a href="#" onclick="onStop()"><fmt:message key="playlist.stop"/></a></b> | </td>
+ <td style="white-space:nowrap;" id="start"><b><a href="#" onclick="onStart()"><fmt:message key="playlist.start"/></a></b> | </td>
+ </c:if>
+
+ <c:if test="${model.player.jukebox}">
+ <td style="white-space:nowrap;">
+ <img src="<spring:theme code="volumeImage"/>" alt="">
+ </td>
+ <td style="white-space:nowrap;">
+ <div class="slider bgcolor2" id="slider-1" style="width:90px">
+ <input class="slider-input" id="slider-input-1" name="slider-input-1">
+ </div>
+ <script type="text/javascript">
+
+ var updateGainTimeoutId = 0;
+ slider = new Slider(document.getElementById("slider-1"), document.getElementById("slider-input-1"));
+ slider.onchange = function () {
+ clearTimeout(updateGainTimeoutId);
+ updateGainTimeoutId = setTimeout("updateGain()", 250);
+ };
+
+ function updateGain() {
+ var gain = slider.getValue() / 100.0;
+ onGain(gain);
+ }
+ </script>
+ </td>
+ </c:if>
+
+ <c:if test="${model.player.web}">
+ <td style="white-space:nowrap;"><a href="#" onclick="onPrevious()"><b>&laquo;</b></a></td>
+ <td style="white-space:nowrap;"><a href="#" onclick="onNext(false)"><b>&raquo;</b></a> |</td>
+ </c:if>
+
+ <td style="white-space:nowrap;"><a href="#" onclick="onClear()"><fmt:message key="playlist.clear"/></a> |</td>
+ <td style="white-space:nowrap;"><a href="#" onclick="onShuffle()"><fmt:message key="playlist.shuffle"/></a> |</td>
+
+ <c:if test="${model.player.web or model.player.jukebox or model.player.external}">
+ <td style="white-space:nowrap;"><a href="#" onclick="onToggleRepeat()"><span id="toggleRepeat"><fmt:message key="playlist.repeat_on"/></span></a> |</td>
+ </c:if>
+
+ <td style="white-space:nowrap;"><a href="#" onclick="onUndo()"><fmt:message key="playlist.undo"/></a> |</td>
+
+ <c:if test="${model.user.settingsRole}">
+ <td style="white-space:nowrap;"><a href="playerSettings.view?id=${model.player.id}" target="main"><fmt:message key="playlist.settings"/></a> |</td>
+ </c:if>
+
+ <td style="white-space:nowrap;"><select id="moreActions" onchange="actionSelected(this.options[selectedIndex].id)">
+ <option id="top" selected="selected"><fmt:message key="playlist.more"/></option>
+ <option style="color:blue;"><fmt:message key="playlist.more.playlist"/></option>
+ <option id="savePlaylist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.save"/></option>
+ <c:if test="${model.user.downloadRole}">
+ <option id="downloadPlaylist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="common.download"/></option>
+ </c:if>
+ <c:if test="${model.user.shareRole}">
+ <option id="sharePlaylist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="main.more.share"/></option>
+ </c:if>
+ <option id="sortByTrack">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.sortbytrack"/></option>
+ <option id="sortByAlbum">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.sortbyalbum"/></option>
+ <option id="sortByArtist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.sortbyartist"/></option>
+ <option style="color:blue;"><fmt:message key="playlist.more.selection"/></option>
+ <option id="selectAll">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.selectall"/></option>
+ <option id="selectNone">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.more.selectnone"/></option>
+ <option id="removeSelected">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.remove"/></option>
+ <c:if test="${model.user.downloadRole}">
+ <option id="download">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="common.download"/></option>
+ </c:if>
+ <option id="appendPlaylist">&nbsp;&nbsp;&nbsp;&nbsp;<fmt:message key="playlist.append"/></option>
+ </select>
+ </td>
+
+ </tr></table>
+</div>
+
+<div style="height:3.2em"></div>
+
+<p id="empty"><em><fmt:message key="playlist.empty"/></em></p>
+
+<table style="border-collapse:collapse;white-space:nowrap;">
+ <tbody id="playlistBody">
+ <tr id="pattern" style="display:none;margin:0;padding:0;border:0">
+ <td class="bgcolor2"><a href="#">
+ <img id="starSong" onclick="onStar(this.id.substring(8) - 1)" src="<spring:theme code="ratingOffImage"/>"
+ alt="" title=""></a></td>
+ <td class="bgcolor2"><a href="#">
+ <img id="removeSong" onclick="onRemove(this.id.substring(10) - 1)" src="<spring:theme code="removeImage"/>"
+ alt="<fmt:message key="playlist.remove"/>" title="<fmt:message key="playlist.remove"/>"></a></td>
+ <td class="bgcolor2"><a href="#">
+ <img id="up" onclick="onUp(this.id.substring(2) - 1)" src="<spring:theme code="upImage"/>"
+ alt="<fmt:message key="playlist.up"/>" title="<fmt:message key="playlist.up"/>"></a></td>
+ <td class="bgcolor2"><a href="#">
+ <img id="down" onclick="onDown(this.id.substring(4) - 1)" src="<spring:theme code="downImage"/>"
+ alt="<fmt:message key="playlist.down"/>" title="<fmt:message key="playlist.down"/>"></a></td>
+
+ <td class="bgcolor2" style="padding-left: 0.1em"><input type="checkbox" class="checkbox" id="songIndex"></td>
+ <td style="padding-right:0.25em"></td>
+
+ <c:if test="${model.visibility.trackNumberVisible}">
+ <td style="padding-right:0.5em;text-align:right"><span class="detail" id="trackNumber">1</span></td>
+ </c:if>
+
+ <td style="padding-right:1.25em">
+ <img id="currentImage" src="<spring:theme code="currentImage"/>" alt="" style="display:none">
+ <c:choose>
+ <c:when test="${model.player.externalWithPlaylist}">
+ <span id="title">Title</span>
+ </c:when>
+ <c:otherwise>
+ <a id="titleUrl" href="#">Title</a>
+ </c:otherwise>
+ </c:choose>
+ </td>
+
+ <c:if test="${model.visibility.albumVisible}">
+ <td style="padding-right:1.25em"><a id="albumUrl" target="main"><span id="album" class="detail">Album</span></a></td>
+ </c:if>
+ <c:if test="${model.visibility.artistVisible}">
+ <td style="padding-right:1.25em"><span id="artist" class="detail">Artist</span></td>
+ </c:if>
+ <c:if test="${model.visibility.genreVisible}">
+ <td style="padding-right:1.25em"><span id="genre" class="detail">Genre</span></td>
+ </c:if>
+ <c:if test="${model.visibility.yearVisible}">
+ <td style="padding-right:1.25em"><span id="year" class="detail">Year</span></td>
+ </c:if>
+ <c:if test="${model.visibility.formatVisible}">
+ <td style="padding-right:1.25em"><span id="format" class="detail">Format</span></td>
+ </c:if>
+ <c:if test="${model.visibility.fileSizeVisible}">
+ <td style="padding-right:1.25em;text-align:right;"><span id="fileSize" class="detail">Format</span></td>
+ </c:if>
+ <c:if test="${model.visibility.durationVisible}">
+ <td style="padding-right:1.25em;text-align:right;"><span id="duration" class="detail">Duration</span></td>
+ </c:if>
+ <c:if test="${model.visibility.bitRateVisible}">
+ <td style="padding-right:0.25em"><span id="bitRate" class="detail">Bit Rate</span></td>
+ </c:if>
+ </tr>
+ </tbody>
+</table>
+
+<div id="dialog-select-playlist" title="<fmt:message key="main.addtoplaylist.title"/>" style="display: none;">
+ <p><fmt:message key="main.addtoplaylist.text"/></p>
+ <div id="dialog-select-playlist-list"></div>
+</div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/playerSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/playerSettings.jsp
new file mode 100644
index 00000000..3381c3a8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/playerSettings.jsp
@@ -0,0 +1,177 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.PlayerSettingsCommand"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+</head>
+<body class="mainframe bgcolor1">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="player"/>
+ <c:param name="restricted" value="${not command.admin}"/>
+</c:import>
+
+<fmt:message key="common.unknown" var="unknown"/>
+
+<c:choose>
+<c:when test="${empty command.players}">
+ <p><fmt:message key="playersettings.noplayers"/></p>
+</c:when>
+<c:otherwise>
+
+<c:url value="playerSettings.view" var="deleteUrl">
+ <c:param name="delete" value="${command.playerId}"/>
+</c:url>
+<c:url value="playerSettings.view" var="cloneUrl">
+ <c:param name="clone" value="${command.playerId}"/>
+</c:url>
+
+<table class="indent">
+ <tr>
+ <td><b><fmt:message key="playersettings.title"/></b></td>
+ <td>
+ <select name="player" onchange="location='playerSettings.view?id=' + options[selectedIndex].value;">
+ <c:forEach items="${command.players}" var="player">
+ <option ${player.id eq command.playerId ? "selected" : ""}
+ value="${player.id}">${player.description}</option>
+ </c:forEach>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td style="padding-right:1em"><div class="forward"><a href="${deleteUrl}"><fmt:message key="playersettings.forget"/></a></div></td>
+ <td><div class="forward"><a href="${cloneUrl}"><fmt:message key="playersettings.clone"/></a></div></td>
+ </tr>
+</table>
+
+<form:form commandName="command" method="post" action="playerSettings.view">
+<form:hidden path="playerId"/>
+
+<table class="ruleTable indent">
+ <c:forEach items="${command.technologyHolders}" var="technologyHolder">
+ <c:set var="technologyName">
+ <fmt:message key="playersettings.technology.${fn:toLowerCase(technologyHolder.name)}.title"/>
+ </c:set>
+
+ <tr>
+ <td class="ruleTableHeader">
+ <form:radiobutton id="radio-${technologyName}" path="technologyName" value="${technologyHolder.name}"/>
+ <b><label for="radio-${technologyName}">${technologyName}</label></b>
+ </td>
+ <td class="ruleTableCell" style="width:40em">
+ <fmt:message key="playersettings.technology.${fn:toLowerCase(technologyHolder.name)}.text"/>
+ </td>
+ </tr>
+ </c:forEach>
+</table>
+
+
+<table class="indent" style="border-spacing:3pt">
+ <tr>
+ <td><fmt:message key="playersettings.type"/></td>
+ <td colspan="3">
+ <c:choose>
+ <c:when test="${empty command.type}">${unknown}</c:when>
+ <c:otherwise>${command.type}</c:otherwise>
+ </c:choose>
+ </td>
+ </tr>
+ <tr>
+ <td><fmt:message key="playersettings.lastseen"/></td>
+ <td colspan="3"><fmt:formatDate value="${command.lastSeen}" type="both" dateStyle="long" timeStyle="medium"/></td>
+ </tr>
+
+ <tr>
+ <td colspan="4">&nbsp;</td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="playersettings.name"/></td>
+ <td><form:input path="name" size="16"/></td>
+ <td colspan="2"><c:import url="helpToolTip.jsp"><c:param name="topic" value="playername"/></c:import></td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="playersettings.coverartsize"/></td>
+ <td>
+ <form:select path="coverArtSchemeName" cssStyle="width:8em">
+ <c:forEach items="${command.coverArtSchemeHolders}" var="coverArtSchemeHolder">
+ <c:set var="coverArtSchemeName">
+ <fmt:message key="playersettings.coverart.${fn:toLowerCase(coverArtSchemeHolder.name)}"/>
+ </c:set>
+ <form:option value="${coverArtSchemeHolder.name}" label="${coverArtSchemeName}"/>
+ </c:forEach>
+ </form:select>
+ </td>
+ <td colspan="2"><c:import url="helpToolTip.jsp"><c:param name="topic" value="cover"/></c:import></td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="playersettings.maxbitrate"/></td>
+ <td>
+ <form:select path="transcodeSchemeName" cssStyle="width:8em">
+ <c:forEach items="${command.transcodeSchemeHolders}" var="transcodeSchemeHolder">
+ <form:option value="${transcodeSchemeHolder.name}" label="${transcodeSchemeHolder.description}"/>
+ </c:forEach>
+ </form:select>
+ </td>
+ <td>
+ <c:import url="helpToolTip.jsp"><c:param name="topic" value="transcode"/></c:import>
+ </td>
+ <td class="warning">
+ <c:if test="${not command.transcodingSupported}">
+ <fmt:message key="playersettings.nolame"/>
+ </c:if>
+ </td>
+ </tr>
+
+</table>
+
+<table class="indent" style="border-spacing:3pt">
+
+ <tr>
+ <td>
+ <form:checkbox path="dynamicIp" id="dynamicIp" cssClass="checkbox"/>
+ <label for="dynamicIp"><fmt:message key="playersettings.dynamicip"/></label>
+ </td>
+ <td><c:import url="helpToolTip.jsp"><c:param name="topic" value="dynamicip"/></c:import></td>
+ </tr>
+
+ <tr>
+ <td>
+ <form:checkbox path="autoControlEnabled" id="autoControlEnabled" cssClass="checkbox"/>
+ <label for="autoControlEnabled"><fmt:message key="playersettings.autocontrol"/></label>
+ </td>
+ <td><c:import url="helpToolTip.jsp"><c:param name="topic" value="autocontrol"/></c:import></td>
+ </tr>
+</table>
+
+ <c:if test="${not empty command.allTranscodings}">
+ <table class="indent">
+ <tr><td><b><fmt:message key="playersettings.transcodings"/></b></td></tr>
+ <c:forEach items="${command.allTranscodings}" var="transcoding" varStatus="loopStatus">
+ <c:if test="${loopStatus.count % 3 == 1}"><tr></c:if>
+ <td style="padding-right:2em">
+ <form:checkbox path="activeTranscodingIds" id="transcoding${transcoding.id}" value="${transcoding.id}" cssClass="checkbox"/>
+ <label for="transcoding${transcoding.id}">${transcoding.name}</label>
+ </td>
+ <c:if test="${loopStatus.count % 3 == 0 or loopStatus.count eq fn:length(command.allTranscodings)}"></tr></c:if>
+ </c:forEach>
+ </table>
+ </c:if>
+
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-top:1em;margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" style="margin-top:1em" onclick="location.href='nowPlaying.view'">
+</form:form>
+
+</c:otherwise>
+</c:choose>
+
+<c:if test="${command.reloadNeeded}">
+ <script language="javascript" type="text/javascript">parent.frames.playQueue.location.href="playQueue.view?"</script>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/playlist.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/playlist.jsp
new file mode 100644
index 00000000..b0cb1f74
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/playlist.jsp
@@ -0,0 +1,235 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/playlistService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ var playlist;
+ var songs;
+
+ function init() {
+ dwr.engine.setErrorHandler(null);
+ $("#dialog-edit").dialog({resizable: true, width:400, position: 'top', modal: true, autoOpen: false,
+ buttons: {
+ "<fmt:message key="common.save"/>": function() {
+ $(this).dialog("close");
+ var name = $("#newName").val();
+ var comment = $("#newComment").val();
+ var isPublic = $("#newPublic").is(":checked");
+ $("#name").html(name);
+ $("#comment").html(comment);
+ playlistService.updatePlaylist(playlist.id, name, comment, isPublic, function (playlistInfo){playlistCallback(playlistInfo); top.left.updatePlaylists()});
+ },
+ "<fmt:message key="common.cancel"/>": function() {
+ $(this).dialog("close");
+ }
+ }});
+
+ $("#dialog-delete").dialog({resizable: false, height: 170, position: 'top', modal: true, autoOpen: false,
+ buttons: {
+ "<fmt:message key="common.delete"/>": function() {
+ $(this).dialog("close");
+ playlistService.deletePlaylist(playlist.id, function (){top.left.updatePlaylists(); location = "home.view";});
+ },
+ "<fmt:message key="common.cancel"/>": function() {
+ $(this).dialog("close");
+ }
+ }});
+ getPlaylist();
+ }
+
+ function getPlaylist() {
+ playlistService.getPlaylist(${model.playlist.id}, playlistCallback);
+ }
+
+ function playlistCallback(playlistInfo) {
+ this.playlist = playlistInfo.playlist;
+ this.songs = playlistInfo.entries;
+
+ if (songs.length == 0) {
+ $("#empty").show();
+ } else {
+ $("#empty").hide();
+ }
+
+
+ $("#songCount").html(playlist.fileCount);
+ $("#duration").html(playlist.durationAsString);
+
+ if (playlist.public) {
+ $("#shared").html("<fmt:message key="playlist2.shared"/>");
+ } else {
+ $("#shared").html("<fmt:message key="playlist2.notshared"/>");
+ }
+
+ // Delete all the rows except for the "pattern" row
+ dwr.util.removeAllRows("playlistBody", { filter:function(tr) {
+ return (tr.id != "pattern");
+ }});
+
+ // Create a new set cloned from the pattern row
+ for (var i = 0; i < songs.length; i++) {
+ var song = songs[i];
+ var id = i + 1;
+ dwr.util.cloneNode("pattern", { idSuffix:id });
+ if (song.starred) {
+ $("#starSong" + id).attr("src", "<spring:theme code='ratingOnImage'/>");
+ } else {
+ $("#starSong" + id).attr("src", "<spring:theme code='ratingOffImage'/>");
+ }
+ if ($("#title" + id)) {
+ $("#title" + id).html(truncate(song.title));
+ $("#title" + id).attr("title", song.title);
+ }
+ if ($("#album" + id)) {
+ $("#album" + id).html(truncate(song.album));
+ $("#album" + id).attr("title", song.album);
+ $("#albumUrl" + id).attr("href", "main.view?id=" + song.id);
+ }
+ if ($("#artist" + id)) {
+ $("#artist" + id).html(truncate(song.artist));
+ $("#artist" + id).attr("title", song.artist);
+ }
+ if ($("#duration" + id)) {
+ $("#duration" + id).html(song.durationAsString);
+ }
+
+ $("#pattern" + id).addClass((i % 2 == 0) ? "bgcolor2" : "bgcolor1");
+ $("#pattern" + id).show();
+ }
+ }
+
+ function truncate(s) {
+ if (s == null) {
+ return s;
+ }
+ var cutoff = 30;
+
+ if (s.length > cutoff) {
+ return s.substring(0, cutoff) + "...";
+ }
+ return s;
+ }
+
+ function onPlay(index) {
+ top.playQueue.onPlay(songs[index].id);
+ }
+ function onPlayAll() {
+ top.playQueue.onPlayPlaylist(playlist.id);
+ }
+ function onAdd(index) {
+ top.playQueue.onAdd(songs[index].id);
+ }
+ function onStar(index) {
+ playlistService.toggleStar(playlist.id, index, playlistCallback);
+ }
+ function onRemove(index) {
+ playlistService.remove(playlist.id, index, function (playlistInfo){playlistCallback(playlistInfo); top.left.updatePlaylists()});
+ }
+ function onUp(index) {
+ playlistService.up(playlist.id, index, playlistCallback);
+ }
+ function onDown(index) {
+ playlistService.down(playlist.id, index, playlistCallback);
+ }
+ function onEditPlaylist() {
+ $("#dialog-edit").dialog("open");
+ }
+ function onDeletePlaylist() {
+ $("#dialog-delete").dialog("open");
+ }
+
+ </script>
+</head>
+<body class="mainframe bgcolor1" onload="init()">
+
+<h1 id="name">${model.playlist.name}</h1>
+<h2>
+ <a href="#" onclick="onPlayAll();"><fmt:message key="common.play"/></a>
+
+ <c:if test="${model.user.downloadRole}">
+ <c:url value="download.view" var="downloadUrl"><c:param name="playlist" value="${model.playlist.id}"/></c:url>
+ | <a href="${downloadUrl}"><fmt:message key="common.download"/></a>
+ </c:if>
+ <c:if test="${model.editAllowed}">
+ | <a href="#" onclick="onEditPlaylist();"><fmt:message key="common.edit"/></a>
+ | <a href="#" onclick="onDeletePlaylist();"><fmt:message key="common.delete"/></a>
+ </c:if>
+ <c:url value="exportPlaylist.view" var="exportUrl"><c:param name="id" value="${model.playlist.id}"/></c:url>
+ | <a href="${exportUrl}"><fmt:message key="playlist2.export"/></a>
+
+</h2>
+
+<div id="comment" class="detail" style="padding-top:0.2em">${model.playlist.comment}</div>
+
+<div class="detail" style="padding-top:0.2em">
+ <fmt:message key="playlist2.created">
+ <fmt:param>${model.playlist.username}</fmt:param>
+ <fmt:param><fmt:formatDate type="date" dateStyle="long" value="${model.playlist.created}"/></fmt:param>
+ </fmt:message>.
+ <span id="shared"></span>.
+ <span id="songCount"></span> <fmt:message key="playlist2.songs"/> (<span id="duration"></span>)
+</div>
+
+<div style="height:0.7em"></div>
+
+<p id="empty" style="display: none;"><em><fmt:message key="playlist2.empty"/></em></p>
+
+<table style="border-collapse:collapse;white-space:nowrap">
+ <tbody id="playlistBody">
+ <tr id="pattern" style="display:none;margin:0;padding:0;border:0">
+ <td class="bgcolor1"><a href="#">
+ <img id="starSong" onclick="onStar(this.id.substring(8) - 1)" src="<spring:theme code="ratingOffImage"/>" alt="" title=""></a></td>
+ <td class="bgcolor1"><a href="#">
+ <img id="play" src="<spring:theme code="playImage"/>" alt="<fmt:message key="common.play"/>" title="<fmt:message key="common.play"/>"
+ onclick="onPlay(this.id.substring(4) - 1)"></a></td>
+ <td class="bgcolor1"><a href="#">
+ <img id="add" src="<spring:theme code="addImage"/>" alt="<fmt:message key="common.add"/>" title="<fmt:message key="common.add"/>"
+ onclick="onAdd(this.id.substring(3) - 1)"></a></td>
+
+ <td style="padding-right:0.25em"></td>
+ <td style="padding-right:1.25em"><span id="title">Title</span></td>
+ <td style="padding-right:1.25em"><a id="albumUrl" target="main"><span id="album" class="detail">Album</span></a></td>
+ <td style="padding-right:1.25em"><span id="artist" class="detail">Artist</span></td>
+ <td style="padding-right:1.25em;text-align:right;"><span id="duration" class="detail">Duration</span></td>
+
+ <c:if test="${model.editAllowed}">
+ <td class="bgcolor1"><a href="#">
+ <img id="removeSong" onclick="onRemove(this.id.substring(10) - 1)" src="<spring:theme code="removeImage"/>"
+ alt="<fmt:message key="playlist.remove"/>" title="<fmt:message key="playlist.remove"/>"></a></td>
+ <td class="bgcolor1"><a href="#">
+ <img id="up" onclick="onUp(this.id.substring(2) - 1)" src="<spring:theme code="upImage"/>"
+ alt="<fmt:message key="playlist.up"/>" title="<fmt:message key="playlist.up"/>"></a></td>
+ <td class="bgcolor1"><a href="#">
+ <img id="down" onclick="onDown(this.id.substring(4) - 1)" src="<spring:theme code="downImage"/>"
+ alt="<fmt:message key="playlist.down"/>" title="<fmt:message key="playlist.down"/>"></a></td>
+ </c:if>
+
+ </tr>
+ </tbody>
+</table>
+
+<div id="dialog-delete" title="<fmt:message key="common.confirm"/>" style="display: none;">
+ <p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span>
+ <fmt:message key="playlist2.confirmdelete"/></p>
+</div>
+
+<div id="dialog-edit" title="<fmt:message key="common.edit"/>" style="display: none;">
+ <form>
+ <label for="newName" style="display:block;"><fmt:message key="playlist2.name"/></label>
+ <input type="text" name="newName" id="newName" value="${model.playlist.name}" class="ui-widget-content"
+ style="display:block;width:95%;"/>
+ <label for="newComment" style="display:block;margin-top:1em"><fmt:message key="playlist2.comment"/></label>
+ <input type="text" name="newComment" id="newComment" value="${model.playlist.comment}" class="ui-widget-content"
+ style="display:block;width:95%;"/>
+ <input type="checkbox" name="newPublic" id="newPublic" ${model.playlist.public ? "checked='checked'" : ""} style="margin-top:1.5em" class="ui-widget-content"/>
+ <label for="newPublic"><fmt:message key="playlist2.public"/></label>
+ </form>
+</div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/podcast.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcast.jsp
new file mode 100644
index 00000000..6f2b88d8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcast.jsp
@@ -0,0 +1,26 @@
+<%@ include file="include.jsp" %>
+<%@ page language="java" contentType="text/xml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<rss version="2.0">
+ <channel>
+ <title>Subsonic Podcast</title>
+ <link>${model.url}</link>
+ <description>Subsonic Podcast</description>
+ <language>en-us</language>
+ <image>
+ <url>http://subsonic.org/pages/inc/img/subsonic.png</url>
+ <title>Subsonic Podcast</title>
+ </image>
+
+ <c:forEach var="podcast" items="${model.podcasts}">
+ <item>
+ <title>${fn:escapeXml(podcast.name)}</title>
+ <link>${model.url}</link>
+ <description>Subsonic playlist "${fn:escapeXml(podcast.name)}"</description>
+ <pubDate>${podcast.publishDate}</pubDate>
+ <enclosure url="${podcast.enclosureUrl}" length="${podcast.length}" type="${podcast.type}"/>
+ </item>
+ </c:forEach>
+
+ </channel>
+</rss>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastReceiver.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastReceiver.jsp
new file mode 100644
index 00000000..35a0ffdb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastReceiver.jsp
@@ -0,0 +1,269 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+</head>
+<body class="mainframe bgcolor1">
+
+<script type="text/javascript" language="javascript">
+ var channelCount = ${fn:length(model.channels)};
+
+ function downloadSelected() {
+ location.href = "podcastReceiverAdmin.view?downloadChannel=" + getSelectedChannels() +
+ "&downloadEpisode=" + getSelectedEpisodes() +
+ "&expandedChannels=" + getExpandedChannels();
+ }
+
+ function deleteSelected() {
+ if (confirm("<fmt:message key="podcastreceiver.confirmdelete"/>")) {
+ location.href = "podcastReceiverAdmin.view?deleteChannel=" + getSelectedChannels() +
+ "&deleteEpisode=" + getSelectedEpisodes() +
+ "&expandedChannels=" + getExpandedChannels();
+ }
+ }
+
+ function refreshChannels() {
+ location.href = "podcastReceiverAdmin.view?refresh=" +
+ "&expandedChannels=" + getExpandedChannels();
+ }
+
+ function refreshPage() {
+ location.href = "podcastReceiver.view?expandedChannels=" + getExpandedChannels();
+ }
+
+ function toggleEpisodes(channelIndex) {
+ for (var i = 0; i < episodeCount; i++) {
+ var row = $("episodeRow" + i);
+ if (row.title == "channel" + channelIndex) {
+ row.toggle();
+ $("channelExpanded" + channelIndex).checked = row.visible() ? "checked" : "";
+ }
+ }
+ }
+
+ function toggleAllEpisodes(visible) {
+ for (var i = 0; i < episodeCount; i++) {
+ var row = $("episodeRow" + i);
+ if (visible) {
+ row.show();
+ } else {
+ row.hide();
+ }
+ }
+ for (i = 0; i < channelCount; i++) {
+ $("channelExpanded" + i).checked = visible ? "checked" : "";
+ }
+ }
+
+ function getExpandedChannels() {
+ var result = "";
+ for (var i = 0; i < channelCount; i++) {
+ var checkbox = $("channelExpanded" + i);
+ if (checkbox.checked) {
+ result += (checkbox.value + " ");
+ }
+ }
+ return result;
+ }
+
+ function getSelectedChannels() {
+ var result = "";
+ for (var i = 0; i < channelCount; i++) {
+ var checkbox = $("channel" + i);
+ if (checkbox.checked) {
+ result += (checkbox.value + " ");
+ }
+ }
+ return result;
+ }
+
+ function getSelectedEpisodes() {
+ var result = "";
+ for (var i = 0; i < episodeCount; i++) {
+ var checkbox = $("episode" + i);
+ if (checkbox.checked) {
+ result += (checkbox.value + " ");
+ }
+ }
+ return result;
+ }
+</script>
+
+<h1>
+ <img src="<spring:theme code="podcastLargeImage"/>" alt=""/>
+ <fmt:message key="podcastreceiver.title"/>
+</h1>
+
+<table><tr>
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:toggleAllEpisodes(true)"><fmt:message key="podcastreceiver.expandall"/></a></div></td>
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:toggleAllEpisodes(false)"><fmt:message key="podcastreceiver.collapseall"/></a></div></td>
+</tr></table>
+
+<table style="border-collapse:collapse;white-space:nowrap">
+
+ <c:set var="episodeCount" value="0"/>
+
+ <c:forEach items="${model.channels}" var="channel" varStatus="i">
+
+ <c:set var="title" value="${channel.key.title}"/>
+ <c:if test="${empty title}">
+ <c:set var="title" value="${channel.key.url}"/>
+ </c:if>
+
+ <c:set var="channelExpanded" value="false"/>
+ <c:forEach items="${model.expandedChannels}" var="expandedChannelId">
+ <c:if test="${expandedChannelId eq channel.key.id}">
+ <c:set var="channelExpanded" value="true"/>
+ </c:if>
+ </c:forEach>
+
+ <tr style="margin:0;padding:0;border:0">
+ <td style="padding-top:1em">
+ <input type="checkbox" class="checkbox" id="channel${i.index}" value="${channel.key.id}"/>
+ <input type="checkbox" class="checkbox" id="channelExpanded${i.index}" value="${channel.key.id}" style="display:none"
+ <c:if test="${channelExpanded}">checked="checked"</c:if>/>
+ </td>
+ <td colspan="6" style="padding-left:0.25em;padding-top:1em">
+ <a href="javascript:toggleEpisodes(${i.index})">
+ <span title="${title}"><b><str:truncateNicely upper="40">${title}</str:truncateNicely></b></span>
+ (${fn:length(channel.value)})
+ </a>
+ </td>
+ <td style="padding-left:1.5em;padding-top:1em;text-align:center;">
+ <span class="detail"><fmt:message key="podcastreceiver.status.${fn:toLowerCase(channel.key.status)}"/></span>
+ </td>
+ <td style="padding-left:1.5em;padding-top:1em">
+ <c:choose>
+ <c:when test="${channel.key.status eq 'ERROR'}">
+ <span class="detail warning" title="${channel.key.errorMessage}"><str:truncateNicely upper="100">${channel.key.errorMessage}</str:truncateNicely></span>
+ </c:when>
+ <c:otherwise>
+ <span class="detail" title="${channel.key.description}"><str:truncateNicely upper="100">${channel.key.description}</str:truncateNicely></span>
+ </c:otherwise>
+ </c:choose>
+ </td>
+ </tr>
+
+ <c:set var="class" value=""/>
+
+ <c:forEach items="${channel.value}" var="episode" varStatus="j">
+
+ <c:choose>
+ <c:when test="${empty class}">
+ <c:set var="class" value="class='bgcolor2'"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="class" value=""/>
+ </c:otherwise>
+ </c:choose>
+ <tr title="channel${i.index}" id="episodeRow${episodeCount}" style="margin:0;padding:0;border:0;display:${channelExpanded ? "table-row" : "none"}">
+
+ <td><input type="checkbox" class="checkbox" id="episode${episodeCount}" value="${episode.id}"/></td>
+
+ <c:choose>
+ <c:when test="${empty episode.path}">
+ <td ${class} colspan="3"/>
+ </c:when>
+ <c:otherwise>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${episode.mediaFileId}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyMode}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole and not model.partyMode}"/>
+ <c:param name="downloadEnabled" value="false"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+ </c:otherwise>
+ </c:choose>
+
+ <c:set var="episodeCount" value="${episodeCount + 1}"/>
+
+
+ <sub:url value="main.view" var="mainUrl">
+ <sub:param name="path" value="${episode.path}"/>
+ </sub:url>
+
+
+ <td ${class} style="padding-left:0.6em">
+ <span title="${episode.title}">
+ <c:choose>
+ <c:when test="${empty episode.path}">
+ <str:truncateNicely upper="40">${episode.title}</str:truncateNicely>
+ </c:when>
+ <c:otherwise>
+ <a target="main" href="${mainUrl}"><str:truncateNicely upper="40">${episode.title}</str:truncateNicely></a>
+ </c:otherwise>
+ </c:choose>
+ </span>
+ </td>
+
+ <td ${class} style="padding-left:1.5em">
+ <span class="detail">${episode.duration}</span>
+ </td>
+
+ <td ${class} style="padding-left:1.5em">
+ <span class="detail"><fmt:formatDate value="${episode.publishDate}" dateStyle="medium"/></span>
+ </td>
+
+ <td ${class} style="padding-left:1.5em;text-align:center">
+ <span class="detail">
+ <c:choose>
+ <c:when test="${episode.status eq 'DOWNLOADING'}">
+ <fmt:formatNumber type="percent" value="${episode.completionRate}"/>
+ </c:when>
+ <c:otherwise>
+ <fmt:message key="podcastreceiver.status.${fn:toLowerCase(episode.status)}"/>
+ </c:otherwise>
+ </c:choose>
+ </span>
+ </td>
+
+ <td ${class} style="padding-left:1.5em">
+ <c:choose>
+ <c:when test="${episode.status eq 'ERROR'}">
+ <span class="detail warning" title="${episode.errorMessage}"><str:truncateNicely upper="100">${episode.errorMessage}</str:truncateNicely></span>
+ </c:when>
+ <c:otherwise>
+ <span class="detail" title="${episode.description}"><str:truncateNicely upper="100">${episode.description}</str:truncateNicely></span>
+ </c:otherwise>
+ </c:choose>
+ </td>
+
+ </tr>
+ </c:forEach>
+ </c:forEach>
+</table>
+
+<script type="text/javascript" language="javascript">
+ var episodeCount = ${episodeCount};
+</script>
+
+<table style="padding-top:1em"><tr>
+ <c:if test="${model.user.podcastRole}">
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:downloadSelected()"><fmt:message key="podcastreceiver.downloadselected"/></a></div></td>
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:deleteSelected()"><fmt:message key="podcastreceiver.deleteselected"/></a></div></td>
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:refreshChannels()"><fmt:message key="podcastreceiver.check"/></a></div></td>
+ </c:if>
+ <td style="padding-right:2em"><div class="forward"><a href="javascript:refreshPage()"><fmt:message key="podcastreceiver.refresh"/></a></div></td>
+ <c:if test="${model.user.adminRole}">
+ <td style="padding-right:2em"><div class="forward"><a href="podcastSettings.view?"><fmt:message key="podcastreceiver.settings"/></a></div></td>
+ </c:if>
+</tr></table>
+
+<c:if test="${model.user.podcastRole}">
+ <form method="post" action="podcastReceiverAdmin.view?">
+ <input type="hidden" name="expandedChannels" value=""/>
+ <table>
+ <tr>
+ <td><fmt:message key="podcastreceiver.subscribe"/></td>
+ <td><input type="text" name="add" value="http://" style="width:30em" onclick="select()"/></td>
+ <td><input type="submit" value="<fmt:message key="common.ok"/>"/></td>
+ </tr>
+ </table>
+ </form>
+</c:if>
+
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastSettings.jsp
new file mode 100644
index 00000000..07d99e28
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/podcastSettings.jsp
@@ -0,0 +1,88 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="podcast"/>
+</c:import>
+
+<form:form commandName="command" action="podcastSettings.view" method="post">
+
+<table class="indent">
+ <tr>
+ <td><fmt:message key="podcastsettings.update"/></td>
+ <td>
+ <form:select path="interval" cssStyle="width:20em">
+ <fmt:message key="podcastsettings.interval.manually" var="never"/>
+ <fmt:message key="podcastsettings.interval.hourly" var="hourly"/>
+ <fmt:message key="podcastsettings.interval.daily" var="daily"/>
+ <fmt:message key="podcastsettings.interval.weekly" var="weekly"/>
+
+ <form:option value="-1" label="${never}"/>
+ <form:option value="1" label="${hourly}"/>
+ <form:option value="24" label="${daily}"/>
+ <form:option value="168" label="${weekly}"/>
+ </form:select>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="podcastsettings.keep"/></td>
+ <td>
+ <form:select path="episodeRetentionCount" cssStyle="width:20em">
+ <fmt:message key="podcastsettings.keep.all" var="all"/>
+ <fmt:message key="podcastsettings.keep.one" var="one"/>
+
+ <form:option value="-1" label="${all}"/>
+ <form:option value="1" label="${one}"/>
+
+ <c:forTokens items="2 3 4 5 10 20 30 50" delims=" " var="count">
+ <fmt:message key="podcastsettings.keep.many" var="many"><fmt:param value="${count}"/></fmt:message>
+ <form:option value="${count}" label="${many}"/>
+ </c:forTokens>
+
+ </form:select>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="podcastsettings.download"/></td>
+ <td>
+ <form:select path="episodeDownloadCount" cssStyle="width:20em">
+ <fmt:message key="podcastsettings.download.all" var="all"/>
+ <fmt:message key="podcastsettings.download.one" var="one"/>
+ <fmt:message key="podcastsettings.download.none" var="none"/>
+
+ <form:option value="-1" label="${all}"/>
+ <form:option value="1" label="${one}"/>
+
+ <c:forTokens items="2 3 4 5 10" delims=" " var="count">
+ <fmt:message key="podcastsettings.download.many" var="many"><fmt:param value="${count}"/></fmt:message>
+ <form:option value="${count}" label="${many}"/>
+ </c:forTokens>
+ <form:option value="0" label="${none}"/>
+
+ </form:select>
+ </td>
+ </tr>
+
+ <tr>
+ <td><fmt:message key="podcastsettings.folder"/></td>
+ <td><form:input path="folder" cssStyle="width:20em"/></td>
+ </tr>
+
+ <tr>
+ <td style="padding-top:1.5em" colspan="2">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+
+</table>
+
+</form:form>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/rating.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/rating.jsp
new file mode 100644
index 00000000..9e956de3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/rating.jsp
@@ -0,0 +1,51 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<%@ include file="include.jsp" %>
+
+<%--
+Creates HTML for displaying the rating stars.
+PARAMETERS
+ path: Album path. May be null if readonly.
+ readonly: Whether rating can be changed.
+ rating: The rating, an integer from 0 (no rating), through 10 (lowest rating), to 50 (highest rating).
+--%>
+
+<c:forEach var="i" begin="1" end="5">
+
+ <sub:url value="setRating.view" var="ratingUrl">
+ <sub:param name="path" value="${param.path}"/>
+ <sub:param name="action" value="rating"/>
+ <sub:param name="rating" value="${i}"/>
+ </sub:url>
+
+ <c:choose>
+ <c:when test="${param.rating ge i * 10}">
+ <spring:theme code="ratingOnImage" var="imageUrl"/>
+ </c:when>
+ <c:when test="${param.rating ge i*10 - 7 and param.rating le i*10 - 3}">
+ <spring:theme code="ratingHalfImage" var="imageUrl"/>
+ </c:when>
+ <c:otherwise>
+ <spring:theme code="ratingOffImage" var="imageUrl"/>
+ </c:otherwise>
+ </c:choose>
+
+ <c:choose>
+ <c:when test="${param.readonly}">
+ <img src="${imageUrl}" style="margin-right:-3px" alt="" title="<fmt:message key="rating.rating"/> ${param.rating/10}">
+ </c:when>
+ <c:otherwise>
+ <a href="${ratingUrl}"><img src="${imageUrl}" style="margin-right:-3px" alt="" title="<fmt:message key="rating.rating"/> ${i}"></a>
+ </c:otherwise>
+ </c:choose>
+
+</c:forEach>
+
+<sub:url value="setRating.view" var="clearRatingUrl">
+ <sub:param name="path" value="${param.path}"/>
+ <sub:param name="action" value="rating"/>
+ <sub:param name="rating" value="0"/>
+</sub:url>
+
+<c:if test="${not param.readonly}">
+ | <a href="${clearRatingUrl}"><img src="<spring:theme code="clearRatingImage"/>" alt="" title="<fmt:message key="rating.clearrating"/>" style="margin-left:-3px; margin-right:5px"></a>
+</c:if>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/recover.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/recover.jsp
new file mode 100644
index 00000000..ff206d14
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/recover.jsp
@@ -0,0 +1,34 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1" onload="document.getElementById('usernameOrEmail').focus()">
+
+<form action="recover.view" method="POST">
+ <div class="bgcolor2" style="border:1px solid black; padding:20px 50px 20px 50px; margin-top:100px">
+
+ <div style="margin-left: auto; margin-right: auto; width: 45em">
+
+ <h1><fmt:message key="recover.title"/></h1>
+ <p style="padding-top: 1em; padding-bottom: 0.5em"><fmt:message key="recover.text"/></p>
+ <input type="text" id="usernameOrEmail" name="usernameOrEmail" style="width:18em;margin-right: 1em">
+ <input name="submit" type="submit" value="<fmt:message key="recover.send"/>">
+
+ <c:if test="${not empty model.sentTo}">
+ <p style="padding-top: 1em"><fmt:message key="recover.success"><fmt:param value="${model.sentTo}"/></fmt:message></p>
+ </c:if>
+
+ <c:if test="${not empty model.error}">
+ <p style="padding-top: 1em" class="warning"><fmt:message key="${model.error}"/></p>
+ </c:if>
+
+ <div class="back" style="margin-top: 1.5em"><a href="login.view"><fmt:message key="common.back"/></a></div>
+
+ </div>
+ </div>
+</form>
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/reload.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/reload.jsp
new file mode 100644
index 00000000..1b8384a7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/reload.jsp
@@ -0,0 +1,11 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head><body>
+
+<c:forEach items="${model.reloadFrames}" var="reloadFrame">
+ <script language="javascript" type="text/javascript">parent.frames.${reloadFrame.frame}.location.href="${reloadFrame.view}"</script>
+</c:forEach>
+
+</body></html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/rest/videoPlayer.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/rest/videoPlayer.jsp
new file mode 100644
index 00000000..ccd18a93
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/rest/videoPlayer.jsp
@@ -0,0 +1,142 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="../include.jsp" %>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" href="../<spring:theme code="styleSheet"/>" type="text/css">
+ <link rel="shortcut icon" href="../<spring:theme code="faviconImage"/>">
+
+ <c:url value="/rest/stream.view" var="streamUrl">
+ <c:param name="c" value="${model.c}"/>
+ <c:param name="v" value="${model.v}"/>
+ <c:param name="id" value="${model.id}"/>
+ </c:url>
+
+ <script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ var player;
+ var position;
+ var maxBitRate = ${model.maxBitRate};
+ var timeOffset = ${model.timeOffset};
+
+ function init() {
+ var flashvars = {
+ id:"player1",
+ skin:"<c:url value="/flash/whotube.zip"/>",
+ screencolor:"000000",
+ autostart:false,
+ bufferlength:4,
+ backcolor:"<spring:theme code="backgroundColor"/>",
+ frontcolor:"<spring:theme code="textColor"/>",
+ provider:"video"
+ };
+ var params = {
+ allowfullscreen:"true",
+ allowscriptaccess:"always"
+ };
+ var attributes = {
+ id:"player1",
+ name:"player1"
+ };
+
+ swfobject.embedSWF("<c:url value="/flash/jw-player-5.6.swf"/>", "placeholder1", "360", "240", "9.0.0", false, flashvars, params, attributes);
+ }
+
+ function playerReady(thePlayer) {
+ player = $("player1");
+ player.addModelListener("TIME", "timeListener");
+
+ <c:if test="${model.autoplay}">
+ play();
+ </c:if>
+ }
+
+ function play() {
+ var list = new Array();
+ list[0] = {
+ file:"${streamUrl}&maxBitRate=" + maxBitRate + "&timeOffset=" + timeOffset + "&p=${model.p}" + "&u=${model.u}",
+ duration:${model.duration} - timeOffset,
+ provider:"video"
+ };
+ player.sendEvent("LOAD", list);
+ player.sendEvent("PLAY");
+ }
+
+ function timeListener(obj) {
+ var newPosition = Math.round(obj.position);
+ if (newPosition != position) {
+ position = newPosition;
+ updatePosition();
+ }
+ }
+
+ function updatePosition() {
+ var pos = parseInt(timeOffset) + parseInt(position);
+
+ var minutes = Math.round(pos / 60);
+ var seconds = pos % 60;
+
+ var result = minutes + ":";
+ if (seconds < 10) {
+ result += "0";
+ }
+ result += seconds;
+ $("position").innerHTML = result;
+ }
+
+ function changeTimeOffset() {
+ timeOffset = $("timeOffset").getValue();
+ play();
+ }
+
+ function changeBitRate() {
+ maxBitRate = $("maxBitRate").getValue();
+ timeOffset = parseInt(timeOffset) + parseInt(position);
+ play();
+ }
+
+ </script>
+</head>
+
+<body class="mainframe bgcolor1" onload="init();">
+<h1>${model.video.title}</h1>
+
+<div id="wrapper" style="padding-top:1em">
+ <div id="placeholder1"><span class="warning"><a href="http://www.adobe.com/go/getflashplayer"><fmt:message key="playlist.getflash"/></a></span></div>
+</div>
+
+<div style="padding-top:1.3em;padding-bottom:0.7em;font-size:16px">
+
+ <span id="position" style="padding-right:0.5em">0:00</span>
+ <select id="timeOffset" onchange="changeTimeOffset();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em;font-size:16px">
+ <c:forEach items="${model.skipOffsets}" var="skipOffset">
+ <c:choose>
+ <c:when test="${skipOffset.value eq model.timeOffset}">
+ <option selected="selected" value="${skipOffset.value}">${skipOffset.key}</option>
+ </c:when>
+ <c:otherwise>
+ <option value="${skipOffset.value}">${skipOffset.key}</option>
+ </c:otherwise>
+ </c:choose>
+ </c:forEach>
+ </select>
+
+ <select id="maxBitRate" onchange="changeBitRate();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em;font-size:16px">
+ <c:forEach items="${model.bitRates}" var="bitRate">
+ <c:choose>
+ <c:when test="${bitRate eq model.maxBitRate}">
+ <option selected="selected" value="${bitRate}">${bitRate} Kbps</option>
+ </c:when>
+ <c:otherwise>
+ <option value="${bitRate}">${bitRate} Kbps</option>
+ </c:otherwise>
+ </c:choose>
+ </c:forEach>
+ </select>
+</div>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/right.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/right.jsp
new file mode 100644
index 00000000..ada7385f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/right.jsp
@@ -0,0 +1,191 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/util.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/chatService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/nowPlayingService.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoom.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/fancyzoom/FancyZoomHTML.js"/>"></script>
+</head>
+<body class="bgcolor1 rightframe" style="padding-top:2em" onload="init()">
+
+<script type="text/javascript">
+ function init() {
+ setupZoom('<c:url value="/"/>');
+ dwr.engine.setErrorHandler(null);
+ <c:if test="${model.showChat}">
+ chatService.addMessage(null);
+ </c:if>
+ }
+</script>
+
+<div id="scanningStatus" style="display: none;" class="warning">
+ <img src="<spring:theme code="scanningImage"/>" title="" alt=""> <fmt:message key="main.scanning"/> <span id="scanCount"></span>
+</div>
+
+<c:if test="${model.showNowPlaying}">
+
+ <!-- This script uses AJAX to periodically retrieve what all users are playing. -->
+ <script type="text/javascript" language="javascript">
+
+ startGetNowPlayingTimer();
+
+ function startGetNowPlayingTimer() {
+ nowPlayingService.getNowPlaying(getNowPlayingCallback);
+ setTimeout("startGetNowPlayingTimer()", 10000);
+ }
+
+ function getNowPlayingCallback(nowPlaying) {
+ var html = nowPlaying.length == 0 ? "" : "<h2><fmt:message key="main.nowplaying"/></h2><table>";
+ for (var i = 0; i < nowPlaying.length; i++) {
+ html += "<tr><td colspan='2' class='detail' style='padding-top:1em;white-space:nowrap'>";
+
+ if (nowPlaying[i].avatarUrl != null) {
+ html += "<img src='" + nowPlaying[i].avatarUrl + "' style='padding-right:5pt'>";
+ }
+ html += "<b>" + nowPlaying[i].username + "</b></td></tr>"
+
+ html += "<tr><td class='detail' style='padding-right:1em'>" +
+ "<a title='" + nowPlaying[i].tooltip + "' target='main' href='" + nowPlaying[i].albumUrl + "'>";
+
+ if (nowPlaying[i].artist != null) {
+ html += "<em>" + nowPlaying[i].artist + "</em><br/>";
+ }
+
+ html += nowPlaying[i].title + "</a><br/>" +
+ "<span class='forward'><a href='" + nowPlaying[i].lyricsUrl + "' onclick=\"return popupSize(this, 'lyrics', 430, 550)\">" +
+ "<fmt:message key="main.lyrics"/>" + "</a></span></td><td style='padding-top:1em'>";
+
+ if (nowPlaying[i].coverArtUrl != null) {
+ html += "<a title='" + nowPlaying[i].tooltip + "' rel='zoom' href='" + nowPlaying[i].coverArtZoomUrl + "'>" +
+ "<img src='" + nowPlaying[i].coverArtUrl + "' width='48' height='48'></a>";
+ }
+ html += "</td></tr>";
+
+ var minutesAgo = nowPlaying[i].minutesAgo;
+ if (minutesAgo > 4) {
+ html += "<tr><td class='detail' colspan='2'>" + minutesAgo + " <fmt:message key="main.minutesago"/></td></tr>";
+ }
+ }
+ html += "</table>";
+ $('nowPlaying').innerHTML = html;
+ prepZooms();
+ }
+ </script>
+
+ <div id="nowPlaying">
+ </div>
+
+</c:if>
+
+<c:if test="${model.showChat}">
+ <script type="text/javascript">
+
+ var revision = 0;
+ startGetMessagesTimer();
+
+ function startGetMessagesTimer() {
+ chatService.getMessages(revision, getMessagesCallback);
+ setTimeout("startGetMessagesTimer()", 10000);
+ }
+
+ function addMessage() {
+ chatService.addMessage($("message").value);
+ dwr.util.setValue("message", null);
+ setTimeout("startGetMessagesTimer()", 500);
+ }
+ function clearMessages() {
+ chatService.clearMessages();
+ setTimeout("startGetMessagesTimer()", 500);
+ }
+ function getMessagesCallback(messages) {
+
+ if (messages == null) {
+ return;
+ }
+ revision = messages.revision;
+
+ // Delete all the rows except for the "pattern" row
+ dwr.util.removeAllRows("chatlog", { filter:function(div) {
+ return (div.id != "pattern");
+ }});
+
+ // Create a new set cloned from the pattern row
+ for (var i = 0; i < messages.messages.length; i++) {
+ var message = messages.messages[i];
+ var id = i + 1;
+ dwr.util.cloneNode("pattern", { idSuffix:id });
+ dwr.util.setValue("user" + id, message.username);
+ dwr.util.setValue("date" + id, " [" + formatDate(message.date) + "]");
+ dwr.util.setValue("content" + id, message.content);
+ $("pattern" + id).show();
+ }
+
+ var clearDiv = $("clearDiv");
+ if (clearDiv) {
+ if (messages.messages.length == 0) {
+ clearDiv.hide();
+ } else {
+ clearDiv.show();
+ }
+ }
+ }
+ function formatDate(date) {
+ var hours = date.getHours();
+ var minutes = date.getMinutes();
+ var result = hours < 10 ? "0" : "";
+ result += hours;
+ result += ":";
+ if (minutes < 10) {
+ result += "0";
+ }
+ result += minutes;
+ return result;
+ }
+ </script>
+
+ <script type="text/javascript">
+
+ startGetScanningStatusTimer();
+
+ function startGetScanningStatusTimer() {
+ nowPlayingService.getScanningStatus(getScanningStatusCallback);
+ }
+
+ function getScanningStatusCallback(scanInfo) {
+ dwr.util.setValue("scanCount", scanInfo.count);
+ if (scanInfo.scanning) {
+ $("scanningStatus").show();
+ setTimeout("startGetScanningStatusTimer()", 1000);
+ } else {
+ $("scanningStatus").hide();
+ setTimeout("startGetScanningStatusTimer()", 15000);
+ }
+ }
+ </script>
+
+ <h2><fmt:message key="main.chat"/></h2>
+ <div style="padding-top:0.3em;padding-bottom:0.3em">
+ <input id="message" value=" <fmt:message key="main.message"/>" style="width:90%" onclick="dwr.util.setValue('message', null);" onkeypress="dwr.util.onReturn(event, addMessage)"/>
+ </div>
+
+ <table>
+ <tbody id="chatlog">
+ <tr id="pattern" style="display:none;margin:0;padding:0 0 0.15em 0;border:0"><td>
+ <span id="user" class="detail" style="font-weight:bold"></span>&nbsp;<span id="date" class="detail"></span> <span id="content"></span></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <c:if test="${model.user.adminRole}">
+ <div id="clearDiv" style="display:none;" class="forward"><a href="#" onclick="clearMessages(); return false;"> <fmt:message key="main.clearchat"/></a></div>
+ </c:if>
+</c:if>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/search.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/search.jsp
new file mode 100644
index 00000000..a01f7afe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/search.jsp
@@ -0,0 +1,150 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<%--@elvariable id="command" type="net.sourceforge.subsonic.command.SearchCommand"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value='/script/scripts.js'/>"></script>
+ <script type="text/javascript" src="<c:url value='/script/prototype.js'/>"></script>
+ <script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
+
+ <script type="text/javascript">
+ function more(rowSelector, moreId) {
+ var rows = $$(rowSelector);
+ for (var i = 0; i < rows.length; i++) {
+ rows[i].show();
+ }
+ $(moreId).hide();
+ }
+ </script>
+
+</head>
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="searchImage"/>" alt=""/>
+ <fmt:message key="search.title"/>
+</h1>
+
+<form:form commandName="command" method="post" action="search.view" name="searchForm">
+ <table>
+ <tr>
+ <td><fmt:message key="search.query"/></td>
+ <td style="padding-left:0.25em"><form:input path="query" size="35"/></td>
+ <td style="padding-left:0.25em"><input type="submit" onclick="search(0)" value="<fmt:message key="search.search"/>"/></td>
+ </tr>
+ </table>
+
+</form:form>
+
+<c:if test="${command.indexBeingCreated}">
+ <p class="warning"><fmt:message key="search.index"/></p>
+</c:if>
+
+<c:if test="${not command.indexBeingCreated and empty command.artists and empty command.albums and empty command.songs}">
+ <p class="warning"><fmt:message key="search.hits.none"/></p>
+</c:if>
+
+<c:if test="${not empty command.artists}">
+ <h2><fmt:message key="search.hits.artists"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${command.artists}" var="match" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${match.path}"/>
+ </sub:url>
+
+ <tr class="artistRow" ${loopStatus.count > 5 ? "style='display:none'" : ""}>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${match.id}"/>
+ <c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
+ <c:param name="downloadEnabled" value="${command.user.downloadRole and not command.partyModeEnabled}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ <a href="${mainUrl}">${match.name}</a>
+ </td>
+ </tr>
+
+ </c:forEach>
+ </table>
+ <c:if test="${fn:length(command.artists) gt 5}">
+ <div id="moreArtists" class="forward"><a href="javascript:noop()" onclick="more('tr.artistRow', 'moreArtists')"><fmt:message key="search.hits.more"/></a></div>
+ </c:if>
+</c:if>
+
+<c:if test="${not empty command.albums}">
+ <h2><fmt:message key="search.hits.albums"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${command.albums}" var="match" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${match.path}"/>
+ </sub:url>
+
+ <tr class="albumRow" ${loopStatus.count > 5 ? "style='display:none'" : ""}>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${match.id}"/>
+ <c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
+ <c:param name="downloadEnabled" value="${command.user.downloadRole and not command.partyModeEnabled}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ <a href="${mainUrl}">${match.albumName}</a>
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:0.25em">
+ <span class="detail">${match.artist}</span>
+ </td>
+ </tr>
+
+ </c:forEach>
+ </table>
+ <c:if test="${fn:length(command.albums) gt 5}">
+ <div id="moreAlbums" class="forward"><a href="javascript:noop()" onclick="more('tr.albumRow', 'moreAlbums')"><fmt:message key="search.hits.more"/></a></div>
+ </c:if>
+</c:if>
+
+
+<c:if test="${not empty command.songs}">
+ <h2><fmt:message key="search.hits.songs"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${command.songs}" var="match" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${match.parentPath}"/>
+ </sub:url>
+
+ <tr class="songRow" ${loopStatus.count > 15 ? "style='display:none'" : ""}>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${match.id}"/>
+ <c:param name="playEnabled" value="${command.user.streamRole and not command.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${command.user.streamRole and (not command.partyModeEnabled or not match.directory)}"/>
+ <c:param name="downloadEnabled" value="${command.user.downloadRole and not command.partyModeEnabled}"/>
+ <c:param name="video" value="${match.video and command.player.web}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ ${match.title}
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:1.25em">
+ <a href="${mainUrl}"><span class="detail">${match.albumName}</span></a>
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:0.25em">
+ <span class="detail">${match.artist}</span>
+ </td>
+ </tr>
+
+ </c:forEach>
+ </table>
+<c:if test="${fn:length(command.songs) gt 15}">
+ <div id="moreSongs" class="forward"><a href="javascript:noop()" onclick="more('tr.songRow', 'moreSongs')"><fmt:message key="search.hits.more"/></a></div>
+</c:if>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/settingsHeader.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/settingsHeader.jsp
new file mode 100644
index 00000000..6f7f240c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/settingsHeader.jsp
@@ -0,0 +1,32 @@
+
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%@ include file="include.jsp" %>
+
+<c:set var="categories" value="${param.restricted ? 'personal password player share' : 'musicFolder general advanced personal user player share network transcoding internetRadio podcast'}"/>
+<h1>
+ <img src="<spring:theme code="settingsImage"/>" alt=""/>
+ <fmt:message key="settingsheader.title"/>
+</h1>
+
+<h2>
+<c:forTokens items="${categories}" delims=" " var="cat" varStatus="loopStatus">
+ <c:choose>
+ <c:when test="${loopStatus.count > 1 and (loopStatus.count - 1) % 6 != 0}">&nbsp;|&nbsp;</c:when>
+ <c:otherwise></h2><h2></c:otherwise>
+ </c:choose>
+
+ <c:url var="url" value="${cat}Settings.view?"/>
+
+ <c:choose>
+ <c:when test="${param.cat eq cat}">
+ <span class="headerSelected"><fmt:message key="settingsheader.${cat}"/></span>
+ </c:when>
+ <c:otherwise>
+ <a href="${url}"><fmt:message key="settingsheader.${cat}"/></a>
+ </c:otherwise>
+ </c:choose>
+
+</c:forTokens>
+</h2>
+
+<p></p>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/shareSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/shareSettings.jsp
new file mode 100644
index 00000000..448f4741
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/shareSettings.jsp
@@ -0,0 +1,72 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+<%--@elvariable id="model" type="Map"--%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="share"/>
+ <c:param name="restricted" value="${not model.user.adminRole}"/>
+</c:import>
+
+<form method="post" action="shareSettings.view">
+
+ <table class="indent" style="border-collapse:collapse;white-space:nowrap">
+ <tr>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.name"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.owner"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.description"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.expires"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.lastvisited"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.visits"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.files"/></th>
+ <th style="padding-left:1em"><fmt:message key="sharesettings.expirein"/></th>
+ <th style="padding-left:1em"><fmt:message key="common.delete"/></th>
+ </tr>
+
+ <c:forEach items="${model.shareInfos}" var="shareInfo" varStatus="loopStatus">
+ <c:set var="share" value="${shareInfo.share}"/>
+ <c:choose>
+ <c:when test="${loopStatus.count % 2 == 1}">
+ <c:set var="class" value="class='bgcolor2'"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="class" value=""/>
+ </c:otherwise>
+ </c:choose>
+
+ <sub:url value="main.view" var="albumUrl">
+ <sub:param name="path" value="${shareInfo.dir.path}"/>
+ </sub:url>
+
+ <tr>
+ <td ${class} style="padding-left:1em"><a href="${model.shareBaseUrl}${share.name}" target="_blank">${share.name}</a></td>
+ <td ${class} style="padding-left:1em">${share.username}</td>
+ <td ${class} style="padding-left:1em"><input type="text" name="description[${share.id}]" size="40" value="${share.description}"/></td>
+ <td ${class} style="padding-left:1em"><fmt:formatDate value="${share.expires}" type="date" dateStyle="medium"/></td>
+ <td ${class} style="padding-left:1em"><fmt:formatDate value="${share.lastVisited}" type="date" dateStyle="medium"/></td>
+ <td ${class} style="padding-left:1em; text-align:right">${share.visitCount}</td>
+ <td ${class} style="padding-left:1em"><a href="${albumUrl}" title="${shareInfo.dir.name}"><str:truncateNicely upper="30">${fn:escapeXml(shareInfo.dir.name)}</str:truncateNicely></a></td>
+ <td ${class} style="padding-left:1em">
+ <label><input type="radio" name="expireIn[${share.id}]" value="7"><fmt:message key="sharesettings.expirein.week"/></label>
+ <label><input type="radio" name="expireIn[${share.id}]" value="30"><fmt:message key="sharesettings.expirein.month"/></label>
+ <label><input type="radio" name="expireIn[${share.id}]" value="365"><fmt:message key="sharesettings.expirein.year"/></label>
+ <label><input type="radio" name="expireIn[${share.id}]" value="0"><fmt:message key="sharesettings.expirein.never"/></label>
+ </td>
+ <td ${class} style="padding-left:1em" align="center" style="padding-left:1em"><input type="checkbox" name="delete[${share.id}]" class="checkbox"/></td>
+ </tr>
+ </c:forEach>
+
+ <tr>
+ <td colspan="4" style="padding-top:1.5em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'">
+ </td>
+ </tr>
+
+ </table>
+</form>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/starred.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/starred.jsp
new file mode 100644
index 00000000..967fc7b7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/starred.jsp
@@ -0,0 +1,131 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <%@ include file="jquery.jsp" %>
+ <script type="text/javascript" src="<c:url value='/dwr/util.js'/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/dwr/interface/starService.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ function toggleStar(mediaFileId, imageId) {
+ if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOnImage"/>") != -1) {
+ $(imageId).attr("src", "<spring:theme code="ratingOffImage"/>");
+ starService.unstar(mediaFileId);
+ }
+ else if ($(imageId).attr("src").indexOf("<spring:theme code="ratingOffImage"/>") != -1) {
+ $(imageId).attr("src", "<spring:theme code="ratingOnImage"/>");
+ starService.star(mediaFileId);
+ }
+ }
+ </script>
+</head>
+<body class="mainframe bgcolor1">
+
+<h1>
+ <fmt:message key="starred.title"/>
+</h1>
+
+<c:if test="${empty model.artists and empty model.albums and empty model.songs}">
+ <p style="padding-top: 1em"><em><fmt:message key="starred.empty"/></em></p>
+</c:if>
+
+<c:if test="${not empty model.artists}">
+ <h2><fmt:message key="search.hits.artists"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${model.artists}" var="artist" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${artist.path}"/>
+ </sub:url>
+
+ <tr>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${artist.id}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole and (not model.partyModeEnabled or not artist.directory)}"/>
+ <c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyModeEnabled}"/>
+ <c:param name="starEnabled" value="true"/>
+ <c:param name="starred" value="${not empty artist.starredDate}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ <a href="${mainUrl}">${artist.name}</a>
+ </td>
+ </tr>
+ </c:forEach>
+ </table>
+</c:if>
+
+<c:if test="${not empty model.albums}">
+ <h2><fmt:message key="search.hits.albums"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${model.albums}" var="album" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${album.path}"/>
+ </sub:url>
+
+ <tr>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${album.id}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole and (not model.partyModeEnabled or not album.directory)}"/>
+ <c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyModeEnabled}"/>
+ <c:param name="starEnabled" value="true"/>
+ <c:param name="starred" value="${not empty album.starredDate}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ <a href="${mainUrl}">${album.albumName}</a>
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:0.25em">
+ <span class="detail">${album.artist}</span>
+ </td>
+ </tr>
+
+ </c:forEach>
+ </table>
+</c:if>
+
+<c:if test="${not empty model.songs}">
+ <h2><fmt:message key="search.hits.songs"/></h2>
+ <table style="border-collapse:collapse">
+ <c:forEach items="${model.songs}" var="song" varStatus="loopStatus">
+
+ <sub:url value="/main.view" var="mainUrl">
+ <sub:param name="path" value="${song.parentPath}"/>
+ </sub:url>
+
+ <tr>
+ <c:import url="playAddDownload.jsp">
+ <c:param name="id" value="${song.id}"/>
+ <c:param name="playEnabled" value="${model.user.streamRole and not model.partyModeEnabled}"/>
+ <c:param name="addEnabled" value="${model.user.streamRole and (not model.partyModeEnabled or not song.directory)}"/>
+ <c:param name="downloadEnabled" value="${model.user.downloadRole and not model.partyModeEnabled}"/>
+ <c:param name="starEnabled" value="true"/>
+ <c:param name="starred" value="${not empty song.starredDate}"/>
+ <c:param name="video" value="${song.video and model.player.web}"/>
+ <c:param name="asTable" value="true"/>
+ </c:import>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-left:0.25em;padding-right:1.25em">
+ ${song.title}
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:1.25em">
+ <a href="${mainUrl}"><span class="detail">${song.albumName}</span></a>
+ </td>
+
+ <td ${loopStatus.count % 2 == 1 ? "class='bgcolor2'" : ""} style="padding-right:0.25em">
+ <span class="detail">${song.artist}</span>
+ </td>
+ </tr>
+
+ </c:forEach>
+ </table>
+</c:if>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/status.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/status.jsp
new file mode 100644
index 00000000..2861022d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/status.jsp
@@ -0,0 +1,93 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <meta http-equiv="CACHE-CONTROL" content="NO-CACHE">
+ <meta http-equiv="REFRESH" content="20;URL=status.view">
+</head>
+<body class="mainframe bgcolor1">
+
+<h1>
+ <img src="<spring:theme code="statusImage"/>" alt="">
+ <fmt:message key="status.title"/>
+</h1>
+
+<table width="100%" class="ruleTable indent">
+ <tr>
+ <th class="ruleTableHeader"><fmt:message key="status.type"/></th>
+ <th class="ruleTableHeader"><fmt:message key="status.player"/></th>
+ <th class="ruleTableHeader"><fmt:message key="status.user"/></th>
+ <th class="ruleTableHeader"><fmt:message key="status.current"/></th>
+ <th class="ruleTableHeader"><fmt:message key="status.transmitted"/></th>
+ <th class="ruleTableHeader"><fmt:message key="status.bitrate"/></th>
+ </tr>
+
+ <c:forEach items="${model.transferStatuses}" var="status">
+
+ <c:choose>
+ <c:when test="${empty status.playerType}">
+ <fmt:message key="common.unknown" var="type"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="type" value="(${status.playerType})"/>
+ </c:otherwise>
+ </c:choose>
+
+ <c:choose>
+ <c:when test="${status.stream}">
+ <fmt:message key="status.stream" var="transferType"/>
+ </c:when>
+ <c:when test="${status.download}">
+ <fmt:message key="status.download" var="transferType"/>
+ </c:when>
+ <c:when test="${status.upload}">
+ <fmt:message key="status.upload" var="transferType"/>
+ </c:when>
+ </c:choose>
+
+ <c:choose>
+ <c:when test="${empty status.username}">
+ <fmt:message key="common.unknown" var="user"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="user" value="${status.username}"/>
+ </c:otherwise>
+ </c:choose>
+
+ <c:choose>
+ <c:when test="${empty status.path}">
+ <fmt:message key="common.unknown" var="current"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="current" value="${status.path}"/>
+ </c:otherwise>
+ </c:choose>
+
+ <sub:url value="/statusChart.view" var="chartUrl">
+ <c:if test="${status.stream}">
+ <sub:param name="type" value="stream"/>
+ </c:if>
+ <c:if test="${status.download}">
+ <sub:param name="type" value="download"/>
+ </c:if>
+ <c:if test="${status.upload}">
+ <sub:param name="type" value="upload"/>
+ </c:if>
+ <sub:param name="index" value="${status.index}"/>
+ </sub:url>
+
+ <tr>
+ <td class="ruleTableCell">${transferType}</td>
+ <td class="ruleTableCell">${status.player}<br>${type}</td>
+ <td class="ruleTableCell">${user}</td>
+ <td class="ruleTableCell">${current}</td>
+ <td class="ruleTableCell">${status.bytes}</td>
+ <td class="ruleTableCell" width="${model.chartWidth}"><img width="${model.chartWidth}" height="${model.chartHeight}" src="${chartUrl}" alt=""></td>
+ </tr>
+ </c:forEach>
+</table>
+
+<div class="forward"><a href="status.view?"><fmt:message key="common.refresh"/></a></div>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/test.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/test.jsp
new file mode 100644
index 00000000..64f7c792
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/test.jsp
@@ -0,0 +1,20 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+</head>
+<body>
+
+<div id="tFfBc" style="opacity:0;">
+ <img src="/coverArt.view?size=200" alt="">
+</div>
+
+<ul>
+ <li><a href="#" onclick="new Effect.Opacity('tFfBc', { from: 1, to: 0 }); return false;">Hide this box</a></li>
+ <li><a href="#" onclick="new Effect.Opacity('tFfBc', { from: 0, to: 1 }); return false;">Show this box</a></li>
+</ul>
+
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/top.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/top.jsp
new file mode 100644
index 00000000..18acb053
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/top.jsp
@@ -0,0 +1,98 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+
+<body class="bgcolor2 topframe" style="margin:0.4em 1em 0.4em 1em">
+
+<fmt:message key="top.home" var="home"/>
+<fmt:message key="top.now_playing" var="nowPlaying"/>
+<fmt:message key="top.starred" var="starred"/>
+<fmt:message key="top.settings" var="settings"/>
+<fmt:message key="top.status" var="status"/>
+<fmt:message key="top.podcast" var="podcast"/>
+<fmt:message key="top.more" var="more"/>
+<fmt:message key="top.help" var="help"/>
+<fmt:message key="top.search" var="search"/>
+
+<table style="margin:0"><tr valign="middle">
+ <td class="logo" style="padding-right:2em"><a href="help.view?" target="main"><img src="<spring:theme code="logoImage"/>" title="${help}" alt=""></a></td>
+
+ <c:if test="${not model.musicFoldersExist}">
+ <td style="padding-right:2em">
+ <p class="warning"><fmt:message key="top.missing"/></p>
+ </td>
+ </c:if>
+
+ <td>
+ <table><tr align="center">
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="home.view?" target="main"><img src="<spring:theme code="homeImage"/>" title="${home}" alt="${home}"></a><br>
+ <a href="home.view?" target="main">${home}</a>
+ </td>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="nowPlaying.view?" target="main"><img src="<spring:theme code="nowPlayingImage"/>" title="${nowPlaying}" alt="${nowPlaying}"></a><br>
+ <a href="nowPlaying.view?" target="main">${nowPlaying}</a>
+ </td>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="starred.view?" target="main"><img src="<spring:theme code="starredImage"/>" title="${starred}" alt="${starred}"></a><br>
+ <a href="starred.view?" target="main">${starred}</a>
+ </td>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="podcastReceiver.view?" target="main"><img src="<spring:theme code="podcastLargeImage"/>" title="${podcast}" alt="${podcast}"></a><br>
+ <a href="podcastReceiver.view?" target="main">${podcast}</a>
+ </td>
+ <c:if test="${model.user.settingsRole}">
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="settings.view?" target="main"><img src="<spring:theme code="settingsImage"/>" title="${settings}" alt="${settings}"></a><br>
+ <a href="settings.view?" target="main">${settings}</a>
+ </td>
+ </c:if>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="status.view?" target="main"><img src="<spring:theme code="statusImage"/>" title="${status}" alt="${status}"></a><br>
+ <a href="status.view?" target="main">${status}</a>
+ </td>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="more.view?" target="main"><img src="<spring:theme code="moreImage"/>" title="${more}" alt="${more}"></a><br>
+ <a href="more.view?" target="main">${more}</a>
+ </td>
+ <td style="min-width:4em;padding-right:1.5em">
+ <a href="help.view?" target="main"><img src="<spring:theme code="helpImage"/>" title="${help}" alt="${help}"></a><br>
+ <a href="help.view?" target="main">${help}</a>
+ </td>
+
+ <td style="padding-left:1em">
+ <form method="post" action="search.view" target="main" name="searchForm">
+ <table><tr>
+ <td><input type="text" name="query" id="query" size="28" value="${search}" onclick="select();"></td>
+ <td><a href="javascript:document.searchForm.submit()"><img src="<spring:theme code="searchImage"/>" alt="${search}" title="${search}"></a></td>
+ </tr></table>
+ </form>
+ </td>
+
+ <td style="padding-left:15pt;text-align:center;">
+ <p class="detail" style="line-height:1.5">
+ <a href="j_acegi_logout" target="_top"><fmt:message key="top.logout"><fmt:param value="${model.user.username}"/></fmt:message></a>
+ <c:if test="${not model.licensed}">
+ <br>
+ <a href="donate.view" target="main"><img src="<spring:theme code="donateSmallImage"/>" alt=""></a>
+ <a href="donate.view" target="main"><fmt:message key="donate.title"/></a>
+ </c:if>
+ </p>
+ </td>
+
+ <c:if test="${model.newVersionAvailable}">
+ <td style="padding-left:15pt">
+ <p class="warning">
+ <fmt:message key="top.upgrade"><fmt:param value="${model.brand}"/><fmt:param value="${model.latestVersion}"/></fmt:message>
+ </p>
+ </td>
+ </c:if>
+ </tr></table>
+ </td>
+
+</tr></table>
+
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/transcodingSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/transcodingSettings.jsp
new file mode 100644
index 00000000..a641cb53
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/transcodingSettings.jsp
@@ -0,0 +1,70 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head>
+<body class="mainframe bgcolor1">
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="transcoding"/>
+</c:import>
+
+<form method="post" action="transcodingSettings.view">
+<table class="indent">
+ <tr>
+ <th><fmt:message key="transcodingsettings.name"/></th>
+ <th><fmt:message key="transcodingsettings.sourceformat"/></th>
+ <th><fmt:message key="transcodingsettings.targetformat"/></th>
+ <th><fmt:message key="transcodingsettings.step1"/></th>
+ <th><fmt:message key="transcodingsettings.step2"/></th>
+ <th style="padding-left:1em"><fmt:message key="common.delete"/></th>
+ </tr>
+
+ <c:forEach items="${model.transcodings}" var="transcoding">
+ <tr>
+ <td><input style="font-family:monospace" type="text" name="name[${transcoding.id}]" size="10" value="${transcoding.name}"/></td>
+ <td><input style="font-family:monospace" type="text" name="sourceFormats[${transcoding.id}]" size="36" value="${transcoding.sourceFormats}"/></td>
+ <td><input style="font-family:monospace" type="text" name="targetFormat[${transcoding.id}]" size="10" value="${transcoding.targetFormat}"/></td>
+ <td><input style="font-family:monospace" type="text" name="step1[${transcoding.id}]" size="60" value="${transcoding.step1}"/></td>
+ <td><input style="font-family:monospace" type="text" name="step2[${transcoding.id}]" size="22" value="${transcoding.step2}"/></td>
+ <td align="center" style="padding-left:1em"><input type="checkbox" name="delete[${transcoding.id}]" class="checkbox"/></td>
+ </tr>
+ </c:forEach>
+
+ <tr>
+ <th colspan="6" align="left" style="padding-top:1em"><fmt:message key="transcodingsettings.add"/></th>
+ </tr>
+
+ <tr>
+ <td><input style="font-family:monospace" type="text" name="name" size="10" value="${model.newTranscoding.name}"/></td>
+ <td><input style="font-family:monospace" type="text" name="sourceFormats" size="36" value="${model.newTranscoding.sourceFormats}"/></td>
+ <td><input style="font-family:monospace" type="text" name="targetFormat" size="10" value="${model.newTranscoding.targetFormat}"/></td>
+ <td><input style="font-family:monospace" type="text" name="step1" size="60" value="${model.newTranscoding.step1}"/></td>
+ <td><input style="font-family:monospace" type="text" name="step2" size="22" value="${model.newTranscoding.step2}"/></td>
+ <td/>
+ </tr>
+
+ <tr>
+ <td colspan="6" style="padding-top:0.1em">
+ <input type="checkbox" id="defaultActive" name="defaultActive" class="checkbox" checked/>
+ <label for="defaultActive"><fmt:message key="transcodingsettings.defaultactive"/></label>
+ </td>
+ </tr>
+</table>
+
+ <p style="padding-top:0.75em">
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'" style="margin-right:1.3em">
+ <a href="http://www.subsonic.org/pages/transcoding.jsp" target="_blank"><fmt:message key="transcodingsettings.recommended"/></a>
+ </p>
+
+</form>
+
+<c:if test="${not empty model.error}">
+ <p class="warning"><fmt:message key="${model.error}"/></p>
+</c:if>
+
+<div style="width:60%">
+ <fmt:message key="transcodingsettings.info"><fmt:param value="${model.transcodeDirectory}"/><fmt:param value="${model.brand}"/></fmt:message>
+</div>
+</body></html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/upload.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/upload.jsp
new file mode 100644
index 00000000..eb79d17c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/upload.jsp
@@ -0,0 +1,29 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+</head><body>
+
+<h1><fmt:message key="upload.title"/></h1>
+
+<c:forEach items="${model.uploadedFiles}" var="file">
+ <p><fmt:message key="upload.success"><fmt:param value="${file.path}"/></fmt:message></p>
+</c:forEach>
+
+<c:forEach items="${model.unzippedFiles}" var="file">
+ <fmt:message key="upload.unzipped"><fmt:param value="${file.path}"/></fmt:message><br/>
+</c:forEach>
+
+<c:choose>
+ <c:when test="${not empty model.exception}">
+ <p><fmt:message key="upload.failed"><fmt:param value="${model.exception.message}"/></fmt:message></p>
+ </c:when>
+ <c:when test="${empty model.uploadedFiles}">
+ <p><fmt:message key="upload.empty"/></p>
+ </c:when>
+</c:choose>
+
+<div class="back"><a href="more.view?"><fmt:message key="common.back"/></a></div>
+</body></html>
+
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/userSettings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/userSettings.jsp
new file mode 100644
index 00000000..a26c9113
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/userSettings.jsp
@@ -0,0 +1,201 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<html><head>
+ <%@ include file="head.jsp" %>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+</head>
+
+<body class="mainframe bgcolor1" onload="enablePasswordChangeFields();">
+<script type="text/javascript" src="<c:url value="/script/wz_tooltip.js"/>"></script>
+<script type="text/javascript" src="<c:url value="/script/tip_balloon.js"/>"></script>
+
+<c:import url="settingsHeader.jsp">
+ <c:param name="cat" value="user"/>
+</c:import>
+
+<script type="text/javascript" language="javascript">
+ function enablePasswordChangeFields() {
+ var changePasswordCheckbox = $("passwordChange");
+ var ldapCheckbox = $("ldapAuthenticated");
+ var passwordChangeTable = $("passwordChangeTable");
+ var passwordChangeCheckboxTable = $("passwordChangeCheckboxTable");
+
+ if (changePasswordCheckbox && changePasswordCheckbox.checked && (ldapCheckbox == null || !ldapCheckbox.checked)) {
+ passwordChangeTable.show();
+ } else {
+ passwordChangeTable.hide();
+ }
+
+ if (changePasswordCheckbox) {
+ if (ldapCheckbox && ldapCheckbox.checked) {
+ passwordChangeCheckboxTable.hide();
+ } else {
+ passwordChangeCheckboxTable.show();
+ }
+ }
+ }
+</script>
+
+<table class="indent">
+ <tr>
+ <td><b><fmt:message key="usersettings.title"/></b></td>
+ <td>
+ <select name="username" onchange="location='userSettings.view?userIndex=' + (selectedIndex - 1);">
+ <option value="">-- <fmt:message key="usersettings.newuser"/> --</option>
+ <c:forEach items="${command.users}" var="user">
+ <option ${user.username eq command.username ? "selected" : ""}
+ value="${user.username}">${user.username}</option>
+ </c:forEach>
+ </select>
+ </td>
+ </tr>
+</table>
+
+<p/>
+
+<form:form method="post" action="userSettings.view" commandName="command">
+ <c:if test="${not command.admin}">
+ <table>
+ <tr>
+ <td><form:checkbox path="adminRole" id="admin" cssClass="checkbox"/></td>
+ <td><label for="admin"><fmt:message key="usersettings.admin"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="settingsRole" id="settings" cssClass="checkbox"/></td>
+ <td><label for="settings"><fmt:message key="usersettings.settings"/></label></td>
+ </tr>
+ <tr>
+ <td style="padding-top:1em"><form:checkbox path="streamRole" id="stream" cssClass="checkbox"/></td>
+ <td style="padding-top:1em"><label for="stream"><fmt:message key="usersettings.stream"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="jukeboxRole" id="jukebox" cssClass="checkbox"/></td>
+ <td><label for="jukebox"><fmt:message key="usersettings.jukebox"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="downloadRole" id="download" cssClass="checkbox"/></td>
+ <td><label for="download"><fmt:message key="usersettings.download"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="uploadRole" id="upload" cssClass="checkbox"/></td>
+ <td><label for="upload"><fmt:message key="usersettings.upload"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="shareRole" id="share" cssClass="checkbox"/></td>
+ <td><label for="share"><fmt:message key="usersettings.share"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="coverArtRole" id="coverArt" cssClass="checkbox"/></td>
+ <td><label for="coverArt"><fmt:message key="usersettings.coverart"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="commentRole" id="comment" cssClass="checkbox"/></td>
+ <td><label for="comment"><fmt:message key="usersettings.comment"/></label></td>
+ </tr>
+ <tr>
+ <td><form:checkbox path="podcastRole" id="podcast" cssClass="checkbox"/></td>
+ <td><label for="podcast"><fmt:message key="usersettings.podcast"/></label></td>
+ </tr>
+ </table>
+ </c:if>
+
+ <table class="indent">
+ <tr>
+ <td><fmt:message key="playersettings.maxbitrate"/></td>
+ <td>
+ <form:select path="transcodeSchemeName" cssStyle="width:8em">
+ <c:forEach items="${command.transcodeSchemeHolders}" var="transcodeSchemeHolder">
+ <form:option value="${transcodeSchemeHolder.name}" label="${transcodeSchemeHolder.description}"/>
+ </c:forEach>
+ </form:select>
+ </td>
+ <td><c:import url="helpToolTip.jsp"><c:param name="topic" value="transcode"/></c:import></td>
+ <c:if test="${not command.transcodingSupported}">
+ <td class="warning"><fmt:message key="playersettings.nolame"/></td>
+ </c:if>
+ </tr>
+ </table>
+
+ <c:if test="${not command.new and not command.admin}">
+ <table class="indent">
+ <tr>
+ <td><form:checkbox path="delete" id="delete" cssClass="checkbox"/></td>
+ <td><label for="delete"><fmt:message key="usersettings.delete"/></label></td>
+ </tr>
+ </table>
+ </c:if>
+
+ <c:if test="${command.ldapEnabled and not command.admin}">
+ <table>
+ <tr>
+ <td><form:checkbox path="ldapAuthenticated" id="ldapAuthenticated" cssClass="checkbox" onclick="javascript:enablePasswordChangeFields()"/></td>
+ <td><label for="ldapAuthenticated"><fmt:message key="usersettings.ldap"/></label></td>
+ <td><c:import url="helpToolTip.jsp"><c:param name="topic" value="ldap"/></c:import></td>
+ </tr>
+ </table>
+ </c:if>
+
+ <c:choose>
+ <c:when test="${command.new}">
+
+ <table class="indent">
+ <tr>
+ <td><fmt:message key="usersettings.username"/></td>
+ <td><form:input path="username"/></td>
+ <td class="warning"><form:errors path="username"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="usersettings.email"/></td>
+ <td><form:input path="email"/></td>
+ <td class="warning"><form:errors path="email"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="usersettings.password"/></td>
+ <td><form:password path="password"/></td>
+ <td class="warning"><form:errors path="password"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="usersettings.confirmpassword"/></td>
+ <td><form:password path="confirmPassword"/></td>
+ <td/>
+ </tr>
+ </table>
+ </c:when>
+
+ <c:otherwise>
+ <table id="passwordChangeCheckboxTable">
+ <tr>
+ <td><form:checkbox path="passwordChange" id="passwordChange" onclick="enablePasswordChangeFields();" cssClass="checkbox"/></td>
+ <td><label for="passwordChange"><fmt:message key="usersettings.changepassword"/></label></td>
+ </tr>
+ </table>
+
+ <table id="passwordChangeTable" style="display:none">
+ <tr>
+ <td><fmt:message key="usersettings.newpassword"/></td>
+ <td><form:password path="password" id="path"/></td>
+ <td class="warning"><form:errors path="password"/></td>
+ </tr>
+ <tr>
+ <td><fmt:message key="usersettings.confirmpassword"/></td>
+ <td><form:password path="confirmPassword" id="confirmPassword"/></td>
+ <td/>
+ </tr>
+ </table>
+
+ <table>
+ <tr>
+ <td><fmt:message key="usersettings.email"/></td>
+ <td><form:input path="email"/></td>
+ <td class="warning"><form:errors path="email"/></td>
+ </tr>
+ </table>
+ </c:otherwise>
+ </c:choose>
+
+ <input type="submit" value="<fmt:message key="common.save"/>" style="margin-top:1.5em;margin-right:0.3em">
+ <input type="button" value="<fmt:message key="common.cancel"/>" onclick="location.href='nowPlaying.view'" style="margin-top:1.5em">
+</form:form>
+
+</body></html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/videoPlayer.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/videoPlayer.jsp
new file mode 100644
index 00000000..681a41a8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/videoPlayer.jsp
@@ -0,0 +1,190 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<html>
+<head>
+ <%@ include file="head.jsp" %>
+
+ <sub:url value="videoPlayer.view" var="baseUrl"><sub:param name="id" value="${model.video.id}"/></sub:url>
+ <sub:url value="main.view" var="backUrl"><sub:param name="id" value="${model.video.id}"/></sub:url>
+
+ <sub:url value="/stream" var="streamUrl">
+ <sub:param name="id" value="${model.video.id}"/>
+ </sub:url>
+
+ <script type="text/javascript" src="<c:url value="/script/swfobject.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/prototype.js"/>"></script>
+ <script type="text/javascript" src="<c:url value="/script/scripts.js"/>"></script>
+ <script type="text/javascript" language="javascript">
+
+ var player;
+ var position;
+ var maxBitRate = ${model.maxBitRate};
+ var timeOffset = ${model.timeOffset};
+
+ function init() {
+
+ var flashvars = {
+ id:"player1",
+ skin:"<c:url value="/flash/whotube.zip"/>",
+// plugins:"metaviewer-1",
+ screencolor:"000000",
+ controlbar:"over",
+ autostart:"false",
+ bufferlength:3,
+ backcolor:"<spring:theme code="backgroundColor"/>",
+ frontcolor:"<spring:theme code="textColor"/>",
+ provider:"video"
+ };
+ var params = {
+ allowfullscreen:"true",
+ allowscriptaccess:"always"
+ };
+ var attributes = {
+ id:"player1",
+ name:"player1"
+ };
+
+ var width = "${model.popout ? '100%' : '600'}";
+ var height = "${model.popout ? '85%' : '360'}";
+ swfobject.embedSWF("<c:url value="/flash/jw-player-5.6.swf"/>", "placeholder1", width, height, "9.0.0", false, flashvars, params, attributes);
+ }
+
+ function playerReady(thePlayer) {
+ player = $("player1");
+ player.addModelListener("TIME", "timeListener");
+
+ <c:if test="${not (model.trial and model.trialExpired)}">
+ play();
+ </c:if>
+ }
+
+ function play() {
+ var list = new Array();
+ list[0] = {
+ file:"${streamUrl}&maxBitRate=" + maxBitRate + "&timeOffset=" + timeOffset + "&player=${model.player}",
+ duration:${model.duration} - timeOffset,
+ provider:"video"
+ };
+ player.sendEvent("LOAD", list);
+ player.sendEvent("PLAY");
+ }
+
+ function timeListener(obj) {
+ var newPosition = Math.round(obj.position);
+ if (newPosition != position) {
+ position = newPosition;
+ updatePosition();
+ }
+ }
+
+ function updatePosition() {
+ var pos = getPosition();
+
+ var minutes = Math.round(pos / 60);
+ var seconds = pos % 60;
+
+ var result = minutes + ":";
+ if (seconds < 10) {
+ result += "0";
+ }
+ result += seconds;
+ $("position").innerHTML = result;
+ }
+
+ function changeTimeOffset() {
+ timeOffset = $("timeOffset").getValue();
+ play();
+ }
+
+ function changeBitRate() {
+ maxBitRate = $("maxBitRate").getValue();
+ timeOffset = getPosition();
+ play();
+ }
+
+ function popout() {
+ var url = "${baseUrl}&maxBitRate=" + maxBitRate + "&timeOffset=" + getPosition() + "&popout=true";
+ popupSize(url, "video", 600, 400);
+ window.location.href = "${backUrl}";
+ }
+
+ function popin() {
+ window.opener.location.href = "${baseUrl}&maxBitRate=" + maxBitRate + "&timeOffset=" + getPosition();
+ window.close();
+ }
+
+ function getPosition() {
+ return parseInt(timeOffset) + parseInt(position);
+ }
+
+ </script>
+</head>
+
+<body class="mainframe bgcolor1" style="padding-bottom:0.5em" onload="init();">
+<c:if test="${not model.popout}">
+ <h1>${model.video.title}</h1>
+</c:if>
+
+<c:if test="${model.trial}">
+ <fmt:formatDate value="${model.trialExpires}" dateStyle="long" var="expiryDate"/>
+
+ <p class="warning" style="padding-top:1em">
+ <c:choose>
+ <c:when test="${model.trialExpired}">
+ <fmt:message key="networksettings.trialexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
+ </c:when>
+ <c:otherwise>
+ <fmt:message
+ key="networksettings.trialnotexpired"><fmt:param>${expiryDate}</fmt:param></fmt:message>
+ </c:otherwise>
+ </c:choose>
+ </p>
+</c:if>
+
+
+<div id="wrapper" style="padding-top:1em">
+ <div id="placeholder1"><a href="http://www.adobe.com/go/getflashplayer" target="_blank"><fmt:message key="playlist.getflash"/></a></div>
+</div>
+
+<div style="padding-top:0.7em;padding-bottom:0.7em">
+
+ <span id="position" style="padding-right:0.5em">0:00</span>
+ <select id="timeOffset" onchange="changeTimeOffset();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em">
+ <c:forEach items="${model.skipOffsets}" var="skipOffset">
+ <c:choose>
+ <c:when test="${skipOffset.value - skipOffset.value mod 60 eq model.timeOffset - model.timeOffset mod 60}">
+ <option selected="selected" value="${skipOffset.value}">${skipOffset.key}</option>
+ </c:when>
+ <c:otherwise>
+ <option value="${skipOffset.value}">${skipOffset.key}</option>
+ </c:otherwise>
+ </c:choose>
+ </c:forEach>
+ </select>
+
+ <select id="maxBitRate" onchange="changeBitRate();" style="padding-left:0.25em;padding-right:0.25em;margin-right:0.5em">
+ <c:forEach items="${model.bitRates}" var="bitRate">
+ <c:choose>
+ <c:when test="${bitRate eq model.maxBitRate}">
+ <option selected="selected" value="${bitRate}">${bitRate} Kbps</option>
+ </c:when>
+ <c:otherwise>
+ <option value="${bitRate}">${bitRate} Kbps</option>
+ </c:otherwise>
+ </c:choose>
+ </c:forEach>
+ </select>
+</div>
+
+<c:choose>
+ <c:when test="${model.popout}">
+ <div class="back"><a href="javascript:popin();"><fmt:message key="common.back"/></a></div>
+ </c:when>
+ <c:otherwise>
+ <div class="back" style="float:left;padding-right:2em"><a href="${backUrl}"><fmt:message key="common.back"/></a></div>
+ <div class="forward" style="float:left;"><a href="javascript:popout();"><fmt:message key="videoPlayer.popout"/></a></div>
+ </c:otherwise>
+</c:choose>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/browse.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/browse.jsp
new file mode 100644
index 00000000..ac1ce096
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/browse.jsp
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+
+ <card id="main" title="Subsonic" newcontext="false">
+
+ <p><small><b>
+
+ <sub:url value="/wap/playlist.view" var="playUrl">
+ <sub:param name="play" value="${model.parent.path}"/>
+ </sub:url>
+ <sub:url value="/wap/playlist.view" var="addUrl">
+ <sub:param name="add" value="${model.parent.path}"/>
+ </sub:url>
+ <sub:url value="/wap/download.view" var="downloadUrl">
+ <sub:param name="path" value="${model.parent.path}"/>
+ </sub:url>
+
+ <c:choose>
+ <c:when test="${fn:length(model.children) eq 1 and model.children[0].file}">
+ <a href="${playUrl}">[<fmt:message key="wap.browse.playone"/>]</a><br/>
+ <a href="${addUrl}">[<fmt:message key="wap.browse.addone"/>]</a><br/>
+ <c:if test="${model.user.downloadRole}">
+ <a href="${downloadUrl}">[<fmt:message key="wap.browse.downloadone"/>]</a><br/>
+ </c:if>
+ </c:when>
+ <c:otherwise>
+ <a href="${playUrl}">[<fmt:message key="wap.browse.playall"/>]</a><br/>
+ <a href="${addUrl}">[<fmt:message key="wap.browse.addall"/>]</a><br/>
+ <c:if test="${model.user.downloadRole}">
+ <a href="${downloadUrl}">[<fmt:message key="wap.browse.downloadall"/>]</a><br/>
+ </c:if>
+ </c:otherwise>
+ </c:choose>
+
+ <a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/>
+ </b></small></p>
+
+ <p><small>
+
+ <c:forEach items="${model.children}" var="child">
+ <sub:url value="/wap/browse.view" var="browseUrl">
+ <sub:param name="path" value="${child.path}"/>
+ </sub:url>
+ <a href="${browseUrl}">${fn:escapeXml(child.title)}</a><br/>
+ </c:forEach>
+
+ </small></p>
+ </card>
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/head.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/head.jsp
new file mode 100644
index 00000000..d902de1d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/head.jsp
@@ -0,0 +1,10 @@
+<%@ include file="../include.jsp" %>
+
+<head>
+ <meta http-equiv="Cache-Control" content="max-age=0" forua="true"/>
+ <meta http-equiv="Cache-Control" content="must-revalidate" forua="true"/>
+</head>
+
+<template>
+ <do type="prev" name="back" label="<fmt:message key="common.back"/>"><prev/></do>
+</template>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/index.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/index.jsp
new file mode 100644
index 00000000..2773a55e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/index.jsp
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+
+ <card id="main" title="Subsonic" newcontext="false">
+ <p>
+ <small>
+
+ <c:choose>
+ <c:when test="${empty model.artists}">
+
+ <b>
+ <a href="<c:url value="/wap/playlist.view"/>">[<fmt:message key="wap.index.playlist"/>]</a>
+ </b>
+ <br/>
+ <b>
+ <a href="<c:url value="/wap/search.view"/>">[<fmt:message key="wap.index.search"/>]</a>
+ </b>
+ <br/>
+ <b>
+ <a href="<c:url value="/wap/settings.view"/>">[<fmt:message key="wap.index.settings"/>]</a>
+ </b>
+ <br/>
+ </small>
+ </p>
+ <p>
+ <small>
+ <c:forEach items="${model.indexes}" var="index">
+ <sub:url var="url" value="/wap/index.view">
+ <sub:param name="index" value="${index.index}"/>
+ </sub:url>
+ <a href="${url}">${index.index}</a>
+ </c:forEach>
+ </c:when>
+
+ <c:otherwise>
+ <c:forEach items="${model.artists}" var="artist">
+ <c:forEach items="${artist.musicFiles}" var="mediaFile">
+ <sub:url var="url" value="/wap/browse.view">
+ <sub:param name="path" value="${mediaFile.path}"/>
+ </sub:url>
+ <a href="${url}">${fn:escapeXml(mediaFile.title)}</a>
+ <br/>
+ </c:forEach>
+ </c:forEach>
+ </c:otherwise>
+ </c:choose>
+
+ <c:if test="${model.noMusic}">
+ <fmt:message key="wap.index.missing"/>
+ </c:if>
+
+ </small>
+ </p>
+ </card>
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/loadPlaylist.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/loadPlaylist.jsp
new file mode 100644
index 00000000..2640cce0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/loadPlaylist.jsp
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+
+ <card id="main" title="Subsonic" newcontext="false">
+ <p><small>
+
+ <c:forEach items="${model.playlists}" var="playlist">
+ <sub:url var="url" value="/wap/playlist.view">
+ <sub:param name="load" value="${playlist.id}"/>
+ </sub:url>
+ <b><a href="${url}">${fn:escapeXml(playlist.name)}</a></b><br/>
+ </c:forEach>
+ </small></p>
+
+ </card>
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/playlist.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/playlist.jsp
new file mode 100644
index 00000000..481e00d3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/playlist.jsp
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+
+ <c:if test="${fn:length(model.players) eq 1}">
+ <c:choose>
+ <c:when test="${empty model.players[0].name}">
+ <c:set var="playerName" value=" - Player ${model.players[0].id}"/>
+ </c:when>
+ <c:otherwise>
+ <c:set var="playerName" value=" - ${model.players[0].name}"/>
+ </c:otherwise>
+ </c:choose>
+ </c:if>
+
+
+ <card id="main" title="Subsonic" newcontext="false">
+ <p><small><b><fmt:message key="wap.playlist.title"/>${playerName}</b></small></p>
+ <p><small>
+
+ <c:choose>
+ <c:when test="${empty model.players}">
+ <fmt:message key="wap.playlist.noplayer"/>
+ </c:when>
+ <c:otherwise>
+ <b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a></b><br/>
+ <b><a href="<c:url value="/wap/loadPlaylist.view"/>">[<fmt:message key="wap.playlist.load"/>]</a></b><br/>
+ <b><a href="<c:url value="/wap/playlist.view?random"/>">[<fmt:message key="wap.playlist.random"/>]</a></b><br/>
+
+ <c:set var="playlist" value="${model.players[0].playlist}"/>
+
+ <c:if test="${not empty playlist.files}">
+ <b><a href="<c:url value="/play.m3u"/>">[<fmt:message key="wap.playlist.play"/>]</a></b><br/>
+ <b><a href="<c:url value="/wap/playlist.view?clear"/>">[<fmt:message key="wap.playlist.clear"/>]</a></b><br/>
+ </small></p>
+ <p><small>
+
+ <c:forEach items="${playlist.files}" var="file" varStatus="loopStatus">
+ <c:set var="isCurrent" value="${(file eq playlist.currentFile) and (loopStatus.count - 1 eq playlist.index)}"/>
+ ${isCurrent ? "<b>" : ""}
+ <a href="<c:url value="/wap/playlist.view?skip=${loopStatus.count - 1}"/>">${fn:escapeXml(file.title)}</a>
+ ${isCurrent ? "</b>" : ""}
+ <br/>
+ </c:forEach>
+ </c:if>
+ </c:otherwise>
+ </c:choose>
+ </small></p>
+ </card>
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/search.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/search.jsp
new file mode 100644
index 00000000..b35b04a3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/search.jsp
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+ <%@ include file="head.jsp" %>
+ <card id="main" title="Subsonic" newcontext="false">
+ <p>
+ <input name="query" value="" size="10"/>
+ <anchor><fmt:message key="wap.search.title"/>
+ <go href="<c:url value="/wap/searchResult.view"/>" method="get">
+ <postfield name="query" value="$query"/>
+ </go>
+ </anchor>
+ </p>
+ </card>
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/searchResult.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/searchResult.jsp
new file mode 100644
index 00000000..2267c069
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/searchResult.jsp
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+ <card id="main" title="Subsonic" newcontext="false">
+ <p><small>
+
+ <c:choose>
+ <c:when test="${model.creatingIndex}">
+ <fmt:message key="wap.searchresult.index"/>
+ </c:when>
+
+ <c:otherwise>
+ <c:forEach items="${model.hits}" var="hit">
+ <sub:url var="url" value="/wap/browse.view">
+ <sub:param name="path" value="${hit.path}"/>
+ </sub:url>
+ <a href="${url}">${fn:escapeXml(hit.title)}</a><br/>
+ </c:forEach>
+ </c:otherwise>
+ </c:choose>
+ </small></p>
+ </card>
+
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/settings.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/settings.jsp
new file mode 100644
index 00000000..5c44e87d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/wap/settings.jsp
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml">
+
+<%@ page language="java" contentType="text/vnd.wap.wml; charset=utf-8" pageEncoding="iso-8859-1"%>
+
+<wml>
+
+ <%@ include file="head.jsp" %>
+ <card id="main" title="Subsonic" newcontext="false">
+ <p><small>
+ <b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/></b>
+ <b><a href="#player">[<fmt:message key="wap.settings.selectplayer"/>]</a></b>
+ </small></p>
+ </card>
+
+ <card id="player" title="Subsonic" newcontext="false">
+ <p><small>
+
+ <b><a href="<c:url value="/wap/index.view"/>">[<fmt:message key="common.home"/>]</a><br/></b>
+ </small></p><p><small>
+
+ <c:choose>
+ <c:when test="${empty model.playerId}">
+ <fmt:message key="wap.settings.allplayers"/>
+ </c:when>
+ <c:otherwise>
+ <a href="<c:url value="/wap/selectPlayer.view"/>"><fmt:message key="wap.settings.allplayers"/></a>
+ </c:otherwise>
+ </c:choose>
+ <br/>
+
+ <c:forEach items="${model.players}" var="player">
+ <c:choose>
+ <c:when test="${player.id eq model.playerId}">
+ ${player}
+ </c:when>
+ <c:otherwise>
+ <a href="<c:url value="/wap/selectPlayer.view?playerId=${player.id}"/>">${player}</a>
+ </c:otherwise>
+ </c:choose>
+ <br/>
+ </c:forEach>
+ </small></p>
+ </card>
+
+</wml>
+
diff --git a/subsonic-main/src/main/webapp/WEB-INF/jsp/xspfPlaylist.jsp b/subsonic-main/src/main/webapp/WEB-INF/jsp/xspfPlaylist.jsp
new file mode 100644
index 00000000..4ee8fb46
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/jsp/xspfPlaylist.jsp
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<%@ include file="include.jsp" %>
+<%@ page language="java" contentType="text/xml; charset=utf-8" pageEncoding="iso-8859-1" %>
+
+<playlist version="0" xmlns="http://xspf.org/ns/0/">
+ <trackList>
+
+<c:forEach var="song" items="${model.songs}">
+
+ <sub:url value="/stream" var="streamUrl">
+ <sub:param name="path" value="${song.musicFile.path}"/>
+ </sub:url>
+
+ <sub:url value="coverArt.view" var="coverArtUrl">
+ <sub:param name="size" value="200"/>
+ <c:if test="${not empty song.coverArtFile}">
+ <sub:param name="path" value="${song.coverArtFile.path}"/>
+ </c:if>
+ </sub:url>
+
+ <track>
+ <location>${streamUrl}</location>
+ <image>${coverArtUrl}</image>
+ <annotation>${song.musicFile.metaData.artist} - ${song.musicFile.title}</annotation>
+ </track>
+
+</c:forEach>
+
+ </trackList>
+</playlist> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/sub.tld b/subsonic-main/src/main/webapp/WEB-INF/sub.tld
new file mode 100644
index 00000000..ef712968
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/sub.tld
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
+ version="2.0">
+
+ <description>Subsonic tag library</description>
+ <display-name>Subsonic tag library</display-name>
+ <tlib-version>1.1</tlib-version>
+ <short-name>sub</short-name>
+ <uri>http://subsonic.org/taglib/sub</uri>
+
+ <tag>
+ <description>
+ Creates a URL with optional query parameters. Similar to 'c:url', but
+ you may specify which character encoding to use for the URL query
+ parameters. If no encoding is specified, the following steps are performed:
+ a) Parameter values are encoded as the hexadecimal representation of the UTF-8 bytes of the original string.
+ b) Parameter names are prepended with the suffix "Utf8Hex"
+ </description>
+ <name>url</name>
+ <tag-class>net.sourceforge.subsonic.taglib.UrlTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+ Name of the exported scoped variable for the
+ processed url. The type of the scoped variable is
+ String.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>URL to be processed.</description>
+ <name>value</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>The encoding to use. Default is ISO-8859-1.</description>
+ <name>encoding</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>Adds a parameter to a containing 'url' tag.</description>
+ <name>param</name>
+ <tag-class>net.sourceforge.subsonic.taglib.ParamTag</tag-class>
+ <body-content>empty</body-content>
+ <attribute>
+ <description>Name of the query string parameter.</description>
+ <name>name</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>Value of the parameter.</description>
+ <name>value</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Converts a byte-count to a formatted string suitable for display to the user, with respect
+ to the current locale.
+ </description>
+ <name>formatBytes</name>
+ <tag-class>net.sourceforge.subsonic.taglib.FormatBytesTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>The byte count.</description>
+ <name>bytes</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Renders a Wiki text with markup to HTML, using the Radeox render engine.
+ </description>
+ <name>wiki</name>
+ <tag-class>net.sourceforge.subsonic.taglib.WikiTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>The Wiki markup text.</description>
+ <name>text</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Escapes the characters in a string using JavaScript rules.
+ </description>
+ <name>escapeJavaScript</name>
+ <tag-class>net.sourceforge.subsonic.taglib.EscapeJavaScriptTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>The string to escape.</description>
+ <name>string</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+</taglib> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/WEB-INF/subsonic-servlet.xml b/subsonic-main/src/main/webapp/WEB-INF/subsonic-servlet.xml
new file mode 100644
index 00000000..b36bb0c8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/subsonic-servlet.xml
@@ -0,0 +1,479 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="leftController" class="net.sourceforge.subsonic.controller.LeftController">
+ <property name="viewName" value="left"/>
+ <property name="mediaScannerService" ref="mediaScannerService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="musicIndexService" ref="musicIndexService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="playlistService" ref="playlistService"/>
+ </bean>
+ <bean id="rightController" class="net.sourceforge.subsonic.controller.RightController">
+ <property name="viewName" value="right"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="statusController" class="net.sourceforge.subsonic.controller.StatusController">
+ <property name="viewName" value="status"/>
+ <property name="statusService" ref="statusService"/>
+ </bean>
+ <bean id="mainController" class="net.sourceforge.subsonic.controller.MainController">
+ <property name="viewName" value="main"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="ratingService" ref="musicInfoService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="adService" ref="adService"/>
+ </bean>
+ <bean id="playlistController" class="net.sourceforge.subsonic.controller.PlaylistController">
+ <property name="viewName" value="playlist"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="importPlaylistController" class="net.sourceforge.subsonic.controller.ImportPlaylistController">
+ <property name="viewName" value="importPlaylist"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playlistService" ref="playlistService"/>
+ </bean>
+ <bean id="topController" class="net.sourceforge.subsonic.controller.TopController">
+ <property name="viewName" value="top"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="versionService" ref="versionService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="helpController" class="net.sourceforge.subsonic.controller.HelpController">
+ <property name="viewName" value="help"/>
+ <property name="versionService" ref="versionService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="moreController" class="net.sourceforge.subsonic.controller.MoreController">
+ <property name="viewName" value="more"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="playerService" ref="playerService"/>
+ </bean>
+ <bean id="uploadController" class="net.sourceforge.subsonic.controller.UploadController">
+ <property name="viewName" value="upload"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="lyricsController" class="net.sourceforge.subsonic.controller.LyricsController">
+ <property name="viewName" value="lyrics"/>
+ </bean>
+ <bean id="allmusicController" class="net.sourceforge.subsonic.controller.AllmusicController">
+ <property name="viewName" value="allmusic"/>
+ </bean>
+ <bean id="podcastController" class="net.sourceforge.subsonic.controller.PodcastController">
+ <property name="viewName" value="podcast"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="podcastReceiverController" class="net.sourceforge.subsonic.controller.PodcastReceiverController">
+ <property name="viewName" value="podcastReceiver"/>
+ <property name="podcastService" ref="podcastService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="podcastReceiverAdminController"
+ class="net.sourceforge.subsonic.controller.PodcastReceiverAdminController">
+ <property name="podcastService" ref="podcastService"/>
+ </bean>
+ <bean id="setMusicFileInfoController" class="net.sourceforge.subsonic.controller.SetMusicFileInfoController">
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="shareManagementController" class="net.sourceforge.subsonic.controller.ShareManagementController">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="shareService" ref="shareService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="setRatingController" class="net.sourceforge.subsonic.controller.SetRatingController">
+ <property name="ratingService" ref="musicInfoService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="randomPlayQueueController" class="net.sourceforge.subsonic.controller.RandomPlayQueueController">
+ <property name="viewName" value="reload"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="reloadFrames">
+ <list>
+ <bean class="net.sourceforge.subsonic.controller.ReloadFrame">
+ <property name="frame" value="playQueue"/>
+ <property name="view" value="playQueue.view?"/>
+ </bean>
+ <bean class="net.sourceforge.subsonic.controller.ReloadFrame">
+ <property name="frame" value="main"/>
+ <property name="view" value="more.view"/>
+ </bean>
+ </list>
+ </property>
+ </bean>
+ <bean id="changeCoverArtController" class="net.sourceforge.subsonic.controller.ChangeCoverArtController">
+ <property name="viewName" value="changeCoverArt"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="nowPlayingController" class="net.sourceforge.subsonic.controller.NowPlayingController">
+ <property name="playerService" ref="playerService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="starredController" class="net.sourceforge.subsonic.controller.StarredController">
+ <property name="viewName" value="starred"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="searchController" class="net.sourceforge.subsonic.controller.SearchController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.SearchCommand"/>
+ <property name="successView" value="search"/>
+ <property name="formView" value="search"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="playerService" ref="playerService"/>
+ </bean>
+ <bean id="settingsController" class="net.sourceforge.subsonic.controller.SettingsController">
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="playerSettingsController" class="net.sourceforge.subsonic.controller.PlayerSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.PlayerSettingsCommand"/>
+ <property name="successView" value="playerSettings"/>
+ <property name="formView" value="playerSettings"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ </bean>
+ <bean id="shareSettingsController" class="net.sourceforge.subsonic.controller.ShareSettingsController">
+ <property name="viewName" value="shareSettings"/>
+ <property name="shareService" ref="shareService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="musicFolderSettingsController" class="net.sourceforge.subsonic.controller.MusicFolderSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.MusicFolderSettingsCommand"/>
+ <property name="successView" value="musicFolderSettings"/>
+ <property name="formView" value="musicFolderSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaScannerService" ref="mediaScannerService"/>
+ <property name="artistDao" ref="artistDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ <property name="mediaFolderDao" ref="mediaFileDao"/>
+ </bean>
+ <bean id="networkSettingsController" class="net.sourceforge.subsonic.controller.NetworkSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.NetworkSettingsCommand"/>
+ <property name="successView" value="networkSettings"/>
+ <property name="formView" value="networkSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="networkService" ref="networkService"/>
+ </bean>
+ <bean id="transcodingSettingsController" class="net.sourceforge.subsonic.controller.TranscodingSettingsController">
+ <property name="viewName" value="transcodingSettings"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="internetRadioSettingsController"
+ class="net.sourceforge.subsonic.controller.InternetRadioSettingsController">
+ <property name="viewName" value="internetRadioSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="podcastSettingsController" class="net.sourceforge.subsonic.controller.PodcastSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.PodcastSettingsCommand"/>
+ <property name="successView" value="podcastSettings"/>
+ <property name="formView" value="podcastSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="podcastService" ref="podcastService"/>
+ </bean>
+ <bean id="generalSettingsController" class="net.sourceforge.subsonic.controller.GeneralSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.GeneralSettingsCommand"/>
+ <property name="successView" value="generalSettings"/>
+ <property name="formView" value="generalSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="advancedSettingsController" class="net.sourceforge.subsonic.controller.AdvancedSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.AdvancedSettingsCommand"/>
+ <property name="successView" value="advancedSettings"/>
+ <property name="formView" value="advancedSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="personalSettingsController" class="net.sourceforge.subsonic.controller.PersonalSettingsController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.PersonalSettingsCommand"/>
+ <property name="successView" value="personalSettings"/>
+ <property name="formView" value="personalSettings"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="avatarUploadController" class="net.sourceforge.subsonic.controller.AvatarUploadController">
+ <property name="viewName" value="avatarUploadResult"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="userSettingsController" class="net.sourceforge.subsonic.controller.UserSettingsController">
+ <property name="sessionForm" value="true"/>
+ <property name="commandClass" value="net.sourceforge.subsonic.command.UserSettingsCommand"/>
+ <property name="validator" ref="userSettingsValidator"/>
+ <property name="successView" value="userSettings"/>
+ <property name="formView" value="userSettings"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ </bean>
+ <bean id="passwordSettingsController" class="net.sourceforge.subsonic.controller.PasswordSettingsController">
+ <property name="sessionForm" value="true"/>
+ <property name="commandClass" value="net.sourceforge.subsonic.command.PasswordSettingsCommand"/>
+ <property name="validator" ref="passwordSettingsValidator"/>
+ <property name="successView" value="passwordSettings"/>
+ <property name="formView" value="passwordSettings"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="homeController" class="net.sourceforge.subsonic.controller.HomeController">
+ <property name="viewName" value="home"/>
+ <property name="ratingService" ref="musicInfoService"/>
+ <property name="mediaScannerService" ref="mediaScannerService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="editTagsController" class="net.sourceforge.subsonic.controller.EditTagsController">
+ <property name="viewName" value="editTags"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="metaDataParserFactory" ref="metaDataParserFactory"/>
+ </bean>
+ <bean id="playQueueController" class="net.sourceforge.subsonic.controller.PlayQueueController">
+ <property name="viewName" value="playQueue"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="coverArtController" class="net.sourceforge.subsonic.controller.CoverArtController">
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="artistDao" ref="artistDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ </bean>
+ <bean id="avatarController" class="net.sourceforge.subsonic.controller.AvatarController">
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="proxyController" class="net.sourceforge.subsonic.controller.ProxyController"/>
+ <bean id="statusChartController" class="net.sourceforge.subsonic.controller.StatusChartController">
+ <property name="statusService" ref="statusService"/>
+ </bean>
+ <bean id="userChartController" class="net.sourceforge.subsonic.controller.UserChartController">
+ <property name="securityService" ref="securityService"/>
+ </bean>
+ <bean id="m3uController" class="net.sourceforge.subsonic.controller.M3UController">
+ <property name="playerService" ref="playerService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ </bean>
+ <bean id="streamController" class="net.sourceforge.subsonic.controller.StreamController">
+ <property name="playerService" ref="playerService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ <property name="audioScrobblerService" ref="audioScrobblerService"/>
+ </bean>
+ <bean id="videoPlayerController" class="net.sourceforge.subsonic.controller.VideoPlayerController">
+ <property name="viewName" value="videoPlayer"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="playerService" ref="playerService"/>
+ </bean>
+ <bean id="externalPlayerController" class="net.sourceforge.subsonic.controller.ExternalPlayerController">
+ <property name="viewName" value="externalPlayer"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="shareDao" ref="shareDao"/>
+ </bean>
+ <bean id="downloadController" class="net.sourceforge.subsonic.controller.DownloadController">
+ <property name="playerService" ref="playerService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ </bean>
+ <bean id="donateController" class="net.sourceforge.subsonic.controller.DonateController">
+ <property name="commandClass" value="net.sourceforge.subsonic.command.DonateCommand"/>
+ <property name="successView" value="donate"/>
+ <property name="formView" value="donate"/>
+ <property name="validator" ref="donateValidator"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="multiController" class="net.sourceforge.subsonic.controller.MultiController">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ <property name="playlistService" ref="playlistService"/>
+ </bean>
+ <bean id="wapController" class="net.sourceforge.subsonic.controller.WapController">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="musicIndexService" ref="musicIndexService"/>
+ </bean>
+ <bean id="restController" class="net.sourceforge.subsonic.controller.RESTController">
+ <property name="settingsService" ref="settingsService"/>
+ <property name="securityService" ref="securityService"/>
+ <property name="playerService" ref="playerService"/>
+ <property name="mediaFileService" ref="mediaFileService"/>
+ <property name="transcodingService" ref="transcodingService"/>
+ <property name="statusService" ref="statusService"/>
+ <property name="searchService" ref="searchService"/>
+ <property name="jukeboxService" ref="jukeboxService"/>
+ <property name="audioScrobblerService" ref="audioScrobblerService"/>
+ <property name="playlistService" ref="playlistService"/>
+ <property name="playQueueService" ref="ajaxPlayQueueService"/>
+ <property name="ratingService" ref="musicInfoService"/>
+ <property name="chatService" ref="ajaxChatService"/>
+ <property name="lyricsService" ref="ajaxLyricsService"/>
+ <property name="podcastService" ref="podcastService"/>
+ <property name="shareService" ref="shareService"/>
+ <property name="mediaFileDao" ref="mediaFileDao"/>
+ <property name="artistDao" ref="artistDao"/>
+ <property name="albumDao" ref="albumDao"/>
+ <property name="downloadController" ref="downloadController"/>
+ <property name="streamController" ref="streamController"/>
+ <property name="coverArtController" ref="coverArtController"/>
+ <property name="avatarController" ref="avatarController"/>
+ <property name="userSettingsController" ref="userSettingsController"/>
+ <property name="leftController" ref="leftController"/>
+ <property name="homeController" ref="homeController"/>
+ </bean>
+ <bean id="dbController" class="net.sourceforge.subsonic.controller.DBController">
+ <property name="viewName" value="db"/>
+ <property name="daoHelper" ref="daoHelper"/>
+ </bean>
+ <bean id="donateValidator" class="net.sourceforge.subsonic.validator.DonateValidator">
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="userSettingsValidator" class="net.sourceforge.subsonic.validator.UserSettingsValidator">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+ <bean id="passwordSettingsValidator" class="net.sourceforge.subsonic.validator.PasswordSettingsValidator"/>
+
+ <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
+ <property name="alwaysUseFullPath" value="true"/>
+ <property name="mappings">
+ <props>
+ <prop key="/main.view">mainController</prop>
+ <prop key="/playlist.view">playlistController</prop>
+ <prop key="/help.view">helpController</prop>
+ <prop key="/lyrics.view">lyricsController</prop>
+ <prop key="/left.view">leftController</prop>
+ <prop key="/right.view">rightController</prop>
+ <prop key="/status.view">statusController</prop>
+ <prop key="/more.view">moreController</prop>
+ <prop key="/upload.view">uploadController</prop>
+ <prop key="/importPlaylist.view">importPlaylistController</prop>
+ <prop key="/exportPlaylist.view">multiController</prop>
+ <prop key="/setMusicFileInfo.view">setMusicFileInfoController</prop>
+ <prop key="/createShare.view">shareManagementController</prop>
+ <prop key="/setRating.view">setRatingController</prop>
+ <prop key="/top.view">topController</prop>
+ <prop key="/randomPlayQueue.view">randomPlayQueueController</prop>
+ <prop key="/changeCoverArt.view">changeCoverArtController</prop>
+ <prop key="/login.view">multiController</prop>
+ <prop key="/recover.view">multiController</prop>
+ <prop key="/accessDenied.view">multiController</prop>
+ <prop key="/notFound.view">multiController</prop>
+ <prop key="/gettingStarted.view">multiController</prop>
+ <prop key="/index.view">multiController</prop>
+ <prop key="/videoPlayer.view">videoPlayerController</prop>
+ <prop key="/nowPlaying.view">nowPlayingController</prop>
+ <prop key="/starred.view">starredController</prop>
+ <prop key="/search.view">searchController</prop>
+ <prop key="/settings.view">settingsController</prop>
+ <prop key="/playerSettings.view">playerSettingsController</prop>
+ <prop key="/shareSettings.view">shareSettingsController</prop>
+ <prop key="/musicFolderSettings.view">musicFolderSettingsController</prop>
+ <prop key="/networkSettings.view">networkSettingsController</prop>
+ <prop key="/transcodingSettings.view">transcodingSettingsController</prop>
+ <prop key="/internetRadioSettings.view">internetRadioSettingsController</prop>
+ <prop key="/podcastSettings.view">podcastSettingsController</prop>
+ <prop key="/generalSettings.view">generalSettingsController</prop>
+ <prop key="/advancedSettings.view">advancedSettingsController</prop>
+ <prop key="/personalSettings.view">personalSettingsController</prop>
+ <prop key="/avatarUpload.view">avatarUploadController</prop>
+ <prop key="/userSettings.view">userSettingsController</prop>
+ <prop key="/passwordSettings.view">passwordSettingsController</prop>
+ <prop key="/allmusic.view">allmusicController</prop>
+ <prop key="/home.view">homeController</prop>
+ <prop key="/editTags.view">editTagsController</prop>
+ <prop key="/playQueue.view">playQueueController</prop>
+ <prop key="/coverArt.view">coverArtController</prop>
+ <prop key="/avatar.view">avatarController</prop>
+ <prop key="/proxy.view">proxyController</prop>
+ <prop key="/statusChart.view">statusChartController</prop>
+ <prop key="/userChart.view">userChartController</prop>
+ <prop key="/download.view">downloadController</prop>
+ <prop key="/donate.view">donateController</prop>
+ <prop key="/db.view">dbController</prop>
+ <prop key="/test.view">multiController</prop>
+ <prop key="/podcastReceiver.view">podcastReceiverController</prop>
+ <prop key="/podcastReceiverAdmin.view">podcastReceiverAdminController</prop>
+ <prop key="/podcast.view">podcastController</prop>
+ <prop key="/podcast/**">podcastController</prop>
+ <prop key="/wap/download.view">downloadController</prop>
+ <prop key="/wap/**">wapController</prop>
+ <prop key="/rest/**">restController</prop>
+ <prop key="/play.m3u">m3uController</prop>
+ <prop key="/stream/**">streamController</prop>
+ <prop key="/share/**">externalPlayerController</prop>
+ </props>
+ </property>
+ </bean>
+
+ <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
+ <property name="basename" value="net.sourceforge.subsonic.i18n.ResourceBundle"/>
+ </bean>
+
+ <bean id="themeSource" class="net.sourceforge.subsonic.theme.SubsonicThemeSource">
+ <property name="basenamePrefix" value="net.sourceforge.subsonic.theme."/>
+ <property name="defaultResourceBundle" value="net.sourceforge.subsonic.theme.default"/>
+ </bean>
+
+ <bean id="localeResolver"
+ class="net.sourceforge.subsonic.i18n.SubsonicLocaleResolver">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="themeResolver"
+ class="net.sourceforge.subsonic.theme.SubsonicThemeResolver">
+ <property name="securityService" ref="securityService"/>
+ <property name="settingsService" ref="settingsService"/>
+ </bean>
+
+ <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
+ <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
+ <property name="prefix" value="/WEB-INF/jsp/"/>
+ <property name="suffix" value=".jsp"/>
+ </bean>
+
+</beans>
diff --git a/subsonic-main/src/main/webapp/WEB-INF/web.xml b/subsonic-main/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..bf484e28
--- /dev/null
+++ b/subsonic-main/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app id="subsonic" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
+
+ <display-name>Subsonic Music Streamer</display-name>
+
+ <!-- Location of application context. Used by ContextLoaderListener. -->
+ <context-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>
+ /WEB-INF/applicationContext-service.xml
+ /WEB-INF/applicationContext-security.xml
+ /WEB-INF/applicationContext-cache.xml
+ </param-value>
+ </context-param>
+
+ <listener>
+ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+ </listener>
+ <listener>
+ <listener-class>net.sf.ehcache.constructs.web.ShutdownListener</listener-class>
+ </listener>
+
+ <servlet>
+ <servlet-name>subsonic</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet>
+ <display-name>DWR Servlet</display-name>
+ <servlet-name>dwr-invoker</servlet-name>
+ <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>*.view</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/podcast</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/wap</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/play.m3u</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/stream/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/rest/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>subsonic</servlet-name>
+ <url-pattern>/share/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>dwr-invoker</servlet-name>
+ <url-pattern>/dwr/*</url-pattern>
+ </servlet-mapping>
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ <welcome-file>index.jsp</welcome-file>
+ </welcome-file-list>
+
+ <error-page>
+ <exception-type>java.lang.Throwable</exception-type>
+ <location>/error.jsp</location>
+ </error-page>
+
+ <filter>
+ <filter-name>BootstrapVerificationFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.BootstrapVerificationFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>BootstrapVerificationFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <filter>
+ <filter-name>ParameterDecodingFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.ParameterDecodingFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>ParameterDecodingFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <filter>
+ <filter-name>RequestEncodingFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.RequestEncodingFilter</filter-class>
+ <init-param>
+ <param-name>encoding</param-name>
+ <param-value>UTF-8</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>RequestEncodingFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <filter>
+ <description>Sets HTTP headers to enable browser caching.</description>
+ <filter-name>CacheFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.ResponseHeaderFilter</filter-class>
+ <init-param>
+ <param-name>Cache-Control</param-name>
+ <param-value>max-age=36000</param-value>
+ </init-param>
+ </filter>
+
+ <filter>
+ <description>Sets HTTP headers to disable browser caching.</description>
+ <filter-name>NoCacheFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.ResponseHeaderFilter</filter-class>
+ <init-param>
+ <param-name>Cache-Control</param-name>
+ <param-value>no-cache, post-check=0, pre-check=0</param-value>
+ </init-param>
+ <init-param>
+ <param-name>Pragma</param-name>
+ <param-value>no-cache</param-value>
+ </init-param>
+ <init-param>
+ <param-name>Expires</param-name>
+ <param-value>Thu, 01 Dec 1994 16:00:00 GMT</param-value>
+ </init-param>
+ </filter>
+
+ <filter>
+ <description>The "Expires" HTTP header is set to avoid overly eager browser caching of
+ pages that implements LastModified.</description>
+ <filter-name>ExpiresFilter</filter-name>
+ <filter-class>net.sourceforge.subsonic.filter.ResponseHeaderFilter</filter-class>
+ <init-param>
+ <param-name>Expires</param-name>
+ <param-value>Thu, 01 Dec 1994 16:00:00 GMT</param-value>
+ </init-param>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>CacheFilter</filter-name>
+ <url-pattern>/icons/*</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>CacheFilter</filter-name>
+ <url-pattern>/style/*</url-pattern>
+ </filter-mapping>
+
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/statusChart.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/userChart.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/playQueue.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/podcastReceiver.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/help.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/top.view</url-pattern>
+ </filter-mapping>
+ <filter-mapping>
+ <filter-name>NoCacheFilter</filter-name>
+ <url-pattern>/home.view</url-pattern>
+ </filter-mapping>
+
+ <filter-mapping>
+ <filter-name>ExpiresFilter</filter-name>
+ <url-pattern>/left.view</url-pattern>
+ </filter-mapping>
+
+ <filter>
+ <filter-name>AcegiFilter</filter-name>
+ <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
+ <init-param>
+ <param-name>targetClass</param-name>
+ <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
+ </init-param>
+ </filter>
+
+ <filter-mapping>
+ <filter-name>AcegiFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+</web-app> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/ad/omakasa.html b/subsonic-main/src/main/webapp/ad/omakasa.html
new file mode 100644
index 00000000..12145df8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/ad/omakasa.html
@@ -0,0 +1,5 @@
+<html>
+<body topmargin=0 leftmargin=0 marginheight=0 marginwidth=0>
+<script type='text/javascript'>amazon_ad_tag = 'subsonic-20'; amazon_ad_width = '120'; amazon_ad_height = '240'; amazon_ad_link_target = 'new';</script><script type='text/javascript' src='http://www.assoc-amazon.com/s/ads.js'></script>
+</body>
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/crossdomain.xml b/subsonic-main/src/main/webapp/crossdomain.xml
new file mode 100644
index 00000000..7f6057fa
--- /dev/null
+++ b/subsonic-main/src/main/webapp/crossdomain.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
+
+<cross-domain-policy>
+ <allow-access-from domain="*" />
+</cross-domain-policy> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/error.jsp b/subsonic-main/src/main/webapp/error.jsp
new file mode 100644
index 00000000..39d38a53
--- /dev/null
+++ b/subsonic-main/src/main/webapp/error.jsp
@@ -0,0 +1,48 @@
+<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" isErrorPage="true" %>
+<%@ page import="java.io.*"%>
+
+<html><head>
+ <!--[if lt IE 7.]>
+ <script defer type="text/javascript" src="script/pngfix.js"></script>
+ <![endif]-->
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" href="style/default.css" type="text/css"/>
+</head>
+
+<body>
+<h1><img src="icons/error.png" alt=""/> Error</h1>
+
+<p>
+ Subsonic encountered an internal error. You can report this error in the
+ <a href="http://forum.subsonic.org" target="_blank">Subsonic Forum</a>.
+ Please include the information below.
+</p>
+
+<%
+ StringWriter sw = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sw));
+
+ long totalMemory = Runtime.getRuntime().totalMemory();
+ long freeMemory = Runtime.getRuntime().freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+%>
+
+<table class="ruleTable indent">
+ <tr><td class="ruleTableHeader">Exception</td>
+ <td class="ruleTableCell"><%=exception.getClass().getName()%></td></tr>
+ <tr><td class="ruleTableHeader">Message</td>
+ <td class="ruleTableCell"><%=exception.getMessage()%></td></tr>
+ <tr><td class="ruleTableHeader">Java version</td>
+ <td class="ruleTableCell"><%=System.getProperty("java.vendor") + ' ' + System.getProperty("java.version")%></td></tr>
+ <tr><td class="ruleTableHeader">Operating system</td>
+ <td class="ruleTableCell"><%=System.getProperty("os.name") + ' ' + System.getProperty("os.version")%></td></tr>
+ <tr><td class="ruleTableHeader">Server</td>
+ <td class="ruleTableCell"><%=application.getServerInfo()%></td></tr>
+ <tr><td class="ruleTableHeader">Memory</td>
+ <td class="ruleTableCell">Used <%=usedMemory/1024L/1024L%> of <%=totalMemory/1024L/1024L%> MB</td></tr>
+ <tr><td class="ruleTableHeader" style="vertical-align:top;">Stack trace</td>
+ <td class="ruleTableCell" style="white-space:pre"><%=sw.getBuffer()%></td></tr>
+</table>
+
+</body>
+</html>
diff --git a/subsonic-main/src/main/webapp/flash/jw-player-5.6.swf b/subsonic-main/src/main/webapp/flash/jw-player-5.6.swf
new file mode 100644
index 00000000..b98411bb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/flash/jw-player-5.6.swf
Binary files differ
diff --git a/subsonic-main/src/main/webapp/flash/whotube.zip b/subsonic-main/src/main/webapp/flash/whotube.zip
new file mode 100644
index 00000000..f78a1acc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/flash/whotube.zip
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/add.gif b/subsonic-main/src/main/webapp/icons/add.gif
new file mode 100644
index 00000000..d1ff937f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/add.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/android.png b/subsonic-main/src/main/webapp/icons/android.png
new file mode 100644
index 00000000..747ee31c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/android.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/back.gif b/subsonic-main/src/main/webapp/icons/back.gif
new file mode 100644
index 00000000..81721dc1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/back.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/add.png b/subsonic-main/src/main/webapp/icons/buuftheme/add.png
new file mode 100644
index 00000000..9b4e0c1f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/android.png b/subsonic-main/src/main/webapp/icons/buuftheme/android.png
new file mode 100644
index 00000000..279b31b0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/android.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/back.png b/subsonic-main/src/main/webapp/icons/buuftheme/back.png
new file mode 100644
index 00000000..9f955e98
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/background_main.png b/subsonic-main/src/main/webapp/icons/buuftheme/background_main.png
new file mode 100644
index 00000000..357ab473
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/background_main.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/clear_rating.png b/subsonic-main/src/main/webapp/icons/buuftheme/clear_rating.png
new file mode 100644
index 00000000..4a83edbd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/donate.png b/subsonic-main/src/main/webapp/icons/buuftheme/donate.png
new file mode 100644
index 00000000..3b693839
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/donate_small.png b/subsonic-main/src/main/webapp/icons/buuftheme/donate_small.png
new file mode 100644
index 00000000..f01e447a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/donate_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/down.png b/subsonic-main/src/main/webapp/icons/buuftheme/down.png
new file mode 100644
index 00000000..9b2aae76
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/download.png b/subsonic-main/src/main/webapp/icons/buuftheme/download.png
new file mode 100644
index 00000000..0ca87330
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/error.png b/subsonic-main/src/main/webapp/icons/buuftheme/error.png
new file mode 100644
index 00000000..bec7cc39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/favicon.ico b/subsonic-main/src/main/webapp/icons/buuftheme/favicon.ico
new file mode 100644
index 00000000..5fe21f0c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/forward.png b/subsonic-main/src/main/webapp/icons/buuftheme/forward.png
new file mode 100644
index 00000000..7bc6d2bc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/gpl.png b/subsonic-main/src/main/webapp/icons/buuftheme/gpl.png
new file mode 100644
index 00000000..cd3deb71
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/gpl.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/help.png b/subsonic-main/src/main/webapp/icons/buuftheme/help.png
new file mode 100644
index 00000000..8dbbb633
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/help_small.png b/subsonic-main/src/main/webapp/icons/buuftheme/help_small.png
new file mode 100644
index 00000000..4d2f25a1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/home.png b/subsonic-main/src/main/webapp/icons/buuftheme/home.png
new file mode 100644
index 00000000..36a27453
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/home_hover.png b/subsonic-main/src/main/webapp/icons/buuftheme/home_hover.png
new file mode 100644
index 00000000..cff7ebc4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/home_hover.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/list_heading.png b/subsonic-main/src/main/webapp/icons/buuftheme/list_heading.png
new file mode 100644
index 00000000..437c55ab
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/list_heading.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/log.png b/subsonic-main/src/main/webapp/icons/buuftheme/log.png
new file mode 100644
index 00000000..d7d13bae
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/logo.png b/subsonic-main/src/main/webapp/icons/buuftheme/logo.png
new file mode 100644
index 00000000..0ac76190
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/more.png b/subsonic-main/src/main/webapp/icons/buuftheme/more.png
new file mode 100644
index 00000000..a679d8b3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/now_playing.png b/subsonic-main/src/main/webapp/icons/buuftheme/now_playing.png
new file mode 100644
index 00000000..472e6cca
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/paypal.gif b/subsonic-main/src/main/webapp/icons/buuftheme/paypal.gif
new file mode 100644
index 00000000..6d8c7f99
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/phone.png b/subsonic-main/src/main/webapp/icons/buuftheme/phone.png
new file mode 100644
index 00000000..8727f23a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/play.png b/subsonic-main/src/main/webapp/icons/buuftheme/play.png
new file mode 100644
index 00000000..29baf96f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/playing.png b/subsonic-main/src/main/webapp/icons/buuftheme/playing.png
new file mode 100644
index 00000000..a870cc1d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/podcast.png b/subsonic-main/src/main/webapp/icons/buuftheme/podcast.png
new file mode 100644
index 00000000..927d9ae3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/podcast_small.png b/subsonic-main/src/main/webapp/icons/buuftheme/podcast_small.png
new file mode 100644
index 00000000..61741833
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/progress.png b/subsonic-main/src/main/webapp/icons/buuftheme/progress.png
new file mode 100644
index 00000000..ddd5a913
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/random.png b/subsonic-main/src/main/webapp/icons/buuftheme/random.png
new file mode 100644
index 00000000..d892b383
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/rating_half.png b/subsonic-main/src/main/webapp/icons/buuftheme/rating_half.png
new file mode 100644
index 00000000..4024923c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/rating_off.png b/subsonic-main/src/main/webapp/icons/buuftheme/rating_off.png
new file mode 100644
index 00000000..97640ef2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/rating_on.png b/subsonic-main/src/main/webapp/icons/buuftheme/rating_on.png
new file mode 100644
index 00000000..cfdc2628
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/remove.png b/subsonic-main/src/main/webapp/icons/buuftheme/remove.png
new file mode 100644
index 00000000..391d89f7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/search.png b/subsonic-main/src/main/webapp/icons/buuftheme/search.png
new file mode 100644
index 00000000..1e81fd02
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/settings.png b/subsonic-main/src/main/webapp/icons/buuftheme/settings.png
new file mode 100644
index 00000000..6d520fa3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/status.png b/subsonic-main/src/main/webapp/icons/buuftheme/status.png
new file mode 100644
index 00000000..1ace9646
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/subair.png b/subsonic-main/src/main/webapp/icons/buuftheme/subair.png
new file mode 100644
index 00000000..a8a36dbe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/subair.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/up.png b/subsonic-main/src/main/webapp/icons/buuftheme/up.png
new file mode 100644
index 00000000..e2f0193c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/upload.png b/subsonic-main/src/main/webapp/icons/buuftheme/upload.png
new file mode 100644
index 00000000..25af770e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/buuftheme/wap.png b/subsonic-main/src/main/webapp/icons/buuftheme/wap.png
new file mode 100644
index 00000000..7a3b688c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/buuftheme/wap.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/clearRating.png b/subsonic-main/src/main/webapp/icons/clearRating.png
new file mode 100644
index 00000000..a41afa13
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/clearRating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/add.png b/subsonic-main/src/main/webapp/icons/coolandclean/add.png
new file mode 100644
index 00000000..1b2b3e57
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/back.png b/subsonic-main/src/main/webapp/icons/coolandclean/back.png
new file mode 100644
index 00000000..187916f2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/background.png b/subsonic-main/src/main/webapp/icons/coolandclean/background.png
new file mode 100644
index 00000000..2de28330
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/background.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/background_main.png b/subsonic-main/src/main/webapp/icons/coolandclean/background_main.png
new file mode 100644
index 00000000..78d50118
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/background_main.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/clear_rating.png b/subsonic-main/src/main/webapp/icons/coolandclean/clear_rating.png
new file mode 100644
index 00000000..711fa862
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/donate.png b/subsonic-main/src/main/webapp/icons/coolandclean/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/down.png b/subsonic-main/src/main/webapp/icons/coolandclean/down.png
new file mode 100644
index 00000000..eeb09a15
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/download.png b/subsonic-main/src/main/webapp/icons/coolandclean/download.png
new file mode 100644
index 00000000..a0551ff7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/error.png b/subsonic-main/src/main/webapp/icons/coolandclean/error.png
new file mode 100644
index 00000000..3416f146
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/favicon.ico b/subsonic-main/src/main/webapp/icons/coolandclean/favicon.ico
new file mode 100644
index 00000000..d568f347
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/forward.png b/subsonic-main/src/main/webapp/icons/coolandclean/forward.png
new file mode 100644
index 00000000..d4541d69
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/help.png b/subsonic-main/src/main/webapp/icons/coolandclean/help.png
new file mode 100644
index 00000000..3e1efec9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/help_small.png b/subsonic-main/src/main/webapp/icons/coolandclean/help_small.png
new file mode 100644
index 00000000..c1967b47
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/home.png b/subsonic-main/src/main/webapp/icons/coolandclean/home.png
new file mode 100644
index 00000000..0b0a9dff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/home_hover.png b/subsonic-main/src/main/webapp/icons/coolandclean/home_hover.png
new file mode 100644
index 00000000..91b069c9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/home_hover.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/list_heading.png b/subsonic-main/src/main/webapp/icons/coolandclean/list_heading.png
new file mode 100644
index 00000000..3b1d2623
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/list_heading.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/log.png b/subsonic-main/src/main/webapp/icons/coolandclean/log.png
new file mode 100644
index 00000000..8cd79b89
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/logo.png b/subsonic-main/src/main/webapp/icons/coolandclean/logo.png
new file mode 100644
index 00000000..b393527e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/more.png b/subsonic-main/src/main/webapp/icons/coolandclean/more.png
new file mode 100644
index 00000000..1825f36e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/now_playing.png b/subsonic-main/src/main/webapp/icons/coolandclean/now_playing.png
new file mode 100644
index 00000000..30997a6c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/paypal.gif b/subsonic-main/src/main/webapp/icons/coolandclean/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/phone.png b/subsonic-main/src/main/webapp/icons/coolandclean/phone.png
new file mode 100644
index 00000000..d8d85646
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/play.png b/subsonic-main/src/main/webapp/icons/coolandclean/play.png
new file mode 100644
index 00000000..bb81db5e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/playing.png b/subsonic-main/src/main/webapp/icons/coolandclean/playing.png
new file mode 100644
index 00000000..a12e02aa
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/podcast.png b/subsonic-main/src/main/webapp/icons/coolandclean/podcast.png
new file mode 100644
index 00000000..cb0c6967
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/podcast_small.png b/subsonic-main/src/main/webapp/icons/coolandclean/podcast_small.png
new file mode 100644
index 00000000..81a851b7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/progress.png b/subsonic-main/src/main/webapp/icons/coolandclean/progress.png
new file mode 100644
index 00000000..ddd5a913
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/random.png b/subsonic-main/src/main/webapp/icons/coolandclean/random.png
new file mode 100644
index 00000000..4316a393
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/rating_half.png b/subsonic-main/src/main/webapp/icons/coolandclean/rating_half.png
new file mode 100644
index 00000000..d8550838
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/rating_off.png b/subsonic-main/src/main/webapp/icons/coolandclean/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/rating_on.png b/subsonic-main/src/main/webapp/icons/coolandclean/rating_on.png
new file mode 100644
index 00000000..b3e99d1d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/remove.png b/subsonic-main/src/main/webapp/icons/coolandclean/remove.png
new file mode 100644
index 00000000..f2280eae
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/search.png b/subsonic-main/src/main/webapp/icons/coolandclean/search.png
new file mode 100644
index 00000000..0f7b5360
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/settings.png b/subsonic-main/src/main/webapp/icons/coolandclean/settings.png
new file mode 100644
index 00000000..a38f5915
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/status.png b/subsonic-main/src/main/webapp/icons/coolandclean/status.png
new file mode 100644
index 00000000..d0e5b4d4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/up.png b/subsonic-main/src/main/webapp/icons/coolandclean/up.png
new file mode 100644
index 00000000..4dc14491
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/coolandclean/upload.png b/subsonic-main/src/main/webapp/icons/coolandclean/upload.png
new file mode 100644
index 00000000..81920b18
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/coolandclean/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/current.gif b/subsonic-main/src/main/webapp/icons/current.gif
new file mode 100644
index 00000000..4176913d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/current.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/add.png b/subsonic-main/src/main/webapp/icons/denim/add.png
new file mode 100644
index 00000000..26e12f87
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/back.png b/subsonic-main/src/main/webapp/icons/denim/back.png
new file mode 100644
index 00000000..295ec91b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/clear_rating.png b/subsonic-main/src/main/webapp/icons/denim/clear_rating.png
new file mode 100644
index 00000000..64d78601
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/donate.png b/subsonic-main/src/main/webapp/icons/denim/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/down.png b/subsonic-main/src/main/webapp/icons/denim/down.png
new file mode 100644
index 00000000..5635af51
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/download.png b/subsonic-main/src/main/webapp/icons/denim/download.png
new file mode 100644
index 00000000..1b24ae83
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/error.png b/subsonic-main/src/main/webapp/icons/denim/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/favicon.ico b/subsonic-main/src/main/webapp/icons/denim/favicon.ico
new file mode 100644
index 00000000..ab7802d8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/forward.png b/subsonic-main/src/main/webapp/icons/denim/forward.png
new file mode 100644
index 00000000..b88d5106
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/help.png b/subsonic-main/src/main/webapp/icons/denim/help.png
new file mode 100644
index 00000000..de1a0f6d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/help_small.png b/subsonic-main/src/main/webapp/icons/denim/help_small.png
new file mode 100644
index 00000000..824519e9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/home.png b/subsonic-main/src/main/webapp/icons/denim/home.png
new file mode 100644
index 00000000..7f4a4d58
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/log.png b/subsonic-main/src/main/webapp/icons/denim/log.png
new file mode 100644
index 00000000..a4fa9297
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/logo.png b/subsonic-main/src/main/webapp/icons/denim/logo.png
new file mode 100644
index 00000000..82191180
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/more.png b/subsonic-main/src/main/webapp/icons/denim/more.png
new file mode 100644
index 00000000..bfc8dbb8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/now_playing.png b/subsonic-main/src/main/webapp/icons/denim/now_playing.png
new file mode 100644
index 00000000..42931eff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/paypal.gif b/subsonic-main/src/main/webapp/icons/denim/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/phone.png b/subsonic-main/src/main/webapp/icons/denim/phone.png
new file mode 100644
index 00000000..693c8620
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/play.png b/subsonic-main/src/main/webapp/icons/denim/play.png
new file mode 100644
index 00000000..cbcb8d40
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/playing.png b/subsonic-main/src/main/webapp/icons/denim/playing.png
new file mode 100644
index 00000000..8b620c2a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/podcast.png b/subsonic-main/src/main/webapp/icons/denim/podcast.png
new file mode 100644
index 00000000..033d32c7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/podcast_small.png b/subsonic-main/src/main/webapp/icons/denim/podcast_small.png
new file mode 100644
index 00000000..87d2fe25
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/progress.png b/subsonic-main/src/main/webapp/icons/denim/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/random.png b/subsonic-main/src/main/webapp/icons/denim/random.png
new file mode 100644
index 00000000..2b0fdb06
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/rating_half.png b/subsonic-main/src/main/webapp/icons/denim/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/rating_off.png b/subsonic-main/src/main/webapp/icons/denim/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/rating_on.png b/subsonic-main/src/main/webapp/icons/denim/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/remove.png b/subsonic-main/src/main/webapp/icons/denim/remove.png
new file mode 100644
index 00000000..c0afb187
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/search.png b/subsonic-main/src/main/webapp/icons/denim/search.png
new file mode 100644
index 00000000..6dea8731
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/settings.png b/subsonic-main/src/main/webapp/icons/denim/settings.png
new file mode 100644
index 00000000..547bef7b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/status.png b/subsonic-main/src/main/webapp/icons/denim/status.png
new file mode 100644
index 00000000..f9c4503f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/up.png b/subsonic-main/src/main/webapp/icons/denim/up.png
new file mode 100644
index 00000000..ee5ad884
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/denim/upload.png b/subsonic-main/src/main/webapp/icons/denim/upload.png
new file mode 100644
index 00000000..cf3f38da
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/denim/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/donate.png b/subsonic-main/src/main/webapp/icons/donate.png
new file mode 100644
index 00000000..3b00577e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/donate_small.png b/subsonic-main/src/main/webapp/icons/donate_small.png
new file mode 100644
index 00000000..3a8c311d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/donate_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/down.gif b/subsonic-main/src/main/webapp/icons/down.gif
new file mode 100644
index 00000000..091805f6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/down.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/download.gif b/subsonic-main/src/main/webapp/icons/download.gif
new file mode 100644
index 00000000..3fcdfd00
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/download.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/error.png b/subsonic-main/src/main/webapp/icons/error.png
new file mode 100644
index 00000000..45b64a79
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/favicon.ico b/subsonic-main/src/main/webapp/icons/favicon.ico
new file mode 100644
index 00000000..d2c13383
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/forward.gif b/subsonic-main/src/main/webapp/icons/forward.gif
new file mode 100644
index 00000000..dfccecb9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/forward.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/gpl.png b/subsonic-main/src/main/webapp/icons/gpl.png
new file mode 100644
index 00000000..b06e0439
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/gpl.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/add.png b/subsonic-main/src/main/webapp/icons/groove/add.png
new file mode 100644
index 00000000..10657ea1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/back.png b/subsonic-main/src/main/webapp/icons/groove/back.png
new file mode 100644
index 00000000..7e1355fd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/background_main.png b/subsonic-main/src/main/webapp/icons/groove/background_main.png
new file mode 100644
index 00000000..27c223fa
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/background_main.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/background_main_blank.png b/subsonic-main/src/main/webapp/icons/groove/background_main_blank.png
new file mode 100644
index 00000000..d0866f7e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/background_main_blank.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/clear_rating.png b/subsonic-main/src/main/webapp/icons/groove/clear_rating.png
new file mode 100644
index 00000000..8cbdc24d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/donate.png b/subsonic-main/src/main/webapp/icons/groove/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/down.png b/subsonic-main/src/main/webapp/icons/groove/down.png
new file mode 100644
index 00000000..748c5143
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/download.png b/subsonic-main/src/main/webapp/icons/groove/download.png
new file mode 100644
index 00000000..748c5143
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/error.png b/subsonic-main/src/main/webapp/icons/groove/error.png
new file mode 100644
index 00000000..decb2c1d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/favicon.ico b/subsonic-main/src/main/webapp/icons/groove/favicon.ico
new file mode 100644
index 00000000..82690e7a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/forward.png b/subsonic-main/src/main/webapp/icons/groove/forward.png
new file mode 100644
index 00000000..4ac6e864
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/help.png b/subsonic-main/src/main/webapp/icons/groove/help.png
new file mode 100644
index 00000000..b9e23c4c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/help_small.png b/subsonic-main/src/main/webapp/icons/groove/help_small.png
new file mode 100644
index 00000000..80a8da30
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/home.png b/subsonic-main/src/main/webapp/icons/groove/home.png
new file mode 100644
index 00000000..f79e99d9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/log.png b/subsonic-main/src/main/webapp/icons/groove/log.png
new file mode 100644
index 00000000..37f2bcd6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/logo.png b/subsonic-main/src/main/webapp/icons/groove/logo.png
new file mode 100644
index 00000000..9f1f5a86
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/more.png b/subsonic-main/src/main/webapp/icons/groove/more.png
new file mode 100644
index 00000000..4ed374dc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/now_playing.png b/subsonic-main/src/main/webapp/icons/groove/now_playing.png
new file mode 100644
index 00000000..4fa0b813
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/paypal.gif b/subsonic-main/src/main/webapp/icons/groove/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/phone.png b/subsonic-main/src/main/webapp/icons/groove/phone.png
new file mode 100644
index 00000000..693c8620
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/play.png b/subsonic-main/src/main/webapp/icons/groove/play.png
new file mode 100644
index 00000000..1735f2b4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/playing.png b/subsonic-main/src/main/webapp/icons/groove/playing.png
new file mode 100644
index 00000000..739cbf92
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/podcast.png b/subsonic-main/src/main/webapp/icons/groove/podcast.png
new file mode 100644
index 00000000..bd9f0364
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/podcast_small.png b/subsonic-main/src/main/webapp/icons/groove/podcast_small.png
new file mode 100644
index 00000000..9379394d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/progress.png b/subsonic-main/src/main/webapp/icons/groove/progress.png
new file mode 100644
index 00000000..7c760d04
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/random.png b/subsonic-main/src/main/webapp/icons/groove/random.png
new file mode 100644
index 00000000..d8e3a679
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/rating_half.png b/subsonic-main/src/main/webapp/icons/groove/rating_half.png
new file mode 100644
index 00000000..08c51e26
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/rating_off.png b/subsonic-main/src/main/webapp/icons/groove/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/rating_on.png b/subsonic-main/src/main/webapp/icons/groove/rating_on.png
new file mode 100644
index 00000000..1c1239fa
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/remove.png b/subsonic-main/src/main/webapp/icons/groove/remove.png
new file mode 100644
index 00000000..e4d8e203
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/search.png b/subsonic-main/src/main/webapp/icons/groove/search.png
new file mode 100644
index 00000000..9e350fb0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/settings.png b/subsonic-main/src/main/webapp/icons/groove/settings.png
new file mode 100644
index 00000000..7bca5d5a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/status.png b/subsonic-main/src/main/webapp/icons/groove/status.png
new file mode 100644
index 00000000..69e94efd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/up.png b/subsonic-main/src/main/webapp/icons/groove/up.png
new file mode 100644
index 00000000..2f730914
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/groove/upload.png b/subsonic-main/src/main/webapp/icons/groove/upload.png
new file mode 100644
index 00000000..2f730914
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/groove/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hd/background.png b/subsonic-main/src/main/webapp/icons/hd/background.png
new file mode 100644
index 00000000..fa6dc078
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hd/background.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/help.png b/subsonic-main/src/main/webapp/icons/help.png
new file mode 100644
index 00000000..22801b8f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/help_small.png b/subsonic-main/src/main/webapp/icons/help_small.png
new file mode 100644
index 00000000..f25fc3fb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/favicon.ico b/subsonic-main/src/main/webapp/icons/hicon/favicon.ico
new file mode 100644
index 00000000..65dd49b4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/help.png b/subsonic-main/src/main/webapp/icons/hicon/help.png
new file mode 100644
index 00000000..5104c843
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/home.png b/subsonic-main/src/main/webapp/icons/hicon/home.png
new file mode 100644
index 00000000..ddb635ee
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/more.png b/subsonic-main/src/main/webapp/icons/hicon/more.png
new file mode 100644
index 00000000..c2d88744
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/now_playing.png b/subsonic-main/src/main/webapp/icons/hicon/now_playing.png
new file mode 100644
index 00000000..96d8166e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/podcast_large.png b/subsonic-main/src/main/webapp/icons/hicon/podcast_large.png
new file mode 100644
index 00000000..cc1e8863
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/settings.png b/subsonic-main/src/main/webapp/icons/hicon/settings.png
new file mode 100644
index 00000000..d253f9e8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/status.png b/subsonic-main/src/main/webapp/icons/hicon/status.png
new file mode 100644
index 00000000..c478dc9c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hicon/subsonic.png b/subsonic-main/src/main/webapp/icons/hicon/subsonic.png
new file mode 100644
index 00000000..083f929e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hicon/subsonic.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/Untitled-1.ico b/subsonic-main/src/main/webapp/icons/hiconi/Untitled-1.ico
new file mode 100644
index 00000000..10aecae7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/Untitled-1.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/help.png b/subsonic-main/src/main/webapp/icons/hiconi/help.png
new file mode 100644
index 00000000..c62172ff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/home.png b/subsonic-main/src/main/webapp/icons/hiconi/home.png
new file mode 100644
index 00000000..0899225c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/more.png b/subsonic-main/src/main/webapp/icons/hiconi/more.png
new file mode 100644
index 00000000..5b021d97
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/now_playing.png b/subsonic-main/src/main/webapp/icons/hiconi/now_playing.png
new file mode 100644
index 00000000..901d7f22
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/podcast_large.png b/subsonic-main/src/main/webapp/icons/hiconi/podcast_large.png
new file mode 100644
index 00000000..258794dd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/settings.png b/subsonic-main/src/main/webapp/icons/hiconi/settings.png
new file mode 100644
index 00000000..a889eabc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/status.png b/subsonic-main/src/main/webapp/icons/hiconi/status.png
new file mode 100644
index 00000000..6934303d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hiconi/subsonic.png b/subsonic-main/src/main/webapp/icons/hiconi/subsonic.png
new file mode 100644
index 00000000..d3aa3d7b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hiconi/subsonic.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/bg.jpg b/subsonic-main/src/main/webapp/icons/hitech/bg.jpg
new file mode 100644
index 00000000..38e290bc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/bg.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/bg2.jpg b/subsonic-main/src/main/webapp/icons/hitech/bg2.jpg
new file mode 100644
index 00000000..4d86e3a1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/bg2.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/favicon.ico b/subsonic-main/src/main/webapp/icons/hitech/favicon.ico
new file mode 100644
index 00000000..3b94d2bd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/help.png b/subsonic-main/src/main/webapp/icons/hitech/help.png
new file mode 100644
index 00000000..d3558111
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/home.png b/subsonic-main/src/main/webapp/icons/hitech/home.png
new file mode 100644
index 00000000..99703bee
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/more.png b/subsonic-main/src/main/webapp/icons/hitech/more.png
new file mode 100644
index 00000000..e98c9875
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/now_playing.png b/subsonic-main/src/main/webapp/icons/hitech/now_playing.png
new file mode 100644
index 00000000..1682aac2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/podcast_large.png b/subsonic-main/src/main/webapp/icons/hitech/podcast_large.png
new file mode 100644
index 00000000..0cd41713
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/settings.png b/subsonic-main/src/main/webapp/icons/hitech/settings.png
new file mode 100644
index 00000000..f736b988
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/status.png b/subsonic-main/src/main/webapp/icons/hitech/status.png
new file mode 100644
index 00000000..2cfebe96
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/hitech/subsonic.png b/subsonic-main/src/main/webapp/icons/hitech/subsonic.png
new file mode 100644
index 00000000..44305b32
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/hitech/subsonic.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/home.png b/subsonic-main/src/main/webapp/icons/home.png
new file mode 100644
index 00000000..7e87c701
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/log.png b/subsonic-main/src/main/webapp/icons/log.png
new file mode 100644
index 00000000..8b2c6f4a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnight/back.png b/subsonic-main/src/main/webapp/icons/midnight/back.png
new file mode 100644
index 00000000..a07f3d89
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnight/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnight/forward.png b/subsonic-main/src/main/webapp/icons/midnight/forward.png
new file mode 100644
index 00000000..000658bc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnight/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/error.png b/subsonic-main/src/main/webapp/icons/midnightfun/error.png
new file mode 100644
index 00000000..e0440842
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/favicon.ico b/subsonic-main/src/main/webapp/icons/midnightfun/favicon.ico
new file mode 100644
index 00000000..17730887
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_Now_Playing.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_Now_Playing.png
new file mode 100644
index 00000000..30997a6c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_Now_Playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_add.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_add.png
new file mode 100644
index 00000000..e1fa11f8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_back.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_back.png
new file mode 100644
index 00000000..5b07c5e5
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_background.gif b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_background.gif
new file mode 100644
index 00000000..3b697cea
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_background.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_clear_Rating.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_clear_Rating.png
new file mode 100644
index 00000000..bc59f297
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_clear_Rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_donate.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_donate.png
new file mode 100644
index 00000000..2453adbe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_down.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_down.png
new file mode 100644
index 00000000..2dd28e91
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_download.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_download.png
new file mode 100644
index 00000000..18ab592a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls.jpg b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls.jpg
new file mode 100644
index 00000000..6a2219b8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls_hover.jpg b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls_hover.jpg
new file mode 100644
index 00000000..d37fcc92
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_form_controls_hover.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_forward.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_forward.png
new file mode 100644
index 00000000..70cfd5f2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help.png
new file mode 100644
index 00000000..209f1d28
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help_small.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help_small.png
new file mode 100644
index 00000000..352fd541
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home.png
new file mode 100644
index 00000000..32727e76
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home_hover.jpg b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home_hover.jpg
new file mode 100644
index 00000000..b06edcfb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_home_hover.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_log.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_log.png
new file mode 100644
index 00000000..722ff9f1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo.png
new file mode 100644
index 00000000..d3cbbdf1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo_favicon.ico b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo_favicon.ico
new file mode 100644
index 00000000..db665a4e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_logo_favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_more.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_more.png
new file mode 100644
index 00000000..4ed5365e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_paypal.gif b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_phone.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_phone.png
new file mode 100644
index 00000000..28e2c561
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_play.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_play.png
new file mode 100644
index 00000000..bbb0f473
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_playing.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_playing.png
new file mode 100644
index 00000000..a1d9feda
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast.png
new file mode 100644
index 00000000..27845212
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast_small.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast_small.png
new file mode 100644
index 00000000..bd1d73d7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_random.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_random.png
new file mode 100644
index 00000000..05032585
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_remove.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_remove.png
new file mode 100644
index 00000000..278879fd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_search.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_search.png
new file mode 100644
index 00000000..130094f2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_settings.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_settings.png
new file mode 100644
index 00000000..a0c28f21
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_status.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_status.png
new file mode 100644
index 00000000..6f8e175b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_table.jpg b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_table.jpg
new file mode 100644
index 00000000..2f53038a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_table.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_text_back.jpg b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_text_back.jpg
new file mode 100644
index 00000000..c70073e2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_text_back.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_up.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_up.png
new file mode 100644
index 00000000..c6bd1fb2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_upload.png b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_upload.png
new file mode 100644
index 00000000..c4488ad4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/midnightfun_upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/progress.png b/subsonic-main/src/main/webapp/icons/midnightfun/progress.png
new file mode 100644
index 00000000..ddd5a913
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/ratingHalf.png b/subsonic-main/src/main/webapp/icons/midnightfun/ratingHalf.png
new file mode 100644
index 00000000..ba171709
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/ratingHalf.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/ratingOff.png b/subsonic-main/src/main/webapp/icons/midnightfun/ratingOff.png
new file mode 100644
index 00000000..bdd052ab
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/ratingOff.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/midnightfun/ratingOn.png b/subsonic-main/src/main/webapp/icons/midnightfun/ratingOn.png
new file mode 100644
index 00000000..d9588501
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/midnightfun/ratingOn.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/monochrome/subdot.png b/subsonic-main/src/main/webapp/icons/monochrome/subdot.png
new file mode 100644
index 00000000..54623ab6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/monochrome/subdot.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/more.png b/subsonic-main/src/main/webapp/icons/more.png
new file mode 100644
index 00000000..cd5a20f6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/now_playing.png b/subsonic-main/src/main/webapp/icons/now_playing.png
new file mode 100644
index 00000000..a63e630d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/paypal.gif b/subsonic-main/src/main/webapp/icons/paypal.gif
new file mode 100644
index 00000000..d017250a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/add.png b/subsonic-main/src/main/webapp/icons/pinkpanther/add.png
new file mode 100644
index 00000000..26e12f87
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/back.png b/subsonic-main/src/main/webapp/icons/pinkpanther/back.png
new file mode 100644
index 00000000..295ec91b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/clear_rating.png b/subsonic-main/src/main/webapp/icons/pinkpanther/clear_rating.png
new file mode 100644
index 00000000..64d78601
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/donate.png b/subsonic-main/src/main/webapp/icons/pinkpanther/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/down.png b/subsonic-main/src/main/webapp/icons/pinkpanther/down.png
new file mode 100644
index 00000000..5635af51
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/download.png b/subsonic-main/src/main/webapp/icons/pinkpanther/download.png
new file mode 100644
index 00000000..1b24ae83
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/error.png b/subsonic-main/src/main/webapp/icons/pinkpanther/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/favicon.ico b/subsonic-main/src/main/webapp/icons/pinkpanther/favicon.ico
new file mode 100644
index 00000000..4348b09e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/forward.png b/subsonic-main/src/main/webapp/icons/pinkpanther/forward.png
new file mode 100644
index 00000000..b88d5106
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/help.png b/subsonic-main/src/main/webapp/icons/pinkpanther/help.png
new file mode 100644
index 00000000..de1a0f6d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/help_small.png b/subsonic-main/src/main/webapp/icons/pinkpanther/help_small.png
new file mode 100644
index 00000000..824519e9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/home.png b/subsonic-main/src/main/webapp/icons/pinkpanther/home.png
new file mode 100644
index 00000000..7f4a4d58
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/log.png b/subsonic-main/src/main/webapp/icons/pinkpanther/log.png
new file mode 100644
index 00000000..a4fa9297
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/logo.png b/subsonic-main/src/main/webapp/icons/pinkpanther/logo.png
new file mode 100644
index 00000000..9bf1eb83
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/more.png b/subsonic-main/src/main/webapp/icons/pinkpanther/more.png
new file mode 100644
index 00000000..bfc8dbb8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/now_playing.png b/subsonic-main/src/main/webapp/icons/pinkpanther/now_playing.png
new file mode 100644
index 00000000..42931eff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/paypal.gif b/subsonic-main/src/main/webapp/icons/pinkpanther/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/phone.png b/subsonic-main/src/main/webapp/icons/pinkpanther/phone.png
new file mode 100644
index 00000000..693c8620
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/play.png b/subsonic-main/src/main/webapp/icons/pinkpanther/play.png
new file mode 100644
index 00000000..cbcb8d40
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/playing.png b/subsonic-main/src/main/webapp/icons/pinkpanther/playing.png
new file mode 100644
index 00000000..8b620c2a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/podcast.png b/subsonic-main/src/main/webapp/icons/pinkpanther/podcast.png
new file mode 100644
index 00000000..033d32c7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/podcast_small.png b/subsonic-main/src/main/webapp/icons/pinkpanther/podcast_small.png
new file mode 100644
index 00000000..87d2fe25
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/progress.png b/subsonic-main/src/main/webapp/icons/pinkpanther/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/random.png b/subsonic-main/src/main/webapp/icons/pinkpanther/random.png
new file mode 100644
index 00000000..2b0fdb06
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/rating_half.png b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/rating_off.png b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/rating_on.png b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/remove.png b/subsonic-main/src/main/webapp/icons/pinkpanther/remove.png
new file mode 100644
index 00000000..c0afb187
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/search.png b/subsonic-main/src/main/webapp/icons/pinkpanther/search.png
new file mode 100644
index 00000000..6dea8731
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/settings.png b/subsonic-main/src/main/webapp/icons/pinkpanther/settings.png
new file mode 100644
index 00000000..547bef7b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/status.png b/subsonic-main/src/main/webapp/icons/pinkpanther/status.png
new file mode 100644
index 00000000..f9c4503f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/up.png b/subsonic-main/src/main/webapp/icons/pinkpanther/up.png
new file mode 100644
index 00000000..ee5ad884
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/pinkpanther/upload.png b/subsonic-main/src/main/webapp/icons/pinkpanther/upload.png
new file mode 100644
index 00000000..cf3f38da
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/pinkpanther/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/play.gif b/subsonic-main/src/main/webapp/icons/play.gif
new file mode 100644
index 00000000..d1ccbcf4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/play.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/podcast.png b/subsonic-main/src/main/webapp/icons/podcast.png
new file mode 100644
index 00000000..c8afc597
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/podcast_large.png b/subsonic-main/src/main/webapp/icons/podcast_large.png
new file mode 100644
index 00000000..e95eb343
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/progress.png b/subsonic-main/src/main/webapp/icons/progress.png
new file mode 100644
index 00000000..ddd5a913
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/random.png b/subsonic-main/src/main/webapp/icons/random.png
new file mode 100644
index 00000000..f25fc3fb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ratingHalf.png b/subsonic-main/src/main/webapp/icons/ratingHalf.png
new file mode 100644
index 00000000..d8550838
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ratingHalf.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ratingOff.png b/subsonic-main/src/main/webapp/icons/ratingOff.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ratingOff.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ratingOn.png b/subsonic-main/src/main/webapp/icons/ratingOn.png
new file mode 100644
index 00000000..b3e99d1d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ratingOn.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/remove.gif b/subsonic-main/src/main/webapp/icons/remove.gif
new file mode 100644
index 00000000..b7656a88
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/remove.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/add.gif b/subsonic-main/src/main/webapp/icons/ripserver/add.gif
new file mode 100644
index 00000000..3902057c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/add.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/back.gif b/subsonic-main/src/main/webapp/icons/ripserver/back.gif
new file mode 100644
index 00000000..c91d92c0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/back.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/background.png b/subsonic-main/src/main/webapp/icons/ripserver/background.png
new file mode 100644
index 00000000..2d4a5c7a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/background.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/clearRating.png b/subsonic-main/src/main/webapp/icons/ripserver/clearRating.png
new file mode 100644
index 00000000..59a5ca41
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/clearRating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/current.gif b/subsonic-main/src/main/webapp/icons/ripserver/current.gif
new file mode 100644
index 00000000..8591e1b6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/current.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/donate.png b/subsonic-main/src/main/webapp/icons/ripserver/donate.png
new file mode 100644
index 00000000..87fb25e1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/down.gif b/subsonic-main/src/main/webapp/icons/ripserver/down.gif
new file mode 100644
index 00000000..903bfc29
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/down.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/download.gif b/subsonic-main/src/main/webapp/icons/ripserver/download.gif
new file mode 100644
index 00000000..b9623b25
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/download.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/error.png b/subsonic-main/src/main/webapp/icons/ripserver/error.png
new file mode 100644
index 00000000..00d9f2c6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/favicon.ico b/subsonic-main/src/main/webapp/icons/ripserver/favicon.ico
new file mode 100644
index 00000000..127d5078
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/forward.gif b/subsonic-main/src/main/webapp/icons/ripserver/forward.gif
new file mode 100644
index 00000000..44199a6d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/forward.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/help.png b/subsonic-main/src/main/webapp/icons/ripserver/help.png
new file mode 100644
index 00000000..cee3d4a0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/help_small.png b/subsonic-main/src/main/webapp/icons/ripserver/help_small.png
new file mode 100644
index 00000000..36a20b64
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/home.png b/subsonic-main/src/main/webapp/icons/ripserver/home.png
new file mode 100644
index 00000000..36eb1c58
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/log.png b/subsonic-main/src/main/webapp/icons/ripserver/log.png
new file mode 100644
index 00000000..8b2c6f4a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/more.png b/subsonic-main/src/main/webapp/icons/ripserver/more.png
new file mode 100644
index 00000000..aee57a10
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/now_playing.png b/subsonic-main/src/main/webapp/icons/ripserver/now_playing.png
new file mode 100644
index 00000000..671c4074
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/paypal.gif b/subsonic-main/src/main/webapp/icons/ripserver/paypal.gif
new file mode 100644
index 00000000..d017250a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/play.gif b/subsonic-main/src/main/webapp/icons/ripserver/play.gif
new file mode 100644
index 00000000..e42ed160
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/play.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/podcast.png b/subsonic-main/src/main/webapp/icons/ripserver/podcast.png
new file mode 100644
index 00000000..f4574525
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/podcast_large.png b/subsonic-main/src/main/webapp/icons/ripserver/podcast_large.png
new file mode 100644
index 00000000..f4574525
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/progress.png b/subsonic-main/src/main/webapp/icons/ripserver/progress.png
new file mode 100644
index 00000000..ddd5a913
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/random.png b/subsonic-main/src/main/webapp/icons/ripserver/random.png
new file mode 100644
index 00000000..ee9750f0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/ratingHalf.png b/subsonic-main/src/main/webapp/icons/ripserver/ratingHalf.png
new file mode 100644
index 00000000..ba171709
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/ratingHalf.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/ratingOff.png b/subsonic-main/src/main/webapp/icons/ripserver/ratingOff.png
new file mode 100644
index 00000000..bdd052ab
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/ratingOff.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/ratingOn.png b/subsonic-main/src/main/webapp/icons/ripserver/ratingOn.png
new file mode 100644
index 00000000..d9588501
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/ratingOn.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/remove.gif b/subsonic-main/src/main/webapp/icons/ripserver/remove.gif
new file mode 100644
index 00000000..33b9414f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/remove.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/search.png b/subsonic-main/src/main/webapp/icons/ripserver/search.png
new file mode 100644
index 00000000..b6dd2039
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/settings.png b/subsonic-main/src/main/webapp/icons/ripserver/settings.png
new file mode 100644
index 00000000..c6f2ed15
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/status.png b/subsonic-main/src/main/webapp/icons/ripserver/status.png
new file mode 100644
index 00000000..fdbc7fd5
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/subsonic_black.png b/subsonic-main/src/main/webapp/icons/ripserver/subsonic_black.png
new file mode 100644
index 00000000..421b61d1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/subsonic_black.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/subsonic_white.png b/subsonic-main/src/main/webapp/icons/ripserver/subsonic_white.png
new file mode 100644
index 00000000..3089427f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/subsonic_white.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/up.gif b/subsonic-main/src/main/webapp/icons/ripserver/up.gif
new file mode 100644
index 00000000..7474ee2e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/up.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/upload.gif b/subsonic-main/src/main/webapp/icons/ripserver/upload.gif
new file mode 100644
index 00000000..43b98d96
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/upload.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/ripserver/wap.png b/subsonic-main/src/main/webapp/icons/ripserver/wap.png
new file mode 100644
index 00000000..0d67e2de
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/ripserver/wap.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/search.png b/subsonic-main/src/main/webapp/icons/search.png
new file mode 100644
index 00000000..7fbfb6f5
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/settings.png b/subsonic-main/src/main/webapp/icons/settings.png
new file mode 100644
index 00000000..9173cbf7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/share_facebook.png b/subsonic-main/src/main/webapp/icons/share_facebook.png
new file mode 100644
index 00000000..30f4d6ce
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/share_facebook.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/share_googleplus.png b/subsonic-main/src/main/webapp/icons/share_googleplus.png
new file mode 100644
index 00000000..fff340f2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/share_googleplus.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/share_twitter.png b/subsonic-main/src/main/webapp/icons/share_twitter.png
new file mode 100644
index 00000000..8b5eeace
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/share_twitter.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/add.png b/subsonic-main/src/main/webapp/icons/simplify/add.png
new file mode 100644
index 00000000..9428fc87
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/back.png b/subsonic-main/src/main/webapp/icons/simplify/back.png
new file mode 100644
index 00000000..05df3b64
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/clear_rating.png b/subsonic-main/src/main/webapp/icons/simplify/clear_rating.png
new file mode 100644
index 00000000..4d028588
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/donate.png b/subsonic-main/src/main/webapp/icons/simplify/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/down.png b/subsonic-main/src/main/webapp/icons/simplify/down.png
new file mode 100644
index 00000000..fe798e45
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/download.png b/subsonic-main/src/main/webapp/icons/simplify/download.png
new file mode 100644
index 00000000..dbebf431
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/error.png b/subsonic-main/src/main/webapp/icons/simplify/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/favicon.ico b/subsonic-main/src/main/webapp/icons/simplify/favicon.ico
new file mode 100644
index 00000000..685c131f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/forward.png b/subsonic-main/src/main/webapp/icons/simplify/forward.png
new file mode 100644
index 00000000..99b0ec04
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/help.png b/subsonic-main/src/main/webapp/icons/simplify/help.png
new file mode 100644
index 00000000..fafc0ac5
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/help_small.png b/subsonic-main/src/main/webapp/icons/simplify/help_small.png
new file mode 100644
index 00000000..063af3e4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/home.png b/subsonic-main/src/main/webapp/icons/simplify/home.png
new file mode 100644
index 00000000..5bbee5cd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/log.png b/subsonic-main/src/main/webapp/icons/simplify/log.png
new file mode 100644
index 00000000..60d07c2b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/logo.png b/subsonic-main/src/main/webapp/icons/simplify/logo.png
new file mode 100644
index 00000000..553a2bf0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/more.png b/subsonic-main/src/main/webapp/icons/simplify/more.png
new file mode 100644
index 00000000..378e05f2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/now_playing.png b/subsonic-main/src/main/webapp/icons/simplify/now_playing.png
new file mode 100644
index 00000000..10cc8cfc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/paypal.gif b/subsonic-main/src/main/webapp/icons/simplify/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/phone.png b/subsonic-main/src/main/webapp/icons/simplify/phone.png
new file mode 100644
index 00000000..693c8620
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/play.png b/subsonic-main/src/main/webapp/icons/simplify/play.png
new file mode 100644
index 00000000..76bc8586
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/playing.png b/subsonic-main/src/main/webapp/icons/simplify/playing.png
new file mode 100644
index 00000000..e46576de
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/podcast.png b/subsonic-main/src/main/webapp/icons/simplify/podcast.png
new file mode 100644
index 00000000..51004d5f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/podcast_small.png b/subsonic-main/src/main/webapp/icons/simplify/podcast_small.png
new file mode 100644
index 00000000..9af49252
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/progress.png b/subsonic-main/src/main/webapp/icons/simplify/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/random.png b/subsonic-main/src/main/webapp/icons/simplify/random.png
new file mode 100644
index 00000000..dfb9ec6e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/rating_half.png b/subsonic-main/src/main/webapp/icons/simplify/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/rating_off.png b/subsonic-main/src/main/webapp/icons/simplify/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/rating_on.png b/subsonic-main/src/main/webapp/icons/simplify/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/remove.png b/subsonic-main/src/main/webapp/icons/simplify/remove.png
new file mode 100644
index 00000000..c0afb187
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/search.png b/subsonic-main/src/main/webapp/icons/simplify/search.png
new file mode 100644
index 00000000..4607c1bd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/settings.png b/subsonic-main/src/main/webapp/icons/simplify/settings.png
new file mode 100644
index 00000000..ff2efcd9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/status.png b/subsonic-main/src/main/webapp/icons/simplify/status.png
new file mode 100644
index 00000000..487c309b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/up.png b/subsonic-main/src/main/webapp/icons/simplify/up.png
new file mode 100644
index 00000000..0b2a5f0b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/simplify/upload.png b/subsonic-main/src/main/webapp/icons/simplify/upload.png
new file mode 100644
index 00000000..078db7c2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/simplify/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/favicon.ico b/subsonic-main/src/main/webapp/icons/slick/favicon.ico
new file mode 100644
index 00000000..c7eb8409
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/help.png b/subsonic-main/src/main/webapp/icons/slick/help.png
new file mode 100644
index 00000000..bb22b95d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/home.png b/subsonic-main/src/main/webapp/icons/slick/home.png
new file mode 100644
index 00000000..6b729b81
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/more.png b/subsonic-main/src/main/webapp/icons/slick/more.png
new file mode 100644
index 00000000..5244ad4a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/now_playing.png b/subsonic-main/src/main/webapp/icons/slick/now_playing.png
new file mode 100644
index 00000000..0c9836a4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/podcast_large.png b/subsonic-main/src/main/webapp/icons/slick/podcast_large.png
new file mode 100644
index 00000000..694636ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/podcast_large.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/settings.png b/subsonic-main/src/main/webapp/icons/slick/settings.png
new file mode 100644
index 00000000..d62f6f98
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/status.png b/subsonic-main/src/main/webapp/icons/slick/status.png
new file mode 100644
index 00000000..f1dcd989
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/subsonic.png b/subsonic-main/src/main/webapp/icons/slick/subsonic.png
new file mode 100644
index 00000000..7d839665
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/subsonic.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/slick/top_bg.jpg b/subsonic-main/src/main/webapp/icons/slick/top_bg.jpg
new file mode 100644
index 00000000..a095ae2e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/slick/top_bg.jpg
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/add.png b/subsonic-main/src/main/webapp/icons/sonic/add.png
new file mode 100644
index 00000000..14695eb2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/back.png b/subsonic-main/src/main/webapp/icons/sonic/back.png
new file mode 100644
index 00000000..3aa8dce5
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/clear_rating.png b/subsonic-main/src/main/webapp/icons/sonic/clear_rating.png
new file mode 100644
index 00000000..5d0104e7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/donate.png b/subsonic-main/src/main/webapp/icons/sonic/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/down.png b/subsonic-main/src/main/webapp/icons/sonic/down.png
new file mode 100644
index 00000000..064a3659
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/download.png b/subsonic-main/src/main/webapp/icons/sonic/download.png
new file mode 100644
index 00000000..752ad374
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/error.png b/subsonic-main/src/main/webapp/icons/sonic/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/favicon.ico b/subsonic-main/src/main/webapp/icons/sonic/favicon.ico
new file mode 100644
index 00000000..ef507a55
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/forward.png b/subsonic-main/src/main/webapp/icons/sonic/forward.png
new file mode 100644
index 00000000..7af499e1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/help.png b/subsonic-main/src/main/webapp/icons/sonic/help.png
new file mode 100644
index 00000000..4f00214f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/help_small.png b/subsonic-main/src/main/webapp/icons/sonic/help_small.png
new file mode 100644
index 00000000..cf98d072
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/home.png b/subsonic-main/src/main/webapp/icons/sonic/home.png
new file mode 100644
index 00000000..50fe549d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/log.png b/subsonic-main/src/main/webapp/icons/sonic/log.png
new file mode 100644
index 00000000..7764ae53
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/logo.png b/subsonic-main/src/main/webapp/icons/sonic/logo.png
new file mode 100644
index 00000000..68aca05c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/more.png b/subsonic-main/src/main/webapp/icons/sonic/more.png
new file mode 100644
index 00000000..66775f30
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/now_playing.png b/subsonic-main/src/main/webapp/icons/sonic/now_playing.png
new file mode 100644
index 00000000..da58451e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/paypal.gif b/subsonic-main/src/main/webapp/icons/sonic/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/phone.png b/subsonic-main/src/main/webapp/icons/sonic/phone.png
new file mode 100644
index 00000000..c2c4f494
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/play.png b/subsonic-main/src/main/webapp/icons/sonic/play.png
new file mode 100644
index 00000000..782ceae1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/playing.png b/subsonic-main/src/main/webapp/icons/sonic/playing.png
new file mode 100644
index 00000000..4983700a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/podcast.png b/subsonic-main/src/main/webapp/icons/sonic/podcast.png
new file mode 100644
index 00000000..98511ad4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/podcast_small.png b/subsonic-main/src/main/webapp/icons/sonic/podcast_small.png
new file mode 100644
index 00000000..d5fa56bd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/progress.png b/subsonic-main/src/main/webapp/icons/sonic/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/random.png b/subsonic-main/src/main/webapp/icons/sonic/random.png
new file mode 100644
index 00000000..6289938c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/rating_half.png b/subsonic-main/src/main/webapp/icons/sonic/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/rating_off.png b/subsonic-main/src/main/webapp/icons/sonic/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/rating_on.png b/subsonic-main/src/main/webapp/icons/sonic/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/remove.png b/subsonic-main/src/main/webapp/icons/sonic/remove.png
new file mode 100644
index 00000000..4f9c28a0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/search.png b/subsonic-main/src/main/webapp/icons/sonic/search.png
new file mode 100644
index 00000000..4228f6bc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/settings.png b/subsonic-main/src/main/webapp/icons/sonic/settings.png
new file mode 100644
index 00000000..9897b183
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/status.png b/subsonic-main/src/main/webapp/icons/sonic/status.png
new file mode 100644
index 00000000..21ce5615
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/up.png b/subsonic-main/src/main/webapp/icons/sonic/up.png
new file mode 100644
index 00000000..19155c33
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic/upload.png b/subsonic-main/src/main/webapp/icons/sonic/upload.png
new file mode 100644
index 00000000..eb082cbc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/add.png b/subsonic-main/src/main/webapp/icons/sonic_blue/add.png
new file mode 100644
index 00000000..d16e3473
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/back.png b/subsonic-main/src/main/webapp/icons/sonic_blue/back.png
new file mode 100644
index 00000000..abf1177f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/clear_rating.png b/subsonic-main/src/main/webapp/icons/sonic_blue/clear_rating.png
new file mode 100644
index 00000000..64d78601
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/donate.png b/subsonic-main/src/main/webapp/icons/sonic_blue/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/down.png b/subsonic-main/src/main/webapp/icons/sonic_blue/down.png
new file mode 100644
index 00000000..16efb09d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/download.png b/subsonic-main/src/main/webapp/icons/sonic_blue/download.png
new file mode 100644
index 00000000..6667c702
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/error.png b/subsonic-main/src/main/webapp/icons/sonic_blue/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/favicon.ico b/subsonic-main/src/main/webapp/icons/sonic_blue/favicon.ico
new file mode 100644
index 00000000..ef507a55
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/forward.png b/subsonic-main/src/main/webapp/icons/sonic_blue/forward.png
new file mode 100644
index 00000000..d288c0ce
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/help.png b/subsonic-main/src/main/webapp/icons/sonic_blue/help.png
new file mode 100644
index 00000000..1ca1f3de
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/help_small.png b/subsonic-main/src/main/webapp/icons/sonic_blue/help_small.png
new file mode 100644
index 00000000..88283157
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/home.png b/subsonic-main/src/main/webapp/icons/sonic_blue/home.png
new file mode 100644
index 00000000..47acba54
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/log.png b/subsonic-main/src/main/webapp/icons/sonic_blue/log.png
new file mode 100644
index 00000000..a4fa9297
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/logo.png b/subsonic-main/src/main/webapp/icons/sonic_blue/logo.png
new file mode 100644
index 00000000..947d6e25
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/more.png b/subsonic-main/src/main/webapp/icons/sonic_blue/more.png
new file mode 100644
index 00000000..53fb8bb8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/now_playing.png b/subsonic-main/src/main/webapp/icons/sonic_blue/now_playing.png
new file mode 100644
index 00000000..42931eff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/paypal.gif b/subsonic-main/src/main/webapp/icons/sonic_blue/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/phone.png b/subsonic-main/src/main/webapp/icons/sonic_blue/phone.png
new file mode 100644
index 00000000..83b16afb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/play.png b/subsonic-main/src/main/webapp/icons/sonic_blue/play.png
new file mode 100644
index 00000000..a6e692a2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/playing.png b/subsonic-main/src/main/webapp/icons/sonic_blue/playing.png
new file mode 100644
index 00000000..44882736
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/podcast.png b/subsonic-main/src/main/webapp/icons/sonic_blue/podcast.png
new file mode 100644
index 00000000..d5cd0f46
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/podcast_small.png b/subsonic-main/src/main/webapp/icons/sonic_blue/podcast_small.png
new file mode 100644
index 00000000..87d2fe25
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/progress.png b/subsonic-main/src/main/webapp/icons/sonic_blue/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/random.png b/subsonic-main/src/main/webapp/icons/sonic_blue/random.png
new file mode 100644
index 00000000..2b0fdb06
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/rating_half.png b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/rating_off.png b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/rating_on.png b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/remove.png b/subsonic-main/src/main/webapp/icons/sonic_blue/remove.png
new file mode 100644
index 00000000..56599a81
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/search.png b/subsonic-main/src/main/webapp/icons/sonic_blue/search.png
new file mode 100644
index 00000000..9c9b3e73
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/settings.png b/subsonic-main/src/main/webapp/icons/sonic_blue/settings.png
new file mode 100644
index 00000000..e3a528a0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/status.png b/subsonic-main/src/main/webapp/icons/sonic_blue/status.png
new file mode 100644
index 00000000..f99cba27
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/up.png b/subsonic-main/src/main/webapp/icons/sonic_blue/up.png
new file mode 100644
index 00000000..98b4a78c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_blue/upload.png b/subsonic-main/src/main/webapp/icons/sonic_blue/upload.png
new file mode 100644
index 00000000..ecfad1a9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_blue/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/add.png b/subsonic-main/src/main/webapp/icons/sonic_white/add.png
new file mode 100644
index 00000000..3579c354
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/add.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/back.png b/subsonic-main/src/main/webapp/icons/sonic_white/back.png
new file mode 100644
index 00000000..0fa80d9a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/back.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/clear_rating.png b/subsonic-main/src/main/webapp/icons/sonic_white/clear_rating.png
new file mode 100644
index 00000000..5d0104e7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/clear_rating.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/donate.png b/subsonic-main/src/main/webapp/icons/sonic_white/donate.png
new file mode 100644
index 00000000..f9c17ceb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/donate.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/down.png b/subsonic-main/src/main/webapp/icons/sonic_white/down.png
new file mode 100644
index 00000000..f509be10
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/down.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/download.png b/subsonic-main/src/main/webapp/icons/sonic_white/download.png
new file mode 100644
index 00000000..ee5ebb58
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/download.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/error.png b/subsonic-main/src/main/webapp/icons/sonic_white/error.png
new file mode 100644
index 00000000..c7d60958
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/error.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/favicon.ico b/subsonic-main/src/main/webapp/icons/sonic_white/favicon.ico
new file mode 100644
index 00000000..ef507a55
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/favicon.ico
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/forward.png b/subsonic-main/src/main/webapp/icons/sonic_white/forward.png
new file mode 100644
index 00000000..cce6e6f4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/forward.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/help.png b/subsonic-main/src/main/webapp/icons/sonic_white/help.png
new file mode 100644
index 00000000..7380ec10
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/help.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/help_small.png b/subsonic-main/src/main/webapp/icons/sonic_white/help_small.png
new file mode 100644
index 00000000..cf98d072
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/help_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/home.png b/subsonic-main/src/main/webapp/icons/sonic_white/home.png
new file mode 100644
index 00000000..882b7f4a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/home.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/log.png b/subsonic-main/src/main/webapp/icons/sonic_white/log.png
new file mode 100644
index 00000000..7764ae53
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/log.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/logo.png b/subsonic-main/src/main/webapp/icons/sonic_white/logo.png
new file mode 100644
index 00000000..68aca05c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/logo.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/more.png b/subsonic-main/src/main/webapp/icons/sonic_white/more.png
new file mode 100644
index 00000000..17274a6b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/more.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/now_playing.png b/subsonic-main/src/main/webapp/icons/sonic_white/now_playing.png
new file mode 100644
index 00000000..da58451e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/now_playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/paypal.gif b/subsonic-main/src/main/webapp/icons/sonic_white/paypal.gif
new file mode 100644
index 00000000..2a587165
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/paypal.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/phone.png b/subsonic-main/src/main/webapp/icons/sonic_white/phone.png
new file mode 100644
index 00000000..c2c4f494
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/phone.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/play.png b/subsonic-main/src/main/webapp/icons/sonic_white/play.png
new file mode 100644
index 00000000..7ee33876
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/play.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/playing.png b/subsonic-main/src/main/webapp/icons/sonic_white/playing.png
new file mode 100644
index 00000000..554d0404
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/playing.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/podcast.png b/subsonic-main/src/main/webapp/icons/sonic_white/podcast.png
new file mode 100644
index 00000000..9d7b47dd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/podcast.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/podcast_small.png b/subsonic-main/src/main/webapp/icons/sonic_white/podcast_small.png
new file mode 100644
index 00000000..d5fa56bd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/podcast_small.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/progress.png b/subsonic-main/src/main/webapp/icons/sonic_white/progress.png
new file mode 100644
index 00000000..13b465ba
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/progress.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/random.png b/subsonic-main/src/main/webapp/icons/sonic_white/random.png
new file mode 100644
index 00000000..6289938c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/random.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/rating_half.png b/subsonic-main/src/main/webapp/icons/sonic_white/rating_half.png
new file mode 100644
index 00000000..08ab0d39
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/rating_half.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/rating_off.png b/subsonic-main/src/main/webapp/icons/sonic_white/rating_off.png
new file mode 100644
index 00000000..a0d9bbb1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/rating_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/rating_on.png b/subsonic-main/src/main/webapp/icons/sonic_white/rating_on.png
new file mode 100644
index 00000000..8c4d6602
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/rating_on.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/remove.png b/subsonic-main/src/main/webapp/icons/sonic_white/remove.png
new file mode 100644
index 00000000..7c63ab7e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/remove.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/search.png b/subsonic-main/src/main/webapp/icons/sonic_white/search.png
new file mode 100644
index 00000000..6b885f91
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/search.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/settings.png b/subsonic-main/src/main/webapp/icons/sonic_white/settings.png
new file mode 100644
index 00000000..707e0a20
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/settings.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/status.png b/subsonic-main/src/main/webapp/icons/sonic_white/status.png
new file mode 100644
index 00000000..53f13830
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/up.png b/subsonic-main/src/main/webapp/icons/sonic_white/up.png
new file mode 100644
index 00000000..1e7260ca
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/up.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/sonic_white/upload.png b/subsonic-main/src/main/webapp/icons/sonic_white/upload.png
new file mode 100644
index 00000000..8dc0cfcb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/sonic_white/upload.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/spinner.gif b/subsonic-main/src/main/webapp/icons/spinner.gif
new file mode 100644
index 00000000..e192ca89
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/spinner.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/star_off.png b/subsonic-main/src/main/webapp/icons/star_off.png
new file mode 100644
index 00000000..ada53fc3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/star_off.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/starred.png b/subsonic-main/src/main/webapp/icons/starred.png
new file mode 100644
index 00000000..5476c401
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/starred.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/status.png b/subsonic-main/src/main/webapp/icons/status.png
new file mode 100644
index 00000000..8df38463
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/status.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/subair.png b/subsonic-main/src/main/webapp/icons/subair.png
new file mode 100644
index 00000000..a8a36dbe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/subair.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/subsonic_black.png b/subsonic-main/src/main/webapp/icons/subsonic_black.png
new file mode 100644
index 00000000..21448a8e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/subsonic_black.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/subsonic_white.png b/subsonic-main/src/main/webapp/icons/subsonic_white.png
new file mode 100644
index 00000000..65db730a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/subsonic_white.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/up.gif b/subsonic-main/src/main/webapp/icons/up.gif
new file mode 100644
index 00000000..20223146
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/up.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/upload.gif b/subsonic-main/src/main/webapp/icons/upload.gif
new file mode 100644
index 00000000..b1605639
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/upload.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/icons/wap.png b/subsonic-main/src/main/webapp/icons/wap.png
new file mode 100644
index 00000000..ea734215
--- /dev/null
+++ b/subsonic-main/src/main/webapp/icons/wap.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/index.html b/subsonic-main/src/main/webapp/index.html
new file mode 100644
index 00000000..5c6cb4b7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/index.html
@@ -0,0 +1,10 @@
+<html>
+
+<head>
+ <meta http-equiv="refresh" content="0;URL=index.view">
+</head>
+
+<body>
+</body>
+
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/index.jsp b/subsonic-main/src/main/webapp/index.jsp
new file mode 100644
index 00000000..f9f5cd30
--- /dev/null
+++ b/subsonic-main/src/main/webapp/index.jsp
@@ -0,0 +1,10 @@
+<html>
+
+<head>
+ <meta http-equiv="refresh" content="0;URL=index.view">
+</head>
+
+<body>
+</body>
+
+</html> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/AC_OETags.js b/subsonic-main/src/main/webapp/script/AC_OETags.js
new file mode 100644
index 00000000..6482d168
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/AC_OETags.js
@@ -0,0 +1,269 @@
+// Flash Player Version Detection - Rev 1.5
+// Detect Client Browser type
+// Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved.
+var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
+var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
+var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;
+
+function ControlVersion()
+{
+ var version;
+ var axo;
+ var e;
+
+ // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry
+
+ try {
+ // version will be set for 7.X or greater players
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 6.X players only
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
+
+ // installed player is some revision of 6.0
+ // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29,
+ // so we have to be careful.
+
+ // default to the first public version
+ version = "WIN 6,0,21,0";
+
+ // throws if AllowScripAccess does not exist (introduced in 6.0r47)
+ axo.AllowScriptAccess = "always";
+
+ // safe to call for 6.0r47 or greater
+ version = axo.GetVariable("$version");
+
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 4.X or 5.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = axo.GetVariable("$version");
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 3.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3");
+ version = "WIN 3,0,18,0";
+ } catch (e) {
+ }
+ }
+
+ if (!version)
+ {
+ try {
+ // version will be set for 2.X player
+ axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ version = "WIN 2,0,0,11";
+ } catch (e) {
+ version = -1;
+ }
+ }
+
+ return version;
+}
+
+// JavaScript helper required to detect Flash Player PlugIn version information
+function GetSwfVer(){
+ // NS/Opera version >= 3 check for Flash plugin in plugin array
+ var flashVer = -1;
+
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) {
+ var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : "";
+ var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description;
+ var descArray = flashDescription.split(" ");
+ var tempArrayMajor = descArray[2].split(".");
+ var versionMajor = tempArrayMajor[0];
+ var versionMinor = tempArrayMajor[1];
+ if ( descArray[3] != "" ) {
+ tempArrayMinor = descArray[3].split("r");
+ } else {
+ tempArrayMinor = descArray[4].split("r");
+ }
+ var versionRevision = tempArrayMinor[1] > 0 ? tempArrayMinor[1] : 0;
+ var flashVer = versionMajor + "." + versionMinor + "." + versionRevision;
+ }
+ }
+ // MSN/WebTV 2.6 supports Flash 4
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4;
+ // WebTV 2.5 supports Flash 3
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3;
+ // older WebTV supports Flash 2
+ else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2;
+ else if ( isIE && isWin && !isOpera ) {
+ flashVer = ControlVersion();
+ }
+ return flashVer;
+}
+
+// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available
+function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision)
+{
+ versionStr = GetSwfVer();
+ if (versionStr == -1 ) {
+ return false;
+ } else if (versionStr != 0) {
+ if(isIE && isWin && !isOpera) {
+ // Given "WIN 2,0,0,11"
+ tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"]
+ tempString = tempArray[1]; // "2,0,0,11"
+ versionArray = tempString.split(","); // ['2', '0', '0', '11']
+ } else {
+ versionArray = versionStr.split(".");
+ }
+ var versionMajor = versionArray[0];
+ var versionMinor = versionArray[1];
+ var versionRevision = versionArray[2];
+
+ // is the major.revision >= requested major.revision AND the minor version >= requested minor
+ if (versionMajor > parseFloat(reqMajorVer)) {
+ return true;
+ } else if (versionMajor == parseFloat(reqMajorVer)) {
+ if (versionMinor > parseFloat(reqMinorVer))
+ return true;
+ else if (versionMinor == parseFloat(reqMinorVer)) {
+ if (versionRevision >= parseFloat(reqRevision))
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+function AC_AddExtension(src, ext)
+{
+ if (src.indexOf('?') != -1)
+ return src.replace(/\?/, ext+'?');
+ else
+ return src + ext;
+}
+
+function AC_Generateobj(objAttrs, params, embedAttrs)
+{
+ var str = '';
+ if (isIE && isWin && !isOpera)
+ {
+ str += '<object ';
+ for (var i in objAttrs)
+ str += i + '="' + objAttrs[i] + '" ';
+ for (var i in params)
+ str += '><param name="' + i + '" value="' + params[i] + '" /> ';
+ str += '></object>';
+ } else {
+ str += '<embed ';
+ for (var i in embedAttrs)
+ str += i + '="' + embedAttrs[i] + '" ';
+ str += '> </embed>';
+ }
+
+ document.write(str);
+}
+
+function AC_FL_RunContent(){
+ var ret =
+ AC_GetArgs
+ ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
+ , "application/x-shockwave-flash"
+ );
+ AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
+}
+
+function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
+ var ret = new Object();
+ ret.embedAttrs = new Object();
+ ret.params = new Object();
+ ret.objAttrs = new Object();
+ for (var i=0; i < args.length; i=i+2){
+ var currArg = args[i].toLowerCase();
+
+ switch (currArg){
+ case "classid":
+ break;
+ case "pluginspage":
+ ret.embedAttrs[args[i]] = args[i+1];
+ break;
+ case "src":
+ case "movie":
+ args[i+1] = AC_AddExtension(args[i+1], ext);
+ ret.embedAttrs["src"] = args[i+1];
+ ret.params[srcParamName] = args[i+1];
+ break;
+ case "onafterupdate":
+ case "onbeforeupdate":
+ case "onblur":
+ case "oncellchange":
+ case "onclick":
+ case "ondblClick":
+ case "ondrag":
+ case "ondragend":
+ case "ondragenter":
+ case "ondragleave":
+ case "ondragover":
+ case "ondrop":
+ case "onfinish":
+ case "onfocus":
+ case "onhelp":
+ case "onmousedown":
+ case "onmouseup":
+ case "onmouseover":
+ case "onmousemove":
+ case "onmouseout":
+ case "onkeypress":
+ case "onkeydown":
+ case "onkeyup":
+ case "onload":
+ case "onlosecapture":
+ case "onpropertychange":
+ case "onreadystatechange":
+ case "onrowsdelete":
+ case "onrowenter":
+ case "onrowexit":
+ case "onrowsinserted":
+ case "onstart":
+ case "onscroll":
+ case "onbeforeeditfocus":
+ case "onactivate":
+ case "onbeforedeactivate":
+ case "ondeactivate":
+ case "type":
+ case "codebase":
+ ret.objAttrs[args[i]] = args[i+1];
+ break;
+ case "id":
+ case "width":
+ case "height":
+ case "align":
+ case "vspace":
+ case "hspace":
+ case "class":
+ case "title":
+ case "accesskey":
+ case "name":
+ case "tabindex":
+ ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
+ break;
+ default:
+ ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
+ }
+ }
+ ret.objAttrs["classid"] = classid;
+ if (mimeType) ret.embedAttrs["type"] = mimeType;
+ return ret;
+}
+
+
diff --git a/subsonic-main/src/main/webapp/script/builder.js b/subsonic-main/src/main/webapp/script/builder.js
new file mode 100644
index 00000000..dba8beca
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/builder.js
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
+
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Builder = {
+ NODEMAP: {
+ AREA: 'map',
+ CAPTION: 'table',
+ COL: 'table',
+ COLGROUP: 'table',
+ LEGEND: 'fieldset',
+ OPTGROUP: 'select',
+ OPTION: 'select',
+ PARAM: 'object',
+ TBODY: 'table',
+ TD: 'table',
+ TFOOT: 'table',
+ TH: 'table',
+ THEAD: 'table',
+ TR: 'table'
+ },
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+ // due to a Firefox bug
+ node: function(elementName) {
+ elementName = elementName.toUpperCase();
+
+ // try innerHTML approach
+ var parentTag = this.NODEMAP[elementName] || 'div';
+ var parentElement = document.createElement(parentTag);
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+ } catch(e) {}
+ var element = parentElement.firstChild || null;
+
+ // see if browser added wrapping tags
+ if(element && (element.tagName.toUpperCase() != elementName))
+ element = element.getElementsByTagName(elementName)[0];
+
+ // fallback to createElement approach
+ if(!element) element = document.createElement(elementName);
+
+ // abort if nothing could be created
+ if(!element) return;
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array) ||
+ arguments[1].tagName) {
+ this._children(element, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length) {
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" +elementName + " " +
+ attrs + "></" + elementName + ">";
+ } catch(e) {}
+ element = parentElement.firstChild || null;
+ // workaround firefox 1.0.X bug
+ if(!element) {
+ element = document.createElement(elementName);
+ for(attr in arguments[1])
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+ }
+ if(element.tagName.toUpperCase() != elementName)
+ element = parentElement.getElementsByTagName(elementName)[0];
+ }
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element, arguments[2]);
+
+ return $(element);
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+
+ ATTR_MAP: {
+ 'className': 'class',
+ 'htmlFor': 'for'
+ },
+
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(children.tagName) {
+ element.appendChild(children);
+ return;
+ }
+ if(typeof children=='object') { // array can hold nodes and text
+ children.flatten().each( function(e) {
+ if(typeof e=='object')
+ element.appendChild(e);
+ else
+ if(Builder._isStringOrNumber(e))
+ element.appendChild(Builder._text(e));
+ });
+ } else
+ if(Builder._isStringOrNumber(children))
+ element.appendChild(Builder._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ },
+ build: function(html) {
+ var element = this.node('div');
+ $(element).update(html.strip());
+ return element.down();
+ },
+ dump: function(scope) {
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
+
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+
+ tags.each( function(tag){
+ scope[tag] = function() {
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
+ };
+ });
+ }
+}; \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/controls.js b/subsonic-main/src/main/webapp/script/controls.js
new file mode 100644
index 00000000..c56ccb79
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/controls.js
@@ -0,0 +1,965 @@
+// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
+
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+if(typeof Effect == 'undefined')
+ throw("controls.js requires including script.aculo.us' effects.js library");
+
+var Autocompleter = { };
+Autocompleter.Base = Class.create({
+ baseInitialize: function(element, update, options) {
+ element = $(element);
+ this.element = element;
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+ this.oldElementValue = this.element.value;
+
+ if(this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || { };
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {
+ setHeight: false,
+ offsetTop: element.offsetHeight
+ });
+ }
+ Effect.Appear(update,{duration:0.15});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+ if(typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+ // Force carriage returns as token delimiters anyway
+ if (!this.options.tokens.include('\n'))
+ this.options.tokens.push('\n');
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix &&
+ (Prototype.Browser.IE) &&
+ (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '<iframe id="' + this.update.id + '_iefix" '+
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+ },
+
+ fixIEOverlapping: function() {
+ Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ },
+
+ hide: function() {
+ this.stopIndicator();
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
+ (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ activate: function() {
+ this.changed = false;
+ this.hasFocus = true;
+ this.getUpdatedChoices();
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 250);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--;
+ else this.index = this.entryCount-1;
+ this.getEntry(this.index).scrollIntoView(true);
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++;
+ else this.index = 0;
+ this.getEntry(this.index).scrollIntoView(false);
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+ var value = '';
+ if (this.options.select) {
+ var nodes = $(selectedElement).select('.' + this.options.select) || [];
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+ } else
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+ var bounds = this.getTokenBounds();
+ if (bounds[0] != -1) {
+ var newValue = this.element.value.substr(0, bounds[0]);
+ var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value + this.element.value.substr(bounds[1]);
+ } else {
+ this.element.value = value;
+ }
+ this.oldElementValue = this.element.value;
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.down());
+
+ if(this.update.firstChild && this.update.down().childNodes) {
+ this.entryCount =
+ this.update.down().childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+ this.index = 0;
+
+ if(this.entryCount==1 && this.options.autoSelect) {
+ this.selectEntry();
+ this.hide();
+ } else {
+ this.render();
+ }
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ this.tokenBounds = null;
+ if(this.getToken().length>=this.options.minChars) {
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ this.oldElementValue = this.element.value;
+ },
+
+ getToken: function() {
+ var bounds = this.getTokenBounds();
+ return this.element.value.substring(bounds[0], bounds[1]).strip();
+ },
+
+ getTokenBounds: function() {
+ if (null != this.tokenBounds) return this.tokenBounds;
+ var value = this.element.value;
+ if (value.strip().empty()) return [-1, 0];
+ var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
+ var offset = (diff == this.oldElementValue.length ? 1 : 0);
+ var prevTokenPos = -1, nextTokenPos = value.length;
+ var tp;
+ for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
+ tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
+ if (tp > prevTokenPos) prevTokenPos = tp;
+ tp = value.indexOf(this.options.tokens[index], diff + offset);
+ if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
+ }
+ return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
+ }
+});
+
+Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
+ var boundary = Math.min(newS.length, oldS.length);
+ for (var index = 0; index < boundary; ++index)
+ if (newS[index] != oldS[index])
+ return index;
+ return boundary;
+};
+
+Ajax.Autocompleter = Class.create(Autocompleter.Base, {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ this.startIndicator();
+
+ var entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create(Autocompleter.Base, {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ elem.substr(entry.length) + "</li>");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
+ return "<ul>" + ret.join('') + "</ul>";
+ }
+ }, options || { });
+ }
+});
+
+// AJAX in-place editor and collection editor
+// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+};
+
+Ajax.InPlaceEditor = Class.create({
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = element = $(element);
+ this.prepareOptions();
+ this._controls = { };
+ arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
+ Object.extend(this.options, options || { });
+ if (!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + '-inplaceeditor';
+ if ($(this.options.formId))
+ this.options.formId = '';
+ }
+ if (this.options.externalControl)
+ this.options.externalControl = $(this.options.externalControl);
+ if (!this.options.externalControl)
+ this.options.externalControlOnly = false;
+ this._originalBackground = this.element.getStyle('background-color') || 'transparent';
+ this.element.title = this.options.clickToEditText;
+ this._boundCancelHandler = this.handleFormCancellation.bind(this);
+ this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
+ this._boundFailureHandler = this.handleAJAXFailure.bind(this);
+ this._boundSubmitHandler = this.handleFormSubmission.bind(this);
+ this._boundWrapperHandler = this.wrapUp.bind(this);
+ this.registerListeners();
+ },
+ checkForEscapeOrReturn: function(e) {
+ if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
+ if (Event.KEY_ESC == e.keyCode)
+ this.handleFormCancellation(e);
+ else if (Event.KEY_RETURN == e.keyCode)
+ this.handleFormSubmission(e);
+ },
+ createControl: function(mode, handler, extraClasses) {
+ var control = this.options[mode + 'Control'];
+ var text = this.options[mode + 'Text'];
+ if ('button' == control) {
+ var btn = document.createElement('input');
+ btn.type = 'submit';
+ btn.value = text;
+ btn.className = 'editor_' + mode + '_button';
+ if ('cancel' == mode)
+ btn.onclick = this._boundCancelHandler;
+ this._form.appendChild(btn);
+ this._controls[mode] = btn;
+ } else if ('link' == control) {
+ var link = document.createElement('a');
+ link.href = '#';
+ link.appendChild(document.createTextNode(text));
+ link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
+ link.className = 'editor_' + mode + '_link';
+ if (extraClasses)
+ link.className += ' ' + extraClasses;
+ this._form.appendChild(link);
+ this._controls[mode] = link;
+ }
+ },
+ createEditField: function() {
+ var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
+ var fld;
+ if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
+ fld = document.createElement('input');
+ fld.type = 'text';
+ var size = this.options.size || this.options.cols || 0;
+ if (0 < size) fld.size = size;
+ } else {
+ fld = document.createElement('textarea');
+ fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
+ fld.cols = this.options.cols || 40;
+ }
+ fld.name = this.options.paramName;
+ fld.value = text; // No HTML breaks conversion anymore
+ fld.className = 'editor_field';
+ if (this.options.submitOnBlur)
+ fld.onblur = this._boundSubmitHandler;
+ this._controls.editor = fld;
+ if (this.options.loadTextURL)
+ this.loadExternalText();
+ this._form.appendChild(this._controls.editor);
+ },
+ createForm: function() {
+ var ipe = this;
+ function addText(mode, condition) {
+ var text = ipe.options['text' + mode + 'Controls'];
+ if (!text || condition === false) return;
+ ipe._form.appendChild(document.createTextNode(text));
+ };
+ this._form = $(document.createElement('form'));
+ this._form.id = this.options.formId;
+ this._form.addClassName(this.options.formClassName);
+ this._form.onsubmit = this._boundSubmitHandler;
+ this.createEditField();
+ if ('textarea' == this._controls.editor.tagName.toLowerCase())
+ this._form.appendChild(document.createElement('br'));
+ if (this.options.onFormCustomization)
+ this.options.onFormCustomization(this, this._form);
+ addText('Before', this.options.okControl || this.options.cancelControl);
+ this.createControl('ok', this._boundSubmitHandler);
+ addText('Between', this.options.okControl && this.options.cancelControl);
+ this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
+ addText('After', this.options.okControl || this.options.cancelControl);
+ },
+ destroy: function() {
+ if (this._oldInnerHTML)
+ this.element.innerHTML = this._oldInnerHTML;
+ this.leaveEditMode();
+ this.unregisterListeners();
+ },
+ enterEditMode: function(e) {
+ if (this._saving || this._editing) return;
+ this._editing = true;
+ this.triggerCallback('onEnterEditMode');
+ if (this.options.externalControl)
+ this.options.externalControl.hide();
+ this.element.hide();
+ this.createForm();
+ this.element.parentNode.insertBefore(this._form, this.element);
+ if (!this.options.loadTextURL)
+ this.postProcessEditField();
+ if (e) Event.stop(e);
+ },
+ enterHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.addClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onEnterHover');
+ },
+ getText: function() {
+ return this.element.innerHTML.unescapeHTML();
+ },
+ handleAJAXFailure: function(transport) {
+ this.triggerCallback('onFailure', transport);
+ if (this._oldInnerHTML) {
+ this.element.innerHTML = this._oldInnerHTML;
+ this._oldInnerHTML = null;
+ }
+ },
+ handleFormCancellation: function(e) {
+ this.wrapUp();
+ if (e) Event.stop(e);
+ },
+ handleFormSubmission: function(e) {
+ var form = this._form;
+ var value = $F(this._controls.editor);
+ this.prepareSubmission();
+ var params = this.options.callback(form, value) || '';
+ if (Object.isString(params))
+ params = params.toQueryParams();
+ params.editorId = this.element.id;
+ if (this.options.htmlResponse) {
+ var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Updater({ success: this.element }, this.url, options);
+ } else {
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: params,
+ onComplete: this._boundWrapperHandler,
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.url, options);
+ }
+ if (e) Event.stop(e);
+ },
+ leaveEditMode: function() {
+ this.element.removeClassName(this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ if (this.options.externalControl)
+ this.options.externalControl.show();
+ this._saving = false;
+ this._editing = false;
+ this._oldInnerHTML = null;
+ this.triggerCallback('onLeaveEditMode');
+ },
+ leaveHover: function(e) {
+ if (this.options.hoverClassName)
+ this.element.removeClassName(this.options.hoverClassName);
+ if (this._saving) return;
+ this.triggerCallback('onLeaveHover');
+ },
+ loadExternalText: function() {
+ this._form.addClassName(this.options.loadingClassName);
+ this._controls.editor.disabled = true;
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._form.removeClassName(this.options.loadingClassName);
+ var text = transport.responseText;
+ if (this.options.stripLoadedTextTags)
+ text = text.stripTags();
+ this._controls.editor.value = text;
+ this._controls.editor.disabled = false;
+ this.postProcessEditField();
+ }.bind(this),
+ onFailure: this._boundFailureHandler
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
+ },
+ postProcessEditField: function() {
+ var fpc = this.options.fieldPostCreation;
+ if (fpc)
+ $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
+ },
+ prepareOptions: function() {
+ this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
+ Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
+ [this._extraDefaultOptions].flatten().compact().each(function(defs) {
+ Object.extend(this.options, defs);
+ }.bind(this));
+ },
+ prepareSubmission: function() {
+ this._saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ registerListeners: function() {
+ this._listeners = { };
+ var listener;
+ $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
+ listener = this[pair.value].bind(this);
+ this._listeners[pair.key] = listener;
+ if (!this.options.externalControlOnly)
+ this.element.observe(pair.key, listener);
+ if (this.options.externalControl)
+ this.options.externalControl.observe(pair.key, listener);
+ }.bind(this));
+ },
+ removeForm: function() {
+ if (!this._form) return;
+ this._form.remove();
+ this._form = null;
+ this._controls = { };
+ },
+ showSaving: function() {
+ this._oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ this.element.addClassName(this.options.savingClassName);
+ this.element.style.backgroundColor = this._originalBackground;
+ this.element.show();
+ },
+ triggerCallback: function(cbName, arg) {
+ if ('function' == typeof this.options[cbName]) {
+ this.options[cbName](this, arg);
+ }
+ },
+ unregisterListeners: function() {
+ $H(this._listeners).each(function(pair) {
+ if (!this.options.externalControlOnly)
+ this.element.stopObserving(pair.key, pair.value);
+ if (this.options.externalControl)
+ this.options.externalControl.stopObserving(pair.key, pair.value);
+ }.bind(this));
+ },
+ wrapUp: function(transport) {
+ this.leaveEditMode();
+ // Can't use triggerCallback due to backward compatibility: requires
+ // binding + direct element
+ this._boundComplete(transport, this.element);
+ }
+});
+
+Object.extend(Ajax.InPlaceEditor.prototype, {
+ dispose: Ajax.InPlaceEditor.prototype.destroy
+});
+
+Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
+ initialize: function($super, element, url, options) {
+ this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
+ $super(element, url, options);
+ },
+
+ createEditField: function() {
+ var list = document.createElement('select');
+ list.name = this.options.paramName;
+ list.size = 1;
+ this._controls.editor = list;
+ this._collection = this.options.collection || [];
+ if (this.options.loadCollectionURL)
+ this.loadCollection();
+ else
+ this.checkForExternalText();
+ this._form.appendChild(this._controls.editor);
+ },
+
+ loadCollection: function() {
+ this._form.addClassName(this.options.loadingClassName);
+ this.showLoadingText(this.options.loadingCollectionText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ var js = transport.responseText.strip();
+ if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
+ throw('Server returned an invalid collection representation.');
+ this._collection = eval(js);
+ this.checkForExternalText();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadCollectionURL, options);
+ },
+
+ showLoadingText: function(text) {
+ this._controls.editor.disabled = true;
+ var tempOption = this._controls.editor.firstChild;
+ if (!tempOption) {
+ tempOption = document.createElement('option');
+ tempOption.value = '';
+ this._controls.editor.appendChild(tempOption);
+ tempOption.selected = true;
+ }
+ tempOption.update((text || '').stripScripts().stripTags());
+ },
+
+ checkForExternalText: function() {
+ this._text = this.getText();
+ if (this.options.loadTextURL)
+ this.loadExternalText();
+ else
+ this.buildOptionList();
+ },
+
+ loadExternalText: function() {
+ this.showLoadingText(this.options.loadingText);
+ var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
+ Object.extend(options, {
+ parameters: 'editorId=' + encodeURIComponent(this.element.id),
+ onComplete: Prototype.emptyFunction,
+ onSuccess: function(transport) {
+ this._text = transport.responseText.strip();
+ this.buildOptionList();
+ }.bind(this),
+ onFailure: this.onFailure
+ });
+ new Ajax.Request(this.options.loadTextURL, options);
+ },
+
+ buildOptionList: function() {
+ this._form.removeClassName(this.options.loadingClassName);
+ this._collection = this._collection.map(function(entry) {
+ return 2 === entry.length ? entry : [entry, entry].flatten();
+ });
+ var marker = ('value' in this.options) ? this.options.value : this._text;
+ var textFound = this._collection.any(function(entry) {
+ return entry[0] == marker;
+ }.bind(this));
+ this._controls.editor.update('');
+ var option;
+ this._collection.each(function(entry, index) {
+ option = document.createElement('option');
+ option.value = entry[0];
+ option.selected = textFound ? entry[0] == marker : 0 == index;
+ option.appendChild(document.createTextNode(entry[1]));
+ this._controls.editor.appendChild(option);
+ }.bind(this));
+ this._controls.editor.disabled = false;
+ Field.scrollFreeActivate(this._controls.editor);
+ }
+});
+
+//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
+//**** This only exists for a while, in order to let ****
+//**** users adapt to the new API. Read up on the new ****
+//**** API and convert your code to it ASAP! ****
+
+Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
+ if (!options) return;
+ function fallback(name, expr) {
+ if (name in options || expr === undefined) return;
+ options[name] = expr;
+ };
+ fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
+ options.cancelLink == options.cancelButton == false ? false : undefined)));
+ fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
+ options.okLink == options.okButton == false ? false : undefined)));
+ fallback('highlightColor', options.highlightcolor);
+ fallback('highlightEndColor', options.highlightendcolor);
+};
+
+Object.extend(Ajax.InPlaceEditor, {
+ DefaultOptions: {
+ ajaxOptions: { },
+ autoRows: 3, // Use when multi-line w/ rows == 1
+ cancelControl: 'link', // 'link'|'button'|false
+ cancelText: 'cancel',
+ clickToEditText: 'Click to edit',
+ externalControl: null, // id|elt
+ externalControlOnly: false,
+ fieldPostCreation: 'activate', // 'activate'|'focus'|false
+ formClassName: 'inplaceeditor-form',
+ formId: null, // id|elt
+ highlightColor: '#ffff99',
+ highlightEndColor: '#ffffff',
+ hoverClassName: '',
+ htmlResponse: true,
+ loadingClassName: 'inplaceeditor-loading',
+ loadingText: 'Loading...',
+ okControl: 'button', // 'link'|'button'|false
+ okText: 'ok',
+ paramName: 'value',
+ rows: 1, // If 1 and multi-line, uses autoRows
+ savingClassName: 'inplaceeditor-saving',
+ savingText: 'Saving...',
+ size: 0,
+ stripLoadedTextTags: false,
+ submitOnBlur: false,
+ textAfterControls: '',
+ textBeforeControls: '',
+ textBetweenControls: ''
+ },
+ DefaultCallbacks: {
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ onComplete: function(transport, element) {
+ // For backward compatibility, this one is bound to the IPE, and passes
+ // the element directly. It was too often customized, so we don't break it.
+ new Effect.Highlight(element, {
+ startcolor: this.options.highlightColor, keepBackgroundImage: true });
+ },
+ onEnterEditMode: null,
+ onEnterHover: function(ipe) {
+ ipe.element.style.backgroundColor = ipe.options.highlightColor;
+ if (ipe._effect)
+ ipe._effect.cancel();
+ },
+ onFailure: function(transport, ipe) {
+ alert('Error communication with the server: ' + transport.responseText.stripTags());
+ },
+ onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
+ onLeaveEditMode: null,
+ onLeaveHover: function(ipe) {
+ ipe._effect = new Effect.Highlight(ipe.element, {
+ startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
+ restorecolor: ipe._originalBackground, keepBackgroundImage: true
+ });
+ }
+ },
+ Listeners: {
+ click: 'enterEditMode',
+ keydown: 'checkForEscapeOrReturn',
+ mouseover: 'enterHover',
+ mouseout: 'leaveHover'
+ }
+});
+
+Ajax.InPlaceCollectionEditor.DefaultOptions = {
+ loadingCollectionText: 'Loading options...'
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create({
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+}); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/effects.js b/subsonic-main/src/main/webapp/script/effects.js
new file mode 100644
index 00000000..f31a81a0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/effects.js
@@ -0,0 +1,1130 @@
+// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008
+
+// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if (this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if (this.slice(0,1) == '#') {
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if (this.length==7) color = this.toLowerCase();
+ }
+ }
+ return (color.length==7 ? color : (arguments[0] || this));
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
+ }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ element.setStyle({fontSize: (percent/100) + 'em'});
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+ return element;
+};
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+ try {
+ element = $(element);
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
+ Transitions: {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + .5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
+ return pos > 1 ? 1 : pos;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
+ },
+ pulse: function(pos, pulses) {
+ return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
+ },
+ spring: function(pos) {
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+ },
+ DefaultOptions: {
+ duration: 1.0, // seconds
+ fps: 100, // 100= assume 66fps max.
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ },
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if (child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ new Element('span', {style: tagifyStyle}).update(
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if (((typeof element == 'object') ||
+ Object.isFunction(element)) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || { });
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+ }, arguments[2] || { });
+ Effect[element.visible() ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = Object.isString(effect.options.queue) ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ this.effects.push(effect);
+
+ if (!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 15);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if (this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ for(var i=0, len=this.effects.length;i<len;i++)
+ this.effects[i] && this.effects[i].loop(timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if (!Object.isString(queueName)) return queueName;
+
+ return this.instances.get(queueName) ||
+ this.instances.set(queueName, new Effect.ScopedQueue());
+ }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+ position: null,
+ start: function(options) {
+ function codeForEvent(options,eventName){
+ return (
+ (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+ (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+ );
+ }
+ if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+ this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn+(this.options.duration*1000);
+ this.fromToDelta = this.options.to-this.options.from;
+ this.totalTime = this.finishOn-this.startOn;
+ this.totalFrames = this.options.fps*this.options.duration;
+
+ this.render = (function() {
+ function dispatch(effect, eventName) {
+ if (effect.options[eventName + 'Internal'])
+ effect.options[eventName + 'Internal'](effect);
+ if (effect.options[eventName])
+ effect.options[eventName](effect);
+ }
+
+ return function(pos) {
+ if (this.state === "idle") {
+ this.state = "running";
+ dispatch(this, 'beforeSetup');
+ if (this.setup) this.setup();
+ dispatch(this, 'afterSetup');
+ }
+ if (this.state === "running") {
+ pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
+ this.position = pos;
+ dispatch(this, 'beforeUpdate');
+ if (this.update) this.update(pos);
+ dispatch(this, 'afterUpdate');
+ }
+ };
+ })();
+
+ this.event('beforeStart');
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if (timePos >= this.startOn) {
+ if (timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if (this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / this.totalTime,
+ frame = (pos * this.totalFrames).round();
+ if (frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ cancel: function() {
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if (this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ var data = $H();
+ for(property in this)
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+ }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if (effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+ initialize: function(object, from, to) {
+ object = Object.isString(object) ? $(object) : object;
+ var args = $A(arguments), method = args.last(),
+ options = args.length == 5 ? args[3] : null;
+ this.method = Object.isFunction(method) ? method.bind(object) :
+ Object.isFunction(object[method]) ? object[method].bind(object) :
+ function(value) { object[method] = value };
+ this.start(Object.extend({ from: from, to: to }, options || { }));
+ },
+ update: function(position) {
+ this.method(position);
+ }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+ initialize: function() {
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+ },
+ update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ // make this work on IE on elements without 'layout'
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ var options = Object.extend({
+ from: this.element.getOpacity() || 0.0,
+ to: 1.0
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ update: function(position) {
+ this.element.setOpacity(position);
+ }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.element.makePositioned();
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
+ if (this.options.mode == 'absolute') {
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ this.element.setStyle({
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
+ top: (this.options.y * position + this.originalTop).round() + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+ initialize: function(element, percent) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = this.element.getStyle('position');
+
+ this.originalStyle = { };
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = this.element.getStyle('font-size') || '100%';
+ ['em','px','%','pt'].each( function(fontSizeType) {
+ if (fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if (this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if (/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if (!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if (this.options.scaleContent && this.fontSize)
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = { };
+ if (this.options.scaleX) d.width = width.round() + 'px';
+ if (this.options.scaleY) d.height = height.round() + 'px';
+ if (this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if (this.elementPositioning == 'absolute') {
+ if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if (this.options.scaleY) d.top = -topd + 'px';
+ if (this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ this.element.setStyle(d);
+ }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = { };
+ if (!this.options.keepBackgroundImage) {
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+ this.element.setStyle({backgroundImage: 'none'});
+ }
+ if (!this.options.endcolor)
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+ if (!this.options.restorecolor)
+ this.options.restorecolor = this.element.getStyle('background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ this.element.setStyle(Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = function(element) {
+ var options = arguments[1] || { },
+ scrollOffsets = document.viewport.getScrollOffsets(),
+ elementOffsets = $(element).cumulativeOffset();
+
+ if (options.offset) elementOffsets[1] += options.offset;
+
+ return new Effect.Tween(null,
+ scrollOffsets.top,
+ elementOffsets[1],
+ options,
+ function(p){ scrollTo(scrollOffsets.left, p.round()); }
+ );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ var options = Object.extend({
+ from: element.getOpacity() || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) {
+ if (effect.options.to!=0) return;
+ effect.element.hide().setStyle({opacity: oldOpacity});
+ }
+ }, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+ to: 1.0,
+ // force Safari to render floated elements properly
+ afterFinishInternal: function(effect) {
+ effect.element.forceRerendering();
+ },
+ beforeSetup: function(effect) {
+ effect.element.setOpacity(effect.options.from).show();
+ }}, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) {
+ Position.absolutize(effect.effects[0].element);
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().setStyle(oldStyle); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ element.makeClipping();
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ return new Effect.Appear(element, Object.extend({
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+ }
+ });
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left'),
+ opacity: element.getInlineOpacity() };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) {
+ effect.effects[0].element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ distance: 20,
+ duration: 0.5
+ }, arguments[1] || {});
+ var distance = parseFloat(options.distance);
+ var split = parseFloat(options.duration) / 10.0;
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left') };
+ return new Effect.Move(element,
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ effect.element.undoPositioned().setStyle(oldStyle);
+ }}); }}); }}); }}); }}); }});
+};
+
+Effect.SlideDown = function(element) {
+ element = $(element).cleanWhitespace();
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: window.opera ? 0 : 1,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.SlideUp = function(element) {
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, window.opera ? 0 : 1,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+ }
+ }, arguments[1] || { })
+ );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ });
+};
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) {
+ effect.element.hide().makeClipping().makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) {
+ effect.effects[0].element.setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+ }
+ }, options)
+ );
+ }
+ });
+};
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) {
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+ }, options)
+ );
+};
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || { },
+ oldOpacity = element.getInlineOpacity(),
+ transition = options.transition || Effect.Transitions.linear,
+ reverser = function(pos){
+ return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
+ };
+
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ element.makeClipping();
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().setStyle(oldStyle);
+ } });
+ }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: { }
+ }, arguments[1] || { });
+
+ if (!Object.isString(options.style)) this.style = $H(options.style);
+ else {
+ if (options.style.include(':'))
+ this.style = options.style.parseStyle();
+ else {
+ this.element.addClassName(options.style);
+ this.style = $H(this.element.getStyles());
+ this.element.removeClassName(options.style);
+ var css = this.element.getStyles();
+ this.style = this.style.reject(function(style) {
+ return style.value == css[style.key];
+ });
+ options.afterFinishInternal = function(effect) {
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ effect.element.style[transform.style] = '';
+ });
+ };
+ }
+ }
+ this.start(options);
+ },
+
+ setup: function(){
+ function parseColor(color){
+ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 );
+ });
+ }
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0], value = pair[1], unit = null;
+
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if (property == 'opacity') {
+ value = parseFloat(value);
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if (Element.CSS_LENGTH.test(value)) {
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+ value = parseFloat(components[1]);
+ unit = (components.length == 3) ? components[2] : null;
+ }
+
+ var originalValue = this.element.getStyle(property);
+ return {
+ style: property.camelize(),
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ };
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ );
+ });
+ },
+ update: function(position) {
+ var style = { }, transform, i = this.transforms.length;
+ while(i--)
+ style[(transform = this.transforms[i]).style] =
+ transform.unit=='color' ? '#'+
+ (Math.round(transform.originalValue[0]+
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+ (Math.round(transform.originalValue[1]+
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+ (Math.round(transform.originalValue[2]+
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+ (transform.originalValue +
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+ (transform.unit === null ? '' : transform.unit);
+ this.element.setStyle(style, true);
+ }
+});
+
+Effect.Transform = Class.create({
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || { };
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ track = $H(track);
+ var data = track.values().first();
+ this.tracks.push($H({
+ ids: track.keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+ var elements = [$(ids) || $$(ids)].flatten();
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+ var style, styleRules = $H();
+ if (Prototype.Browser.WebKit)
+ style = new Element('div',{style:this}).style;
+ else {
+ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+ style = String.__parseStyleElement.childNodes[0].style;
+ }
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if (style[property]) styleRules.set(property, style[property]);
+ });
+
+ if (Prototype.Browser.IE && this.include('opacity'))
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+ return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+ Element.getStyles = function(element) {
+ var css = document.defaultView.getComputedStyle($(element), null);
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+ styles[property] = css[property];
+ return styles;
+ });
+ };
+} else {
+ Element.getStyles = function(element) {
+ element = $(element);
+ var css = element.currentStyle, styles;
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+ results[property] = css[property];
+ return results;
+ });
+ if (!styles.opacity) styles.opacity = element.getOpacity();
+ return styles;
+ };
+}
+
+Effect.Methods = {
+ morph: function(element, style) {
+ element = $(element);
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+ return element;
+ },
+ visualEffect: function(element, effect, options) {
+ element = $(element);
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[klass](element, options);
+ return element;
+ },
+ highlight: function(element, options) {
+ element = $(element);
+ new Effect.Highlight(element, options);
+ return element;
+ }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+ 'pulsate shake puff squish switchOff dropOut').each(
+ function(effect) {
+ Effect.Methods[effect] = function(element, options){
+ element = $(element);
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+ return element;
+ };
+ }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+ function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoom.js b/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoom.js
new file mode 100644
index 00000000..c98624fa
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoom.js
@@ -0,0 +1,761 @@
+// FancyZoom.js - v1.1 - http://www.fancyzoom.com
+//
+// Copyright (c) 2008 Cabel Sasser / Panic Inc
+// All rights reserved.
+//
+// Requires: FancyZoomHTML.js
+// Instructions: Include JS files in page, call setupZoom() in onLoad. That's it!
+// Any <a href> links to images will be updated to zoom inline.
+// Add rel="nozoom" to your <a href> to disable zooming for an image.
+//
+// Redistribution and use of this effect in source form, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * USE OF SOURCE ON COMMERCIAL (FOR-PROFIT) WEBSITE REQUIRES ONE-TIME LICENSE FEE PER DOMAIN.
+// Reasonably priced! Visit www.fancyzoom.com for licensing instructions. Thanks!
+//
+// * Non-commercial (personal) website use is permitted without license/payment!
+//
+// * Redistribution of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistribution of source code and derived works cannot be sold without specific
+// written prior permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var includeCaption = true; // Turn on the "caption" feature, and write out the caption HTML
+var zoomTime = 5; // Milliseconds between frames of zoom animation
+var zoomSteps = 15; // Number of zoom animation frames
+var includeFade = 1; // Set to 1 to fade the image in / out as it zooms
+var minBorder = 90; // Amount of padding between large, scaled down images, and the window edges
+var shadowSettings = '0px 5px 25px rgba(0, 0, 0, '; // Blur, radius, color of shadow for compatible browsers
+
+// Location of the zoom and shadow images
+var zoomImagesURI;
+
+// Init. Do not add anything below this line, unless it's something awesome.
+
+var myWidth = 0, myHeight = 0, myScroll = 0; myScrollWidth = 0; myScrollHeight = 0;
+var zoomOpen = false, preloadFrame = 1, preloadActive = false, preloadTime = 0, imgPreload = new Image();
+var preloadAnimTimer = 0;
+
+var zoomActive = new Array(); var zoomTimer = new Array();
+var zoomOrigW = new Array(); var zoomOrigH = new Array();
+var zoomOrigX = new Array(); var zoomOrigY = new Array();
+
+var zoomID = "ZoomBox";
+var theID = "ZoomImage";
+var zoomCaption = "ZoomCaption";
+var zoomCaptionDiv = "ZoomCapDiv";
+
+if (navigator.userAgent.indexOf("MSIE") != -1) {
+ var browserIsIE = true;
+}
+
+// Zoom: Setup The Page! Called in your <body>'s onLoad handler.
+
+function setupZoom(baseURI) {
+ zoomImagesURI = baseURI + 'script/fancyzoom/images/';
+
+ prepZooms();
+ insertZoomHTML();
+ zoomdiv = document.getElementById(zoomID);
+ zoomimg = document.getElementById(theID);
+}
+
+// Zoom: Inject Javascript functions into hrefs with a "zoom" rel.
+// This is done at page load time via an onLoad() handler.
+
+function prepZooms() {
+ if (! document.getElementsByTagName) {
+ return;
+ }
+ var links = document.getElementsByTagName("a");
+ for (i = 0; i < links.length; i++) {
+ if (links[i].getAttribute("href")) {
+ if (links[i].getAttribute("rel") == "zoom") {
+ links[i].onclick = function (event) { return zoomClick(this, event); };
+ links[i].onmouseover = function () { zoomPreload(this); };
+ }
+ }
+ }
+}
+
+// Zoom: Load an image into an image object. When done loading, function sets preloadActive to false,
+// so other bits know that they can proceed with the zoom.
+// Preloaded image is stored in imgPreload and swapped out in the zoom function.
+
+function zoomPreload(from) {
+
+ var theimage = from.getAttribute("href");
+
+ // Only preload if we have to, i.e. the image isn't this image already
+
+ if (imgPreload.src.indexOf(from.getAttribute("href").substr(from.getAttribute("href").lastIndexOf("/"))) == -1) {
+ preloadActive = true;
+ imgPreload = new Image();
+
+ // Set a function to fire when the preload is complete, setting flags along the way.
+
+ imgPreload.onload = function() {
+ preloadActive = false;
+ }
+
+ // Load it!
+ imgPreload.src = theimage;
+ }
+}
+
+// Zoom: Start the preloading animation cycle.
+
+function preloadAnimStart() {
+ preloadTime = new Date();
+ document.getElementById("ZoomSpin").style.left = (myWidth / 2) + 'px';
+ document.getElementById("ZoomSpin").style.top = ((myHeight / 2) + myScroll) + 'px';
+ document.getElementById("ZoomSpin").style.visibility = "visible";
+ preloadFrame = 1;
+ document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';
+ preloadAnimTimer = setInterval("preloadAnim()", 100);
+}
+
+// Zoom: Display and ANIMATE the jibber-jabber widget. Once preloadActive is false, bail and zoom it up!
+
+function preloadAnim(from) {
+ if (preloadActive != false) {
+ document.getElementById("SpinImage").src = zoomImagesURI+'zoom-spin-'+preloadFrame+'.png';
+ preloadFrame++;
+ if (preloadFrame > 12) preloadFrame = 1;
+ } else {
+ document.getElementById("ZoomSpin").style.visibility = "hidden";
+ clearInterval(preloadAnimTimer);
+ preloadAnimTimer = 0;
+ zoomIn(preloadFrom);
+ }
+}
+
+// ZOOM CLICK: We got a click! Should we do the zoom? Or wait for the preload to complete?
+// todo?: Double check that imgPreload src = clicked src
+
+function zoomClick(from, evt) {
+
+ var shift = getShift(evt);
+
+ // Check for Command / Alt key. If pressed, pass them through -- don't zoom!
+ if (! evt && window.event && (window.event.metaKey || window.event.altKey)) {
+ return true;
+ } else if (evt && (evt.metaKey|| evt.altKey)) {
+ return true;
+ }
+
+ // Get browser dimensions
+ getSize();
+
+ // If preloading still, wait, and display the spinner.
+ if (preloadActive == true) {
+ // But only display the spinner if it's not already being displayed!
+ if (preloadAnimTimer == 0) {
+ preloadFrom = from;
+ preloadAnimStart();
+ }
+ } else {
+ // Otherwise, we're loaded: do the zoom!
+ zoomIn(from, shift);
+ }
+
+ return false;
+
+}
+
+// Zoom: Move an element in to endH endW, using zoomHost as a starting point.
+// "from" is an object reference to the href that spawned the zoom.
+
+function zoomIn(from, shift) {
+
+ zoomimg.src = from.getAttribute("href");
+
+ // Determine the zoom settings from where we came from, the element in the <a>.
+ // If there's no element in the <a>, or we can't get the width, make stuff up
+
+ if (from.childNodes[0].width) {
+ startW = from.childNodes[0].width;
+ startH = from.childNodes[0].height;
+ startPos = findElementPos(from.childNodes[0]);
+ } else {
+ startW = 50;
+ startH = 12;
+ startPos = findElementPos(from);
+ }
+
+ hostX = startPos[0];
+ hostY = startPos[1];
+
+ // Make up for a scrolled containing div.
+ // TODO: This HAS to move into findElementPos.
+
+ if (document.getElementById('scroller')) {
+ hostX = hostX - document.getElementById('scroller').scrollLeft;
+ }
+
+ // Determine the target zoom settings from the preloaded image object
+
+ endW = imgPreload.width;
+ endH = imgPreload.height;
+
+ // Start! But only if we're not zooming already!
+
+ if (zoomActive[theID] != true) {
+
+ // Clear everything out just in case something is already open
+
+ if (document.getElementById("ShadowBox")) {
+ document.getElementById("ShadowBox").style.visibility = "hidden";
+ } else if (! browserIsIE) {
+
+ // Wipe timer if shadow is fading in still
+ if (fadeActive["ZoomImage"]) {
+ clearInterval(fadeTimer["ZoomImage"]);
+ fadeActive["ZoomImage"] = false;
+ fadeTimer["ZoomImage"] = false;
+ }
+
+ document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';
+ }
+
+ document.getElementById("ZoomClose").style.visibility = "hidden";
+
+ // Setup the CAPTION, if existing. Hide it first, set the text.
+
+ if (includeCaption) {
+ document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
+ if (from.getAttribute('title') && includeCaption) {
+ // Yes, there's a caption, set it up
+ document.getElementById(zoomCaption).innerHTML = from.getAttribute('title');
+ } else {
+ document.getElementById(zoomCaption).innerHTML = "";
+ }
+ }
+
+ // Store original position in an array for future zoomOut.
+
+ zoomOrigW[theID] = startW;
+ zoomOrigH[theID] = startH;
+ zoomOrigX[theID] = hostX;
+ zoomOrigY[theID] = hostY;
+
+ // Now set the starting dimensions
+
+ zoomimg.style.width = startW + 'px';
+ zoomimg.style.height = startH + 'px';
+ zoomdiv.style.left = hostX + 'px';
+ zoomdiv.style.top = hostY + 'px';
+
+ // Show the zooming image container, make it invisible
+
+ if (includeFade == 1) {
+ setOpacity(0, zoomID);
+ }
+ zoomdiv.style.visibility = "visible";
+
+ // If it's too big to fit in the window, shrink the width and height to fit (with ratio).
+
+ sizeRatio = endW / endH;
+ if (endW > myWidth - minBorder) {
+ endW = myWidth - minBorder;
+ endH = endW / sizeRatio;
+ }
+ if (endH > myHeight - minBorder) {
+ endH = myHeight - minBorder;
+ endW = endH * sizeRatio;
+ }
+
+ zoomChangeX = ((myWidth / 2) - (endW / 2) - hostX);
+ zoomChangeY = (((myHeight / 2) - (endH / 2) - hostY) + myScroll);
+ zoomChangeW = (endW - startW);
+ zoomChangeH = (endH - startH);
+
+ // Shift key?
+
+ if (shift) {
+ tempSteps = zoomSteps * 7;
+ } else {
+ tempSteps = zoomSteps;
+ }
+
+ // Setup Zoom
+
+ zoomCurrent = 0;
+
+ // Setup Fade with Zoom, If Requested
+
+ if (includeFade == 1) {
+ fadeCurrent = 0;
+ fadeAmount = (0 - 100) / tempSteps;
+ } else {
+ fadeAmount = 0;
+ }
+
+ // Do It!
+
+ zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+hostX+", "+zoomChangeX+", "+hostY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDoneIn(zoomID)')", zoomTime);
+ zoomActive[theID] = true;
+ }
+}
+
+// Zoom it back out.
+
+function zoomOut(from, evt) {
+
+ // Get shift key status.
+ // IE events don't seem to get passed through the function, so grab it from the window.
+
+ if (getShift(evt)) {
+ tempSteps = zoomSteps * 7;
+ } else {
+ tempSteps = zoomSteps;
+ }
+
+ // Check to see if something is happening/open
+
+ if (zoomActive[theID] != true) {
+
+ // First, get rid of the shadow if necessary.
+
+ if (document.getElementById("ShadowBox")) {
+ document.getElementById("ShadowBox").style.visibility = "hidden";
+ } else if (! browserIsIE) {
+
+ // Wipe timer if shadow is fading in still
+ if (fadeActive["ZoomImage"]) {
+ clearInterval(fadeTimer["ZoomImage"]);
+ fadeActive["ZoomImage"] = false;
+ fadeTimer["ZoomImage"] = false;
+ }
+
+ document.getElementById("ZoomImage").style.webkitBoxShadow = shadowSettings + '0.0)';
+ }
+
+ // ..and the close box...
+
+ document.getElementById("ZoomClose").style.visibility = "hidden";
+
+ // ...and the caption if necessary!
+
+ if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
+ // fadeElementSetup(zoomCaptionDiv, 100, 0, 5, 1);
+ document.getElementById(zoomCaptionDiv).style.visibility = "hidden";
+ }
+
+ // Now, figure out where we came from, to get back there
+
+ startX = parseInt(zoomdiv.style.left);
+ startY = parseInt(zoomdiv.style.top);
+ startW = zoomimg.width;
+ startH = zoomimg.height;
+ zoomChangeX = zoomOrigX[theID] - startX;
+ zoomChangeY = zoomOrigY[theID] - startY;
+ zoomChangeW = zoomOrigW[theID] - startW;
+ zoomChangeH = zoomOrigH[theID] - startH;
+
+ // Setup Zoom
+
+ zoomCurrent = 0;
+
+ // Setup Fade with Zoom, If Requested
+
+ if (includeFade == 1) {
+ fadeCurrent = 0;
+ fadeAmount = (100 - 0) / tempSteps;
+ } else {
+ fadeAmount = 0;
+ }
+
+ // Do It!
+
+ zoomTimer[theID] = setInterval("zoomElement('"+zoomID+"', '"+theID+"', "+zoomCurrent+", "+startW+", "+zoomChangeW+", "+startH+", "+zoomChangeH+", "+startX+", "+zoomChangeX+", "+startY+", "+zoomChangeY+", "+tempSteps+", "+includeFade+", "+fadeAmount+", 'zoomDone(zoomID, theID)')", zoomTime);
+ zoomActive[theID] = true;
+ }
+}
+
+// Finished Zooming In
+
+function zoomDoneIn(zoomdiv, theID) {
+
+ // Note that it's open
+
+ zoomOpen = true;
+ zoomdiv = document.getElementById(zoomdiv);
+
+ // Position the table shadow behind the zoomed in image, and display it
+
+ if (document.getElementById("ShadowBox")) {
+
+ setOpacity(0, "ShadowBox");
+ shadowdiv = document.getElementById("ShadowBox");
+
+ shadowLeft = parseInt(zoomdiv.style.left) - 13;
+ shadowTop = parseInt(zoomdiv.style.top) - 8;
+ shadowWidth = zoomdiv.offsetWidth + 26;
+ shadowHeight = zoomdiv.offsetHeight + 26;
+
+ shadowdiv.style.width = shadowWidth + 'px';
+ shadowdiv.style.height = shadowHeight + 'px';
+ shadowdiv.style.left = shadowLeft + 'px';
+ shadowdiv.style.top = shadowTop + 'px';
+
+ document.getElementById("ShadowBox").style.visibility = "visible";
+ fadeElementSetup("ShadowBox", 0, 100, 5);
+
+ } else if (! browserIsIE) {
+ // Or, do a fade of the modern shadow
+ fadeElementSetup("ZoomImage", 0, .8, 5, 0, "shadow");
+ }
+
+ // Position and display the CAPTION, if existing
+
+ if (includeCaption && document.getElementById(zoomCaption).innerHTML != "") {
+ // setOpacity(0, zoomCaptionDiv);
+ zoomcapd = document.getElementById(zoomCaptionDiv);
+ zoomcapd.style.top = parseInt(zoomdiv.style.top) + (zoomdiv.offsetHeight + 15) + 'px';
+ zoomcapd.style.left = (myWidth / 2) - (zoomcapd.offsetWidth / 2) + 'px';
+ zoomcapd.style.visibility = "visible";
+ // fadeElementSetup(zoomCaptionDiv, 0, 100, 5);
+ }
+
+ // Display Close Box (fade it if it's not IE)
+
+ if (!browserIsIE) setOpacity(0, "ZoomClose");
+ document.getElementById("ZoomClose").style.visibility = "visible";
+ if (!browserIsIE) fadeElementSetup("ZoomClose", 0, 100, 5);
+
+ // Get keypresses
+ document.onkeypress = getKey;
+
+}
+
+// Finished Zooming Out
+
+function zoomDone(zoomdiv, theID) {
+
+ // No longer open
+
+ zoomOpen = false;
+
+ // Clear stuff out, clean up
+
+ zoomOrigH[theID] = "";
+ zoomOrigW[theID] = "";
+ document.getElementById(zoomdiv).style.visibility = "hidden";
+ zoomActive[theID] == false;
+
+ // Stop getting keypresses
+
+ document.onkeypress = null;
+
+}
+
+// Actually zoom the element
+
+function zoomElement(zoomdiv, theID, zoomCurrent, zoomStartW, zoomChangeW, zoomStartH, zoomChangeH, zoomStartX, zoomChangeX, zoomStartY, zoomChangeY, zoomSteps, includeFade, fadeAmount, execWhenDone) {
+
+ // console.log("Zooming Step #"+zoomCurrent+ " of "+zoomSteps+" (zoom " + zoomStartW + "/" + zoomChangeW + ") (zoom " + zoomStartH + "/" + zoomChangeH + ") (zoom " + zoomStartX + "/" + zoomChangeX + ") (zoom " + zoomStartY + "/" + zoomChangeY + ") Fade: "+fadeAmount);
+
+ // Test if we're done, or if we continue
+
+ if (zoomCurrent == (zoomSteps + 1)) {
+ zoomActive[theID] = false;
+ clearInterval(zoomTimer[theID]);
+
+ if (execWhenDone != "") {
+ eval(execWhenDone);
+ }
+ } else {
+
+ // Do the Fade!
+
+ if (includeFade == 1) {
+ if (fadeAmount < 0) {
+ setOpacity(Math.abs(zoomCurrent * fadeAmount), zoomdiv);
+ } else {
+ setOpacity(100 - (zoomCurrent * fadeAmount), zoomdiv);
+ }
+ }
+
+ // Calculate this step's difference, and move it!
+
+ moveW = cubicInOut(zoomCurrent, zoomStartW, zoomChangeW, zoomSteps);
+ moveH = cubicInOut(zoomCurrent, zoomStartH, zoomChangeH, zoomSteps);
+ moveX = cubicInOut(zoomCurrent, zoomStartX, zoomChangeX, zoomSteps);
+ moveY = cubicInOut(zoomCurrent, zoomStartY, zoomChangeY, zoomSteps);
+
+ document.getElementById(zoomdiv).style.left = moveX + 'px';
+ document.getElementById(zoomdiv).style.top = moveY + 'px';
+ zoomimg.style.width = moveW + 'px';
+ zoomimg.style.height = moveH + 'px';
+
+ zoomCurrent++;
+
+ clearInterval(zoomTimer[theID]);
+ zoomTimer[theID] = setInterval("zoomElement('"+zoomdiv+"', '"+theID+"', "+zoomCurrent+", "+zoomStartW+", "+zoomChangeW+", "+zoomStartH+", "+zoomChangeH+", "+zoomStartX+", "+zoomChangeX+", "+zoomStartY+", "+zoomChangeY+", "+zoomSteps+", "+includeFade+", "+fadeAmount+", '"+execWhenDone+"')", zoomTime);
+ }
+}
+
+// Zoom Utility: Get Key Press when image is open, and act accordingly
+
+function getKey(evt) {
+ if (! evt) {
+ theKey = event.keyCode;
+ } else {
+ theKey = evt.keyCode;
+ }
+
+ if (theKey == 27) { // ESC
+ zoomOut(this, evt);
+ }
+}
+
+////////////////////////////
+//
+// FADE Functions
+//
+
+function fadeOut(elem) {
+ if (elem.id) {
+ fadeElementSetup(elem.id, 100, 0, 10);
+ }
+}
+
+function fadeIn(elem) {
+ if (elem.id) {
+ fadeElementSetup(elem.id, 0, 100, 10);
+ }
+}
+
+// Fade: Initialize the fade function
+
+var fadeActive = new Array();
+var fadeQueue = new Array();
+var fadeTimer = new Array();
+var fadeClose = new Array();
+var fadeMode = new Array();
+
+function fadeElementSetup(theID, fdStart, fdEnd, fdSteps, fdClose, fdMode) {
+
+ // alert("Fading: "+theID+" Steps: "+fdSteps+" Mode: "+fdMode);
+
+ if (fadeActive[theID] == true) {
+ // Already animating, queue up this command
+ fadeQueue[theID] = new Array(theID, fdStart, fdEnd, fdSteps);
+ } else {
+ fadeSteps = fdSteps;
+ fadeCurrent = 0;
+ fadeAmount = (fdStart - fdEnd) / fadeSteps;
+ fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
+ fadeActive[theID] = true;
+ fadeMode[theID] = fdMode;
+
+ if (fdClose == 1) {
+ fadeClose[theID] = true;
+ } else {
+ fadeClose[theID] = false;
+ }
+ }
+}
+
+// Fade: Do the fade. This function will call itself, modifying the parameters, so
+// many instances can run concurrently. Can fade using opacity, or fade using a box-shadow.
+
+function fadeElement(theID, fadeCurrent, fadeAmount, fadeSteps) {
+
+ if (fadeCurrent == fadeSteps) {
+
+ // We're done, so clear.
+
+ clearInterval(fadeTimer[theID]);
+ fadeActive[theID] = false;
+ fadeTimer[theID] = false;
+
+ // Should we close it once the fade is complete?
+
+ if (fadeClose[theID] == true) {
+ document.getElementById(theID).style.visibility = "hidden";
+ }
+
+ // Hang on.. did a command queue while we were working? If so, make it happen now
+
+ if (fadeQueue[theID] && fadeQueue[theID] != false) {
+ fadeElementSetup(fadeQueue[theID][0], fadeQueue[theID][1], fadeQueue[theID][2], fadeQueue[theID][3]);
+ fadeQueue[theID] = false;
+ }
+ } else {
+
+ fadeCurrent++;
+
+ // Now actually do the fade adjustment.
+
+ if (fadeMode[theID] == "shadow") {
+
+ // Do a special fade on the webkit-box-shadow of the object
+
+ if (fadeAmount < 0) {
+ document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (Math.abs(fadeCurrent * fadeAmount)) + ')';
+ } else {
+ document.getElementById(theID).style.webkitBoxShadow = shadowSettings + (100 - (fadeCurrent * fadeAmount)) + ')';
+ }
+
+ } else {
+
+ // Set the opacity depending on if we're adding or subtracting (pos or neg)
+
+ if (fadeAmount < 0) {
+ setOpacity(Math.abs(fadeCurrent * fadeAmount), theID);
+ } else {
+ setOpacity(100 - (fadeCurrent * fadeAmount), theID);
+ }
+ }
+
+ // Keep going, and send myself the updated variables
+ clearInterval(fadeTimer[theID]);
+ fadeTimer[theID] = setInterval("fadeElement('"+theID+"', '"+fadeCurrent+"', '"+fadeAmount+"', '"+fadeSteps+"')", 15);
+ }
+}
+
+////////////////////////////
+//
+// UTILITY functions
+//
+
+// Utility: Set the opacity, compatible with a number of browsers. Value from 0 to 100.
+
+function setOpacity(opacity, theID) {
+
+ var object = document.getElementById(theID).style;
+
+ // If it's 100, set it to 99 for Firefox.
+
+ if (navigator.userAgent.indexOf("Firefox") != -1) {
+ if (opacity == 100) { opacity = 99.9999; } // This is majorly awkward
+ }
+
+ // Multi-browser opacity setting
+
+ object.filter = "alpha(opacity=" + opacity + ")"; // IE/Win
+ object.opacity = (opacity / 100); // Safari 1.2, Firefox+Mozilla
+
+}
+
+// Utility: Math functions for animation calucations - From http://www.robertpenner.com/easing/
+//
+// t = time, b = begin, c = change, d = duration
+// time = current frame, begin is fixed, change is basically finish - begin, duration is fixed (frames),
+
+function linear(t, b, c, d)
+{
+ return c*t/d + b;
+}
+
+function sineInOut(t, b, c, d)
+{
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+}
+
+function cubicIn(t, b, c, d) {
+ return c*(t/=d)*t*t + b;
+}
+
+function cubicOut(t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+}
+
+function cubicInOut(t, b, c, d)
+{
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+}
+
+function bounceOut(t, b, c, d)
+{
+ if ((t/=d) < (1/2.75)){
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)){
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)){
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+}
+
+
+// Utility: Get the size of the window, and set myWidth and myHeight
+// Credit to quirksmode.org
+
+function getSize() {
+
+ // Window Size
+
+ if (self.innerHeight) { // Everyone but IE
+ myWidth = window.innerWidth;
+ myHeight = window.innerHeight;
+ myScroll = window.pageYOffset;
+ } else if (document.documentElement && document.documentElement.clientHeight) { // IE6 Strict
+ myWidth = document.documentElement.clientWidth;
+ myHeight = document.documentElement.clientHeight;
+ myScroll = document.documentElement.scrollTop;
+ } else if (document.body) { // Other IE, such as IE7
+ myWidth = document.body.clientWidth;
+ myHeight = document.body.clientHeight;
+ myScroll = document.body.scrollTop;
+ }
+
+ // Page size w/offscreen areas
+
+ if (window.innerHeight && window.scrollMaxY) {
+ myScrollWidth = document.body.scrollWidth;
+ myScrollHeight = window.innerHeight + window.scrollMaxY;
+ } else if (document.body.scrollHeight > document.body.offsetHeight) { // All but Explorer Mac
+ myScrollWidth = document.body.scrollWidth;
+ myScrollHeight = document.body.scrollHeight;
+ } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+ myScrollWidth = document.body.offsetWidth;
+ myScrollHeight = document.body.offsetHeight;
+ }
+}
+
+// Utility: Get Shift Key Status
+// IE events don't seem to get passed through the function, so grab it from the window.
+
+function getShift(evt) {
+ var shift = false;
+ if (! evt && window.event) {
+ shift = window.event.shiftKey;
+ } else if (evt) {
+ shift = evt.shiftKey;
+ if (shift) evt.stopPropagation(); // Prevents Firefox from doing shifty things
+ }
+ return shift;
+}
+
+// Utility: Find the Y position of an element on a page. Return Y and X as an array
+
+function findElementPos(elemFind)
+{
+ var elemX = 0;
+ var elemY = 0;
+ do {
+ elemX += elemFind.offsetLeft;
+ elemY += elemFind.offsetTop;
+ } while ( elemFind = elemFind.offsetParent )
+
+ return Array(elemX, elemY);
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoomHTML.js b/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoomHTML.js
new file mode 100644
index 00000000..7644a9a8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/FancyZoomHTML.js
@@ -0,0 +1,318 @@
+// FancyZoomHTML.js - v1.0
+// Used to draw necessary HTML elements for FancyZoom
+//
+// Copyright (c) 2008 Cabel Sasser / Panic Inc
+// All rights reserved.
+
+function insertZoomHTML() {
+
+ // All of this junk creates the three <div>'s used to hold the closebox, image, and zoom shadow.
+
+ var inBody = document.getElementsByTagName("body").item(0);
+
+ // WAIT SPINNER
+
+ var inSpinbox = document.createElement("div");
+ inSpinbox.setAttribute('id', 'ZoomSpin');
+ inSpinbox.style.position = 'absolute';
+ inSpinbox.style.left = '10px';
+ inSpinbox.style.top = '10px';
+ inSpinbox.style.visibility = 'hidden';
+ inSpinbox.style.zIndex = '525';
+ inBody.insertBefore(inSpinbox, inBody.firstChild);
+
+ var inSpinImage = document.createElement("img");
+ inSpinImage.setAttribute('id', 'SpinImage');
+ inSpinImage.setAttribute('src', zoomImagesURI+'zoom-spin-1.png');
+ inSpinbox.appendChild(inSpinImage);
+
+ // ZOOM IMAGE
+ //
+ // <div id="ZoomBox">
+ // <a href="javascript:zoomOut();"><img src="/images/spacer.gif" id="ZoomImage" border="0"></a> <!-- THE IMAGE -->
+ // <div id="ZoomClose">
+ // <a href="javascript:zoomOut();"><img src="/images/closebox.png" width="30" height="30" border="0"></a>
+ // </div>
+ // </div>
+
+ var inZoombox = document.createElement("div");
+ inZoombox.setAttribute('id', 'ZoomBox');
+
+ inZoombox.style.position = 'absolute';
+ inZoombox.style.left = '10px';
+ inZoombox.style.top = '10px';
+ inZoombox.style.visibility = 'hidden';
+ inZoombox.style.zIndex = '499';
+
+ inBody.insertBefore(inZoombox, inSpinbox.nextSibling);
+
+ var inImage1 = document.createElement("img");
+ inImage1.onclick = function (event) { zoomOut(this, event); return false; };
+ inImage1.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inImage1.setAttribute('id','ZoomImage');
+ inImage1.setAttribute('border', '0');
+ // inImage1.setAttribute('onMouseOver', 'zoomMouseOver();')
+ // inImage1.setAttribute('onMouseOut', 'zoomMouseOut();')
+
+ // This must be set first, so we can later test it using webkitBoxShadow.
+ inImage1.setAttribute('style', '-webkit-box-shadow: '+shadowSettings+'0.0)');
+ inImage1.style.display = 'block';
+ inImage1.style.width = '10px';
+ inImage1.style.height = '10px';
+ inImage1.style.cursor = 'pointer'; // -webkit-zoom-out?
+ inZoombox.appendChild(inImage1);
+
+ var inClosebox = document.createElement("div");
+ inClosebox.setAttribute('id', 'ZoomClose');
+ inClosebox.style.position = 'absolute';
+
+ // In MSIE, we need to put the close box inside the image.
+ // It's 2008 and I'm having to do a browser detect? Sigh.
+ if (browserIsIE) {
+ inClosebox.style.left = '-1px';
+ inClosebox.style.top = '0px';
+ } else {
+ inClosebox.style.left = '-15px';
+ inClosebox.style.top = '-15px';
+ }
+
+ inClosebox.style.visibility = 'hidden';
+ inZoombox.appendChild(inClosebox);
+
+ var inImage2 = document.createElement("img");
+ inImage2.onclick = function (event) { zoomOut(this, event); return false; };
+ inImage2.setAttribute('src',zoomImagesURI+'closebox.png');
+ inImage2.setAttribute('width','30');
+ inImage2.setAttribute('height','30');
+ inImage2.setAttribute('border','0');
+ inImage2.style.cursor = 'pointer';
+ inClosebox.appendChild(inImage2);
+
+ // SHADOW
+ // Only draw the table-based shadow if the programatic webkitBoxShadow fails!
+ // Also, don't draw it if we're IE -- it wouldn't look quite right anyway.
+
+ if (! document.getElementById('ZoomImage').style.webkitBoxShadow && ! browserIsIE) {
+
+ // SHADOW BASE
+
+ var inFixedBox = document.createElement("div");
+ inFixedBox.setAttribute('id', 'ShadowBox');
+ inFixedBox.style.position = 'absolute';
+ inFixedBox.style.left = '50px';
+ inFixedBox.style.top = '50px';
+ inFixedBox.style.width = '100px';
+ inFixedBox.style.height = '100px';
+ inFixedBox.style.visibility = 'hidden';
+ inFixedBox.style.zIndex = '498';
+ inBody.insertBefore(inFixedBox, inZoombox.nextSibling);
+
+ // SHADOW
+ // Now, the shadow table. Skip if not compatible, or irrevelant with -box-shadow.
+
+ // <div id="ShadowBox"><table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0"> X
+ // <tr height="25">
+ // <td width="27"><img src="/images/zoom-shadow1.png" width="27" height="25"></td>
+ // <td background="/images/zoom-shadow2.png">&nbsp;</td>
+ // <td width="27"><img src="/images/zoom-shadow3.png" width="27" height="25"></td>
+ // </tr>
+
+ var inShadowTable = document.createElement("table");
+ inShadowTable.setAttribute('border', '0');
+ inShadowTable.setAttribute('width', '100%');
+ inShadowTable.setAttribute('height', '100%');
+ inShadowTable.setAttribute('cellpadding', '0');
+ inShadowTable.setAttribute('cellspacing', '0');
+ inFixedBox.appendChild(inShadowTable);
+
+ var inShadowTbody = document.createElement("tbody"); // Needed for IE (for HTML4).
+ inShadowTable.appendChild(inShadowTbody);
+
+ var inRow1 = document.createElement("tr");
+ inRow1.style.height = '25px';
+ inShadowTbody.appendChild(inRow1);
+
+ var inCol1 = document.createElement("td");
+ inCol1.style.width = '27px';
+ inRow1.appendChild(inCol1);
+ var inShadowImg1 = document.createElement("img");
+ inShadowImg1.setAttribute('src', zoomImagesURI+'zoom-shadow1.png');
+ inShadowImg1.setAttribute('width', '27');
+ inShadowImg1.setAttribute('height', '25');
+ inShadowImg1.style.display = 'block';
+ inCol1.appendChild(inShadowImg1);
+
+ var inCol2 = document.createElement("td");
+ inCol2.setAttribute('background', zoomImagesURI+'zoom-shadow2.png');
+ inRow1.appendChild(inCol2);
+ // inCol2.innerHTML = '<img src=';
+ var inSpacer1 = document.createElement("img");
+ inSpacer1.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inSpacer1.setAttribute('height', '1');
+ inSpacer1.setAttribute('width', '1');
+ inSpacer1.style.display = 'block';
+ inCol2.appendChild(inSpacer1);
+
+ var inCol3 = document.createElement("td");
+ inCol3.style.width = '27px';
+ inRow1.appendChild(inCol3);
+ var inShadowImg3 = document.createElement("img");
+ inShadowImg3.setAttribute('src', zoomImagesURI+'zoom-shadow3.png');
+ inShadowImg3.setAttribute('width', '27');
+ inShadowImg3.setAttribute('height', '25');
+ inShadowImg3.style.display = 'block';
+ inCol3.appendChild(inShadowImg3);
+
+ // <tr>
+ // <td background="/images/zoom-shadow4.png">&nbsp;</td>
+ // <td bgcolor="#ffffff">&nbsp;</td>
+ // <td background="/images/zoom-shadow5.png">&nbsp;</td>
+ // </tr>
+
+ inRow2 = document.createElement("tr");
+ inShadowTbody.appendChild(inRow2);
+
+ var inCol4 = document.createElement("td");
+ inCol4.setAttribute('background', zoomImagesURI+'zoom-shadow4.png');
+ inRow2.appendChild(inCol4);
+ // inCol4.innerHTML = '&nbsp;';
+ var inSpacer2 = document.createElement("img");
+ inSpacer2.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inSpacer2.setAttribute('height', '1');
+ inSpacer2.setAttribute('width', '1');
+ inSpacer2.style.display = 'block';
+ inCol4.appendChild(inSpacer2);
+
+ var inCol5 = document.createElement("td");
+ inCol5.setAttribute('bgcolor', '#ffffff');
+ inRow2.appendChild(inCol5);
+ // inCol5.innerHTML = '&nbsp;';
+ var inSpacer3 = document.createElement("img");
+ inSpacer3.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inSpacer3.setAttribute('height', '1');
+ inSpacer3.setAttribute('width', '1');
+ inSpacer3.style.display = 'block';
+ inCol5.appendChild(inSpacer3);
+
+ var inCol6 = document.createElement("td");
+ inCol6.setAttribute('background', zoomImagesURI+'zoom-shadow5.png');
+ inRow2.appendChild(inCol6);
+ // inCol6.innerHTML = '&nbsp;';
+ var inSpacer4 = document.createElement("img");
+ inSpacer4.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inSpacer4.setAttribute('height', '1');
+ inSpacer4.setAttribute('width', '1');
+ inSpacer4.style.display = 'block';
+ inCol6.appendChild(inSpacer4);
+
+ // <tr height="26">
+ // <td width="27"><img src="/images/zoom-shadow6.png" width="27" height="26"</td>
+ // <td background="/images/zoom-shadow7.png">&nbsp;</td>
+ // <td width="27"><img src="/images/zoom-shadow8.png" width="27" height="26"></td>
+ // </tr>
+ // </table>
+
+ var inRow3 = document.createElement("tr");
+ inRow3.style.height = '26px';
+ inShadowTbody.appendChild(inRow3);
+
+ var inCol7 = document.createElement("td");
+ inCol7.style.width = '27px';
+ inRow3.appendChild(inCol7);
+ var inShadowImg7 = document.createElement("img");
+ inShadowImg7.setAttribute('src', zoomImagesURI+'zoom-shadow6.png');
+ inShadowImg7.setAttribute('width', '27');
+ inShadowImg7.setAttribute('height', '26');
+ inShadowImg7.style.display = 'block';
+ inCol7.appendChild(inShadowImg7);
+
+ var inCol8 = document.createElement("td");
+ inCol8.setAttribute('background', zoomImagesURI+'zoom-shadow7.png');
+ inRow3.appendChild(inCol8);
+ // inCol8.innerHTML = '&nbsp;';
+ var inSpacer5 = document.createElement("img");
+ inSpacer5.setAttribute('src',zoomImagesURI+'spacer.gif');
+ inSpacer5.setAttribute('height', '1');
+ inSpacer5.setAttribute('width', '1');
+ inSpacer5.style.display = 'block';
+ inCol8.appendChild(inSpacer5);
+
+ var inCol9 = document.createElement("td");
+ inCol9.style.width = '27px';
+ inRow3.appendChild(inCol9);
+ var inShadowImg9 = document.createElement("img");
+ inShadowImg9.setAttribute('src', zoomImagesURI+'zoom-shadow8.png');
+ inShadowImg9.setAttribute('width', '27');
+ inShadowImg9.setAttribute('height', '26');
+ inShadowImg9.style.display = 'block';
+ inCol9.appendChild(inShadowImg9);
+ }
+
+ if (includeCaption) {
+
+ // CAPTION
+ //
+ // <div id="ZoomCapDiv" style="margin-left: 13px; margin-right: 13px;">
+ // <table border="1" cellpadding="0" cellspacing="0">
+ // <tr height="26">
+ // <td><img src="zoom-caption-l.png" width="13" height="26"></td>
+ // <td rowspan="3" background="zoom-caption-fill.png"><div id="ZoomCaption"></div></td>
+ // <td><img src="zoom-caption-r.png" width="13" height="26"></td>
+ // </tr>
+ // </table>
+ // </div>
+
+ var inCapDiv = document.createElement("div");
+ inCapDiv.setAttribute('id', 'ZoomCapDiv');
+ inCapDiv.style.position = 'absolute';
+ inCapDiv.style.visibility = 'hidden';
+ inCapDiv.style.marginLeft = 'auto';
+ inCapDiv.style.marginRight = 'auto';
+ inCapDiv.style.zIndex = '501';
+
+ inBody.insertBefore(inCapDiv, inZoombox.nextSibling);
+
+ var inCapTable = document.createElement("table");
+ inCapTable.setAttribute('border', '0');
+ inCapTable.setAttribute('cellPadding', '0'); // Wow. These honestly need to
+ inCapTable.setAttribute('cellSpacing', '0'); // be intercapped to work in IE. WTF?
+ inCapDiv.appendChild(inCapTable);
+
+ var inTbody = document.createElement("tbody"); // Needed for IE (for HTML4).
+ inCapTable.appendChild(inTbody);
+
+ var inCapRow1 = document.createElement("tr");
+ inTbody.appendChild(inCapRow1);
+
+ var inCapCol1 = document.createElement("td");
+ inCapCol1.setAttribute('align', 'right');
+ inCapRow1.appendChild(inCapCol1);
+ var inCapImg1 = document.createElement("img");
+ inCapImg1.setAttribute('src', zoomImagesURI+'zoom-caption-l.png');
+ inCapImg1.setAttribute('width', '13');
+ inCapImg1.setAttribute('height', '26');
+ inCapImg1.style.display = 'block';
+ inCapCol1.appendChild(inCapImg1);
+
+ var inCapCol2 = document.createElement("td");
+ inCapCol2.setAttribute('background', zoomImagesURI+'zoom-caption-fill.png');
+ inCapCol2.setAttribute('id', 'ZoomCaption');
+ inCapCol2.setAttribute('valign', 'middle');
+ inCapCol2.style.fontSize = '14px';
+ inCapCol2.style.fontFamily = 'Helvetica';
+ inCapCol2.style.fontWeight = 'bold';
+ inCapCol2.style.color = '#ffffff';
+ inCapCol2.style.textShadow = '0px 2px 4px #000000';
+ inCapCol2.style.whiteSpace = 'nowrap';
+ inCapRow1.appendChild(inCapCol2);
+
+ var inCapCol3 = document.createElement("td");
+ inCapRow1.appendChild(inCapCol3);
+ var inCapImg2 = document.createElement("img");
+ inCapImg2.setAttribute('src', zoomImagesURI+'zoom-caption-r.png');
+ inCapImg2.setAttribute('width', '13');
+ inCapImg2.setAttribute('height', '26');
+ inCapImg2.style.display = 'block';
+ inCapCol3.appendChild(inCapImg2);
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/closebox.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/closebox.png
new file mode 100644
index 00000000..4de4396d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/closebox.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/spacer.gif b/subsonic-main/src/main/webapp/script/fancyzoom/images/spacer.gif
new file mode 100644
index 00000000..5bfd67a2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/spacer.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-fill.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-fill.png
new file mode 100644
index 00000000..1e341533
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-fill.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-l.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-l.png
new file mode 100644
index 00000000..a63ea481
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-l.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-r.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-r.png
new file mode 100644
index 00000000..15980d58
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-caption-r.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow1.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow1.png
new file mode 100644
index 00000000..8b48000b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow1.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow2.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow2.png
new file mode 100644
index 00000000..09209f37
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow2.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow3.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow3.png
new file mode 100644
index 00000000..7636fec2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow3.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow4.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow4.png
new file mode 100644
index 00000000..c7f148d9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow4.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow5.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow5.png
new file mode 100644
index 00000000..2a75b82a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow5.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow6.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow6.png
new file mode 100644
index 00000000..65801aa0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow6.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow7.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow7.png
new file mode 100644
index 00000000..cb447608
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow7.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow8.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow8.png
new file mode 100644
index 00000000..f1c6acdb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-shadow8.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-1.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-1.png
new file mode 100644
index 00000000..5615629c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-1.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-10.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-10.png
new file mode 100644
index 00000000..77595dbd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-10.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-11.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-11.png
new file mode 100644
index 00000000..c2147d56
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-11.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-12.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-12.png
new file mode 100644
index 00000000..cf027248
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-12.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-2.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-2.png
new file mode 100644
index 00000000..95eaae89
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-2.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-3.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-3.png
new file mode 100644
index 00000000..9e2b9cb3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-3.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-4.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-4.png
new file mode 100644
index 00000000..a39c0fc0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-4.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-5.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-5.png
new file mode 100644
index 00000000..80bea63b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-5.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-6.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-6.png
new file mode 100644
index 00000000..b962e5f7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-6.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-7.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-7.png
new file mode 100644
index 00000000..9b6e489f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-7.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-8.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-8.png
new file mode 100644
index 00000000..fe147d5f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-8.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-9.png b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-9.png
new file mode 100644
index 00000000..b321b1c7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/fancyzoom/images/zoom-spin-9.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/jquery-1.7.1.min.js b/subsonic-main/src/main/webapp/script/jquery-1.7.1.min.js
new file mode 100644
index 00000000..198b3ff0
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/jquery-1.7.1.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.1 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function cb(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function ca(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bE.test(a)?d(a,e):ca(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)ca(a+"["+e+"]",b[e],c,d);else d(a,b)}function b_(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function b$(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bT,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=b$(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=b$(a,c,d,e,"*",g));return l}function bZ(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bP),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bC(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bx:by,g=0,h=e.length;if(d>0){if(c!=="border")for(;g<h;g++)c||(d-=parseFloat(f.css(a,"padding"+e[g]))||0),c==="margin"?d+=parseFloat(f.css(a,c+e[g]))||0:d-=parseFloat(f.css(a,"border"+e[g]+"Width"))||0;return d+"px"}d=bz(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0;if(c)for(;g<h;g++)d+=parseFloat(f.css(a,"padding"+e[g]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+e[g]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+e[g]))||0);return d+"px"}function bp(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c+(i[c][d].namespace?".":"")+i[c][d].namespace,i[c][d],i[c][d].data)}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?m(g):h==="function"&&(!a.unique||!o.has(g))&&c.push(g)},n=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,l=j||0,j=0,k=c.length;for(;c&&l<k;l++)if(c[l].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}i=!1,c&&(a.once?e===!0?o.disable():c=[]:d&&d.length&&(e=d.shift(),o.fireWith(e[0],e[1])))},o={add:function(){if(c){var a=c.length;m(arguments),i?k=c.length:e&&e!==!0&&(j=a,n(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){i&&f<=k&&(k--,f<=l&&l--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&o.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(i?a.once||d.push([b,c]):(!a.once||!e)&&n(b,c));return this},fire:function(){o.fireWith(this,arguments);return this},fired:function(){return!!e}};return o};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p,q=c.createElement("div"),r=c.documentElement;q.setAttribute("className","t"),q.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="<div "+n+"><div></div></div>"+"<table "+n+" cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="<div style='width:4px;'></div>",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h=null;if(typeof a=="undefined"){if(this.length){h=f.data(this[0]);if(this[0].nodeType===1&&!f._data(this[0],"parsedAttrs")){e=this[0].attributes;for(var i=0,j=e.length;i<j;i++)g=e[i].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),l(this[0],g,h[g]));f._data(this[0],"parsedAttrs",!0)}}return h}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split("."),d[1]=d[1]?"."+d[1]:"";if(c===b){h=this.triggerHandler("getData"+d[1]+"!",[d[0]]),h===b&&this.length&&(h=f.data(this[0],a),h=l(this[0],a,h));return h===b&&d[1]?this.data(d[0]):h}return this.each(function(){var b=f(this),e=[d[0],c];b.triggerHandler("setData"+d[1]+"!",e),f.data(this,a,c),b.triggerHandler("changeData"+d[1]+"!",e)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise()}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h<g;h++)e=d[h],e&&(c=f.propFix[e]||e,f.attr(a,e,""),a.removeAttribute(v?e:c),u.test(e)&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};
+f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=[],j,k,l,m,n,o,p,q,r,s,t;g[0]=c,c.delegateTarget=this;if(e&&!c.target.disabled&&(!c.button||c.type!=="click")){m=f(this),m.context=this.ownerDocument||this;for(l=c.target;l!=this;l=l.parentNode||this){o={},q=[],m[0]=l;for(j=0;j<e;j++)r=d[j],s=r.selector,o[s]===b&&(o[s]=r.quick?H(l,r.quick):m.is(s)),o[s]&&q.push(r);q.length&&i.push({elem:l,matches:q})}}d.length>e&&i.push({elem:this,matches:d.slice(e)});for(j=0;j<i.length&&!c.isPropagationStopped();j++){p=i[j],c.currentTarget=p.elem;for(k=0;k<p.matches.length&&!c.isImmediatePropagationStopped();k++){r=p.matches[k];if(h||!c.namespace&&!r.namespace||c.namespace_re&&c.namespace_re.test(r.namespace))c.data=r.data,c.handleObj=r,n=((f.event.special[r.origType]||{}).handle||r.handler).apply(p.elem,g),n!==b&&(c.result=n,n===!1&&(c.preventDefault(),c.stopPropagation()))}}return c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0)}),d._submit_attached=!0)})},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on.call(this,a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.type+"."+e.namespace:e.type,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.POS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function()
+{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bp)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1></$2>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bn(k[i]);else bn(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||be.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bq=/alpha\([^)]*\)/i,br=/opacity=([^)]*)/,bs=/([A-Z]|^ms)/g,bt=/^-?\d+(?:px)?$/i,bu=/^-?\d/,bv=/^([\-+])=([\-+.\de]+)/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Left","Right"],by=["Top","Bottom"],bz,bA,bB;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bz(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bv.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bz)return bz(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bC(a,b,d);f.swap(a,bw,function(){e=bC(a,b,d)});return e}},set:function(a,b){if(!bt.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cv(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cu("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cu("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cv(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cn.test(h)?(o=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),o?(f._data(this,"toggle"+i,o==="show"?"hide":"show"),j[o]()):j[h]()):(k=co.exec(h),l=j.cur(),k?(m=parseFloat(k[2]),n=k[3]||(f.cssNumber[i]?"":"px"),n!=="px"&&(f.style(this,i,(m||1)+n),l=(m||1)/j.cur()*l,f.style(this,i,l+n)),k[1]&&(m=(k[1]==="-="?-1:1)*m+l),j.custom(l,m,n)):j.custom(l,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:cu("show",1),slideUp:cu("hide",1),slideToggle:cu("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cr||cs(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){e.options.hide&&f._data(e.elem,"fxshow"+e.prop)===b&&f._data(e.elem,"fxshow"+e.prop,e.start)},h()&&f.timers.push(h)&&!cp&&(cp=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cr||cs(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cp),cp=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(["width","height"],function(a,b){f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cy(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.support.fixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.support.fixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/jquery-ui-1.8.18.custom.min.js b/subsonic-main/src/main/webapp/script/jquery-ui-1.8.18.custom.min.js
new file mode 100644
index 00000000..f00a62f1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/jquery-ui-1.8.18.custom.min.js
@@ -0,0 +1,356 @@
+/*!
+ * jQuery UI 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */(function(a,b){function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;if(!b.href||!g||f.nodeName.toLowerCase()!=="map")return!1;h=a("img[usemap=#"+g+"]")[0];return!!h&&d(h)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}a.ui=a.ui||{};a.ui.version||(a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)});return c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){if(c===b)return g["inner"+d].call(this);return this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){if(typeof b!="number")return g["outer"+d].call(this,b);return this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!!d&&!!a.element[0].parentNode)for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;if(b[d]>0)return!0;b[d]=1,e=b[d]>0,b[d]=0;return e},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}}))})(jQuery);/*!
+ * jQuery UI Widget 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}});return d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e;if(f&&e.charAt(0)==="_")return h;f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b){h=f;return!1}}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))});return h}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}this._setOptions(e);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);this.element.trigger(c,d);return!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);/*!
+ * jQuery UI Mouse 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent")){a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation();return!1}}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(b){if(!c){this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted){b.preventDefault();return!0}}!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0;return!0}},_mouseMove:function(b){if(a.browser.msie&&!(document.documentMode>=9)&&!b.button)return this._mouseUp(b);if(this._mouseStarted){this._mouseDrag(b);return b.preventDefault()}this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b));return!this._mouseStarted},_mouseUp:function(b){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);/*
+ * jQuery UI Position 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1];return this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]!==e){var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0}},top:function(b,c){if(c.at[1]!==e){var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];if(!c||!c.ownerDocument)return null;if(b)return this.each(function(){a.offset.setOffset(this,b)});return h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);/*
+ * jQuery UI Draggable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!!this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy();return this}},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle"))return!1;this.handle=this._getHandle(b);if(!this.handle)return!1;c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")});return!0},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment();if(this._trigger("start",b)===!1){this._clear();return!1}this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.helper.addClass("ui-draggable-dragging"),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b);return!0},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1){this._mouseUp({});return!1}this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";a.ui.ddmanager&&a.ui.ddmanager.drag(this,b);return!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var d=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){d._trigger("stop",b)!==!1&&d._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b);return a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)});return c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute");return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.left<h[0]&&(f=h[0]+this.offset.click.left),b.pageY-this.offset.click.top<h[1]&&(g=h[1]+this.offset.click.top),b.pageX-this.offset.click.left>h[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.top<h[1]||j-this.offset.click.top>h[3]?j-this.offset.click.top<h[1]?j+c.grid[1]:j-c.grid[1]:j:j;var k=c.grid[0]?this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0]:this.originalPageX;f=h?k-this.offset.click.left<h[0]||k-this.offset.click.left>h[2]?k-this.offset.click.left<h[0]?k+c.grid[0]:k-c.grid[0]:k:k}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(b,c,d){d=d||this._uiHash(),a.ui.plugin.call(this,b,[c,d]),b=="drag"&&(this.positionAbs=this._convertPositionTo("absolute"));return a.Widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(a){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),a.extend(a.ui.draggable,{version:"1.8.18"}),a.ui.plugin.add("draggable","connectToSortable",{start:function(b,c){var d=a(this).data("draggable"),e=d.options,f=a.extend({},c,{item:d.element});d.sortables=[],a(e.connectToSortable).each(function(){var c=a.data(this,"sortable");c&&!c.options.disabled&&(d.sortables.push({instance:c,shouldRevert:c.options.revert}),c.refreshPositions(),c._trigger("activate",b,f))})},stop:function(b,c){var d=a(this).data("draggable"),e=a.extend({},c,{item:d.element});a.each(d.sortables,function(){this.instance.isOver?(this.instance.isOver=0,d.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(b),this.instance.options.helper=this.instance.options._helper,d.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",b,e))})},drag:function(b,c){var d=a(this).data("draggable"),e=this,f=function(b){var c=this.offset.click.top,d=this.offset.click.left,e=this.positionAbs.top,f=this.positionAbs.left,g=b.height,h=b.width,i=b.top,j=b.left;return a.ui.isOver(e+c,f+d,i,j,g,h)};a.each(d.sortables,function(f){this.instance.positionAbs=d.positionAbs,this.instance.helperProportions=d.helperProportions,this.instance.offset.click=d.offset.click,this.instance._intersectsWith(this.instance.containerCache)?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=a(e).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return c.helper[0]},b.target=this.instance.currentItem[0],this.instance._mouseCapture(b,!0),this.instance._mouseStart(b,!0,!0),this.instance.offset.click.top=d.offset.click.top,this.instance.offset.click.left=d.offset.click.left,this.instance.offset.parent.left-=d.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=d.offset.parent.top-this.instance.offset.parent.top,d._trigger("toSortable",b),d.dropped=this.instance.element,d.currentItem=d.element,this.instance.fromOutside=d),this.instance.currentItem&&this.instance._mouseDrag(b)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",b,this.instance._uiHash(this.instance)),this.instance._mouseStop(b,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),d._trigger("fromSortable",b),d.dropped=!1)})}}),a.ui.plugin.add("draggable","cursor",{start:function(b,c){var d=a("body"),e=a(this).data("draggable").options;d.css("cursor")&&(e._cursor=d.css("cursor")),d.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;d._cursor&&a("body").css("cursor",d._cursor)}}),a.ui.plugin.add("draggable","opacity",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("opacity")&&(e._opacity=d.css("opacity")),d.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;d._opacity&&a(c.helper).css("opacity",d._opacity)}}),a.ui.plugin.add("draggable","scroll",{start:function(b,c){var d=a(this).data("draggable");d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"&&(d.overflowOffset=d.scrollParent.offset())},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=!1;if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"){if(!e.axis||e.axis!="x")d.overflowOffset.top+d.scrollParent[0].offsetHeight-b.pageY<e.scrollSensitivity?d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop+e.scrollSpeed:b.pageY-d.overflowOffset.top<e.scrollSensitivity&&(d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop-e.scrollSpeed);if(!e.axis||e.axis!="y")d.overflowOffset.left+d.scrollParent[0].offsetWidth-b.pageX<e.scrollSensitivity?d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft+e.scrollSpeed:b.pageX-d.overflowOffset.left<e.scrollSensitivity&&(d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft-e.scrollSpeed)}else{if(!e.axis||e.axis!="x")b.pageY-a(document).scrollTop()<e.scrollSensitivity?f=a(document).scrollTop(a(document).scrollTop()-e.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<e.scrollSensitivity&&(f=a(document).scrollTop(a(document).scrollTop()+e.scrollSpeed));if(!e.axis||e.axis!="y")b.pageX-a(document).scrollLeft()<e.scrollSensitivity?f=a(document).scrollLeft(a(document).scrollLeft()-e.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<e.scrollSensitivity&&(f=a(document).scrollLeft(a(document).scrollLeft()+e.scrollSpeed))}f!==!1&&a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(d,b)}}),a.ui.plugin.add("draggable","snap",{start:function(b,c){var d=a(this).data("draggable"),e=d.options;d.snapElements=[],a(e.snap.constructor!=String?e.snap.items||":data(draggable)":e.snap).each(function(){var b=a(this),c=b.offset();this!=d.element[0]&&d.snapElements.push({item:this,width:b.outerWidth(),height:b.outerHeight(),top:c.top,left:c.left})})},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=e.snapTolerance,g=c.offset.left,h=g+d.helperProportions.width,i=c.offset.top,j=i+d.helperProportions.height;for(var k=d.snapElements.length-1;k>=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f<g&&g<m+f&&n-f<i&&i<o+f||l-f<g&&g<m+f&&n-f<j&&j<o+f||l-f<h&&h<m+f&&n-f<i&&i<o+f||l-f<h&&h<m+f&&n-f<j&&j<o+f)){d.snapElements[k].snapping&&d.options.snap.release&&d.options.snap.release.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=!1;continue}if(e.snapMode!="inner"){var p=Math.abs(n-j)<=f,q=Math.abs(o-i)<=f,r=Math.abs(l-h)<=f,s=Math.abs(m-g)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n-d.helperProportions.height,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l-d.helperProportions.width}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m}).left-d.margins.left)}var t=p||q||r||s;if(e.snapMode!="outer"){var p=Math.abs(n-i)<=f,q=Math.abs(o-j)<=f,r=Math.abs(l-g)<=f,s=Math.abs(m-h)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o-d.helperProportions.height,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m-d.helperProportions.width}).left-d.margins.left)}!d.snapElements[k].snapping&&(p||q||r||s||t)&&d.options.snap.snap&&d.options.snap.snap.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=p||q||r||s||t}}}),a.ui.plugin.add("draggable","stack",{start:function(b,c){var d=a(this).data("draggable").options,e=a.makeArray(a(d.stack)).sort(function(b,c){return(parseInt(a(b).css("zIndex"),10)||0)-(parseInt(a(c).css("zIndex"),10)||0)});if(!!e.length){var f=parseInt(e[0].style.zIndex)||0;a(e).each(function(a){this.style.zIndex=f+a}),this[0].style.zIndex=f+e.length}}}),a.ui.plugin.add("draggable","zIndex",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("zIndex")&&(e._zIndex=d.css("zIndex")),d.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;d._zIndex&&a(c.helper).css("zIndex",d._zIndex)}})})(jQuery);/*
+ * jQuery UI Droppable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */(function(a,b){a.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var b=this.options,c=b.accept;this.isover=0,this.isout=1,this.accept=a.isFunction(c)?c:function(a){return a.is(c)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},a.ui.ddmanager.droppables[b.scope]=a.ui.ddmanager.droppables[b.scope]||[],a.ui.ddmanager.droppables[b.scope].push(this),b.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++)b[c]==this&&b.splice(c,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(b,c){b=="accept"&&(this.accept=a.isFunction(c)?c:function(a){return a.is(c)}),a.Widget.prototype._setOption.apply(this,arguments)},_activate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),c&&this._trigger("activate",b,this.ui(c))},_deactivate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),c&&this._trigger("deactivate",b,this.ui(c))},_over:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",b,this.ui(c)))},_out:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",b,this.ui(c)))},_drop:function(b,c){var d=c||a.ui.ddmanager.current;if(!d||(d.currentItem||d.element)[0]==this.element[0])return!1;var e=!1;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var b=a.data(this,"droppable");if(b.options.greedy&&!b.options.disabled&&b.options.scope==d.options.scope&&b.accept.call(b.element[0],d.currentItem||d.element)&&a.ui.intersect(d,a.extend(b,{offset:b.element.offset()}),b.options.tolerance)){e=!0;return!1}});if(e)return!1;if(this.accept.call(this.element[0],d.currentItem||d.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",b,this.ui(d));return this.element}return!1},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}}),a.extend(a.ui.droppable,{version:"1.8.18"}),a.ui.intersect=function(b,c,d){if(!c.offset)return!1;var e=(b.positionAbs||b.position.absolute).left,f=e+b.helperProportions.width,g=(b.positionAbs||b.position.absolute).top,h=g+b.helperProportions.height,i=c.offset.left,j=i+c.proportions.width,k=c.offset.top,l=k+c.proportions.height;switch(d){case"fit":return i<=e&&f<=j&&k<=g&&h<=l;case"intersect":return i<e+b.helperProportions.width/2&&f-b.helperProportions.width/2<j&&k<g+b.helperProportions.height/2&&h-b.helperProportions.height/2<l;case"pointer":var m=(b.positionAbs||b.position.absolute).left+(b.clickOffset||b.offset.click).left,n=(b.positionAbs||b.position.absolute).top+(b.clickOffset||b.offset.click).top,o=a.ui.isOver(n,m,k,i,c.proportions.height,c.proportions.width);return o;case"touch":return(g>=k&&g<=l||h>=k&&h<=l||g<k&&h>l)&&(e>=i&&e<=j||f>=i&&f<=j||e<i&&f>j);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();droppablesLoop:for(var g=0;g<d.length;g++){if(d[g].options.disabled||b&&!d[g].accept.call(d[g].element[0],b.currentItem||b.element))continue;for(var h=0;h<f.length;h++)if(f[h]==d[g].element[0]){d[g].proportions.height=0;continue droppablesLoop}d[g].visible=d[g].element.css("display")!="none";if(!d[g].visible)continue;e=="mousedown"&&d[g]._activate.call(d[g],c),d[g].offset=d[g].element.offset(),d[g].proportions={width:d[g].element[0].offsetWidth,height:d[g].element[0].offsetHeight}}},drop:function(b,c){var d=!1;a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){!this.options||(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)&&(d=this._drop.call(this,c)||d),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],b.currentItem||b.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,c)))});return d},dragStart:function(b,c){b.element.parents(":not(body,html)").bind("scroll.droppable",function(){b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)})},drag:function(b,c){b.options.refreshPositions&&a.ui.ddmanager.prepareOffsets(b,c),a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var d=a.ui.intersect(b,this,this.options.tolerance),e=!d&&this.isover==1?"isout":d&&this.isover==0?"isover":null;if(!e)return;var f;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");g.length&&(f=a.data(g[0],"droppable"),f.greedyChild=e=="isover"?1:0)}f&&e=="isover"&&(f.isover=0,f.isout=1,f._out.call(f,c)),this[e]=1,this[e=="isout"?"isover":"isout"]=0,this[e=="isover"?"_over":"_out"].call(this,c),f&&e=="isout"&&(f.isout=0,f.isover=1,f._over.call(f,c))}})},dragStop:function(b,c){b.element.parents(":not(body,html)").unbind("scroll.droppable"),b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)}}})(jQuery);/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.resizable",a.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var b=this,c=this.options;this.element.addClass("ui-resizable"),a.extend(this,{_aspectRatio:!!c.aspectRatio,aspectRatio:c.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:c.helper||c.ghost||c.animate?c.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(a('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=c.handles||(a(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var d=this.handles.split(",");this.handles={};for(var e=0;e<d.length;e++){var f=a.trim(d[e]),g="ui-resizable-"+f,h=a('<div class="ui-resizable-handle '+g+'"></div>');/sw|se|ne|nw/.test(f)&&h.css({zIndex:++c.zIndex}),"se"==f&&h.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[f]=".ui-resizable-"+f,this.element.append(h)}}this._renderAxis=function(b){b=b||this.element;for(var c in this.handles){this.handles[c].constructor==String&&(this.handles[c]=a(this.handles[c],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var d=a(this.handles[c],this.element),e=0;e=/sw|ne|nw|se|n|s/.test(c)?d.outerHeight():d.outerWidth();var f=["padding",/ne|nw|n/.test(c)?"Top":/se|sw|s/.test(c)?"Bottom":/^e$/.test(c)?"Right":"Left"].join("");b.css(f,e),this._proportionallyResize()}if(!a(this.handles[c]).length)continue}},this._renderAxis(this.element),this._handles=a(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!b.resizing){if(this.className)var a=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=a&&a[1]?a[1]:"se"}}),c.autoHide&&(this._handles.hide(),a(this.element).addClass("ui-resizable-autohide").hover(function(){c.disabled||(a(this).removeClass("ui-resizable-autohide"),b._handles.show())},function(){c.disabled||b.resizing||(a(this).addClass("ui-resizable-autohide"),b._handles.hide())})),this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(b){a(b).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var c=this.element;c.after(this.originalElement.css({position:c.css("position"),width:c.outerWidth(),height:c.outerHeight(),top:c.css("top"),left:c.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle),b(this.originalElement);return this},_mouseCapture:function(b){var c=!1;for(var d in this.handles)a(this.handles[d])[0]==b.target&&(c=!0);return!this.options.disabled&&c},_mouseStart:function(b){var d=this.options,e=this.element.position(),f=this.element;this.resizing=!0,this.documentScroll={top:a(document).scrollTop(),left:a(document).scrollLeft()},(f.is(".ui-draggable")||/absolute/.test(f.css("position")))&&f.css({position:"absolute",top:e.top,left:e.left}),this._renderProxy();var g=c(this.helper.css("left")),h=c(this.helper.css("top"));d.containment&&(g+=a(d.containment).scrollLeft()||0,h+=a(d.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:g,top:h},this.size=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalSize=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalPosition={left:g,top:h},this.sizeDiff={width:f.outerWidth()-f.width(),height:f.outerHeight()-f.height()},this.originalMousePosition={left:b.pageX,top:b.pageY},this.aspectRatio=typeof d.aspectRatio=="number"?d.aspectRatio:this.originalSize.width/this.originalSize.height||1;var i=a(".ui-resizable-"+this.axis).css("cursor");a("body").css("cursor",i=="auto"?this.axis+"-resize":i),f.addClass("ui-resizable-resizing"),this._propagate("start",b);return!0},_mouseDrag:function(b){var c=this.helper,d=this.options,e={},f=this,g=this.originalMousePosition,h=this.axis,i=b.pageX-g.left||0,j=b.pageY-g.top||0,k=this._change[h];if(!k)return!1;var l=k.apply(this,[b,i,j]),m=a.browser.msie&&a.browser.version<7,n=this.sizeDiff;this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)l=this._updateRatio(l,b);l=this._respectSize(l,b),this._propagate("resize",b),c.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",b,this.ui());return!1},_mouseStop:function(b){this.resizing=!1;var c=this.options,d=this;if(this._helper){var e=this._proportionallyResizeElements,f=e.length&&/textarea/i.test(e[0].nodeName),g=f&&a.ui.hasScroll(e[0],"left")?0:d.sizeDiff.height,h=f?0:d.sizeDiff.width,i={width:d.helper.width()-h,height:d.helper.height()-g},j=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,k=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;c.animate||this.element.css(a.extend(i,{top:k,left:j})),d.helper.height(d.size.height),d.helper.width(d.size.width),this._helper&&!c.animate&&this._proportionallyResize()}a("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",b),this._helper&&this.helper.remove();return!1},_updateVirtualBoundaries:function(a){var b=this.options,c,e,f,g,h;h={minWidth:d(b.minWidth)?b.minWidth:0,maxWidth:d(b.maxWidth)?b.maxWidth:Infinity,minHeight:d(b.minHeight)?b.minHeight:0,maxHeight:d(b.maxHeight)?b.maxHeight:Infinity};if(this._aspectRatio||a)c=h.minHeight*this.aspectRatio,f=h.minWidth/this.aspectRatio,e=h.maxHeight*this.aspectRatio,g=h.maxWidth/this.aspectRatio,c>h.minWidth&&(h.minWidth=c),f>h.minHeight&&(h.minHeight=f),e<h.maxWidth&&(h.maxWidth=e),g<h.maxHeight&&(h.maxHeight=g);this._vBoundaries=h},_updateCache:function(a){var b=this.options;this.offset=this.helper.offset(),d(a.left)&&(this.position.left=a.left),d(a.top)&&(this.position.top=a.top),d(a.height)&&(this.size.height=a.height),d(a.width)&&(this.size.width=a.width)},_updateRatio:function(a,b){var c=this.options,e=this.position,f=this.size,g=this.axis;d(a.height)?a.width=a.height*this.aspectRatio:d(a.width)&&(a.height=a.width/this.aspectRatio),g=="sw"&&(a.left=e.left+(f.width-a.width),a.top=null),g=="nw"&&(a.top=e.top+(f.height-a.height),a.left=e.left+(f.width-a.width));return a},_respectSize:function(a,b){var c=this.helper,e=this._vBoundaries,f=this._aspectRatio||b.shiftKey,g=this.axis,h=d(a.width)&&e.maxWidth&&e.maxWidth<a.width,i=d(a.height)&&e.maxHeight&&e.maxHeight<a.height,j=d(a.width)&&e.minWidth&&e.minWidth>a.width,k=d(a.height)&&e.minHeight&&e.minHeight>a.height;j&&(a.width=e.minWidth),k&&(a.height=e.minHeight),h&&(a.width=e.maxWidth),i&&(a.height=e.maxHeight);var l=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height,n=/sw|nw|w/.test(g),o=/nw|ne|n/.test(g);j&&n&&(a.left=l-e.minWidth),h&&n&&(a.left=l-e.maxWidth),k&&o&&(a.top=m-e.minHeight),i&&o&&(a.top=m-e.maxHeight);var p=!a.width&&!a.height;p&&!a.left&&a.top?a.top=null:p&&!a.top&&a.left&&(a.left=null);return a},_proportionallyResize:function(){var b=this.options;if(!!this._proportionallyResizeElements.length){var c=this.helper||this.element;for(var d=0;d<this._proportionallyResizeElements.length;d++){var e=this._proportionallyResizeElements[d];if(!this.borderDif){var f=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],g=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];this.borderDif=a.map(f,function(a,b){var c=parseInt(a,10)||0,d=parseInt(g[b],10)||0;return c+d})}if(a.browser.msie&&(!!a(c).is(":hidden")||!!a(c).parents(":hidden").length))continue;e.css({height:c.height()-this.borderDif[0]-this.borderDif[2]||0,width:c.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var b=this.element,c=this.options;this.elementOffset=b.offset();if(this._helper){this.helper=this.helper||a('<div style="overflow:hidden;"></div>');var d=a.browser.msie&&a.browser.version<7,e=d?1:0,f=d?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++c.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(a,b,c){return{width:this.originalSize.width+b}},w:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{left:f.left+b,width:e.width-b}},n:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{top:f.top+c,height:e.height-c}},s:function(a,b,c){return{height:this.originalSize.height+c}},se:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},sw:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,c,d]))},ne:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},nw:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,c,d]))}},_propagate:function(b,c){a.ui.plugin.call(this,b,[c,this.ui()]),b!="resize"&&this._trigger(b,c,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),a.extend(a.ui.resizable,{version:"1.8.18"}),a.ui.plugin.add("resizable","alsoResize",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.data("resizable-alsoresize",{width:parseInt(b.width(),10),height:parseInt(b.height(),10),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10)})})};typeof e.alsoResize=="object"&&!e.alsoResize.parentNode?e.alsoResize.length?(e.alsoResize=e.alsoResize[0],f(e.alsoResize)):a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.originalSize,g=d.originalPosition,h={height:d.size.height-f.height||0,width:d.size.width-f.width||0,top:d.position.top-g.top||0,left:d.position.left-g.left||0},i=function(b,d){a(b).each(function(){var b=a(this),e=a(this).data("resizable-alsoresize"),f={},g=d&&d.length?d:b.parents(c.originalElement[0]).length?["width","height"]:["width","height","top","left"];a.each(g,function(a,b){var c=(e[b]||0)+(h[b]||0);c&&c>=0&&(f[b]=c||null)}),b.css(f)})};typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a,b){i(a,b)}):i(e.alsoResize)},stop:function(b,c){a(this).removeData("resizable-alsoresize")}}),a.ui.plugin.add("resizable","animate",{stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d._proportionallyResizeElements,g=f.length&&/textarea/i.test(f[0].nodeName),h=g&&a.ui.hasScroll(f[0],"left")?0:d.sizeDiff.height,i=g?0:d.sizeDiff.width,j={width:d.size.width-i,height:d.size.height-h},k=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,l=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;d.element.animate(a.extend(j,l&&k?{top:l,left:k}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var c={width:parseInt(d.element.css("width"),10),height:parseInt(d.element.css("height"),10),top:parseInt(d.element.css("top"),10),left:parseInt(d.element.css("left"),10)};f&&f.length&&a(f[0]).css({width:c.width,height:c.height}),d._updateCache(c),d._propagate("resize",b)}})}}),a.ui.plugin.add("resizable","containment",{start:function(b,d){var e=a(this).data("resizable"),f=e.options,g=e.element,h=f.containment,i=h instanceof a?h.get(0):/parent/.test(h)?g.parent().get(0):h;if(!!i){e.containerElement=a(i);if(/document/.test(h)||h==document)e.containerOffset={left:0,top:0},e.containerPosition={left:0,top:0},e.parentData={element:a(document),left:0,top:0,width:a(document).width(),height:a(document).height()||document.body.parentNode.scrollHeight};else{var j=a(i),k=[];a(["Top","Right","Left","Bottom"]).each(function(a,b){k[a]=c(j.css("padding"+b))}),e.containerOffset=j.offset(),e.containerPosition=j.position(),e.containerSize={height:j.innerHeight()-k[3],width:j.innerWidth()-k[1]};var l=e.containerOffset,m=e.containerSize.height,n=e.containerSize.width,o=a.ui.hasScroll(i,"left")?i.scrollWidth:n,p=a.ui.hasScroll(i)?i.scrollHeight:m;e.parentData={element:i,left:l.left,top:l.top,width:o,height:p}}}},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.containerSize,g=d.containerOffset,h=d.size,i=d.position,j=d._aspectRatio||b.shiftKey,k={top:0,left:0},l=d.containerElement;l[0]!=document&&/static/.test(l.css("position"))&&(k=g),i.left<(d._helper?g.left:0)&&(d.size.width=d.size.width+(d._helper?d.position.left-g.left:d.position.left-k.left),j&&(d.size.height=d.size.width/e.aspectRatio),d.position.left=e.helper?g.left:0),i.top<(d._helper?g.top:0)&&(d.size.height=d.size.height+(d._helper?d.position.top-g.top:d.position.top),j&&(d.size.width=d.size.height*e.aspectRatio),d.position.top=d._helper?g.top:0),d.offset.left=d.parentData.left+d.position.left,d.offset.top=d.parentData.top+d.position.top;var m=Math.abs((d._helper?d.offset.left-k.left:d.offset.left-k.left)+d.sizeDiff.width),n=Math.abs((d._helper?d.offset.top-k.top:d.offset.top-g.top)+d.sizeDiff.height),o=d.containerElement.get(0)==d.element.parent().get(0),p=/relative|absolute/.test(d.containerElement.css("position"));o&&p&&(m-=d.parentData.left),m+d.size.width>=d.parentData.width&&(d.size.width=d.parentData.width-m,j&&(d.size.height=d.size.width/d.aspectRatio)),n+d.size.height>=d.parentData.height&&(d.size.height=d.parentData.height-n,j&&(d.size.width=d.size.height*d.aspectRatio))},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.position,g=d.containerOffset,h=d.containerPosition,i=d.containerElement,j=a(d.helper),k=j.offset(),l=j.outerWidth()-d.sizeDiff.width,m=j.outerHeight()-d.sizeDiff.height;d._helper&&!e.animate&&/relative/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m}),d._helper&&!e.animate&&/static/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m})}}),a.ui.plugin.add("resizable","ghost",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size;d.ghost=d.originalElement.clone(),d.ghost.css({opacity:.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof e.ghost=="string"?e.ghost:""),d.ghost.appendTo(d.helper)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})},stop:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.helper&&d.helper.get(0).removeChild(d.ghost.get(0))}}),a.ui.plugin.add("resizable","grid",{resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size,g=d.originalSize,h=d.originalPosition,i=d.axis,j=e._aspectRatio||b.shiftKey;e.grid=typeof e.grid=="number"?[e.grid,e.grid]:e.grid;var k=Math.round((f.width-g.width)/(e.grid[0]||1))*(e.grid[0]||1),l=Math.round((f.height-g.height)/(e.grid[1]||1))*(e.grid[1]||1);/^(se|s|e)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l):/^(ne)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l):/^(sw)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.left=h.left-k):(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l,d.position.left=h.left-k)}});var c=function(a){return parseInt(a,10)||0},d=function(a){return!isNaN(parseInt(a,10))}})(jQuery);/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.selectable",a.ui.mouse,{options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var b=this;this.element.addClass("ui-selectable"),this.dragged=!1;var c;this.refresh=function(){c=a(b.options.filter,b.element[0]),c.addClass("ui-selectee"),c.each(function(){var b=a(this),c=b.offset();a.data(this,"selectable-item",{element:this,$element:b,left:c.left,top:c.top,right:c.left+b.outerWidth(),bottom:c.top+b.outerHeight(),startselected:!1,selected:b.hasClass("ui-selected"),selecting:b.hasClass("ui-selecting"),unselecting:b.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=c.addClass("ui-selectee"),this._mouseInit(),this.helper=a("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable"),this._mouseDestroy();return this},_mouseStart:function(b){var c=this;this.opos=[b.pageX,b.pageY];if(!this.options.disabled){var d=this.options;this.selectees=a(d.filter,this.element[0]),this._trigger("start",b),a(d.appendTo).append(this.helper),this.helper.css({left:b.clientX,top:b.clientY,width:0,height:0}),d.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var d=a.data(this,"selectable-item");d.startselected=!0,!b.metaKey&&!b.ctrlKey&&(d.$element.removeClass("ui-selected"),d.selected=!1,d.$element.addClass("ui-unselecting"),d.unselecting=!0,c._trigger("unselecting",b,{unselecting:d.element}))}),a(b.target).parents().andSelf().each(function(){var d=a.data(this,"selectable-item");if(d){var e=!b.metaKey&&!b.ctrlKey||!d.$element.hasClass("ui-selected");d.$element.removeClass(e?"ui-unselecting":"ui-selected").addClass(e?"ui-selecting":"ui-unselecting"),d.unselecting=!e,d.selecting=e,d.selected=e,e?c._trigger("selecting",b,{selecting:d.element}):c._trigger("unselecting",b,{unselecting:d.element});return!1}})}},_mouseDrag:function(b){var c=this;this.dragged=!0;if(!this.options.disabled){var d=this.options,e=this.opos[0],f=this.opos[1],g=b.pageX,h=b.pageY;if(e>g){var i=g;g=e,e=i}if(f>h){var i=h;h=f,f=i}this.helper.css({left:e,top:f,width:g-e,height:h-f}),this.selectees.each(function(){var i=a.data(this,"selectable-item");if(!!i&&i.element!=c.element[0]){var j=!1;d.tolerance=="touch"?j=!(i.left>g||i.right<e||i.top>h||i.bottom<f):d.tolerance=="fit"&&(j=i.left>e&&i.right<g&&i.top>f&&i.bottom<h),j?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,c._trigger("selecting",b,{selecting:i.element}))):(i.selecting&&((b.metaKey||b.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),c._trigger("unselecting",b,{unselecting:i.element}))),i.selected&&!b.metaKey&&!b.ctrlKey&&!i.startselected&&(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,c._trigger("unselecting",b,{unselecting:i.element})))}});return!1}},_mouseStop:function(b){var c=this;this.dragged=!1;var d=this.options;a(".ui-unselecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-unselecting"),d.unselecting=!1,d.startselected=!1,c._trigger("unselected",b,{unselected:d.element})}),a(".ui-selecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected"),d.selecting=!1,d.selected=!0,d.startselected=!0,c._trigger("selected",b,{selected:d.element})}),this._trigger("stop",b),this.helper.remove();return!1}}),a.extend(a.ui.selectable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Sortable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},destroy:function(){a.Widget.prototype.destroy.call(this),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var b=this.items.length-1;b>=0;b--)this.items[b].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f){e=a(this);return!1}});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}this.currentItem=e,this._removeCurrentsFromItems();return!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b);return!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY<c.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+c.scrollSpeed:b.pageY-this.overflowOffset.top<c.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-c.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-b.pageX<c.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+c.scrollSpeed:b.pageX-this.overflowOffset.left<c.scrollSensitivity&&(this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft-c.scrollSpeed)):(b.pageY-a(document).scrollTop()<c.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-c.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<c.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+c.scrollSpeed)),b.pageX-a(document).scrollLeft()<c.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-c.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<c.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+c.scrollSpeed))),d!==!1&&a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var e=this.items.length-1;e>=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs;return!1},_mouseStop:function(b,c){if(!!b){a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1}},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem));return this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"=");return d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")});return d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+j<i&&b+k>f&&b+k<g;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?l:f<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<g&&h<d+this.helperProportions.height/2&&e-this.helperProportions.height/2<i},_intersectsWithPointer:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left,b.width),e=c&&d,f=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();if(!e)return!1;return this.floating?g&&g=="right"||f=="down"?2:1:f&&(f=="down"?2:1)},_intersectsWithSides:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top+b.height/2,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left+b.width/2,b.width),e=this._getDragVerticalDirection(),f=this._getDragHorizontalDirection();return this.floating&&f?f=="right"&&d||f=="left"&&!d:e&&(e=="down"&&c||e=="up"&&!c)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a),this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(b){this.items=[],this.containers=[this];var c=this.items,d=this,e=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],b,{item:this.currentItem}):a(this.options.items,this.element),this]],f=this._connectWith();if(f&&this.ready)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i<m;i++){var n=a(l[i]);n.data(this.widgetName+"-item",k),c.push({item:n,instance:k,width:0,height:0,left:0,top:0})}}},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var c=this.items.length-1;c>=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];e||(b.style.visibility="hidden");return b},update:function(a,b){if(!e||!!d.forcePlaceholderSize)b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!!c)if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.items[i][this.containers[d].floating?"left":"top"];Math.abs(j-h)<f&&(f=Math.abs(j-h),g=this.items[i])}if(!g&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[d],g?this._rearrange(b,g,null,!0):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("change",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1}},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.currentItem])):c.helper=="clone"?this.currentItem.clone():this.currentItem;d.parents("body").length||a(c.appendTo!="parent"?c.appendTo:this.currentItem[0].parentNode)[0].appendChild(d[0]),d[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.width==""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].style.height==""||c.forceHelperSize)&&d.height(this.currentItem.height());return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)){var c=a(b.containment)[0],d=a(b.containment).offset(),e=a(c).css("overflow")!="hidden";this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var f=b.pageX,g=b.pageY;if(this.originalPosition){this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(f=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(g=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.top<this.containment[1]||h-this.offset.click.top>this.containment[3]?h-this.offset.click.top<this.containment[1]?h+c.grid[1]:h-c.grid[1]:h:h;var i=this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0];f=this.containment?i-this.offset.click.left<this.containment[0]||i-this.offset.click.left>this.containment[2]?i-this.offset.click.left<this.containment[0]?i+c.grid[0]:i-c.grid[0]:i:i}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_rearrange:function(a,b,c,d){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var e=this,f=this.counter;window.setTimeout(function(){f==e.counter&&e.refreshPositions(!d)},0)},_clear:function(b,c){this.reverting=!1;var d=[],e=this;!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var f in this._storedCSS)if(this._storedCSS[f]=="auto"||this._storedCSS[f]=="static")this._storedCSS[f]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!c&&d.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!c&&d.push(function(a){this._trigger("update",a,this._uiHash())});if(!a.ui.contains(this.element[0],this.currentItem[0])){c||d.push(function(a){this._trigger("remove",a,this._uiHash())});for(var f=this.containers.length-1;f>=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return!1}c||this._trigger("beforeStop",b,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!c){for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}this.fromOutside=!1;return!0},_trigger:function(){a.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(b){var c=b||this;return{helper:c.helper,placeholder:c.placeholder||a([]),position:c.position,originalPosition:c.originalPosition,offset:c.positionAbs,item:c.currentItem,sender:b?b.element:null}}}),a.extend(a.ui.sortable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:!0,clearStyle:!1,collapsible:!1,event:"click",fillSpace:!1,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var b=this,c=b.options;b.running=0,b.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"),b.headers=b.element.find(c.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c.disabled||a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c.disabled||a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c.disabled||a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c.disabled||a(this).removeClass("ui-state-focus")}),b.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(c.navigation){var d=b.element.find("a").filter(c.navigationFilter).eq(0);if(d.length){var e=d.closest(".ui-accordion-header");e.length?b.active=e:b.active=d.closest(".ui-accordion-content").prev()}}b.active=b._findActive(b.active||c.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top"),b.active.next().addClass("ui-accordion-content-active"),b._createIcons(),b.resize(),b.element.attr("role","tablist"),b.headers.attr("role","tab").bind("keydown.accordion",function(a){return b._keydown(a)}).next().attr("role","tabpanel"),b.headers.not(b.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide(),b.active.length?b.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):b.headers.eq(0).attr("tabIndex",0),a.browser.safari||b.headers.find("a").attr("tabIndex",-1),c.event&&b.headers.bind(c.event.split(" ").join(".accordion ")+".accordion",function(a){b._clickHandler.call(b,a,this),a.preventDefault()})},_createIcons:function(){var b=this.options;b.icons&&(a("<span></span>").addClass("ui-icon "+b.icons.header).prependTo(this.headers),this.active.children(".ui-icon").toggleClass(b.icons.header).toggleClass(b.icons.headerSelected),this.element.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.children(".ui-icon").remove(),this.element.removeClass("ui-accordion-icons")},destroy:function(){var b=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"),this.headers.find("a").removeAttr("tabIndex"),this._destroyIcons();var c=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");(b.autoHeight||b.fillHeight)&&c.css("height","");return a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b=="active"&&this.activate(c),b=="icons"&&(this._destroyIcons(),c&&this._createIcons()),b=="disabled"&&this.headers.add(this.headers.next())[c?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(b){if(!(this.options.disabled||b.altKey||b.ctrlKey)){var c=a.ui.keyCode,d=this.headers.length,e=this.headers.index(b.target),f=!1;switch(b.keyCode){case c.RIGHT:case c.DOWN:f=this.headers[(e+1)%d];break;case c.LEFT:case c.UP:f=this.headers[(e-1+d)%d];break;case c.SPACE:case c.ENTER:this._clickHandler({target:b.target},b.target),b.preventDefault()}if(f){a(b.target).attr("tabIndex",-1),a(f).attr("tabIndex",0),f.focus();return!1}return!0}},resize:function(){var b=this.options,c;if(b.fillSpace){if(a.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}c=this.element.parent().height(),a.browser.msie&&this.element.parent().css("overflow",d),this.headers.each(function(){c-=a(this).outerHeight(!0)}),this.headers.next().each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")}else b.autoHeight&&(c=0,this.headers.next().each(function(){c=Math.max(c,a(this).height("").height())}).height(c));return this},activate:function(a){this.options.active=a;var b=this._findActive(a)[0];this._clickHandler({target:b},b);return this},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===!1?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,c){var d=this.options;if(!d.disabled){if(!b.target){if(!d.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),this.active.next().addClass("ui-accordion-content-active");var e=this.active.next(),f={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:e},g=this.active=a([]);this._toggle(g,e,f);return}var h=a(b.currentTarget||c),i=h[0]===this.active[0];d.active=d.collapsible&&i?!1:this.headers.index(h);if(this.running||!d.collapsible&&i)return;var j=this.active,g=h.next(),e=this.active.next(),f={options:d,newHeader:i&&d.collapsible?a([]):h,oldHeader:this.active,newContent:i&&d.collapsible?a([]):g,oldContent:e},k=this.headers.index(this.active[0])>this.headers.index(h[0]);this.active=i?a([]):h,this._toggle(g,e,f,i,k),j.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),i||(h.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected),h.next().addClass("ui-accordion-content-active"));return}},_toggle:function(b,c,d,e,f){var g=this,h=g.options;g.toShow=b,g.toHide=c,g.data=d;var i=function(){if(!!g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data),g.running=c.size()===0?b.size():c.size();if(h.animated){var j={};h.collapsible&&e?j={toShow:a([]),toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace}:j={toShow:b,toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace},h.proxied||(h.proxied=h.animated),h.proxiedDuration||(h.proxiedDuration=h.duration),h.animated=a.isFunction(h.proxied)?h.proxied(j):h.proxied,h.duration=a.isFunction(h.proxiedDuration)?h.proxiedDuration(j):h.proxiedDuration;var k=a.ui.accordion.animations,l=h.duration,m=h.animated;m&&!k[m]&&!a.easing[m]&&(m="slide"),k[m]||(k[m]=function(a){this.slide(a,{easing:m,duration:l||700})}),k[m](j)}else h.collapsible&&e?b.toggle():(c.hide(),b.show()),i(!0);c.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur(),b.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;this.running||(this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""}),this.toHide.removeClass("ui-accordion-content-active"),this.toHide.length&&(this.toHide.parent()[0].className=this.toHide.parent()[0].className),this._trigger("change",null,this.data))}}),a.extend(a.ui.accordion,{version:"1.8.18",animations:{slide:function(b,c){b=a.extend({easing:"swing",duration:300},b,c);if(!b.toHide.size())b.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},b);else{if(!b.toShow.size()){b.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},b);return}var d=b.toShow.css("overflow"),e=0,f={},g={},h=["height","paddingTop","paddingBottom"],i,j=b.toShow;i=j[0].style.width,j.width(j.parent().width()-parseFloat(j.css("paddingLeft"))-parseFloat(j.css("paddingRight"))-(parseFloat(j.css("borderLeftWidth"))||0)-(parseFloat(j.css("borderRightWidth"))||0)),a.each(h,function(c,d){g[d]="hide";var e=(""+a.css(b.toShow[0],d)).match(/^([\d+-.]+)(.*)$/);f[d]={value:e[1],unit:e[2]||"px"}}),b.toShow.css({height:0,overflow:"hidden"}).show(),b.toHide.filter(":hidden").each(b.complete).end().filter(":visible").animate(g,{step:function(a,c){c.prop=="height"&&(e=c.end-c.start===0?0:(c.now-c.start)/(c.end-c.start)),b.toShow[0].style[c.prop]=e*f[c.prop].value+f[c.prop].unit},duration:b.duration,easing:b.easing,complete:function(){b.autoHeight||b.toShow.css("height",""),b.toShow.css({width:i,overflow:d}),b.complete()}})}},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1e3:200})}}})})(jQuery);/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */(function(a,b){var c=0;a.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var b=this,c=this.element[0].ownerDocument,d;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!b.options.disabled&&!b.element.propAttr("readOnly")){d=!1;var e=a.ui.keyCode;switch(c.keyCode){case e.PAGE_UP:b._move("previousPage",c);break;case e.PAGE_DOWN:b._move("nextPage",c);break;case e.UP:b._move("previous",c),c.preventDefault();break;case e.DOWN:b._move("next",c),c.preventDefault();break;case e.ENTER:case e.NUMPAD_ENTER:b.menu.active&&(d=!0,c.preventDefault());case e.TAB:if(!b.menu.active)return;b.menu.select(c);break;case e.ESCAPE:b.element.val(b.term),b.close(c);break;default:clearTimeout(b.searching),b.searching=setTimeout(function(){b.term!=b.element.val()&&(b.selectedItem=null,b.search(null,c))},b.options.delay)}}}).bind("keypress.autocomplete",function(a){d&&(d=!1,a.preventDefault())}).bind("focus.autocomplete",function(){b.options.disabled||(b.selectedItem=null,b.previous=b.element.val())}).bind("blur.autocomplete",function(a){b.options.disabled||(clearTimeout(b.searching),b.closing=setTimeout(function(){b.close(a),b._change(a)},150))}),this._initSource(),this.response=function(){return b._response.apply(b,arguments)},this.menu=a("<ul></ul>").addClass("ui-autocomplete").appendTo(a(this.options.appendTo||"body",c)[0]).mousedown(function(c){var d=b.menu.element[0];a(c.target).closest(".ui-menu-item").length||setTimeout(function(){a(document).one("mousedown",function(c){c.target!==b.element[0]&&c.target!==d&&!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTimeout(b.closing)},13)}).menu({focus:function(a,c){var d=c.item.data("item.autocomplete");!1!==b._trigger("focus",a,{item:d})&&/^key/.test(a.originalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=d.item.data("item.autocomplete"),f=b.previous;b.element[0]!==c.activeElement&&(b.element.focus(),b.previous=f,setTimeout(function(){b.previous=f,b.selectedItem=e},1)),!1!==b._trigger("select",a,{item:e})&&b.element.val(e.value),b.term=b.element.val(),b.close(a),b.selectedItem=e},blur:function(a,c){b.menu.element.is(":visible")&&b.element.val()!==b.term&&b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.beforeunloadHandler=function(){b.element.removeAttr("autocomplete")},a(window).bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.menu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandler),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b==="source"&&this._initSource(),b==="appendTo"&&this.menu.element.appendTo(a(c||"body",this.element[0].ownerDocument)[0]),b==="disabled"&&c&&this.xhr&&this.xhr.abort()},_initSource:function(){var b=this,d,e;a.isArray(this.options.source)?(d=this.options.source,this.source=function(b,c){c(a.ui.autocomplete.filter(d,b.term))}):typeof this.options.source=="string"?(e=this.options.source,this.source=function(d,f){b.xhr&&b.xhr.abort(),b.xhr=a.ajax({url:e,data:d,dataType:"json",context:{autocompleteRequest:++c},success:function(a,b){this.autocompleteRequest===c&&f(a)},error:function(){this.autocompleteRequest===c&&f([])}})}):this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val(),this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==!1)return this._search(a)},_search:function(a){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.source({term:a},this.response)},_response:function(a){!this.options.disabled&&a&&a.length?(a=this._normalize(a),this._suggest(a),this._trigger("open")):this.close(),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.deactivate(),this._trigger("close",a))},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(b){if(b.length&&b[0].label&&b[0].value)return b;return a.map(b,function(b){if(typeof b=="string")return{label:b,value:b};return a.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(b){var c=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(c,b),this.menu.deactivate(),this.menu.refresh(),c.show(),this._resizeMenu(),c.position(a.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(new a.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(b,c){var d=this;a.each(c,function(a,c){d._renderItem(b,c)})},_renderItem:function(b,c){return a("<li></li>").data("item.autocomplete",c).append(a("<a></a>").text(c.label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visible"))this.search(null,b);else{if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.deactivate();return}this.menu[a](b)}},widget:function(){return this.menu.element}}),a.extend(a.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(b,c){var d=new RegExp(a.ui.autocomplete.escapeRegex(c),"i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(jQuery),function(a){a.widget("ui.menu",{_create:function(){var b=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){!a(c.target).closest(".ui-menu-item a").length||(c.preventDefault(),b.select(c))}),this.refresh()},refresh:function(){var b=this,c=this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouseleave(function(){b.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.scrollTop(),e=this.element.height();c<0?this.element.scrollTop(d+c):c>=e&&this.element.scrollTop(d+c-e+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end(),this._trigger("focus",a,{item:b})},deactivate:function(){!this.active||(this.active.children("a").removeClass("ui-state-hover").removeAttr("id"),this._trigger("blur"),this.active=null)},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.active)this.activate(c,this.element.children(b));else{var d=this.active[a+"All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,this.element.children(b))}},nextPage:function(b){if(this.hasScroll()){if(!this.active||this.last()){this.activate(b,this.element.children(".ui-menu-item:first"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!this.active||this.first()){this.activate(b,this.element.children(".ui-menu-item:last"));return}var c=this.active.offset().top,d=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c+d-a(this).height();return b<10&&b>-10}),result.length||(result=this.element.children(".ui-menu-item:first")),this.activate(b,result)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[a.fn.prop?"prop":"attr"]("scrollHeight")},select:function(a){this._trigger("selected",a,{item:this.active})}})}(jQuery);/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form}));return e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.propAttr("disabled"):this.element.propAttr("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){h.disabled||(a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active"))}).bind("mouseleave.button",function(){h.disabled||a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){f||b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){h.disabled||(f=!1,d=a.pageX,e=a.pageY)}).bind("mouseup.button",function(a){!h.disabled&&(d!==a.pageX||e!==a.pageY)&&(f=!0)})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);b==="disabled"?c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1):this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||"&#160;",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){b.close(a);return!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1!==c._trigger("beforeClose",b)){c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d);return c}},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;if(e.modal&&!b||!e.stack&&!e.modal)return d._trigger("focus",c);e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c);return d},open:function(){if(!this._isOpen){var b=this,c=b.options,d=b.uiDialog;b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode===a.ui.keyCode.TAB){var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey){d.focus(1);return!1}if(b.target===d[0]&&b.shiftKey){e.focus(1);return!1}}}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open");return b}},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){a!=="click"&&(a in f?e[a](b):e.attr(a,b))}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||"&#160;"))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.18",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");b||(this.uuid+=1,b=this.uuid);return"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});a.fn.bgiframe&&c.bgiframe(),this.instances.push(c);return c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;if(a.browser.msie&&a.browser.version<7){b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return b<c?a(window).height()+"px":b+"px"}return a(document).height()+"px"},width:function(){var b,c;if(a.browser.msie){b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return b<c?a(window).width()+"px":b+"px"}return a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i<g;i+=1)h.push(f);this.handles=e.add(a(h.join("")).appendTo(b.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(a){a.preventDefault()}).hover(function(){d.disabled||a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){d.disabled?a(this).blur():(a(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),a(this).addClass("ui-state-focus"))}).blur(function(){a(this).removeClass("ui-state-focus")}),this.handles.each(function(b){a(this).data("index.ui-slider-handle",b)}),this.handles.keydown(function(d){var e=a(this).data("index.ui-slider-handle"),f,g,h,i;if(!b.options.disabled){switch(d.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:d.preventDefault();if(!b._keySliding){b._keySliding=!0,a(this).addClass("ui-state-active"),f=b._start(d,e);if(f===!1)return}}i=b.options.step,b.options.values&&b.options.values.length?g=h=b.values(e):g=h=b.value();switch(d.keyCode){case a.ui.keyCode.HOME:h=b._valueMin();break;case a.ui.keyCode.END:h=b._valueMax();break;case a.ui.keyCode.PAGE_UP:h=b._trimAlignValue(g+(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(g-(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g===b._valueMax())return;h=b._trimAlignValue(g+i);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g===b._valueMin())return;h=b._trimAlignValue(g-i)}b._slide(d,e,h)}}).keyup(function(c){var d=a(this).data("index.ui-slider-handle");b._keySliding&&(b._keySliding=!1,b._stop(c,d),b._change(c,d),a(this).removeClass("ui-state-active"))}),this._refreshValue(),this._animateOff=!1},destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"),this._mouseDestroy();return this},_mouseCapture:function(b){var c=this.options,d,e,f,g,h,i,j,k,l;if(c.disabled)return!1;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),d={x:b.pageX,y:b.pageY},e=this._normValueFromMouse(d),f=this._valueMax()-this._valueMin()+1,h=this,this.handles.each(function(b){var c=Math.abs(e-h.values(b));f>c&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i);if(j===!1)return!1;this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0;return!0},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);this._slide(a,this._handleIndex,c);return!1},_mouseStop:function(a){this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1;return!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e;return this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values());return this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c<d)&&(c=d),c!==this.values(b)&&(e=this.values(),e[b]=c,f=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e}),d=this.values(b?0:1),f!==!1&&this.values(b,c,!0))):c!==this.value()&&(f=this._trigger("slide",a,{handle:this.handles[b],value:c}),f!==!1&&this.value(c))},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("change",a,c)}},value:function(a){if(arguments.length)this.options.value=this._trimAlignValue(a),this._refreshValue(),this._change(null,0);else return this._value()},values:function(b,c){var d,e,f;if(arguments.length>1)this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);else{if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()}},_setOption:function(b,c){var d,e=0;a.isArray(this.options.values)&&(e=this.options.values.length),a.Widget.prototype._setOption.apply(this,arguments);switch(b){case"disabled":c?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.propAttr("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.propAttr("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(d=0;d<e;d+=1)this._change(null,d);this._animateOff=!1}},_value:function(){var a=this.options.value;a=this._trimAlignValue(a);return a},_values:function(a){var b,c,d;if(arguments.length){b=this.options.values[a],b=this._trimAlignValue(b);return b}c=this.options.values.slice();for(d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;Math.abs(c)*2>=b&&(d+=c>0?b:-b);return parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){function f(){return++d}function e(){return++c}var c=0,d=0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie:null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(!0)},_setOption:function(a,b){if(a=="selected"){if(this.options.collapsible&&b==this.options.selected)return;this.select(b)}else this.options[a]=b,this._tabify()},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0].style.removeAttribute("filter")}var d=this,e=this.options,f=/^#.+/;this.list=this.element.find("ol,ul").eq(0),this.lis=a(" > li:has(a[href])",this.list),this.anchors=this.lis.map(function(){return a("a",this)[0]}),this.panels=a([]),this.anchors.each(function(b,c){var g=a(c).attr("href"),h=g.split("#")[0],i;h&&(h===location.toString().split("#")[0]||(i=a("base")[0])&&h===i.href)&&(g=c.hash,c.href=g);if(f.test(g))d.panels=d.panels.add(d.element.find(d._sanitizeSelector(g)));else if(g&&g!=="#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*$/,""));var j=d._tabId(c);c.href="#"+j;var k=d.element.find("#"+j);k.length||(k=a(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("destroy.tabs",!0)),d.panels=d.panels.add(k)}else e.disabled.push(b)}),c?(this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top"),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e.selected===b?(location.hash&&this.anchors.each(function(a,b){if(b.hash==location.hash){e.selected=a;return!1}}),typeof e.selected!="number"&&e.cookie&&(e.selected=parseInt(d._cookie(),10)),typeof e.selected!="number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=e.selected||(this.lis.length?0:-1)):e.selected===null&&(e.selected=-1),e.selected=e.selected>=0&&this.anchors[e.selected]||e.selected<0?e.selected:0,e.disabled=a.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selected,e.disabled)!=-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected ui-state-active"),e.selected>=0&&this.anchors.length&&(d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.element.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.selected],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.anchors).unbind(".tabs"),d.lis=d.anchors=d.panels=null})):e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e.selected,e.cookie);for(var g=0,h;h=this.lis[g];g++)a(h)[a.inArray(g,e.disabled)!=-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");e.cache===!1&&this.anchors.removeData("cache.tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=="mouseover"){var i=function(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui-state-"+a)},j=function(a,b){b.removeClass("ui-state-"+a)};this.lis.bind("mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.tabs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function(){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=e.fx[0],l=e.fx[1]):k=l=e.fx);var n=l?function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-tabs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show",null,d._ui(b,c[0]))},o=k?function(a,b){b.animate(k,k.duration||"normal",function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var b=this,c=a(b).closest("li"),f=d.panels.filter(":not(.ui-tabs-hide)"),g=d.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-selected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-state-processing")||d.panels.filter(":animated").length||d._trigger("select",null,d._ui(this,g[0]))===!1){this.blur();return!1}e.selected=d.anchors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected")){e.selected=-1,e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur();return!1}if(!f.length){e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur();return!1}}e.cookie&&d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs",function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifier.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(){return!1})},_getIndex:function(a){typeof a=="string"&&(a=this.anchors.index(this.anchors.filter("[href$="+a+"]")));return a},destroy:function(){var b=this.options;this.abort(),this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each(function(){var b=a.data(this,"href.tabs");b&&(this.href=b);var c=a(this).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeData(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie(null,b.cookie);return this},add:function(c,d,e){e===b&&(e=this.anchors.length);var f=this,g=this.options,h=a(g.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,d)),i=c.indexOf("#")?this._tabId(a("a",h)[0]):c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",!0);var j=f.element.find("#"+i);j.length||(j=a(g.panelTemplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide"),e>=this.lis.length?(h.appendTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.lis[e]),j.insertBefore(this.panels[e])),g.disabled=a.map(g.disabled,function(a,b){return a>=e?++a:a}),this._tabify(),this.anchors.length==1&&(g.selected=0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass("ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,this._ui(this.anchors[e],this.panels[e]));return this},remove:function(b){b=this._getIndex(b);var c=this.options,d=this.lis.eq(b).remove(),e=this.panels.eq(b).remove();d.hasClass("ui-tabs-selected")&&this.anchors.length>1&&this.select(b+(b+1<this.anchors.length?1:-1)),c.disabled=a.map(a.grep(c.disabled,function(a,c){return a!=b}),function(a,c){return a>=b?--a:a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0]));return this},enable:function(b){b=this._getIndex(b);var c=this.options;if(a.inArray(b,c.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled"),c.disabled=a.grep(c.disabled,function(a,c){return a!=b}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(a){a=this._getIndex(a);var b=this,c=this.options;a!=c.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a])));return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this},load:function(b){b=this._getIndex(b);var c=this,d=this.options,e=this.anchors.eq(b)[0],f=a.data(e,"load.tabs");this.abort();if(!f||this.element.queue("tabs").length!==0&&a.data(e,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(d.spinner){var g=a("span",e);g.data("label.tabs",g.html()).html(d.spinner)}this.xhr=a.ajax(a.extend({},d.ajaxOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}})),c.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cleanup();return this},url:function(a,b){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",b);return this},length:function(){return this.anchors.length}}),a.extend(a.ui.tabs,{version:"1.8.18"}),a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(a,b){var c=this,d=this.options,e=c._rotate||(c._rotate=function(b){clearTimeout(c.rotation),c.rotation=setTimeout(function(){var a=d.selected;c.select(++a<c.anchors.length?a:0)},a),b&&b.stopPropagation()}),f=c._unrotate||(c._unrotate=b?function(a){t=d.selected,e()}:function(a){a.clientX&&c.rotate(null)});a?(this.element.bind("tabsshow",e),this.anchors.bind(d.event+".tabs",f),e()):(clearTimeout(c.rotation),this.element.unbind("tabsshow",e),this.anchors.unbind(d.event+".tabs",f),delete this._rotate,delete this._unrotate);return this}})})(jQuery);/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */(function($,undefined){function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);!c.length||c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);!$.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])&&!!d.length&&(d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover"))})}function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}$.extend($.ui,{datepicker:{version:"1.8.18"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){extendRemove(this._defaults,a||{});return this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);c.hasClass(this.markerClassName)||(this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a))},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$('<span class="'+this._appendClass+'">'+c+"</span>"),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('<button type="button"></button>').addClass(this._triggerClass).html(g==""?f:$("<img/>").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){$.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]);return!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;d<a.length;d++)a[d].length>b&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);c.hasClass(this.markerClassName)||(c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block"))},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+g+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f);return this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})}},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return!0;return!1},_getInst:function(a){try{return $.data(a,PROP_NAME)}catch(b){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(a,b,c){var d=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?$.extend({},$.datepicker._defaults):d?b=="all"?$.extend({},d.settings):this._get(d,b):null;var e=b||{};typeof b=="string"&&(e={},e[b]=c);if(d){this._curInst==d&&this._hideDatepicker();var f=this._getDateDatepicker(a,!0),g=this._getMinMaxDate(d,"min"),h=this._getMinMaxDate(d,"max");extendRemove(d.settings,e),g!==null&&e.dateFormat!==undefined&&e.minDate===undefined&&(d.settings.minDate=this._formatDate(d,g)),h!==null&&e.dateFormat!==undefined&&e.maxDate===undefined&&(d.settings.maxDate=this._formatDate(d,h)),this._attachments($(a),d),this._autoSize(d),this._setDate(d,f),this._updateAlternate(d),this._updateDatepicker(d)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);c&&!c.inline&&this._setDateFromField(c,b);return c?this._getDate(c):null},_doKeyDown:function(a){var b=$.datepicker._getInst(a.target),c=!0,d=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=!0;if($.datepicker._datepickerShowing)switch(a.keyCode){case 9:$.datepicker._hideDatepicker(),c=!1;break;case 13:var e=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",b.dpDiv);e[0]&&$.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,e[0]);var f=$.datepicker._get(b,"onSelect");if(f){var g=$.datepicker._formatDate(b);f.apply(b.input?b.input[0]:null,[g,b])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 35:(a.ctrlKey||a.metaKey)&&$.datepicker._clearDate(a.target),c=a.ctrlKey||a.metaKey;break;case 36:(a.ctrlKey||a.metaKey)&&$.datepicker._gotoToday(a.target),c=a.ctrlKey||a.metaKey;break;case 37:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?1:-1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 38:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,-7,"D"),c=a.ctrlKey||a.metaKey;break;case 39:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?-1:1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 40:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,7,"D"),c=a.ctrlKey||a.metaKey;break;default:c=!1}else a.keyCode==36&&a.ctrlKey?$.datepicker._showDatepicker(this):c=!1;c&&(a.preventDefault(),a.stopPropagation())},_doKeyPress:function(a){var b=$.datepicker._getInst(a.target);if($.datepicker._get(b,"constrainInput")){var c=$.datepicker._possibleChars($.datepicker._get(b,"dateFormat")),d=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||d<" "||!c||c.indexOf(d)>-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(a){$.datepicker.log(a)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if(!$.datepicker._isDisabledDatepicker(a)&&$.datepicker._lastInput!=a){var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){e|=$(this).css("position")=="fixed";return!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0);return b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=$.data(a,PROP_NAME))&&this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=this,f=function(){$.datepicker._tidyDialog(b),e._curInst=null};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,f):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,f),c||f(),this._datepickerShowing=!1;var g=this._get(b,"onClose");g&&g.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!!$.datepicker._curInst){var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);this._isDisabledDatepicker(d[0])||(this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e))},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if(!$(d).hasClass(this._unselectableClass)&&!this._isDisabledDatepicker(e[0])){var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();b.setMonth(0),b.setDate(1);return Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1<a.length&&a.charAt(s+1)==b;c&&s++;return c},o=function(a){var c=n(a),d=a=="@"?14:a=="!"?20:a=="y"&&c?4:a=="o"?3:2,e=new RegExp("^\\d{1,"+d+"}"),f=b.substring(r).match(e);if(!f)throw"Missing number at position "+r;r+=f[0].length;return parseInt(f[0],10)},p=function(a,c,d){var e=$.map(n(a)?d:c,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)}),f=-1;$.each(e,function(a,c){var d=c[1];if(b.substr(r,d.length).toLowerCase()==d.toLowerCase()){f=c[0],r+=d.length;return!1}});if(f!=-1)return f+1;throw"Unknown name at position "+r},q=function(){if(b.charAt(r)!=a.charAt(s))throw"Unexpected literal at position "+r;r++},r=0;for(var s=0;s<a.length;s++)if(m)a.charAt(s)=="'"&&!n("'")?m=!1:q();else switch(a.charAt(s)){case"d":k=o("d");break;case"D":p("D",e,f);break;case"o":l=o("o");break;case"m":j=o("m");break;case"M":j=p("M",g,h);break;case"y":i=o("y");break;case"@":var t=new Date(o("@"));i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"!":var t=new Date((o("!")-this._ticksTo1970)/1e4);i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"'":n("'")?q():m=!0;break;default:q()}if(r<b.length)throw"Extra/unparsed characters found in date: "+b.substring(r);i==-1?i=(new Date).getFullYear():i<100&&(i+=(new Date).getFullYear()-(new Date).getFullYear()%100+(i<=d?0:-100));if(l>-1){j=1,k=l;for(;;){var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+1<a.length&&a.charAt(m+1)==b;c&&m++;return c},i=function(a,b,c){var d=""+b;if(h(a))while(d.length<c)d="0"+d;return d},j=function(a,b,c,d){return h(a)?d[b]:c[b]},k="",l=!1;if(b)for(var m=0;m<a.length;m++)if(l)a.charAt(m)=="'"&&!h("'")?l=!1:k+=a.charAt(m);else switch(a.charAt(m)){case"d":k+=i("d",b.getDate(),2);break;case"D":k+=j("D",b.getDay(),d,e);break;case"o":k+=i("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":k+=i("m",b.getMonth()+1,2);break;case"M":k+=j("M",b.getMonth(),f,g);break;case"y":k+=h("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case"@":k+=b.getTime();break;case"!":k+=b.getTime()*1e4+this._ticksTo1970;break;case"'":h("'")?k+="'":l=!0;break;default:k+=a.charAt(m)}return k},_possibleChars:function(a){var b="",c=!1,d=function(b){var c=e+1<a.length&&a.charAt(e+1)==b;c&&e++;return c};for(var e=0;e<a.length;e++)if(c)a.charAt(e)=="'"&&!d("'")?c=!1:b+=a.charAt(e);else switch(a.charAt(e)){case"d":case"m":case"y":case"@":b+="0123456789";break;case"D":case"M":return null;case"'":d("'")?b+="'":c=!0;break;default:b+=a.charAt(e)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e,f;e=f=this._getDefaultDate(a);var g=this._getFormatConfig(a);try{e=this.parseDate(c,d,g)||f}catch(h){this.log(h),d=b?"":d}a.selectedDay=e.getDate(),a.drawMonth=a.selectedMonth=e.getMonth(),a.drawYear=a.selectedYear=e.getFullYear(),a.currentDay=d?e.getDate():0,a.currentMonth=d?e.getMonth():0,a.currentYear=d?e.getFullYear():0,this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var d=function(a){var b=new Date;b.setDate(b.getDate()+a);return b},e=function(b){try{return $.datepicker.parseDate($.datepicker._get(a,"dateFormat"),b,$.datepicker._getFormatConfig(a))}catch(c){}var d=(b.toLowerCase().match(/^c/)?$.datepicker._getDate(a):null)||new Date,e=d.getFullYear(),f=d.getMonth(),g=d.getDate(),h=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,i=h.exec(b);while(i){switch(i[2]||"d"){case"d":case"D":g+=parseInt(i[1],10);break;case"w":case"W":g+=parseInt(i[1],10)*7;break;case"m":case"M":f+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f));break;case"y":case"Y":e+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f))}i=h.exec(b)}return new Date(e,f,g)},f=b==null||b===""?c:typeof b=="string"?e(b):typeof b=="number"?isNaN(b)?c:d(b):new Date(b.getTime());f=f&&f.toString()=="Invalid Date"?c:f,f&&(f.setHours(0),f.setMinutes(0),f.setSeconds(0),f.setMilliseconds(0));return this._daylightSavingAdjust(f)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&p<l?l:p;while(this._daylightSavingAdjust(new Date(o,n,1))>p)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', -"+i+", 'M');\""+' title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>":e?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', +"+i+", 'M');\""+' title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":e?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+dpuuid+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>",x=d?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?w:"")+(this._isInRange(a,v)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._gotoToday('#"+a.id+"');\""+">"+u+"</button>":"")+(c?"":w)+"</div>":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L<g[0];L++){var M="";this.maxRows=4;for(var N=0;N<g[1];N++){var O=this._daylightSavingAdjust(new Date(o,n,a.selectedDay)),P=" ui-corner-all",Q="";if(j){Q+='<div class="ui-datepicker-group';if(g[1]>1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+P+'">'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var R=z?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="<th"+((S+y+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+A[T]+'">'+C[T]+"</span></th>"}Q+=R+"</tr></thead><tbody>";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z<X;Z++){Q+="<tr>";var _=z?'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(Y)+"</td>":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Y<l||m&&Y>m;_+='<td class="'+((S+y+6)%7>=5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.getTime()==O.getTime()&&n==a.selectedMonth&&a._keyEvent||J.getTime()==Y.getTime()&&J.getTime()==O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()==k.getTime()?" "+this._currentClass:"")+(Y.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title="'+ba[2]+'"':"")+(bc?"":' onclick="DP_jQuery_'+dpuuid+".datepicker._selectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return false;"')+">"+(bb&&!G?"&#xa0;":bc?'<span class="ui-state-default">'+Y.getDate()+"</span>":'<a class="ui-state-default'+(Y.getTime()==b.getTime()?" ui-state-highlight":"")+(Y.getTime()==k.getTime()?" ui-state-active":"")+(bb?" ui-priority-secondary":"")+'" href="#">'+Y.getDate()+"</a>")+"</td>",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+"</tr>"}n++,n>11&&(n=0,o++),Q+="</tbody></table>"+(j?"</div>"+(g[0]>0&&N==g[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),M+=Q}K+=M}K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),
+a._keyEvent=!1;return K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='<div class="ui-datepicker-title">',m="";if(f||!i)m+='<span class="ui-datepicker-month">'+g[b]+"</span>";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" "+">";for(var p=0;p<12;p++)(!n||p>=d.getMonth())&&(!o||p<=e.getMonth())&&(m+='<option value="'+p+'"'+(p==b?' selected="selected"':"")+">"+h[p]+"</option>");m+="</select>"}k||(l+=m+(f||!i||!j?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+='<span class="ui-datepicker-year">'+c+"</span>";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" "+">";for(;t<=u;t++)a.yearshtml+='<option value="'+t+'"'+(t==c?' selected="selected"':"")+">"+t+"</option>";a.yearshtml+="</select>",l+=a.yearshtml,a.yearshtml=null}}l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?"&#xa0;":"")+m),l+="</div>";return l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;e=d&&e>d?d:e;return e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth()));return this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));return this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)})},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.18",window["DP_jQuery_"+dpuuid]=$})(jQuery);/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===b)return this._value();this._setOption("value",a);return this},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;typeof a!="number"&&(a=0);return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Effects 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.18",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){b=="toggle"&&(b=a.is(":hidden")?"show":"hide");return b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);if(b<1)return-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c;return h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);if((c/=f/2)<1)return e/2*c*c*(((g*=1.525)+1)*c-g)+d;return e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){if(c<f/2)return a.easing.easeInBounce(b,c*2,0,e,f)*.5+d;return a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);/*
+ * jQuery UI Effects Blind 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.blind=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=f=="vertical"?"height":"width",i=f=="vertical"?g.height():g.width();e=="show"&&g.css(h,0);var j={};j[h]=e=="show"?i:0,g.animate(j,b.duration,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Bounce 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.bounce=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"up",g=b.options.distance||20,h=b.options.times||5,i=b.duration||250;/show|hide/.test(e)&&d.push("opacity"),a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",g=b.options.distance||(j=="top"?c.outerHeight({margin:!0})/3:c.outerWidth({margin:!0})/3);e=="show"&&c.css("opacity",0).css(j,k=="pos"?-g:g),e=="hide"&&(g=g/(h*2)),e!="hide"&&h--;if(e=="show"){var l={opacity:1};l[j]=(k=="pos"?"+=":"-=")+g,c.animate(l,i/2,b.options.easing),g=g/2,h--}for(var m=0;m<h;m++){var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing),g=e=="hide"?g*2:g/2}if(e=="hide"){var l={opacity:0};l[j]=(k=="pos"?"-=":"+=")+g,c.animate(l,i/2,b.options.easing,function(){c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}else{var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Clip 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.clip=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","height","width"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=c[0].tagName=="IMG"?g:c,i={size:f=="vertical"?"height":"width",position:f=="vertical"?"top":"left"},j=f=="vertical"?h.height():h.width();e=="show"&&(h.css(i.size,0),h.css(i.position,j/2));var k={};k[i.size]=e=="show"?j:0,k[i.position]=e=="show"?0:j/2,h.animate(k,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Drop 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.drop=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","opacity"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0})/2:c.outerWidth({margin:!0})/2);e=="show"&&c.css("opacity",0).css(g,h=="pos"?-i:i);var j={opacity:e=="show"?1:0};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Explode 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.explode=function(b){return this.queue(function(){var c=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3,d=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;b.options.mode=b.options.mode=="toggle"?a(this).is(":visible")?"hide":"show":b.options.mode;var e=a(this).show().css("visibility","hidden"),f=e.offset();f.top-=parseInt(e.css("marginTop"),10)||0,f.left-=parseInt(e.css("marginLeft"),10)||0;var g=e.outerWidth(!0),h=e.outerHeight(!0);for(var i=0;i<c;i++)for(var j=0;j<d;j++)e.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-j*(g/d),top:-i*(h/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/d,height:h/c,left:f.left+j*(g/d)+(b.options.mode=="show"?(j-Math.floor(d/2))*(g/d):0),top:f.top+i*(h/c)+(b.options.mode=="show"?(i-Math.floor(c/2))*(h/c):0),opacity:b.options.mode=="show"?0:1}).animate({left:f.left+j*(g/d)+(b.options.mode=="show"?0:(j-Math.floor(d/2))*(g/d)),top:f.top+i*(h/c)+(b.options.mode=="show"?0:(i-Math.floor(c/2))*(h/c)),opacity:b.options.mode=="show"?1:0},b.duration||500);setTimeout(function(){b.options.mode=="show"?e.css({visibility:"visible"}):e.css({visibility:"visible"}).hide(),b.callback&&b.callback.apply(e[0]),e.dequeue(),a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);/*
+ * jQuery UI Effects Fade 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fade=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide");c.animate({opacity:d},{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Fold 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fold=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.size||15,g=!!b.options.horizFirst,h=b.duration?b.duration/2:a.fx.speeds._default/2;a.effects.save(c,d),c.show();var i=a.effects.createWrapper(c).css({overflow:"hidden"}),j=e=="show"!=g,k=j?["width","height"]:["height","width"],l=j?[i.width(),i.height()]:[i.height(),i.width()],m=/([0-9]+)%/.exec(f);m&&(f=parseInt(m[1],10)/100*l[e=="hide"?0:1]),e=="show"&&i.css(g?{height:0,width:f}:{height:f,width:0});var n={},p={};n[k[0]]=e=="show"?l[0]:f,p[k[1]]=e=="show"?l[1]:0,i.animate(n,h,b.options.easing).animate(p,h,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Highlight 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Pulsate 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.pulsate=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"show");times=(b.options.times||5)*2-1,duration=b.duration?b.duration/2:a.fx.speeds._default/2,isVisible=c.is(":visible"),animateTo=0,isVisible||(c.css("opacity",0).show(),animateTo=1),(d=="hide"&&isVisible||d=="show"&&!isVisible)&&times--;for(var e=0;e<times;e++)c.animate({opacity:animateTo},duration,b.options.easing),animateTo=(animateTo+1)%2;c.animate({opacity:animateTo},duration,b.options.easing,function(){animateTo==0&&c.hide(),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}).dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Scale 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.puff=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide"),e=parseInt(b.options.percent,10)||150,f=e/100,g={height:c.height(),width:c.width()};a.extend(b.options,{fade:!0,mode:d,percent:d=="hide"?e:100,from:d=="hide"?g:{height:g.height*f,width:g.width*f}}),c.effect("scale",b.options,b.duration,b.callback),c.dequeue()})},a.effects.scale=function(b){return this.queue(function(){var c=a(this),d=a.extend(!0,{},b.options),e=a.effects.setMode(c,b.options.mode||"effect"),f=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:e=="hide"?0:100),g=b.options.direction||"both",h=b.options.origin;e!="effect"&&(d.origin=h||["middle","center"],d.restore=!0);var i={height:c.height(),width:c.width()};c.from=b.options.from||(e=="show"?{height:0,width:0}:i);var j={y:g!="horizontal"?f/100:1,x:g!="vertical"?f/100:1};c.to={height:i.height*j.y,width:i.width*j.x},b.options.fade&&(e=="show"&&(c.from.opacity=0,c.to.opacity=1),e=="hide"&&(c.from.opacity=1,c.to.opacity=0)),d.from=c.from,d.to=c.to,d.mode=e,c.effect("size",d,b.duration,b.callback),c.dequeue()})},a.effects.size=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","width","height","overflow","opacity"],e=["position","top","bottom","left","right","overflow","opacity"],f=["width","height","overflow"],g=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],i=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],j=a.effects.setMode(c,b.options.mode||"effect"),k=b.options.restore||!1,l=b.options.scale||"both",m=b.options.origin,n={height:c.height(),width:c.width()};c.from=b.options.from||n,c.to=b.options.to||n;if(m){var p=a.effects.getBaseline(m,n);c.from.top=(n.height-c.from.height)*p.y,c.from.left=(n.width-c.from.width)*p.x,c.to.top=(n.height-c.to.height)*p.y,c.to.left=(n.width-c.to.width)*p.x}var q={from:{y:c.from.height/n.height,x:c.from.width/n.width},to:{y:c.to.height/n.height,x:c.to.width/n.width}};if(l=="box"||l=="both")q.from.y!=q.to.y&&(d=d.concat(h),c.from=a.effects.setTransition(c,h,q.from.y,c.from),c.to=a.effects.setTransition(c,h,q.to.y,c.to)),q.from.x!=q.to.x&&(d=d.concat(i),c.from=a.effects.setTransition(c,i,q.from.x,c.from),c.to=a.effects.setTransition(c,i,q.to.x,c.to));(l=="content"||l=="both")&&q.from.y!=q.to.y&&(d=d.concat(g),c.from=a.effects.setTransition(c,g,q.from.y,c.from),c.to=a.effects.setTransition(c,g,q.to.y,c.to)),a.effects.save(c,k?d:e),c.show(),a.effects.createWrapper(c),c.css("overflow","hidden").css(c.from);if(l=="content"||l=="both")h=h.concat(["marginTop","marginBottom"]).concat(g),i=i.concat(["marginLeft","marginRight"]),f=d.concat(h).concat(i),c.find("*[width]").each(function(){child=a(this),k&&a.effects.save(child,f);var c={height:child.height(),width:child.width()};child.from={height:c.height*q.from.y,width:c.width*q.from.x},child.to={height:c.height*q.to.y,width:c.width*q.to.x},q.from.y!=q.to.y&&(child.from=a.effects.setTransition(child,h,q.from.y,child.from),child.to=a.effects.setTransition(child,h,q.to.y,child.to)),q.from.x!=q.to.x&&(child.from=a.effects.setTransition(child,i,q.from.x,child.from),child.to=a.effects.setTransition(child,i,q.to.x,child.to)),child.css(child.from),child.animate(child.to,b.duration,b.options.easing,function(){k&&a.effects.restore(child,f)})});c.animate(c.to,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){c.to.opacity===0&&c.css("opacity",c.from.opacity),j=="hide"&&c.hide(),a.effects.restore(c,k?d:e),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Shake 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.shake=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"left",g=b.options.distance||20,h=b.options.times||3,i=b.duration||b.options.duration||140;a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",l={},m={},n={};l[j]=(k=="pos"?"-=":"+=")+g,m[j]=(k=="pos"?"+=":"-=")+g*2,n[j]=(k=="pos"?"-=":"+=")+g*2,c.animate(l,i,b.options.easing);for(var p=1;p<h;p++)c.animate(m,i,b.options.easing).animate(n,i,b.options.easing);c.animate(m,i,b.options.easing).animate(l,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Slide 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.slide=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"show"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c).css({overflow:"hidden"});var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0}):c.outerWidth({margin:!0}));e=="show"&&c.css(g,h=="pos"?isNaN(i)?"-"+i:-i:i);var j={};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Transfer 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/pngfix.js b/subsonic-main/src/main/webapp/script/pngfix.js
new file mode 100644
index 00000000..87d4b1cd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/pngfix.js
@@ -0,0 +1,39 @@
+/*
+
+Correctly handle PNG transparency in Win IE 5.5 & 6.
+http://homepage.ntlworld.com/bobosola. Updated 18-Jan-2006.
+
+Use in <HEAD> with DEFER keyword wrapped in conditional comments:
+<!--[if lt IE 7]>
+<script defer type="text/javascript" src="pngfix.js"></script>
+<![endif]-->
+
+*/
+
+var arVersion = navigator.appVersion.split("MSIE")
+var version = parseFloat(arVersion[1])
+
+if ((version >= 5.5) && (document.body.filters))
+{
+ for(var i=0; i<document.images.length; i++)
+ {
+ var img = document.images[i]
+ var imgName = img.src.toUpperCase()
+ if (imgName.substring(imgName.length-3, imgName.length) == "PNG")
+ {
+ var imgID = (img.id) ? "id='" + img.id + "' " : ""
+ var imgClass = (img.className) ? "class='" + img.className + "' " : ""
+ var imgTitle = (img.title) ? "title='" + img.title + "' " : "title='" + img.alt + "' "
+ var imgStyle = "display:inline-block;" + img.style.cssText
+ if (img.align == "left") imgStyle = "float:left;" + imgStyle
+ if (img.align == "right") imgStyle = "float:right;" + imgStyle
+ if (img.parentElement.href) imgStyle = "cursor:hand;" + imgStyle
+ var strNewHTML = "<span " + imgID + imgClass + imgTitle
+ + " style=\"" + "width:" + img.width + "px; height:" + img.height + "px;" + imgStyle + ";"
+ + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
+ + "(src=\'" + img.src + "\', sizingMethod='image');\"></span>"
+ img.outerHTML = strNewHTML
+ i = i-1
+ }
+ }
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/prototype.js b/subsonic-main/src/main/webapp/script/prototype.js
new file mode 100644
index 00000000..dfe8ab4e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/prototype.js
@@ -0,0 +1,4320 @@
+/* Prototype JavaScript framework, version 1.6.0.3
+ * (c) 2005-2008 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.3',
+
+ Browser: {
+ IE: !!(window.attachEvent &&
+ navigator.userAgent.indexOf('Opera') === -1),
+ Opera: navigator.userAgent.indexOf('Opera') > -1,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&
+ navigator.userAgent.indexOf('KHTML') === -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ SelectorsAPI: !!document.querySelector,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div')['__proto__'] &&
+ document.createElement('div')['__proto__'] !==
+ document.createElement('form')['__proto__']
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value;
+ value = (function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method);
+
+ value.valueOf = method.valueOf.bind(method);
+ value.toString = method.toString.bind(method);
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return !!(object && object.nodeType == 1);
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
+ .replace(/\s+/g, '').split(',');
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ defer: function() {
+ var args = [0.01].concat($A(arguments));
+ return this.delay.apply(this, args);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ },
+ unescapeHTML: function() {
+ return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ iterator.call(context, value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ var index = -number, slices = [], array = this.toArray();
+ if (number < 1) return array;
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator.call(context, value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator.call(context, value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator.call(context, value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator.call(context, value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator.call(context, value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator || Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator.call(context, value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ this.each(function(value, index) {
+ memo = iterator.call(context, memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator.call(context, value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator.call(context, value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator || Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator.call(context, value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator.call(context, value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ return this.map(function(value, index) {
+ return {
+ value: value,
+ criteria: iterator.call(context, value, index)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ // In Safari, only use the `toArray` method if it's not a NodeList.
+ // A NodeList is a function, has an function `item` property, and a numeric
+ // `length` property. Adapted from Google Doctype.
+ if (!(typeof iterable === 'function' && typeof iterable.length ===
+ 'number' && typeof iterable.item === 'function') && iterable.toArray)
+ return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator, context) {
+ $R(0, this, true).each(iterator, context);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ // simulating poorly supported hasOwnProperty
+ if (this._object[key] !== Object.prototype[key])
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.inject([], function(results, pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return results.concat(values.map(toQueryPair.curry(key)));
+ } else results.push(toQueryPair(key, values));
+ return results;
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+ if (element) this.Element.prototype = element.prototype;
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ element = $(element);
+ element.style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ element = $(element);
+ element.style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ Element.select(element, expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (ancestor.contains)
+ return ancestor.contains(element) && ancestor !== element;
+
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value || value == 'auto') {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = element.getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (Prototype.Browser.Opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName.toUpperCase() == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return element;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return element;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ if (element.offsetParent) return $(element.offsetParent);
+ if (element == document.body) return $(element);
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(document.body);
+ },
+
+ viewportOffset: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return element.title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ // IE throws an error if element is not in document
+ try { element.offsetParent }
+ catch(e) { return $(document.body) }
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ try { element.offsetParent }
+ catch(e) { return Element._returnOffset(0,0) }
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
+ function(proceed, element) {
+ try { element.offsetParent }
+ catch(e) { return Element._returnOffset(0,0) }
+ return proceed(element);
+ }
+ );
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName.toUpperCase() == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return !!(node && node.specified);
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div')['__proto__']) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div')['__proto__'];
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName.toUpperCase(), property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName)['__proto__'];
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { }, B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ if (B.WebKit && !document.evaluate) {
+ // Safari <3.0 needs self.innerWidth/Height
+ dimensions[d] = self['inner' + D];
+ } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
+ // Opera <9.5 needs document.body.clientWidth/Height
+ dimensions[d] = document.body['client' + D]
+ } else {
+ dimensions[d] = document.documentElement['client' + D];
+ }
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+
+ if (this.shouldUseSelectorsAPI()) {
+ this.mode = 'selectorsAPI';
+ } else if (this.shouldUseXPath()) {
+ this.mode = 'xpath';
+ this.compileXPathMatcher();
+ } else {
+ this.mode = "normal";
+ this.compileMatcher();
+ }
+
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(e))
+ return false;
+
+ return true;
+ },
+
+ shouldUseSelectorsAPI: function() {
+ if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
+
+ if (!Selector._div) Selector._div = new Element('div');
+
+ // Make sure the browser treats the selector as valid. Test on an
+ // isolated element to minimize cost of this check.
+ try {
+ Selector._div.querySelector(this.expression);
+ } catch(e) {
+ return false;
+ }
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ var e = this.expression, results;
+
+ switch (this.mode) {
+ case 'selectorsAPI':
+ // querySelectorAll queries document-wide, then filters to descendants
+ // of the context element. That's not what we want.
+ // Add an explicit context to the selector if necessary.
+ if (root !== document) {
+ var oldId = root.id, id = $(root).identify();
+ e = "#" + id + " " + e;
+ }
+
+ results = $A(root.querySelectorAll(e)).map(Element.extend);
+ root.id = oldId;
+
+ return results;
+ case 'xpath':
+ return document._getElementsByXPath(this.xpath, root);
+ default:
+ return this.matcher(root);
+ }
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0)]",
+ 'checked': "[@checked]",
+ 'disabled': "[(@disabled) and (@type!='hidden')]",
+ 'enabled': "[not(@disabled) and (@type!='hidden')]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || node.firstChild) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled && (!node.type || node.type !== 'hidden'))
+ results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
+ '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
+ '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
+ '-').include('-' + (v || "").toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, value) {
+ if (Object.isUndefined(value))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, currentValue, single = !Object.isArray(value);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ currentValue = this.optionValue(opt);
+ if (single) {
+ if (currentValue == value) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = value.include(currentValue);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ event = Event.extend(event);
+
+ var node = event.target,
+ type = event.type,
+ currentTarget = event.currentTarget;
+
+ if (currentTarget && currentTarget.tagName) {
+ // Firefox screws up the "click" event when moving between radio buttons
+ // via arrow keys. It also screws up the "load" and "error" events on images,
+ // reporting the document as the target instead of the original image.
+ if (type === 'load' || type === 'error' ||
+ (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+ && currentTarget.type === 'radio'))
+ node = currentTarget;
+ }
+ if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
+ return Element.extend(node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ var docElement = document.documentElement,
+ body = document.body || { scrollLeft: 0, scrollTop: 0 };
+ return {
+ x: event.pageX || (event.clientX +
+ (docElement.scrollLeft || body.scrollLeft) -
+ (docElement.clientLeft || 0)),
+ y: event.pageY || (event.clientY +
+ (docElement.scrollTop || body.scrollTop) -
+ (docElement.clientTop || 0))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._prototypeEventID = [++arguments.callee.id];
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event);
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+
+ // Internet Explorer needs to remove event handlers on page unload
+ // in order to avoid memory leaks.
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ // Safari has a dummy event handler on page unload so that it won't
+ // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
+ // object when page is returned to via the back button using its bfcache.
+ if (Prototype.Browser.WebKit) {
+ window.addEventListener('unload', Prototype.emptyFunction, false);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ document.loaded = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods(); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/scripts.js b/subsonic-main/src/main/webapp/script/scripts.js
new file mode 100644
index 00000000..959ed7a6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/scripts.js
@@ -0,0 +1,21 @@
+function noop() {
+}
+
+function popup(mylink, windowname) {
+ return popupSize(mylink, windowname, 400, 200);
+}
+
+function popupSize(mylink, windowname, width, height) {
+ var href;
+ if (typeof(mylink) == "string") {
+ href = mylink;
+ } else {
+ href = mylink.href;
+ }
+
+ var w = window.open(href, windowname, "width=" + width + ",height=" + height + ",scrollbars=yes,resizable=yes");
+ w.focus();
+ w.moveTo(300, 200);
+ return false;
+}
+
diff --git a/subsonic-main/src/main/webapp/script/smooth-scroll.js b/subsonic-main/src/main/webapp/script/smooth-scroll.js
new file mode 100644
index 00000000..9199732b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/smooth-scroll.js
@@ -0,0 +1,102 @@
+/*--------------------------------------------------------------------------
+ * Smooth Scroller Script, version 1.0.1
+ * (c) 2007 Dezinerfolio Inc. <midart@gmail.com>
+ *
+ * For details, please check the website : http://dezinerfolio.com/
+ *
+/*--------------------------------------------------------------------------*/
+
+Scroller = {
+ // control the speed of the scroller.
+ // dont change it here directly, please use Scroller.speed=50;
+ speed:10,
+
+ // returns the Y position of the div
+ gy: function (d) {
+ gy = d.offsetTop
+ if (d.offsetParent) while (d = d.offsetParent) gy += d.offsetTop
+ return gy
+ },
+
+ // returns the current scroll position
+ scrollTop: function (){
+ body=document.body
+ d=document.documentElement
+ if (body && body.scrollTop) return body.scrollTop
+ if (d && d.scrollTop) return d.scrollTop
+ if (window.pageYOffset) return window.pageYOffset
+ return 0
+ },
+
+ // attach an event for an element
+ // (element, type, function)
+ add: function(event, body, d) {
+ if (event.addEventListener) return event.addEventListener(body, d,false)
+ if (event.attachEvent) return event.attachEvent('on'+body, d)
+ },
+
+ // kill an event of an element
+ end: function(e){
+ if (window.event) {
+ window.event.cancelBubble = true
+ window.event.returnValue = false
+ return;
+ }
+ if (e.preventDefault && e.stopPropagation) {
+ e.preventDefault()
+ e.stopPropagation()
+ }
+ },
+
+ // move the scroll bar to the particular div.
+ scroll: function(d){
+ i = window.innerHeight || document.documentElement.clientHeight;
+ h=document.body.scrollHeight;
+ a = Scroller.scrollTop()
+ if(d>a)
+ if(h-d>i)
+ a+=Math.ceil((d-a)/Scroller.speed)
+ else
+ a+=Math.ceil((d-a-(h-d))/Scroller.speed)
+ else
+ a = a+(d-a)/Scroller.speed;
+ window.scrollTo(0,a)
+ if(a==d || Scroller.offsetTop==a)clearInterval(Scroller.interval)
+ Scroller.offsetTop=a
+ },
+ // initializer that adds the renderer to the onload function of the window
+ init: function(){
+ Scroller.add(window,'load', Scroller.render)
+ },
+
+ // this method extracts all the anchors and validates then as # and attaches the events.
+ render: function(){
+ a = document.getElementsByTagName('a');
+ Scroller.end(this);
+ window.onscroll
+ for (i=0;i<a.length;i++) {
+ l = a[i];
+ if(l.href && l.href.indexOf('#') != -1 && ((l.pathname==location.pathname) || ('/'+l.pathname==location.pathname)) ){
+ Scroller.add(l,'click',Scroller.end)
+ l.onclick = function(){
+ Scroller.end(this);
+ l=this.hash.substr(1);
+ a = document.getElementsByTagName('a');
+ for (i=0;i<a.length;i++) {
+ if(a[i].name == l){
+ clearInterval(Scroller.interval);
+ Scroller.interval=setInterval('Scroller.scroll('+Scroller.gy(a[i])+')',10);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+// invoke the initializer of the scroller
+Scroller.init();
+
+
+/*------------------------------------------------------------
+ * END OF CODE
+/*-----------------------------------------------------------*/ \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/swfobject.js b/subsonic-main/src/main/webapp/script/swfobject.js
new file mode 100644
index 00000000..8eafe9dd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/swfobject.js
@@ -0,0 +1,4 @@
+/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+*/
+var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}(); \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon.js b/subsonic-main/src/main/webapp/script/tip_balloon.js
new file mode 100644
index 00000000..dc9d2e44
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon.js
@@ -0,0 +1,221 @@
+/*
+tip_balloon.js v. 1.81
+
+The latest version is available at
+http://www.walterzorn.com
+or http://www.devira.com
+or http://www.walterzorn.de
+
+Initial author: Walter Zorn
+Last modified: 2.2.2009
+
+Extension for the tooltip library wz_tooltip.js.
+Implements balloon tooltips.
+*/
+
+// Make sure that the core file wz_tooltip.js is included first
+if(typeof config == "undefined")
+ alert("Error:\nThe core tooltip script file 'wz_tooltip.js' must be included first, before the plugin files!");
+
+// Here we define new global configuration variable(s) (as members of the
+// predefined "config." class).
+// From each of these config variables, wz_tooltip.js will automatically derive
+// a command which can be passed to Tip() or TagToTip() in order to customize
+// tooltips individually. These command names are just the config variable
+// name(s) translated to uppercase,
+// e.g. from config. Balloon a command BALLOON will automatically be
+// created.
+
+//=================== GLOBAL TOOLTIP CONFIGURATION =========================//
+config. Balloon = false // true or false - set to true if you want this to be the default behaviour
+config. BalloonImgPath = "script/tip_balloon/" // Path to images (border, corners, stem), in quotes. Path must be relative to your HTML file.
+// Sizes of balloon images
+config. BalloonEdgeSize = 6 // Integer - sidelength of quadratic corner images
+config. BalloonStemWidth = 15 // Integer
+config. BalloonStemHeight = 19 // Integer
+config. BalloonStemOffset = -7 // Integer - horizontal offset of left stem edge from mouse (recommended: -stemwidth/2 to center the stem above the mouse)
+config. BalloonImgExt = "gif";// File name extension of default balloon images, e.g. "gif" or "png"
+//======= END OF TOOLTIP CONFIG, DO NOT CHANGE ANYTHING BELOW ==============//
+
+
+// Create a new tt_Extension object (make sure that the name of that object,
+// here balloon, is unique amongst the extensions available for wz_tooltips.js):
+var balloon = new tt_Extension();
+
+// Implement extension eventhandlers on which our extension should react
+
+balloon.OnLoadConfig = function()
+{
+ if(tt_aV[BALLOON])
+ {
+ // Turn off native style properties which are not appropriate
+ balloon.padding = Math.max(tt_aV[PADDING] - tt_aV[BALLOONEDGESIZE], 0);
+ balloon.width = tt_aV[WIDTH];
+ //if(tt_bBoxOld)
+ // balloon.width += (balloon.padding << 1);
+ tt_aV[BORDERWIDTH] = 0;
+ tt_aV[WIDTH] = 0;
+ tt_aV[PADDING] = 0;
+ tt_aV[BGCOLOR] = "";
+ tt_aV[BGIMG] = "";
+ tt_aV[SHADOW] = false;
+ // Append slash to img path if missing
+ if(tt_aV[BALLOONIMGPATH].charAt(tt_aV[BALLOONIMGPATH].length - 1) != '/')
+ tt_aV[BALLOONIMGPATH] += "/";
+ return true;
+ }
+ return false;
+};
+balloon.OnCreateContentString = function()
+{
+ if(!tt_aV[BALLOON])
+ return false;
+
+ var aImg, sImgZ, sCssCrn, sVaT, sVaB, sCss0;
+
+ // Cache balloon images in advance:
+ // Either use the pre-cached default images...
+ if(tt_aV[BALLOONIMGPATH] == config.BalloonImgPath)
+ aImg = balloon.aDefImg;
+ // ...or load images from different directory
+ else
+ aImg = Balloon_CacheImgs(tt_aV[BALLOONIMGPATH], tt_aV[BALLOONIMGEXT]);
+ sCss0 = 'padding:0;margin:0;border:0;line-height:0;overflow:hidden;';
+ sCssCrn = ' style="position:relative;width:' + tt_aV[BALLOONEDGESIZE] + 'px;' + sCss0 + 'overflow:hidden;';
+ sVaT = 'vertical-align:top;" valign="top"';
+ sVaB = 'vertical-align:bottom;" valign="bottom"';
+ sImgZ = '" style="' + sCss0 + '" />';
+
+ tt_sContent = '<table border="0" cellpadding="0" cellspacing="0" style="width:auto;padding:0;margin:0;left:0;top:0;"><tr>'
+ // Left-top corner
+ + '<td' + sCssCrn + sVaB + '>'
+ + '<img src="' + aImg[1].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
+ + '</td>'
+ // Top border
+ + '<td valign="bottom" style="position:relative;' + sCss0 + '">'
+ + '<img id="bALlOOnT" style="position:relative;top:1px;z-index:1;display:none;' + sCss0 + '" src="' + aImg[9].src + '" width="' + tt_aV[BALLOONSTEMWIDTH] + '" height="' + tt_aV[BALLOONSTEMHEIGHT] + '" />'
+ + '<div style="position:relative;z-index:0;top:0;' + sCss0 + 'width:auto;height:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[2].src + ');">'
+ + '</div>'
+ + '</td>'
+ // Right-top corner
+ + '<td' + sCssCrn + sVaB + '>'
+ + '<img src="' + aImg[3].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
+ + '</td>'
+ + '</tr><tr>'
+ // Left border (background-repeat fix courtesy Dirk Schnitzler)
+ + '<td style="position:relative;background-repeat:repeat;' + sCss0 + 'width:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[8].src + ');">'
+ // Redundant image for bugous old Geckos which won't auto-expand TD height to 100%
+ + '<img width="' + tt_aV[BALLOONEDGESIZE] + '" height="100%" src="' + aImg[8].src + sImgZ
+ + '</td>'
+ // Content
+ + '<td id="bALlO0nBdY" style="position:relative;line-height:normal;background-repeat:repeat;'
+ + ';background-image:url(' + aImg[0].src + ')'
+ + ';color:' + tt_aV[FONTCOLOR]
+ + ';font-family:' + tt_aV[FONTFACE]
+ + ';font-size:' + tt_aV[FONTSIZE]
+ + ';font-weight:' + tt_aV[FONTWEIGHT]
+ + ';text-align:' + tt_aV[TEXTALIGN]
+ + ';padding:' + balloon.padding + 'px'
+ + ';width:' + ((balloon.width > 0) ? (balloon.width + 'px') : 'auto')
+ + ';">' + tt_sContent + '</td>'
+ // Right border
+ + '<td style="position:relative;background-repeat:repeat;' + sCss0 + 'width:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[4].src + ');">'
+ // Image redundancy for bugous old Geckos that won't auto-expand TD height to 100%
+ + '<img width="' + tt_aV[BALLOONEDGESIZE] + '" height="100%" src="' + aImg[4].src + sImgZ
+ + '</td>'
+ + '</tr><tr>'
+ // Left-bottom corner
+ + '<td' + sCssCrn + sVaT + '>'
+ + '<img src="' + aImg[7].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
+ + '</td>'
+ // Bottom border
+ + '<td valign="top" style="position:relative;' + sCss0 + '">'
+ + '<div style="position:relative;left:0;top:0;' + sCss0 + 'width:auto;height:' + tt_aV[BALLOONEDGESIZE] + 'px;background-image:url(' + aImg[6].src + ');"></div>'
+ + '<img id="bALlOOnB" style="position:relative;top:-1px;left:2px;z-index:1;display:none;' + sCss0 + '" src="' + aImg[10].src + '" width="' + tt_aV[BALLOONSTEMWIDTH] + '" height="' + tt_aV[BALLOONSTEMHEIGHT] + '" />'
+ + '</td>'
+ // Right-bottom corner
+ + '<td' + sCssCrn + sVaT + '>'
+ + '<img src="' + aImg[5].src + '" width="' + tt_aV[BALLOONEDGESIZE] + '" height="' + tt_aV[BALLOONEDGESIZE] + sImgZ
+ + '</td>'
+ + '</tr></table>';//alert(tt_sContent);
+ return true;
+};
+balloon.OnSubDivsCreated = function()
+{
+ if(tt_aV[BALLOON])
+ {
+ var bdy = tt_GetElt("bALlO0nBdY");
+
+ // Insert a TagToTip() HTML element into the central body TD
+ if (tt_t2t && !tt_aV[COPYCONTENT] && bdy)
+ tt_MovDomNode(tt_t2t, tt_GetDad(tt_t2t), bdy);
+ balloon.iStem = tt_aV[ABOVE] * 1;
+ balloon.aStem = [tt_GetElt("bALlOOnT"), tt_GetElt("bALlOOnB")];
+ balloon.aStem[balloon.iStem].style.display = "inline";
+ if (balloon.width < -1)
+ Balloon_MaxW(bdy);
+ return true;
+ }
+ return false;
+};
+// Display the stem appropriately
+balloon.OnMoveAfter = function()
+{
+ if(tt_aV[BALLOON])
+ {
+ var iStem = (tt_aV[ABOVE] != tt_bJmpVert) * 1;
+
+ // Tooltip position vertically flipped?
+ if(iStem != balloon.iStem)
+ {
+ // Display opposite stem
+ balloon.aStem[balloon.iStem].style.display = "none";
+ balloon.aStem[iStem].style.display = "inline";
+ balloon.iStem = iStem;
+ }
+
+ balloon.aStem[iStem].style.left = Balloon_CalcStemX() + "px";
+ return true;
+ }
+ return false;
+};
+function Balloon_CalcStemX()
+{
+ var x = tt_musX - tt_x + tt_aV[BALLOONSTEMOFFSET] - tt_aV[BALLOONEDGESIZE];
+ return Math.max(Math.min(x, tt_w - tt_aV[BALLOONSTEMWIDTH] - (tt_aV[BALLOONEDGESIZE] << 1) - 2), 2);
+}
+function Balloon_CacheImgs(sPath, sExt)
+{
+ var asImg = ["background", "lt", "t", "rt", "r", "rb", "b", "lb", "l", "stemt", "stemb"],
+ n = asImg.length,
+ aImg = new Array(n),
+ img;
+
+ while(n)
+ {--n;
+ img = aImg[n] = new Image();
+ img.src = sPath + asImg[n] + "." + sExt;
+ }
+ return aImg;
+}
+function Balloon_MaxW(bdy)
+{
+ if (bdy)
+ {
+ var iAdd = tt_bBoxOld ? (balloon.padding << 1) : 0, w = tt_GetDivW(bdy);
+ if (w > -balloon.width + iAdd)
+ bdy.style.width = (-balloon.width + iAdd) + "px";
+ }
+}
+// This mechanism pre-caches the default images specified by
+// congif.BalloonImgPath, so, whenever a balloon tip using these default images
+// is created, no further server connection is necessary.
+function Balloon_PreCacheDefImgs()
+{
+ // Append slash to img path if missing
+ if(config.BalloonImgPath.charAt(config.BalloonImgPath.length - 1) != '/')
+ config.BalloonImgPath += "/";
+ // Preload default images into array
+ balloon.aDefImg = Balloon_CacheImgs(config.BalloonImgPath, config.BalloonImgExt);
+}
+Balloon_PreCacheDefImgs();
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/b.gif b/subsonic-main/src/main/webapp/script/tip_balloon/b.gif
new file mode 100644
index 00000000..ff0c04b2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/b.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/background.gif b/subsonic-main/src/main/webapp/script/tip_balloon/background.gif
new file mode 100644
index 00000000..cb61e7ff
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/background.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/l.gif b/subsonic-main/src/main/webapp/script/tip_balloon/l.gif
new file mode 100644
index 00000000..14a68d93
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/l.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/lb.gif b/subsonic-main/src/main/webapp/script/tip_balloon/lb.gif
new file mode 100644
index 00000000..713c3739
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/lb.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/lt.gif b/subsonic-main/src/main/webapp/script/tip_balloon/lt.gif
new file mode 100644
index 00000000..93c71f46
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/lt.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/r.gif b/subsonic-main/src/main/webapp/script/tip_balloon/r.gif
new file mode 100644
index 00000000..be041ff9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/r.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/rb.gif b/subsonic-main/src/main/webapp/script/tip_balloon/rb.gif
new file mode 100644
index 00000000..70b6834e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/rb.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/rt.gif b/subsonic-main/src/main/webapp/script/tip_balloon/rt.gif
new file mode 100644
index 00000000..2752a0cc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/rt.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/stemb.gif b/subsonic-main/src/main/webapp/script/tip_balloon/stemb.gif
new file mode 100644
index 00000000..92f2ad51
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/stemb.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/stemt.gif b/subsonic-main/src/main/webapp/script/tip_balloon/stemt.gif
new file mode 100644
index 00000000..d259a4ae
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/stemt.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/tip_balloon/t.gif b/subsonic-main/src/main/webapp/script/tip_balloon/t.gif
new file mode 100644
index 00000000..6fec86f8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/tip_balloon/t.gif
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/webfx/boxsizing.htc b/subsonic-main/src/main/webapp/script/webfx/boxsizing.htc
new file mode 100644
index 00000000..fbeaa56c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/boxsizing.htc
@@ -0,0 +1,157 @@
+<component lightWeight="true">
+<attach event="onpropertychange" onevent="checkPropertyChange()" />
+<attach event="ondetach" onevent="restore()" />
+<script>
+//<![CDATA[
+
+var doc = element.document;
+
+function init() {
+ updateBorderBoxWidth();
+ updateBorderBoxHeight();
+}
+
+function restore() {
+ element.runtimeStyle.width = "";
+ element.runtimeStyle.height = "";
+}
+
+/* border width getters */
+function getBorderWidth(sSide) {
+ if (element.currentStyle["border" + sSide + "Style"] == "none")
+ return 0;
+ var n = parseInt(element.currentStyle["border" + sSide + "Width"]);
+ return n || 0;
+}
+
+function getBorderLeftWidth() { return getBorderWidth("Left"); }
+function getBorderRightWidth() { return getBorderWidth("Right"); }
+function getBorderTopWidth() { return getBorderWidth("Top"); }
+function getBorderBottomWidth() { return getBorderWidth("Bottom"); }
+/* end border width getters */
+
+/* padding getters */
+function getPadding(sSide) {
+ var n = parseInt(element.currentStyle["padding" + sSide]);
+ return n || 0;
+}
+
+function getPaddingLeft() { return getPadding("Left"); }
+function getPaddingRight() { return getPadding("Right"); }
+function getPaddingTop() { return getPadding("Top"); }
+function getPaddingBottom() { return getPadding("Bottom"); }
+/* end padding getters */
+
+function getBoxSizing() {
+ var s = element.style;
+ var cs = element.currentStyle
+
+ if (typeof s.boxSizing != "undefined" && s.boxSizing != "")
+ return s.boxSizing;
+ if (typeof s["box-sizing"] != "undefined" && s["box-sizing"] != "")
+ return s["box-sizing"];
+ if (typeof cs.boxSizing != "undefined" && cs.boxSizing != "")
+ return cs.boxSizing;
+ if (typeof cs["box-sizing"] != "undefined" && cs["box-sizing"] != "")
+ return cs["box-sizing"];
+ return getDocumentBoxSizing();
+}
+
+function getDocumentBoxSizing() {
+ if (doc.compatMode == null || doc.compatMode == "BackCompat")
+ return "border-box";
+ return "content-box"
+}
+
+/* width and height setters */
+function setBorderBoxWidth(n) {
+ element.runtimeStyle.width = Math.max(0, n - getBorderLeftWidth() -
+ getPaddingLeft() - getPaddingRight() - getBorderRightWidth()) + "px";
+}
+
+function setBorderBoxHeight(n) {
+ element.runtimeStyle.height = Math.max(0, n - getBorderTopWidth() -
+ getPaddingTop() - getPaddingBottom() - getBorderBottomWidth()) + "px";
+}
+
+function setContentBoxWidth(n) {
+ element.runtimeStyle.width = Math.max(0, n + getBorderLeftWidth() +
+ getPaddingLeft() + getPaddingRight() + getBorderRightWidth()) + "px";
+}
+
+function setContentBoxHeight(n) {
+ element.runtimeStyle.height = Math.max(0, n + getBorderTopWidth() +
+ getPaddingTop() + getPaddingBottom() + getBorderBottomWidth()) + "px";
+}
+/* end width and height setters */
+
+function updateBorderBoxWidth() {
+ element.runtimeStyle.width = "";
+ if (getDocumentBoxSizing() == getBoxSizing())
+ return;
+ var csw = element.currentStyle.width;
+ if (csw != "auto" && csw.indexOf("px") != -1) {
+ if (getBoxSizing() == "border-box")
+ setBorderBoxWidth(parseInt(csw));
+ else
+ setContentBoxWidth(parseInt(csw));
+ }
+}
+
+function updateBorderBoxHeight() {
+ element.runtimeStyle.height = "";
+ if (getDocumentBoxSizing() == getBoxSizing())
+ return;
+ var csh = element.currentStyle.height;
+ if (csh != "auto" && csh.indexOf("px") != -1) {
+ if (getBoxSizing() == "border-box")
+ setBorderBoxHeight(parseInt(csh));
+ else
+ setContentBoxHeight(parseInt(csh));
+ }
+}
+
+function checkPropertyChange() {
+ var pn = event.propertyName;
+ var undef;
+
+ if (pn == "style.boxSizing" && element.style.boxSizing == "") {
+ element.style.removeAttribute("boxSizing");
+ element.runtimeStyle.boxSizing = undef;
+ }
+
+
+ switch (pn) {
+ case "style.width":
+ case "style.borderLeftWidth":
+ case "style.borderLeftStyle":
+ case "style.borderRightWidth":
+ case "style.borderRightStyle":
+ case "style.paddingLeft":
+ case "style.paddingRight":
+ updateBorderBoxWidth();
+ break;
+
+ case "style.height":
+ case "style.borderTopWidth":
+ case "style.borderTopStyle":
+ case "style.borderBottomWidth":
+ case "style.borderBottomStyle":
+ case "style.paddingTop":
+ case "style.paddingBottom":
+ updateBorderBoxHeight();
+ break;
+
+ case "className":
+ case "style.boxSizing":
+ updateBorderBoxWidth();
+ updateBorderBoxHeight();
+ break;
+ }
+}
+
+init();
+
+//]]>
+</script>
+</component> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.hover.png b/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.hover.png
new file mode 100644
index 00000000..d2fd059d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.hover.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.png b/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.png
new file mode 100644
index 00000000..b3af6bbf
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/handle.horizontal.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/webfx/handle.vertical.hover.png b/subsonic-main/src/main/webapp/script/webfx/handle.vertical.hover.png
new file mode 100644
index 00000000..379cdc29
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/handle.vertical.hover.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/webfx/handle.vertical.png b/subsonic-main/src/main/webapp/script/webfx/handle.vertical.png
new file mode 100644
index 00000000..537135e8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/handle.vertical.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/script/webfx/luna.css b/subsonic-main/src/main/webapp/script/webfx/luna.css
new file mode 100644
index 00000000..b01107cb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/luna.css
@@ -0,0 +1,75 @@
+.dynamic-slider-control {
+ position: relative;
+ /*background-color: ThreeDFace;*/
+ -moz-user-focus: normal;
+ -moz-user-select: none;
+ cursor: default;
+}
+
+.horizontal {
+ width: 200px;
+ height: 27px;
+}
+
+.vertical {
+ width: 29px;
+ height: 200px;
+}
+
+.dynamic-slider-control input {
+ display: none;
+}
+
+.dynamic-slider-control .handle {
+ position: absolute;
+ font-size: 1px;
+ overflow: hidden;
+ -moz-user-select: none;
+ cursor: default;
+}
+
+.dynamic-slider-control.horizontal .handle {
+ width: 11px;
+ height: 21px;
+ background-image: url("handle.horizontal.png");
+}
+
+.dynamic-slider-control.horizontal .handle div {}
+.dynamic-slider-control.horizontal .handle.hover {
+ background-image: url("handle.horizontal.hover.png");
+}
+
+.dynamic-slider-control.vertical .handle {
+ width: 25px;
+ height: 13px;
+ background-image: url("handle.vertical.png");
+}
+
+.dynamic-slider-control.vertical .handle.hover {
+ background-image: url("handle.vertical.hover.png");
+}
+
+.dynamic-slider-control .line {
+ position: absolute;
+ font-size: 0.01mm;
+ overflow: hidden;
+ border: 1px solid;
+ border-color: ThreeDShadow ThreeDHighlight
+ ThreeDHighlight ThreeDShadow;
+ -moz-border-radius: 50%;
+
+ behavior: url("boxsizing.htc"); /* ie path bug */
+ box-sizing: content-box;
+ -moz-box-sizing: content-box;
+}
+.dynamic-slider-control.vertical .line {
+ width: 2px;
+}
+
+.dynamic-slider-control.horizontal .line {
+ height: 2px;
+}
+
+.dynamic-slider-control .line div {
+ display: none;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/webfx/range.js b/subsonic-main/src/main/webapp/script/webfx/range.js
new file mode 100644
index 00000000..53c8f34e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/range.js
@@ -0,0 +1,132 @@
+/*----------------------------------------------------------------------------\
+| Range Class |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| Used to model the data used when working with sliders, scrollbars and |
+| progress bars. Based on the ideas of the javax.swing.BoundedRangeModel |
+| interface defined by Sun for Java; http://java.sun.com/products/jfc/ |
+| swingdoc-api-1.0.3/com/sun/java/swing/BoundedRangeModel.html |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2005, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2005-10-27 | Use Math.round instead of Math.floor |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+
+function Range() {
+ this._value = 0;
+ this._minimum = 0;
+ this._maximum = 100;
+ this._extent = 0;
+
+ this._isChanging = false;
+}
+
+Range.prototype.setValue = function (value) {
+ value = Math.round(parseFloat(value));
+ if (isNaN(value)) return;
+ if (this._value != value) {
+ if (value + this._extent > this._maximum)
+ this._value = this._maximum - this._extent;
+ else if (value < this._minimum)
+ this._value = this._minimum;
+ else
+ this._value = value;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getValue = function () {
+ return this._value;
+};
+
+Range.prototype.setExtent = function (extent) {
+ if (this._extent != extent) {
+ if (extent < 0)
+ this._extent = 0;
+ else if (this._value + extent > this._maximum)
+ this._extent = this._maximum - this._value;
+ else
+ this._extent = extent;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getExtent = function () {
+ return this._extent;
+};
+
+Range.prototype.setMinimum = function (minimum) {
+ if (this._minimum != minimum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._minimum = minimum;
+
+ if (minimum > this._value)
+ this.setValue(minimum);
+ if (minimum > this._maximum) {
+ this._extent = 0;
+ this.setMaximum(minimum);
+ this.setValue(minimum)
+ }
+ if (minimum + this._extent > this._maximum)
+ this._extent = this._maximum - this._minimum;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMinimum = function () {
+ return this._minimum;
+};
+
+Range.prototype.setMaximum = function (maximum) {
+ if (this._maximum != maximum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._maximum = maximum;
+
+ if (maximum < this._value)
+ this.setValue(maximum - this._extent);
+ if (maximum < this._minimum) {
+ this._extent = 0;
+ this.setMinimum(maximum);
+ this.setValue(this._maximum);
+ }
+ if (maximum < this._minimum + this._extent)
+ this._extent = this._maximum - this._minimum;
+ if (maximum < this._value + this._extent)
+ this._extent = this._maximum - this._value;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMaximum = function () {
+ return this._maximum;
+};
diff --git a/subsonic-main/src/main/webapp/script/webfx/slider.js b/subsonic-main/src/main/webapp/script/webfx/slider.js
new file mode 100644
index 00000000..ddc6d756
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/slider.js
@@ -0,0 +1,489 @@
+/*----------------------------------------------------------------------------\
+| Slider 1.02 |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| A slider control that degrades to an input control for non supported |
+| browsers. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2003, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| Dependencies: timer.js - an OO abstraction of timers |
+| range.js - provides the data model for the slider |
+| winclassic.css or any other css file describing the look |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2003-03-27 | Added a test in the constructor for missing oElement arg |
+| 2003-11-27 | Only use mousewheel when focused |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+Slider.isSupported = typeof document.createElement != "undefined" &&
+ typeof document.documentElement != "undefined" &&
+ typeof document.documentElement.offsetWidth == "number";
+
+
+function Slider(oElement, oInput, sOrientation) {
+ if (!oElement) return;
+ this._orientation = sOrientation || "horizontal";
+ this._range = new Range();
+ this._range.setExtent(0);
+ this._blockIncrement = 10;
+ this._unitIncrement = 1;
+ this._timer = new Timer(100);
+
+
+ if (Slider.isSupported && oElement) {
+
+ this.document = oElement.ownerDocument || oElement.document;
+
+ this.element = oElement;
+ this.element.slider = this;
+ this.element.unselectable = "on";
+
+ // add class name tag to class name
+ this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
+
+ // create line
+ this.line = this.document.createElement("DIV");
+ this.line.className = "line";
+ this.line.unselectable = "on";
+ this.line.appendChild(this.document.createElement("DIV"));
+ this.element.appendChild(this.line);
+
+ // create handle
+ this.handle = this.document.createElement("DIV");
+ this.handle.className = "handle";
+ this.handle.unselectable = "on";
+ this.handle.appendChild(this.document.createElement("DIV"));
+ this.handle.firstChild.appendChild(
+ this.document.createTextNode(String.fromCharCode(160)));
+ this.element.appendChild(this.handle);
+ }
+
+ this.input = oInput;
+
+ // events
+ var oThis = this;
+ this._range.onchange = function () {
+ oThis.recalculate();
+ if (typeof oThis.onchange == "function")
+ oThis.onchange();
+ };
+
+ if (Slider.isSupported && oElement) {
+ this.element.onfocus = Slider.eventHandlers.onfocus;
+ this.element.onblur = Slider.eventHandlers.onblur;
+ this.element.onmousedown = Slider.eventHandlers.onmousedown;
+ this.element.onmouseover = Slider.eventHandlers.onmouseover;
+ this.element.onmouseout = Slider.eventHandlers.onmouseout;
+ this.element.onkeydown = Slider.eventHandlers.onkeydown;
+ this.element.onkeypress = Slider.eventHandlers.onkeypress;
+ this.element.onmousewheel = Slider.eventHandlers.onmousewheel;
+ this.handle.onselectstart =
+ this.element.onselectstart = function () { return false; };
+
+ this._timer.ontimer = function () {
+ oThis.ontimer();
+ };
+
+ // extra recalculate for ie
+ window.setTimeout(function() {
+ oThis.recalculate();
+ }, 1);
+ }
+ else {
+ this.input.onchange = function (e) {
+ oThis.setValue(oThis.input.value);
+ };
+ }
+}
+
+Slider.eventHandlers = {
+
+ // helpers to make events a bit easier
+ getEvent: function (e, el) {
+ if (!e) {
+ if (el)
+ e = el.document.parentWindow.event;
+ else
+ e = window.event;
+ }
+ if (!e.srcElement) {
+ var el = e.target;
+ while (el != null && el.nodeType != 1)
+ el = el.parentNode;
+ e.srcElement = el;
+ }
+ if (typeof e.offsetX == "undefined") {
+ e.offsetX = e.layerX;
+ e.offsetY = e.layerY;
+ }
+
+ return e;
+ },
+
+ getDocument: function (e) {
+ if (e.target)
+ return e.target.ownerDocument;
+ return e.srcElement.document;
+ },
+
+ getSlider: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.slider == null) {
+ el = el.parentNode;
+ }
+ if (el)
+ return el.slider;
+ return null;
+ },
+
+ getLine: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.className != "line") {
+ el = el.parentNode;
+ }
+ return el;
+ },
+
+ getHandle: function (e) {
+ var el = e.target || e.srcElement;
+ var re = /handle/;
+ while (el != null && !re.test(el.className)) {
+ el = el.parentNode;
+ }
+ return el;
+ },
+ // end helpers
+
+ onfocus: function (e) {
+ var s = this.slider;
+ s._focused = true;
+ s.handle.className = "handle hover";
+ },
+
+ onblur: function (e) {
+ var s = this.slider
+ s._focused = false;
+ s.handle.className = "handle";
+ },
+
+ onmouseover: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle)
+ s.handle.className = "handle hover";
+ },
+
+ onmouseout: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle && !s._focused)
+ s.handle.className = "handle";
+ },
+
+ onmousedown: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s.element.focus)
+ s.element.focus();
+
+ Slider._currentInstance = s;
+ var doc = s.document;
+
+ if (doc.addEventListener) {
+ doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.attachEvent) {
+ doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.setCapture();
+ }
+
+ if (Slider.eventHandlers.getHandle(e)) { // start drag
+ Slider._sliderDragData = {
+ screenX: e.screenX,
+ screenY: e.screenY,
+ dx: e.screenX - s.handle.offsetLeft,
+ dy: e.screenY - s.handle.offsetTop,
+ startValue: s.getValue(),
+ slider: s
+ };
+ }
+ else {
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ s._increasing = null;
+ s.ontimer();
+ }
+ },
+
+ onmousemove: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+
+ if (Slider._sliderDragData) { // drag
+ var s = Slider._sliderDragData.slider;
+
+ var boundSize = s.getMaximum() - s.getMinimum();
+ var size, pos, reset;
+
+ if (s._orientation == "horizontal") {
+ size = s.element.offsetWidth - s.handle.offsetWidth;
+ pos = e.screenX - Slider._sliderDragData.dx;
+ reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
+ }
+ else {
+ size = s.element.offsetHeight - s.handle.offsetHeight;
+ pos = s.element.offsetHeight - s.handle.offsetHeight -
+ (e.screenY - Slider._sliderDragData.dy);
+ reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
+ }
+ s.setValue(reset ? Slider._sliderDragData.startValue :
+ s.getMinimum() + boundSize * pos / size);
+ return false;
+ }
+ else {
+ var s = Slider._currentInstance;
+ if (s != null) {
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ }
+ }
+
+ },
+
+ onmouseup: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = Slider._currentInstance;
+ var doc = s.document;
+ if (doc.removeEventListener) {
+ doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.detachEvent) {
+ doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.releaseCapture();
+ }
+
+ if (Slider._sliderDragData) { // end drag
+ Slider._sliderDragData = null;
+ }
+ else {
+ s._timer.stop();
+ s._increasing = null;
+ }
+ Slider._currentInstance = null;
+ },
+
+ onkeydown: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ //var s = Slider.eventHandlers.getSlider(e);
+ var s = this.slider;
+ var kc = e.keyCode;
+ switch (kc) {
+ case 33: // page up
+ s.setValue(s.getValue() + s.getBlockIncrement());
+ break;
+ case 34: // page down
+ s.setValue(s.getValue() - s.getBlockIncrement());
+ break;
+ case 35: // end
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMaximum() :
+ s.getMinimum());
+ break;
+ case 36: // home
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMinimum() :
+ s.getMaximum());
+ break;
+ case 38: // up
+ case 39: // right
+ s.setValue(s.getValue() + s.getUnitIncrement());
+ break;
+
+ case 37: // left
+ case 40: // down
+ s.setValue(s.getValue() - s.getUnitIncrement());
+ break;
+ }
+
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onkeypress: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var kc = e.keyCode;
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onmousewheel: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s._focused) {
+ s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
+ // windows inverts this on horizontal sliders. That does not
+ // make sense to me
+ return false;
+ }
+ }
+};
+
+
+
+Slider.prototype.classNameTag = "dynamic-slider-control";
+
+Slider.prototype.setValue = function (v) {
+ this._range.setValue(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getValue = function () {
+ return this._range.getValue();
+};
+
+Slider.prototype.setMinimum = function (v) {
+ this._range.setMinimum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMinimum = function () {
+ return this._range.getMinimum();
+};
+
+Slider.prototype.setMaximum = function (v) {
+ this._range.setMaximum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMaximum = function () {
+ return this._range.getMaximum();
+};
+
+Slider.prototype.setUnitIncrement = function (v) {
+ this._unitIncrement = v;
+};
+
+Slider.prototype.getUnitIncrement = function () {
+ return this._unitIncrement;
+};
+
+Slider.prototype.setBlockIncrement = function (v) {
+ this._blockIncrement = v;
+};
+
+Slider.prototype.getBlockIncrement = function () {
+ return this._blockIncrement;
+};
+
+Slider.prototype.getOrientation = function () {
+ return this._orientation;
+};
+
+Slider.prototype.setOrientation = function (sOrientation) {
+ if (sOrientation != this._orientation) {
+ if (Slider.isSupported && this.element) {
+ // add class name tag to class name
+ this.element.className = this.element.className.replace(this._orientation,
+ sOrientation);
+ }
+ this._orientation = sOrientation;
+ this.recalculate();
+
+ }
+};
+
+Slider.prototype.recalculate = function() {
+ if (!Slider.isSupported || !this.element) return;
+
+ var w = this.element.offsetWidth;
+ var h = this.element.offsetHeight;
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var lw = this.line.offsetWidth;
+ var lh = this.line.offsetHeight;
+
+ // this assumes a border-box layout
+
+ if (this._orientation == "horizontal") {
+ this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+ this.handle.style.top = (h - hh) / 2 + "px";
+
+ this.line.style.top = (h - lh) / 2 + "px";
+ this.line.style.left = hw / 2 + "px";
+ //this.line.style.right = hw / 2 + "px";
+ this.line.style.width = Math.max(0, w - hw - 2)+ "px";
+ this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
+ }
+ else {
+ this.handle.style.left = (w - hw) / 2 + "px";
+ this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+
+ this.line.style.left = (w - lw) / 2 + "px";
+ this.line.style.top = hh / 2 + "px";
+ this.line.style.height = Math.max(0, h - hh - 2) + "px"; //hard coded border width
+ //this.line.style.bottom = hh / 2 + "px";
+ this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px"; //hard coded border width
+ }
+};
+
+Slider.prototype.ontimer = function () {
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var hl = this.handle.offsetLeft;
+ var ht = this.handle.offsetTop;
+
+ if (this._orientation == "horizontal") {
+ if (this._mouseX > hl + hw &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ else if (this._mouseX < hl &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ }
+ else {
+ if (this._mouseY > ht + hh &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ else if (this._mouseY < ht &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ }
+
+ this._timer.start();
+}; \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/webfx/timer.js b/subsonic-main/src/main/webapp/script/webfx/timer.js
new file mode 100644
index 00000000..0c1e897f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/webfx/timer.js
@@ -0,0 +1,62 @@
+/*----------------------------------------------------------------------------\
+| Timer Class |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| Object Oriented Encapsulation of setTimeout fires ontimer when the timer |
+| is triggered. Does not work in IE 5.00 |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+function Timer(nPauseTime) {
+ this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
+ this._timer = null;
+ this._isStarted = false;
+}
+
+Timer.prototype.start = function () {
+ if (this.isStarted())
+ this.stop();
+ var oThis = this;
+ this._timer = window.setTimeout(function () {
+ if (typeof oThis.ontimer == "function")
+ oThis.ontimer();
+ }, this._pauseTime);
+ this._isStarted = false;
+};
+
+Timer.prototype.stop = function () {
+ if (this._timer != null)
+ window.clearTimeout(this._timer);
+ this._isStarted = false;
+};
+
+Timer.prototype.isStarted = function () {
+ return this._isStarted;
+};
+
+Timer.prototype.getPauseTime = function () {
+ return this._pauseTime;
+};
+
+Timer.prototype.setPauseTime = function (nPauseTime) {
+ this._pauseTime = nPauseTime;
+}; \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/script/wz_tooltip.js b/subsonic-main/src/main/webapp/script/wz_tooltip.js
new file mode 100644
index 00000000..01f55f2d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/script/wz_tooltip.js
@@ -0,0 +1,1301 @@
+/* This notice must be untouched at all times.
+Copyright (c) 2002-2008 Walter Zorn. All rights reserved.
+
+wz_tooltip.js v. 5.31
+
+The latest version is available at
+http://www.walterzorn.com
+or http://www.devira.com
+or http://www.walterzorn.de
+
+Created 1.12.2002 by Walter Zorn (Web: http://www.walterzorn.com )
+Last modified: 7.11.2008
+
+Easy-to-use cross-browser tooltips.
+Just include the script at the beginning of the <body> section, and invoke
+Tip('Tooltip text') to show and UnTip() to hide the tooltip, from the desired
+HTML eventhandlers. Example:
+<a onmouseover="Tip('Some text')" onmouseout="UnTip()" href="index.htm">My home page</a>
+No container DIV required.
+By default, width and height of tooltips are automatically adapted to content.
+Is even capable of dynamically converting arbitrary HTML elements to tooltips
+by calling TagToTip('ID_of_HTML_element_to_be_converted') instead of Tip(),
+which means you can put important, search-engine-relevant stuff into tooltips.
+Appearance & behaviour of tooltips can be individually configured
+via commands passed to Tip() or TagToTip().
+
+Tab Width: 4
+LICENSE: LGPL
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License (LGPL) as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+For more details on the GNU Lesser General Public License,
+see http://www.gnu.org/copyleft/lesser.html
+*/
+
+var config = new Object();
+
+
+//=================== GLOBAL TOOLTIP CONFIGURATION =========================//
+var tt_Debug = true // false or true - recommended: false once you release your page to the public
+var tt_Enabled = true // Allows to (temporarily) suppress tooltips, e.g. by providing the user with a button that sets this global variable to false
+var TagsToTip = true // false or true - if true, HTML elements to be converted to tooltips via TagToTip() are automatically hidden;
+ // if false, you should hide those HTML elements yourself
+
+// For each of the following config variables there exists a command, which is
+// just the variablename in uppercase, to be passed to Tip() or TagToTip() to
+// configure tooltips individually. Individual commands override global
+// configuration. Order of commands is arbitrary.
+// Example: onmouseover="Tip('Tooltip text', LEFT, true, BGCOLOR, '#FF9900', FADEIN, 400)"
+
+config. Above = false // false or true - tooltip above mousepointer
+config. BgColor = '#E2E7FF' // Background colour (HTML colour value, in quotes)
+config. BgImg = '' // Path to background image, none if empty string ''
+config. BorderColor = '#003099'
+config. BorderStyle = 'solid' // Any permitted CSS value, but I recommend 'solid', 'dotted' or 'dashed'
+config. BorderWidth = 1
+config. CenterMouse = false // false or true - center the tip horizontally below (or above) the mousepointer
+config. ClickClose = false // false or true - close tooltip if the user clicks somewhere
+config. ClickSticky = false // false or true - make tooltip sticky if user left-clicks on the hovered element while the tooltip is active
+config. CloseBtn = false // false or true - closebutton in titlebar
+config. CloseBtnColors = ['#990000', '#FFFFFF', '#DD3333', '#FFFFFF'] // [Background, text, hovered background, hovered text] - use empty strings '' to inherit title colours
+config. CloseBtnText = '&nbsp;X&nbsp;' // Close button text (may also be an image tag)
+config. CopyContent = true // When converting a HTML element to a tooltip, copy only the element's content, rather than converting the element by its own
+config. Delay = 400 // Time span in ms until tooltip shows up
+config. Duration = 0 // Time span in ms after which the tooltip disappears; 0 for infinite duration, < 0 for delay in ms _after_ the onmouseout until the tooltip disappears
+config. Exclusive = false // false or true - no other tooltip can appear until the current one has actively been closed
+config. FadeIn = 100 // Fade-in duration in ms, e.g. 400; 0 for no animation
+config. FadeOut = 100
+config. FadeInterval = 30 // Duration of each fade step in ms (recommended: 30) - shorter is smoother but causes more CPU-load
+config. Fix = null // Fixated position, two modes. Mode 1: x- an y-coordinates in brackets, e.g. [210, 480]. Mode 2: Show tooltip at a position related to an HTML element: [ID of HTML element, x-offset, y-offset from HTML element], e.g. ['SomeID', 10, 30]. Value null (default) for no fixated positioning.
+config. FollowMouse = true // false or true - tooltip follows the mouse
+config. FontColor = '#000044'
+config. FontFace = 'Verdana,Geneva,sans-serif'
+config. FontSize = '8pt' // E.g. '9pt' or '12px' - unit is mandatory
+config. FontWeight = 'normal' // 'normal' or 'bold';
+config. Height = 0 // Tooltip height; 0 for automatic adaption to tooltip content, < 0 (e.g. -100) for a maximum for automatic adaption
+config. JumpHorz = false // false or true - jump horizontally to other side of mouse if tooltip would extend past clientarea boundary
+config. JumpVert = true // false or true - jump vertically "
+config. Left = false // false or true - tooltip on the left of the mouse
+config. OffsetX = 14 // Horizontal offset of left-top corner from mousepointer
+config. OffsetY = 8 // Vertical offset
+config. Opacity = 100 // Integer between 0 and 100 - opacity of tooltip in percent
+config. Padding = 3 // Spacing between border and content
+config. Shadow = false // false or true
+config. ShadowColor = '#C0C0C0'
+config. ShadowWidth = 5
+config. Sticky = false // false or true - fixate tip, ie. don't follow the mouse and don't hide on mouseout
+config. TextAlign = 'left' // 'left', 'right' or 'justify'
+config. Title = '' // Default title text applied to all tips (no default title: empty string '')
+config. TitleAlign = 'left' // 'left' or 'right' - text alignment inside the title bar
+config. TitleBgColor = '' // If empty string '', BorderColor will be used
+config. TitleFontColor = '#FFFFFF' // Color of title text - if '', BgColor (of tooltip body) will be used
+config. TitleFontFace = '' // If '' use FontFace (boldified)
+config. TitleFontSize = '' // If '' use FontSize
+config. TitlePadding = 2
+config. Width = 0 // Tooltip width; 0 for automatic adaption to tooltip content; < -1 (e.g. -240) for a maximum width for that automatic adaption;
+ // -1: tooltip width confined to the width required for the titlebar
+//======= END OF TOOLTIP CONFIG, DO NOT CHANGE ANYTHING BELOW ==============//
+
+
+
+
+//===================== PUBLIC =============================================//
+function Tip()
+{
+ tt_Tip(arguments, null);
+}
+function TagToTip()
+{
+ var t2t = tt_GetElt(arguments[0]);
+ if(t2t)
+ tt_Tip(arguments, t2t);
+}
+function UnTip()
+{
+ tt_OpReHref();
+ if(tt_aV[DURATION] < 0 && (tt_iState & 0x2))
+ tt_tDurt.Timer("tt_HideInit()", -tt_aV[DURATION], true);
+ else if(!(tt_aV[STICKY] && (tt_iState & 0x2)))
+ tt_HideInit();
+}
+
+//================== PUBLIC PLUGIN API =====================================//
+// Extension eventhandlers currently supported:
+// OnLoadConfig, OnCreateContentString, OnSubDivsCreated, OnShow, OnMoveBefore,
+// OnMoveAfter, OnHideInit, OnHide, OnKill
+
+var tt_aElt = new Array(10), // Container DIV, outer title & body DIVs, inner title & body TDs, closebutton SPAN, shadow DIVs, and IFRAME to cover windowed elements in IE
+tt_aV = new Array(), // Caches and enumerates config data for currently active tooltip
+tt_sContent, // Inner tooltip text or HTML
+tt_t2t, tt_t2tDad, // Tag converted to tip, and its DOM parent element
+tt_musX, tt_musY,
+tt_over,
+tt_x, tt_y, tt_w, tt_h; // Position, width and height of currently displayed tooltip
+
+function tt_Extension()
+{
+ tt_ExtCmdEnum();
+ tt_aExt[tt_aExt.length] = this;
+ return this;
+}
+function tt_SetTipPos(x, y)
+{
+ var css = tt_aElt[0].style;
+
+ tt_x = x;
+ tt_y = y;
+ css.left = x + "px";
+ css.top = y + "px";
+ if(tt_ie56)
+ {
+ var ifrm = tt_aElt[tt_aElt.length - 1];
+ if(ifrm)
+ {
+ ifrm.style.left = css.left;
+ ifrm.style.top = css.top;
+ }
+ }
+}
+function tt_HideInit()
+{
+ if(tt_iState)
+ {
+ tt_ExtCallFncs(0, "HideInit");
+ tt_iState &= ~(0x4 | 0x8);
+ if(tt_flagOpa && tt_aV[FADEOUT])
+ {
+ tt_tFade.EndTimer();
+ if(tt_opa)
+ {
+ var n = Math.round(tt_aV[FADEOUT] / (tt_aV[FADEINTERVAL] * (tt_aV[OPACITY] / tt_opa)));
+ tt_Fade(tt_opa, tt_opa, 0, n);
+ return;
+ }
+ }
+ tt_tHide.Timer("tt_Hide();", 1, false);
+ }
+}
+function tt_Hide()
+{
+ if(tt_db && tt_iState)
+ {
+ tt_OpReHref();
+ if(tt_iState & 0x2)
+ {
+ tt_aElt[0].style.visibility = "hidden";
+ tt_ExtCallFncs(0, "Hide");
+ }
+ tt_tShow.EndTimer();
+ tt_tHide.EndTimer();
+ tt_tDurt.EndTimer();
+ tt_tFade.EndTimer();
+ if(!tt_op && !tt_ie)
+ {
+ tt_tWaitMov.EndTimer();
+ tt_bWait = false;
+ }
+ if(tt_aV[CLICKCLOSE] || tt_aV[CLICKSTICKY])
+ tt_RemEvtFnc(document, "mouseup", tt_OnLClick);
+ tt_ExtCallFncs(0, "Kill");
+ // In case of a TagToTip tip, hide converted DOM node and
+ // re-insert it into DOM
+ if(tt_t2t && !tt_aV[COPYCONTENT])
+ tt_UnEl2Tip();
+ tt_iState = 0;
+ tt_over = null;
+ tt_ResetMainDiv();
+ if(tt_aElt[tt_aElt.length - 1])
+ tt_aElt[tt_aElt.length - 1].style.display = "none";
+ }
+}
+function tt_GetElt(id)
+{
+ return(document.getElementById ? document.getElementById(id)
+ : document.all ? document.all[id]
+ : null);
+}
+function tt_GetDivW(el)
+{
+ return(el ? (el.offsetWidth || el.style.pixelWidth || 0) : 0);
+}
+function tt_GetDivH(el)
+{
+ return(el ? (el.offsetHeight || el.style.pixelHeight || 0) : 0);
+}
+function tt_GetScrollX()
+{
+ return(window.pageXOffset || (tt_db ? (tt_db.scrollLeft || 0) : 0));
+}
+function tt_GetScrollY()
+{
+ return(window.pageYOffset || (tt_db ? (tt_db.scrollTop || 0) : 0));
+}
+function tt_GetClientW()
+{
+ return tt_GetWndCliSiz("Width");
+}
+function tt_GetClientH()
+{
+ return tt_GetWndCliSiz("Height");
+}
+function tt_GetEvtX(e)
+{
+ return (e ? ((typeof(e.pageX) != tt_u) ? e.pageX : (e.clientX + tt_GetScrollX())) : 0);
+}
+function tt_GetEvtY(e)
+{
+ return (e ? ((typeof(e.pageY) != tt_u) ? e.pageY : (e.clientY + tt_GetScrollY())) : 0);
+}
+function tt_AddEvtFnc(el, sEvt, PFnc)
+{
+ if(el)
+ {
+ if(el.addEventListener)
+ el.addEventListener(sEvt, PFnc, false);
+ else
+ el.attachEvent("on" + sEvt, PFnc);
+ }
+}
+function tt_RemEvtFnc(el, sEvt, PFnc)
+{
+ if(el)
+ {
+ if(el.removeEventListener)
+ el.removeEventListener(sEvt, PFnc, false);
+ else
+ el.detachEvent("on" + sEvt, PFnc);
+ }
+}
+function tt_GetDad(el)
+{
+ return(el.parentNode || el.parentElement || el.offsetParent);
+}
+function tt_MovDomNode(el, dadFrom, dadTo)
+{
+ if(dadFrom)
+ dadFrom.removeChild(el);
+ if(dadTo)
+ dadTo.appendChild(el);
+}
+
+//====================== PRIVATE ===========================================//
+var tt_aExt = new Array(), // Array of extension objects
+
+tt_db, tt_op, tt_ie, tt_ie56, tt_bBoxOld, // Browser flags
+tt_body,
+tt_ovr_, // HTML element the mouse is currently over
+tt_flagOpa, // Opacity support: 1=IE, 2=Khtml, 3=KHTML, 4=Moz, 5=W3C
+tt_maxPosX, tt_maxPosY,
+tt_iState = 0, // Tooltip active |= 1, shown |= 2, move with mouse |= 4, exclusive |= 8
+tt_opa, // Currently applied opacity
+tt_bJmpVert, tt_bJmpHorz,// Tip temporarily on other side of mouse
+tt_elDeHref, // The tag from which we've removed the href attribute
+// Timer
+tt_tShow = new Number(0), tt_tHide = new Number(0), tt_tDurt = new Number(0),
+tt_tFade = new Number(0), tt_tWaitMov = new Number(0),
+tt_bWait = false,
+tt_u = "undefined";
+
+
+function tt_Init()
+{
+ tt_MkCmdEnum();
+ // Send old browsers instantly to hell
+ if(!tt_Browser() || !tt_MkMainDiv())
+ return;
+ tt_IsW3cBox();
+ tt_OpaSupport();
+ tt_AddEvtFnc(document, "mousemove", tt_Move);
+ // In Debug mode we search for TagToTip() calls in order to notify
+ // the user if they've forgotten to set the TagsToTip config flag
+ if(TagsToTip || tt_Debug)
+ tt_SetOnloadFnc();
+ // Ensure the tip be hidden when the page unloads
+ tt_AddEvtFnc(window, "unload", tt_Hide);
+}
+// Creates command names by translating config variable names to upper case
+function tt_MkCmdEnum()
+{
+ var n = 0;
+ for(var i in config)
+ eval("window." + i.toString().toUpperCase() + " = " + n++);
+ tt_aV.length = n;
+}
+function tt_Browser()
+{
+ var n, nv, n6, w3c;
+
+ n = navigator.userAgent.toLowerCase(),
+ nv = navigator.appVersion;
+ tt_op = (document.defaultView && typeof(eval("w" + "indow" + "." + "o" + "p" + "er" + "a")) != tt_u);
+ tt_ie = n.indexOf("msie") != -1 && document.all && !tt_op;
+ if(tt_ie)
+ {
+ var ieOld = (!document.compatMode || document.compatMode == "BackCompat");
+ tt_db = !ieOld ? document.documentElement : (document.body || null);
+ if(tt_db)
+ tt_ie56 = parseFloat(nv.substring(nv.indexOf("MSIE") + 5)) >= 5.5
+ && typeof document.body.style.maxHeight == tt_u;
+ }
+ else
+ {
+ tt_db = document.documentElement || document.body ||
+ (document.getElementsByTagName ? document.getElementsByTagName("body")[0]
+ : null);
+ if(!tt_op)
+ {
+ n6 = document.defaultView && typeof document.defaultView.getComputedStyle != tt_u;
+ w3c = !n6 && document.getElementById;
+ }
+ }
+ tt_body = (document.getElementsByTagName ? document.getElementsByTagName("body")[0]
+ : (document.body || null));
+ if(tt_ie || n6 || tt_op || w3c)
+ {
+ if(tt_body && tt_db)
+ {
+ if(document.attachEvent || document.addEventListener)
+ return true;
+ }
+ else
+ tt_Err("wz_tooltip.js must be included INSIDE the body section,"
+ + " immediately after the opening <body> tag.", false);
+ }
+ tt_db = null;
+ return false;
+}
+function tt_MkMainDiv()
+{
+ // Create the tooltip DIV
+ if(tt_body.insertAdjacentHTML)
+ tt_body.insertAdjacentHTML("afterBegin", tt_MkMainDivHtm());
+ else if(typeof tt_body.innerHTML != tt_u && document.createElement && tt_body.appendChild)
+ tt_body.appendChild(tt_MkMainDivDom());
+ if(window.tt_GetMainDivRefs /* FireFox Alzheimer */ && tt_GetMainDivRefs())
+ return true;
+ tt_db = null;
+ return false;
+}
+function tt_MkMainDivHtm()
+{
+ return(
+ '<div id="WzTtDiV"></div>' +
+ (tt_ie56 ? ('<iframe id="WzTtIfRm" src="javascript:false" scrolling="no" frameborder="0" style="filter:Alpha(opacity=0);position:absolute;top:0px;left:0px;display:none;"></iframe>')
+ : '')
+ );
+}
+function tt_MkMainDivDom()
+{
+ var el = document.createElement("div");
+ if(el)
+ el.id = "WzTtDiV";
+ return el;
+}
+function tt_GetMainDivRefs()
+{
+ tt_aElt[0] = tt_GetElt("WzTtDiV");
+ if(tt_ie56 && tt_aElt[0])
+ {
+ tt_aElt[tt_aElt.length - 1] = tt_GetElt("WzTtIfRm");
+ if(!tt_aElt[tt_aElt.length - 1])
+ tt_aElt[0] = null;
+ }
+ if(tt_aElt[0])
+ {
+ var css = tt_aElt[0].style;
+
+ css.visibility = "hidden";
+ css.position = "absolute";
+ css.overflow = "hidden";
+ return true;
+ }
+ return false;
+}
+function tt_ResetMainDiv()
+{
+ tt_SetTipPos(0, 0);
+ tt_aElt[0].innerHTML = "";
+ tt_aElt[0].style.width = "0px";
+ tt_h = 0;
+}
+function tt_IsW3cBox()
+{
+ var css = tt_aElt[0].style;
+
+ css.padding = "10px";
+ css.width = "40px";
+ tt_bBoxOld = (tt_GetDivW(tt_aElt[0]) == 40);
+ css.padding = "0px";
+ tt_ResetMainDiv();
+}
+function tt_OpaSupport()
+{
+ var css = tt_body.style;
+
+ tt_flagOpa = (typeof(css.KhtmlOpacity) != tt_u) ? 2
+ : (typeof(css.KHTMLOpacity) != tt_u) ? 3
+ : (typeof(css.MozOpacity) != tt_u) ? 4
+ : (typeof(css.opacity) != tt_u) ? 5
+ : (typeof(css.filter) != tt_u) ? 1
+ : 0;
+}
+// Ported from http://dean.edwards.name/weblog/2006/06/again/
+// (Dean Edwards et al.)
+function tt_SetOnloadFnc()
+{
+ tt_AddEvtFnc(document, "DOMContentLoaded", tt_HideSrcTags);
+ tt_AddEvtFnc(window, "load", tt_HideSrcTags);
+ if(tt_body.attachEvent)
+ tt_body.attachEvent("onreadystatechange",
+ function() {
+ if(tt_body.readyState == "complete")
+ tt_HideSrcTags();
+ } );
+ if(/WebKit|KHTML/i.test(navigator.userAgent))
+ {
+ var t = setInterval(function() {
+ if(/loaded|complete/.test(document.readyState))
+ {
+ clearInterval(t);
+ tt_HideSrcTags();
+ }
+ }, 10);
+ }
+}
+function tt_HideSrcTags()
+{
+ if(!window.tt_HideSrcTags || window.tt_HideSrcTags.done)
+ return;
+ window.tt_HideSrcTags.done = true;
+ if(!tt_HideSrcTagsRecurs(tt_body))
+ tt_Err("There are HTML elements to be converted to tooltips.\nIf you"
+ + " want these HTML elements to be automatically hidden, you"
+ + " must edit wz_tooltip.js, and set TagsToTip in the global"
+ + " tooltip configuration to true.", true);
+}
+function tt_HideSrcTagsRecurs(dad)
+{
+ var ovr, asT2t;
+ // Walk the DOM tree for tags that have an onmouseover or onclick attribute
+ // containing a TagToTip('...') call.
+ // (.childNodes first since .children is bugous in Safari)
+ var a = dad.childNodes || dad.children || null;
+
+ for(var i = a ? a.length : 0; i;)
+ {--i;
+ if(!tt_HideSrcTagsRecurs(a[i]))
+ return false;
+ ovr = a[i].getAttribute ? (a[i].getAttribute("onmouseover") || a[i].getAttribute("onclick"))
+ : (typeof a[i].onmouseover == "function") ? (a[i].onmouseover || a[i].onclick)
+ : null;
+ if(ovr)
+ {
+ asT2t = ovr.toString().match(/TagToTip\s*\(\s*'[^'.]+'\s*[\),]/);
+ if(asT2t && asT2t.length)
+ {
+ if(!tt_HideSrcTag(asT2t[0]))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+function tt_HideSrcTag(sT2t)
+{
+ var id, el;
+
+ // The ID passed to the found TagToTip() call identifies an HTML element
+ // to be converted to a tooltip, so hide that element
+ id = sT2t.replace(/.+'([^'.]+)'.+/, "$1");
+ el = tt_GetElt(id);
+ if(el)
+ {
+ if(tt_Debug && !TagsToTip)
+ return false;
+ else
+ el.style.display = "none";
+ }
+ else
+ tt_Err("Invalid ID\n'" + id + "'\npassed to TagToTip()."
+ + " There exists no HTML element with that ID.", true);
+ return true;
+}
+function tt_Tip(arg, t2t)
+{
+ if(!tt_db || (tt_iState & 0x8))
+ return;
+ if(tt_iState)
+ tt_Hide();
+ if(!tt_Enabled)
+ return;
+ tt_t2t = t2t;
+ if(!tt_ReadCmds(arg))
+ return;
+ tt_iState = 0x1 | 0x4;
+ tt_AdaptConfig1();
+ tt_MkTipContent(arg);
+ tt_MkTipSubDivs();
+ tt_FormatTip();
+ tt_bJmpVert = false;
+ tt_bJmpHorz = false;
+ tt_maxPosX = tt_GetClientW() + tt_GetScrollX() - tt_w - 1;
+ tt_maxPosY = tt_GetClientH() + tt_GetScrollY() - tt_h - 1;
+ tt_AdaptConfig2();
+ // Ensure the tip be shown and positioned before the first onmousemove
+ tt_OverInit();
+ tt_ShowInit();
+ tt_Move();
+}
+function tt_ReadCmds(a)
+{
+ var i;
+
+ // First load the global config values, to initialize also values
+ // for which no command is passed
+ i = 0;
+ for(var j in config)
+ tt_aV[i++] = config[j];
+ // Then replace each cached config value for which a command is
+ // passed (ensure the # of command args plus value args be even)
+ if(a.length & 1)
+ {
+ for(i = a.length - 1; i > 0; i -= 2)
+ tt_aV[a[i - 1]] = a[i];
+ return true;
+ }
+ tt_Err("Incorrect call of Tip() or TagToTip().\n"
+ + "Each command must be followed by a value.", true);
+ return false;
+}
+function tt_AdaptConfig1()
+{
+ tt_ExtCallFncs(0, "LoadConfig");
+ // Inherit unspecified title formattings from body
+ if(!tt_aV[TITLEBGCOLOR].length)
+ tt_aV[TITLEBGCOLOR] = tt_aV[BORDERCOLOR];
+ if(!tt_aV[TITLEFONTCOLOR].length)
+ tt_aV[TITLEFONTCOLOR] = tt_aV[BGCOLOR];
+ if(!tt_aV[TITLEFONTFACE].length)
+ tt_aV[TITLEFONTFACE] = tt_aV[FONTFACE];
+ if(!tt_aV[TITLEFONTSIZE].length)
+ tt_aV[TITLEFONTSIZE] = tt_aV[FONTSIZE];
+ if(tt_aV[CLOSEBTN])
+ {
+ // Use title colours for non-specified closebutton colours
+ if(!tt_aV[CLOSEBTNCOLORS])
+ tt_aV[CLOSEBTNCOLORS] = new Array("", "", "", "");
+ for(var i = 4; i;)
+ {--i;
+ if(!tt_aV[CLOSEBTNCOLORS][i].length)
+ tt_aV[CLOSEBTNCOLORS][i] = (i & 1) ? tt_aV[TITLEFONTCOLOR] : tt_aV[TITLEBGCOLOR];
+ }
+ // Enforce titlebar be shown
+ if(!tt_aV[TITLE].length)
+ tt_aV[TITLE] = " ";
+ }
+ // Circumvents broken display of images and fade-in flicker in Geckos < 1.8
+ if(tt_aV[OPACITY] == 100 && typeof tt_aElt[0].style.MozOpacity != tt_u && !Array.every)
+ tt_aV[OPACITY] = 99;
+ // Smartly shorten the delay for fade-in tooltips
+ if(tt_aV[FADEIN] && tt_flagOpa && tt_aV[DELAY] > 100)
+ tt_aV[DELAY] = Math.max(tt_aV[DELAY] - tt_aV[FADEIN], 100);
+}
+function tt_AdaptConfig2()
+{
+ if(tt_aV[CENTERMOUSE])
+ {
+ tt_aV[OFFSETX] -= ((tt_w - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0)) >> 1);
+ tt_aV[JUMPHORZ] = false;
+ }
+}
+// Expose content globally so extensions can modify it
+function tt_MkTipContent(a)
+{
+ if(tt_t2t)
+ {
+ if(tt_aV[COPYCONTENT])
+ tt_sContent = tt_t2t.innerHTML;
+ else
+ tt_sContent = "";
+ }
+ else
+ tt_sContent = a[0];
+ tt_ExtCallFncs(0, "CreateContentString");
+}
+function tt_MkTipSubDivs()
+{
+ var sCss = 'position:relative;margin:0px;padding:0px;border-width:0px;left:0px;top:0px;line-height:normal;width:auto;',
+ sTbTrTd = ' cellspacing="0" cellpadding="0" border="0" style="' + sCss + '"><tbody style="' + sCss + '"><tr><td ';
+
+ tt_aElt[0].style.width = tt_GetClientW() + "px";
+ tt_aElt[0].innerHTML =
+ (''
+ + (tt_aV[TITLE].length ?
+ ('<div id="WzTiTl" style="position:relative;z-index:1;">'
+ + '<table id="WzTiTlTb"' + sTbTrTd + 'id="WzTiTlI" style="' + sCss + '">'
+ + tt_aV[TITLE]
+ + '</td>'
+ + (tt_aV[CLOSEBTN] ?
+ ('<td align="right" style="' + sCss
+ + 'text-align:right;">'
+ + '<span id="WzClOsE" style="position:relative;left:2px;padding-left:2px;padding-right:2px;'
+ + 'cursor:' + (tt_ie ? 'hand' : 'pointer')
+ + ';" onmouseover="tt_OnCloseBtnOver(1)" onmouseout="tt_OnCloseBtnOver(0)" onclick="tt_HideInit()">'
+ + tt_aV[CLOSEBTNTEXT]
+ + '</span></td>')
+ : '')
+ + '</tr></tbody></table></div>')
+ : '')
+ + '<div id="WzBoDy" style="position:relative;z-index:0;">'
+ + '<table' + sTbTrTd + 'id="WzBoDyI" style="' + sCss + '">'
+ + tt_sContent
+ + '</td></tr></tbody></table></div>'
+ + (tt_aV[SHADOW]
+ ? ('<div id="WzTtShDwR" style="position:absolute;overflow:hidden;"></div>'
+ + '<div id="WzTtShDwB" style="position:relative;overflow:hidden;"></div>')
+ : '')
+ );
+ tt_GetSubDivRefs();
+ // Convert DOM node to tip
+ if(tt_t2t && !tt_aV[COPYCONTENT])
+ tt_El2Tip();
+ tt_ExtCallFncs(0, "SubDivsCreated");
+}
+function tt_GetSubDivRefs()
+{
+ var aId = new Array("WzTiTl", "WzTiTlTb", "WzTiTlI", "WzClOsE", "WzBoDy", "WzBoDyI", "WzTtShDwB", "WzTtShDwR");
+
+ for(var i = aId.length; i; --i)
+ tt_aElt[i] = tt_GetElt(aId[i - 1]);
+}
+function tt_FormatTip()
+{
+ var css, w, h, pad = tt_aV[PADDING], padT, wBrd = tt_aV[BORDERWIDTH],
+ iOffY, iOffSh, iAdd = (pad + wBrd) << 1;
+
+ //--------- Title DIV ----------
+ if(tt_aV[TITLE].length)
+ {
+ padT = tt_aV[TITLEPADDING];
+ css = tt_aElt[1].style;
+ css.background = tt_aV[TITLEBGCOLOR];
+ css.paddingTop = css.paddingBottom = padT + "px";
+ css.paddingLeft = css.paddingRight = (padT + 2) + "px";
+ css = tt_aElt[3].style;
+ css.color = tt_aV[TITLEFONTCOLOR];
+ if(tt_aV[WIDTH] == -1)
+ css.whiteSpace = "nowrap";
+ css.fontFamily = tt_aV[TITLEFONTFACE];
+ css.fontSize = tt_aV[TITLEFONTSIZE];
+ css.fontWeight = "bold";
+ css.textAlign = tt_aV[TITLEALIGN];
+ // Close button DIV
+ if(tt_aElt[4])
+ {
+ css = tt_aElt[4].style;
+ css.background = tt_aV[CLOSEBTNCOLORS][0];
+ css.color = tt_aV[CLOSEBTNCOLORS][1];
+ css.fontFamily = tt_aV[TITLEFONTFACE];
+ css.fontSize = tt_aV[TITLEFONTSIZE];
+ css.fontWeight = "bold";
+ }
+ if(tt_aV[WIDTH] > 0)
+ tt_w = tt_aV[WIDTH];
+ else
+ {
+ tt_w = tt_GetDivW(tt_aElt[3]) + tt_GetDivW(tt_aElt[4]);
+ // Some spacing between title DIV and closebutton
+ if(tt_aElt[4])
+ tt_w += pad;
+ // Restrict auto width to max width
+ if(tt_aV[WIDTH] < -1 && tt_w > -tt_aV[WIDTH])
+ tt_w = -tt_aV[WIDTH];
+ }
+ // Ensure the top border of the body DIV be covered by the title DIV
+ iOffY = -wBrd;
+ }
+ else
+ {
+ tt_w = 0;
+ iOffY = 0;
+ }
+
+ //-------- Body DIV ------------
+ css = tt_aElt[5].style;
+ css.top = iOffY + "px";
+ if(wBrd)
+ {
+ css.borderColor = tt_aV[BORDERCOLOR];
+ css.borderStyle = tt_aV[BORDERSTYLE];
+ css.borderWidth = wBrd + "px";
+ }
+ if(tt_aV[BGCOLOR].length)
+ css.background = tt_aV[BGCOLOR];
+ if(tt_aV[BGIMG].length)
+ css.backgroundImage = "url(" + tt_aV[BGIMG] + ")";
+ css.padding = pad + "px";
+ css.textAlign = tt_aV[TEXTALIGN];
+ if(tt_aV[HEIGHT])
+ {
+ css.overflow = "auto";
+ if(tt_aV[HEIGHT] > 0)
+ css.height = (tt_aV[HEIGHT] + iAdd) + "px";
+ else
+ tt_h = iAdd - tt_aV[HEIGHT];
+ }
+ // TD inside body DIV
+ css = tt_aElt[6].style;
+ css.color = tt_aV[FONTCOLOR];
+ css.fontFamily = tt_aV[FONTFACE];
+ css.fontSize = tt_aV[FONTSIZE];
+ css.fontWeight = tt_aV[FONTWEIGHT];
+ css.textAlign = tt_aV[TEXTALIGN];
+ if(tt_aV[WIDTH] > 0)
+ w = tt_aV[WIDTH];
+ // Width like title (if existent)
+ else if(tt_aV[WIDTH] == -1 && tt_w)
+ w = tt_w;
+ else
+ {
+ // Measure width of the body's inner TD, as some browsers would expand
+ // the container and outer body DIV to 100%
+ w = tt_GetDivW(tt_aElt[6]);
+ // Restrict auto width to max width
+ if(tt_aV[WIDTH] < -1 && w > -tt_aV[WIDTH])
+ w = -tt_aV[WIDTH];
+ }
+ if(w > tt_w)
+ tt_w = w;
+ tt_w += iAdd;
+
+ //--------- Shadow DIVs ------------
+ if(tt_aV[SHADOW])
+ {
+ tt_w += tt_aV[SHADOWWIDTH];
+ iOffSh = Math.floor((tt_aV[SHADOWWIDTH] * 4) / 3);
+ // Bottom shadow
+ css = tt_aElt[7].style;
+ css.top = iOffY + "px";
+ css.left = iOffSh + "px";
+ css.width = (tt_w - iOffSh - tt_aV[SHADOWWIDTH]) + "px";
+ css.height = tt_aV[SHADOWWIDTH] + "px";
+ css.background = tt_aV[SHADOWCOLOR];
+ // Right shadow
+ css = tt_aElt[8].style;
+ css.top = iOffSh + "px";
+ css.left = (tt_w - tt_aV[SHADOWWIDTH]) + "px";
+ css.width = tt_aV[SHADOWWIDTH] + "px";
+ css.background = tt_aV[SHADOWCOLOR];
+ }
+ else
+ iOffSh = 0;
+
+ //-------- Container DIV -------
+ tt_SetTipOpa(tt_aV[FADEIN] ? 0 : tt_aV[OPACITY]);
+ tt_FixSize(iOffY, iOffSh);
+}
+// Fixate the size so it can't dynamically change while the tooltip is moving.
+function tt_FixSize(iOffY, iOffSh)
+{
+ var wIn, wOut, h, add, pad = tt_aV[PADDING], wBrd = tt_aV[BORDERWIDTH], i;
+
+ tt_aElt[0].style.width = tt_w + "px";
+ tt_aElt[0].style.pixelWidth = tt_w;
+ wOut = tt_w - ((tt_aV[SHADOW]) ? tt_aV[SHADOWWIDTH] : 0);
+ // Body
+ wIn = wOut;
+ if(!tt_bBoxOld)
+ wIn -= (pad + wBrd) << 1;
+ tt_aElt[5].style.width = wIn + "px";
+ // Title
+ if(tt_aElt[1])
+ {
+ wIn = wOut - ((tt_aV[TITLEPADDING] + 2) << 1);
+ if(!tt_bBoxOld)
+ wOut = wIn;
+ tt_aElt[1].style.width = wOut + "px";
+ tt_aElt[2].style.width = wIn + "px";
+ }
+ // Max height specified
+ if(tt_h)
+ {
+ h = tt_GetDivH(tt_aElt[5]);
+ if(h > tt_h)
+ {
+ if(!tt_bBoxOld)
+ tt_h -= (pad + wBrd) << 1;
+ tt_aElt[5].style.height = tt_h + "px";
+ }
+ }
+ tt_h = tt_GetDivH(tt_aElt[0]) + iOffY;
+ // Right shadow
+ if(tt_aElt[8])
+ tt_aElt[8].style.height = (tt_h - iOffSh) + "px";
+ i = tt_aElt.length - 1;
+ if(tt_aElt[i])
+ {
+ tt_aElt[i].style.width = tt_w + "px";
+ tt_aElt[i].style.height = tt_h + "px";
+ }
+}
+function tt_DeAlt(el)
+{
+ var aKid;
+
+ if(el)
+ {
+ if(el.alt)
+ el.alt = "";
+ if(el.title)
+ el.title = "";
+ aKid = el.childNodes || el.children || null;
+ if(aKid)
+ {
+ for(var i = aKid.length; i;)
+ tt_DeAlt(aKid[--i]);
+ }
+ }
+}
+// This hack removes the native tooltips over links in Opera
+function tt_OpDeHref(el)
+{
+ if(!tt_op)
+ return;
+ if(tt_elDeHref)
+ tt_OpReHref();
+ while(el)
+ {
+ if(el.hasAttribute && el.hasAttribute("href"))
+ {
+ el.t_href = el.getAttribute("href");
+ el.t_stats = window.status;
+ el.removeAttribute("href");
+ el.style.cursor = "hand";
+ tt_AddEvtFnc(el, "mousedown", tt_OpReHref);
+ window.status = el.t_href;
+ tt_elDeHref = el;
+ break;
+ }
+ el = tt_GetDad(el);
+ }
+}
+function tt_OpReHref()
+{
+ if(tt_elDeHref)
+ {
+ tt_elDeHref.setAttribute("href", tt_elDeHref.t_href);
+ tt_RemEvtFnc(tt_elDeHref, "mousedown", tt_OpReHref);
+ window.status = tt_elDeHref.t_stats;
+ tt_elDeHref = null;
+ }
+}
+function tt_El2Tip()
+{
+ var css = tt_t2t.style;
+
+ // Store previous positioning
+ tt_t2t.t_cp = css.position;
+ tt_t2t.t_cl = css.left;
+ tt_t2t.t_ct = css.top;
+ tt_t2t.t_cd = css.display;
+ // Store the tag's parent element so we can restore that DOM branch
+ // when the tooltip is being hidden
+ tt_t2tDad = tt_GetDad(tt_t2t);
+ tt_MovDomNode(tt_t2t, tt_t2tDad, tt_aElt[6]);
+ css.display = "block";
+ css.position = "static";
+ css.left = css.top = css.marginLeft = css.marginTop = "0px";
+}
+function tt_UnEl2Tip()
+{
+ // Restore positioning and display
+ var css = tt_t2t.style;
+
+ css.display = tt_t2t.t_cd;
+ tt_MovDomNode(tt_t2t, tt_GetDad(tt_t2t), tt_t2tDad);
+ css.position = tt_t2t.t_cp;
+ css.left = tt_t2t.t_cl;
+ css.top = tt_t2t.t_ct;
+ tt_t2tDad = null;
+}
+function tt_OverInit()
+{
+ if(window.event)
+ tt_over = window.event.target || window.event.srcElement;
+ else
+ tt_over = tt_ovr_;
+ tt_DeAlt(tt_over);
+ tt_OpDeHref(tt_over);
+}
+function tt_ShowInit()
+{
+ tt_tShow.Timer("tt_Show()", tt_aV[DELAY], true);
+ if(tt_aV[CLICKCLOSE] || tt_aV[CLICKSTICKY])
+ tt_AddEvtFnc(document, "mouseup", tt_OnLClick);
+}
+function tt_Show()
+{
+ var css = tt_aElt[0].style;
+
+ // Override the z-index of the topmost wz_dragdrop.js D&D item
+ css.zIndex = Math.max((window.dd && dd.z) ? (dd.z + 2) : 0, 1010);
+ if(tt_aV[STICKY] || !tt_aV[FOLLOWMOUSE])
+ tt_iState &= ~0x4;
+ if(tt_aV[EXCLUSIVE])
+ tt_iState |= 0x8;
+ if(tt_aV[DURATION] > 0)
+ tt_tDurt.Timer("tt_HideInit()", tt_aV[DURATION], true);
+ tt_ExtCallFncs(0, "Show")
+ css.visibility = "visible";
+ tt_iState |= 0x2;
+ if(tt_aV[FADEIN])
+ tt_Fade(0, 0, tt_aV[OPACITY], Math.round(tt_aV[FADEIN] / tt_aV[FADEINTERVAL]));
+ tt_ShowIfrm();
+}
+function tt_ShowIfrm()
+{
+ if(tt_ie56)
+ {
+ var ifrm = tt_aElt[tt_aElt.length - 1];
+ if(ifrm)
+ {
+ var css = ifrm.style;
+ css.zIndex = tt_aElt[0].style.zIndex - 1;
+ css.display = "block";
+ }
+ }
+}
+function tt_Move(e)
+{
+ if(e)
+ tt_ovr_ = e.target || e.srcElement;
+ e = e || window.event;
+ if(e)
+ {
+ tt_musX = tt_GetEvtX(e);
+ tt_musY = tt_GetEvtY(e);
+ }
+ if(tt_iState & 0x4)
+ {
+ // Prevent jam of mousemove events
+ if(!tt_op && !tt_ie)
+ {
+ if(tt_bWait)
+ return;
+ tt_bWait = true;
+ tt_tWaitMov.Timer("tt_bWait = false;", 1, true);
+ }
+ if(tt_aV[FIX])
+ {
+ tt_iState &= ~0x4;
+ tt_PosFix();
+ }
+ else if(!tt_ExtCallFncs(e, "MoveBefore"))
+ tt_SetTipPos(tt_Pos(0), tt_Pos(1));
+ tt_ExtCallFncs([tt_musX, tt_musY], "MoveAfter")
+ }
+}
+function tt_Pos(iDim)
+{
+ var iX, bJmpMod, cmdAlt, cmdOff, cx, iMax, iScrl, iMus, bJmp;
+
+ // Map values according to dimension to calculate
+ if(iDim)
+ {
+ bJmpMod = tt_aV[JUMPVERT];
+ cmdAlt = ABOVE;
+ cmdOff = OFFSETY;
+ cx = tt_h;
+ iMax = tt_maxPosY;
+ iScrl = tt_GetScrollY();
+ iMus = tt_musY;
+ bJmp = tt_bJmpVert;
+ }
+ else
+ {
+ bJmpMod = tt_aV[JUMPHORZ];
+ cmdAlt = LEFT;
+ cmdOff = OFFSETX;
+ cx = tt_w;
+ iMax = tt_maxPosX;
+ iScrl = tt_GetScrollX();
+ iMus = tt_musX;
+ bJmp = tt_bJmpHorz;
+ }
+ if(bJmpMod)
+ {
+ if(tt_aV[cmdAlt] && (!bJmp || tt_CalcPosAlt(iDim) >= iScrl + 16))
+ iX = tt_PosAlt(iDim);
+ else if(!tt_aV[cmdAlt] && bJmp && tt_CalcPosDef(iDim) > iMax - 16)
+ iX = tt_PosAlt(iDim);
+ else
+ iX = tt_PosDef(iDim);
+ }
+ else
+ {
+ iX = iMus;
+ if(tt_aV[cmdAlt])
+ iX -= cx + tt_aV[cmdOff] - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0);
+ else
+ iX += tt_aV[cmdOff];
+ }
+ // Prevent tip from extending past clientarea boundary
+ if(iX > iMax)
+ iX = bJmpMod ? tt_PosAlt(iDim) : iMax;
+ // In case of insufficient space on both sides, ensure the left/upper part
+ // of the tip be visible
+ if(iX < iScrl)
+ iX = bJmpMod ? tt_PosDef(iDim) : iScrl;
+ return iX;
+}
+function tt_PosDef(iDim)
+{
+ if(iDim)
+ tt_bJmpVert = tt_aV[ABOVE];
+ else
+ tt_bJmpHorz = tt_aV[LEFT];
+ return tt_CalcPosDef(iDim);
+}
+function tt_PosAlt(iDim)
+{
+ if(iDim)
+ tt_bJmpVert = !tt_aV[ABOVE];
+ else
+ tt_bJmpHorz = !tt_aV[LEFT];
+ return tt_CalcPosAlt(iDim);
+}
+function tt_CalcPosDef(iDim)
+{
+ return iDim ? (tt_musY + tt_aV[OFFSETY]) : (tt_musX + tt_aV[OFFSETX]);
+}
+function tt_CalcPosAlt(iDim)
+{
+ var cmdOff = iDim ? OFFSETY : OFFSETX;
+ var dx = tt_aV[cmdOff] - (tt_aV[SHADOW] ? tt_aV[SHADOWWIDTH] : 0);
+ if(tt_aV[cmdOff] > 0 && dx <= 0)
+ dx = 1;
+ return((iDim ? (tt_musY - tt_h) : (tt_musX - tt_w)) - dx);
+}
+function tt_PosFix()
+{
+ var iX, iY;
+
+ if(typeof(tt_aV[FIX][0]) == "number")
+ {
+ iX = tt_aV[FIX][0];
+ iY = tt_aV[FIX][1];
+ }
+ else
+ {
+ if(typeof(tt_aV[FIX][0]) == "string")
+ el = tt_GetElt(tt_aV[FIX][0]);
+ // First slot in array is direct reference to HTML element
+ else
+ el = tt_aV[FIX][0];
+ iX = tt_aV[FIX][1];
+ iY = tt_aV[FIX][2];
+ // By default, vert pos is related to bottom edge of HTML element
+ if(!tt_aV[ABOVE] && el)
+ iY += tt_GetDivH(el);
+ for(; el; el = el.offsetParent)
+ {
+ iX += el.offsetLeft || 0;
+ iY += el.offsetTop || 0;
+ }
+ }
+ // For a fixed tip positioned above the mouse, use the bottom edge as anchor
+ // (recommended by Christophe Rebeschini, 31.1.2008)
+ if(tt_aV[ABOVE])
+ iY -= tt_h;
+ tt_SetTipPos(iX, iY);
+}
+function tt_Fade(a, now, z, n)
+{
+ if(n)
+ {
+ now += Math.round((z - now) / n);
+ if((z > a) ? (now >= z) : (now <= z))
+ now = z;
+ else
+ tt_tFade.Timer(
+ "tt_Fade("
+ + a + "," + now + "," + z + "," + (n - 1)
+ + ")",
+ tt_aV[FADEINTERVAL],
+ true
+ );
+ }
+ now ? tt_SetTipOpa(now) : tt_Hide();
+}
+function tt_SetTipOpa(opa)
+{
+ // To circumvent the opacity nesting flaws of IE, we set the opacity
+ // for each sub-DIV separately, rather than for the container DIV.
+ tt_SetOpa(tt_aElt[5], opa);
+ if(tt_aElt[1])
+ tt_SetOpa(tt_aElt[1], opa);
+ if(tt_aV[SHADOW])
+ {
+ opa = Math.round(opa * 0.8);
+ tt_SetOpa(tt_aElt[7], opa);
+ tt_SetOpa(tt_aElt[8], opa);
+ }
+}
+function tt_OnCloseBtnOver(iOver)
+{
+ var css = tt_aElt[4].style;
+
+ iOver <<= 1;
+ css.background = tt_aV[CLOSEBTNCOLORS][iOver];
+ css.color = tt_aV[CLOSEBTNCOLORS][iOver + 1];
+}
+function tt_OnLClick(e)
+{
+ // Ignore right-clicks
+ e = e || window.event;
+ if(!((e.button && e.button & 2) || (e.which && e.which == 3)))
+ {
+ if(tt_aV[CLICKSTICKY] && (tt_iState & 0x4))
+ {
+ tt_aV[STICKY] = true;
+ tt_iState &= ~0x4;
+ }
+ else if(tt_aV[CLICKCLOSE])
+ tt_HideInit();
+ }
+}
+function tt_Int(x)
+{
+ var y;
+
+ return(isNaN(y = parseInt(x)) ? 0 : y);
+}
+Number.prototype.Timer = function(s, iT, bUrge)
+{
+ if(!this.value || bUrge)
+ this.value = window.setTimeout(s, iT);
+}
+Number.prototype.EndTimer = function()
+{
+ if(this.value)
+ {
+ window.clearTimeout(this.value);
+ this.value = 0;
+ }
+}
+function tt_GetWndCliSiz(s)
+{
+ var db, y = window["inner" + s], sC = "client" + s, sN = "number";
+ if(typeof y == sN)
+ {
+ var y2;
+ return(
+ // Gecko or Opera with scrollbar
+ // ... quirks mode
+ ((db = document.body) && typeof(y2 = db[sC]) == sN && y2 && y2 <= y) ? y2
+ // ... strict mode
+ : ((db = document.documentElement) && typeof(y2 = db[sC]) == sN && y2 && y2 <= y) ? y2
+ // No scrollbar, or clientarea size == 0, or other browser (KHTML etc.)
+ : y
+ );
+ }
+ // IE
+ return(
+ // document.documentElement.client+s functional, returns > 0
+ ((db = document.documentElement) && (y = db[sC])) ? y
+ // ... not functional, in which case document.body.client+s
+ // is the clientarea size, fortunately
+ : document.body[sC]
+ );
+}
+function tt_SetOpa(el, opa)
+{
+ var css = el.style;
+
+ tt_opa = opa;
+ if(tt_flagOpa == 1)
+ {
+ if(opa < 100)
+ {
+ // Hacks for bugs of IE:
+ // 1.) Once a CSS filter has been applied, fonts are no longer
+ // anti-aliased, so we store the previous 'non-filter' to be
+ // able to restore it
+ if(typeof(el.filtNo) == tt_u)
+ el.filtNo = css.filter;
+ // 2.) A DIV cannot be made visible in a single step if an
+ // opacity < 100 has been applied while the DIV was hidden
+ var bVis = css.visibility != "hidden";
+ // 3.) In IE6, applying an opacity < 100 has no effect if the
+ // element has no layout (position, size, zoom, ...)
+ css.zoom = "100%";
+ if(!bVis)
+ css.visibility = "visible";
+ css.filter = "alpha(opacity=" + opa + ")";
+ if(!bVis)
+ css.visibility = "hidden";
+ }
+ else if(typeof(el.filtNo) != tt_u)
+ // Restore 'non-filter'
+ css.filter = el.filtNo;
+ }
+ else
+ {
+ opa /= 100.0;
+ switch(tt_flagOpa)
+ {
+ case 2:
+ css.KhtmlOpacity = opa; break;
+ case 3:
+ css.KHTMLOpacity = opa; break;
+ case 4:
+ css.MozOpacity = opa; break;
+ case 5:
+ css.opacity = opa; break;
+ }
+ }
+}
+function tt_Err(sErr, bIfDebug)
+{
+ if(tt_Debug || !bIfDebug)
+ alert("Tooltip Script Error Message:\n\n" + sErr);
+}
+
+//============ EXTENSION (PLUGIN) MANAGER ===============//
+function tt_ExtCmdEnum()
+{
+ var s;
+
+ // Add new command(s) to the commands enum
+ for(var i in config)
+ {
+ s = "window." + i.toString().toUpperCase();
+ if(eval("typeof(" + s + ") == tt_u"))
+ {
+ eval(s + " = " + tt_aV.length);
+ tt_aV[tt_aV.length] = null;
+ }
+ }
+}
+function tt_ExtCallFncs(arg, sFnc)
+{
+ var b = false;
+ for(var i = tt_aExt.length; i;)
+ {--i;
+ var fnc = tt_aExt[i]["On" + sFnc];
+ // Call the method the extension has defined for this event
+ if(fnc && fnc(arg))
+ b = true;
+ }
+ return b;
+}
+
+tt_Init();
diff --git a/subsonic-main/src/main/webapp/style/barents.css b/subsonic-main/src/main/webapp/style/barents.css
new file mode 100644
index 00000000..75fd3323
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/barents.css
@@ -0,0 +1,30 @@
+/*
+ * CSS styleshet for the "Barents Sea" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "hd.css";
+
+body {
+ margin:0.75em;
+ font-size: 9pt;
+}
+
+.detail, .albumComment, .log {
+ font-size: 8pt;
+}
+
+/* Font 1 */
+body, h2, form, label, table, a {
+ font-family: verdana, arial, sans-serif;
+}
+
+/* Font 2 */
+h1, h1 a, input, select {
+ font-family: arial, sans-serif;
+}
+
+h1, h2, th {
+ font-weight:bold;
+}
diff --git a/subsonic-main/src/main/webapp/style/black.css b/subsonic-main/src/main/webapp/style/black.css
new file mode 100644
index 00000000..a28d8d93
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/black.css
@@ -0,0 +1,17 @@
+/*
+ * CSS styleshet for the "Back In Black" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "midnight.css";
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #333333
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: black
+}
diff --git a/subsonic-main/src/main/webapp/style/buuftheme.css b/subsonic-main/src/main/webapp/style/buuftheme.css
new file mode 100644
index 00000000..5f84f239
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/buuftheme.css
@@ -0,0 +1,120 @@
+/*
+ * CSS styleshet for the "Cool and Clean" theme.
+ *
+ * Author: Dan Eriksen (dan.o.eriksen[at]gmail.com)
+ *
+ * Icons used are released under Creative Commons.
+ * Heading icons are part of the Albook icon pack created by Laurent Baumann (http://lbaumann.com/)
+ * Main theme icon part of itunes icon pack created by Michael Flarup (http://pixelresort.com)
+ *
+ *
+ * Edited for BUUF Theme by Fractal Systems
+ * BUUF artwork: Based on icons by Paul Davey aka Mattahan. All rights reserved.
+ */
+
+@import "default.css";
+
+/* The primary background colour, light gray. Images used to force background on left frame headings and playlist. */
+.bgcolor1 {
+ background-image:url( "../icons/buuftheme/list_heading.png" );
+ background-repeat:repeat;
+ background-color: #3C2D22;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #4C4039;
+}
+
+/* Background for selected headers. */
+.headerSelected {
+ background-image:url( "../icons/buuftheme/home_hover.png" );
+ background-repeat:repeat;
+ color: #333333;
+}
+
+
+/* Foreground colour used for h1, h2, b, tr, details and albumcomments. */
+h1, h2, b, tr, .detail, .albumComment {
+ color: #ffAE00;
+}
+
+/* Table sizing */
+table {
+ font-size: 100%;
+ line-height: 130%;
+ padding: 0;
+ border: 0;
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-image:url( "../icons/buuftheme/background_main.png" );
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-color: #4C4039;
+ background-position:right;
+}
+
+/* Right frame background. */
+.rightframe {
+ background-color: #4C4039;
+ background-image:none;
+}
+
+/* Back image */
+.back {
+ background-image:url( "../icons/buuftheme/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/buuftheme/forward.png" );
+}
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #ffAE00;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: none;
+ color: #169F30;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #AA1F15;
+}
+
+/* The primary foreground colour. */
+body {
+ background-color: #D1D9E1;
+ scrollbar-arrow-color: #636363;
+ scrollbar-track-color: #D1D9E1;
+}
+
+/* The album comments. */
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+}
+
+/* The log. */
+.log {
+ font-weight: bold;
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+}
+
+/* The help, status & web payer settings tables. */
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+}
+
diff --git a/subsonic-main/src/main/webapp/style/coolandclean.css b/subsonic-main/src/main/webapp/style/coolandclean.css
new file mode 100644
index 00000000..0786b15b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/coolandclean.css
@@ -0,0 +1,116 @@
+/*
+ * CSS styleshet for the "Cool and Clean" theme.
+ *
+ * Author: Dan Eriksen (dan.o.eriksen[at]gmail.com)
+ *
+ * Icons used are released under Creative Commons.
+ * Heading icons are part of the Albook icon pack created by Laurent Baumann (http://lbaumann.com/)
+ * Main theme icon part of itunes icon pack created by Michael Flarup (http://pixelresort.com)
+ */
+
+@import "default.css";
+
+/* The primary background colour, light gray. Images used to force background on left frame headings and playlist. */
+.bgcolor1 {
+ background-image:url( "../icons/coolandclean/list_heading.png" );
+ background-repeat:repeat;
+ background-color: #F7F7F7;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #D1D9E1;
+}
+
+/* Background for selected headers. */
+.headerSelected {
+ background-image:url( "../icons/coolandclean/home_hover.png" );
+ background-repeat:repeat;
+ color: #333333;
+}
+
+
+/* Foreground colour used for h1, h2, b, tr, details and albumcomments. */
+h1, h2, b, tr, .detail, .albumComment {
+ color: #333333;
+}
+
+/* Table sizing */
+table {
+ font-size: 100%;
+ line-height: 130%;
+ padding: 0;
+ border: 0;
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-image:url( "../icons/coolandclean/background_main.png" );
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-color: #D1D9E1;
+ background-position:right;
+}
+
+/* Right frame background. */
+.rightframe {
+ background-color: #D1D9E1;
+ background-image:none;
+}
+
+/* Back image */
+.back {
+ background-image:url( "../icons/coolandclean/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/coolandclean/forward.png" );
+}
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #656569;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: none;
+ color: #333333;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #AA1F15;
+}
+
+/* The primary foreground colour. */
+body {
+ background-color: #D1D9E1;
+ scrollbar-arrow-color: #636363;
+ scrollbar-track-color: #D1D9E1;
+}
+
+/* The album comments. */
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+}
+
+/* The log. */
+.log {
+ font-weight: bold;
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+}
+
+/* The help, status & web payer settings tables. */
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+}
+
diff --git a/subsonic-main/src/main/webapp/style/default.css b/subsonic-main/src/main/webapp/style/default.css
new file mode 100644
index 00000000..50182327
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/default.css
@@ -0,0 +1,245 @@
+/*
+ * CSS styleshet for default theme.
+ *
+ * Note that attributes that are typically changed by theme authors are
+ * placed at the top.
+ *
+ * Author: Sindre Mehus
+ */
+
+/* The primary background color (light blue). */
+.bgcolor1 {
+ background-color: #EFEFEF;
+}
+
+/* The secondary background color (darker blue). */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #DEE3E7
+}
+
+/* Put stuff here if you need to customize any of the frames. */
+.mainframe {
+}
+.topframe {
+}
+.leftframe {
+}
+.rightframe {
+}
+.playlistframe {
+}
+
+/* Background color for selected header. */
+.headerSelected {
+ background-color: lightyellow;
+}
+
+/* Background color for form controls (use default). */
+input, select {
+}
+
+/* Hover color for form controls (use default). */
+input:hover, select:hover {
+}
+
+/* The primary foreground color (black). */
+body {
+ color: black;
+}
+
+/* The secondary foreground color used for h1, details etc (gray). */
+h1, .detail, .albumComment {
+ color: #696969;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #333333;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #006699
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #DD6900
+}
+
+/* Color for warning messages. */
+.warning {
+ color: red;
+}
+
+/* Simple dark border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid black;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #DEE3E7;
+ scrollbar-highlight-color: #FFFFFF;
+ scrollbar-shadow-color: #DEE3E7;
+ scrollbar-3dlight-color: #D1D7DC;
+ scrollbar-arrow-color: #006699;
+ scrollbar-track-color: #EFEFEF;
+ scrollbar-darkshadow-color: #98AAB1;
+}
+
+/* Font 1 */
+body, h2, form, label, table, a {
+ font-family: verdana, arial, sans-serif;
+}
+
+/* Font 2 */
+h1, h1 a, .logo {
+ font-family: arial, sans-serif;
+}
+
+/***************************************************************************************
+ * The rest of the CSS is typically not changed in other themes (but not necessarily so).
+ ***************************************************************************************/
+
+body {
+ padding:0;
+ border:0;
+ margin:0.75em;
+ font-size: 9pt;
+ line-height: 1.5em;
+}
+
+p {
+ padding:0;
+ border:0;
+ margin:0 0 1em 0;
+}
+
+.dense {
+ white-space: nowrap;
+ margin: 0;
+ line-height: 1.3em
+}
+
+h1 {
+ white-space: nowrap;
+ font-size: 140%;
+ padding: 0 0 0.2em 0;
+ border: 0;
+ margin: 0;
+}
+
+h2 {
+ white-space: nowrap;
+ font-size: 100%;
+ margin: 1em 0 0.2em 0;
+}
+
+form {
+ font-size: 100%;
+ line-height: 140%;
+ padding: 0;
+ border: 0;
+ margin: 0;
+}
+
+input, select, textarea {
+ font-size: 9pt;
+}
+
+label {
+ font-size: 100%;
+ line-height: 140%;
+}
+
+table {
+ font-size: 100%;
+ line-height: 140%;
+ padding: 0;
+ border: 0;
+ margin: 0 0 0.4em 0;
+}
+
+/* Table with some white space above it.*/
+table.indent {
+ margin: 1em 0 0.4em 0;
+}
+
+a {
+ font-size: 100%;
+ text-decoration: none
+}
+
+img {
+ border-style: none;
+ border: 0;
+ margin: 0;
+ padding: 0;
+ vertical-align: middle;
+}
+
+.headerSelected {
+ padding: 0.25em;
+}
+
+.detail {
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1.25em;
+}
+
+.warning {
+ white-space: nowrap;
+}
+
+.logo {
+ white-space: nowrap;
+ font-size: 16pt;
+}
+
+.back, .forward {
+ background-position:center left;
+ background-repeat:no-repeat;
+ padding-left: 16px;
+ line-height: 16px;
+}
+
+.back {
+ background-image:url("../icons/back.gif");
+}
+
+.forward {
+ background-image:url("../icons/forward.gif");
+}
+
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+}
+
+.log {
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+}
+
+.checkbox {
+}
+
+/* Table with simple lines between the cells. */
+.ruleTable {
+ border-collapse: collapse;
+}
+
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+}
+
+.ruleTableHeader {
+ font-weight: bold;
+}
diff --git a/subsonic-main/src/main/webapp/style/denim.css b/subsonic-main/src/main/webapp/style/denim.css
new file mode 100644
index 00000000..b273a559
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/denim.css
@@ -0,0 +1,90 @@
+/*
+ * CSS styleshet for the "Denim" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #456993;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #456993;
+ color: #EFE9D9;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #EFE9D9;
+ background-color: #456993;
+ border: 1px solid #EFE9D9;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #EFE9D9;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #EFE9D9;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #456993;
+ background-position:right;
+}
+
+
+/* Back image */
+.back {
+ background-image:url( "../icons/denim/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/denim/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #B6BEC2;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #B6BEC2;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+/* The primary foreground colour. */
+body {
+ color: #EFE9D9;
+ background-color: #DDDDDD;
+}
+
+html {
+ background-color: transparent;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/groove.css b/subsonic-main/src/main/webapp/style/groove.css
new file mode 100644
index 00000000..88ae3e2d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/groove.css
@@ -0,0 +1,102 @@
+/*
+ * CSS styleshet for the "Groove" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ * Moified version of the "Cool and Clean" and "Monochrome Black" themes.
+ * (Cool and Clean created by: Dan Eriksen - dan.o.eriksen[at]gmail.com )
+ * (Monochrome Black created by: David D - ddavis1[at]gmail.com )
+ *
+ *
+ * Icons used are released under Creative Commons.
+ * Heading icons: (http://toffeenut.deviantart.com/art/Black-Neon-Agua-iPhone-Theme-85452072)
+ * Main theme icon part of itunes icon pack created by Michael Flarup (http://pixelresort.com)
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #222;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #222;
+ color: #FFFFFF;
+
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #FFFFFF;
+ background-color: #222;
+ border: 1px solid #eee;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #FFFFFF;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #FFFFFF;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-image:url( "../icons/groove/background_main.png" );
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-color: #222;
+ background-position:right;
+}
+
+
+/* Back image */
+.back {
+ background-image:url( "../icons/groove/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/groove/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #FFFFFF;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #FFFFFF;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+/* The primary foreground colour. */
+body {
+ color: #FFFFFF;
+ background-color: #DDDDDD;
+}
+
+html {
+ background-color: transparent;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/groove_simple.css b/subsonic-main/src/main/webapp/style/groove_simple.css
new file mode 100644
index 00000000..83b3ac98
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/groove_simple.css
@@ -0,0 +1,32 @@
+/*
+ * CSS styleshet for the "Groove (Simple)" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ * Moified version of the "Cool and Clean" and "Monochrome Black" themes.
+ * (Cool and Clean created by: Dan Eriksen - dan.o.eriksen[at]gmail.com )
+ * (Monochrome Black created by: David D - ddavis1[at]gmail.com )
+ *
+ *
+ * Icons used are released under Creative Commons.
+ * Heading icons: (http://toffeenut.deviantart.com/art/Black-Neon-Agua-iPhone-Theme-85452072)
+ * Main theme icon part of itunes icon pack created by Michael Flarup (http://pixelresort.com)
+ */
+
+@import "groove.css";
+
+/* Main frame image */
+.mainframe {
+ background-image:url( "../icons/groove_simple/background_main_blank.png" );
+}
+
+
+/* Back image */
+.back {
+ background-image:url( "../icons/groove/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/groove/forward.png" );
+}
diff --git a/subsonic-main/src/main/webapp/style/hd.css b/subsonic-main/src/main/webapp/style/hd.css
new file mode 100644
index 00000000..3a5c3f7d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hd.css
@@ -0,0 +1,93 @@
+/*
+ * CSS styleshet for the "HD-1080" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "midnight.css";
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #000843;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #00042E
+}
+
+/* Put stuff here if you need to customize the body of the main frame. */
+.mainframe {
+ background-image:url('../icons/hd/background.png');
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-color: #00042E
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ background-color: #CFD1D6;
+}
+
+/* The primary foreground color. */
+body {
+ color: #CFD1D6;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #CFD1D6;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #CFD1D6;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #97D9FF
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ color: orange;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #00042E;
+ scrollbar-highlight-color: #000843;
+ scrollbar-shadow-color: #00042E;
+ scrollbar-3dlight-color: #00042E;
+ scrollbar-arrow-color: #000843;
+ scrollbar-track-color: #000843;
+ scrollbar-darkshadow-color: #000843;
+}
+
+/* Font 1 */
+body, h2, form, input, select, label, table, a, h1, h1 a, .logo {
+ font-family: calibri, arial, sans-serif;
+}
+
+body {
+ margin:1.5em;
+ font-size: 14pt;
+}
+
+h1, h2, th {
+ font-weight:normal;
+}
+
+input, select {
+ font-size: 100%;
+}
+
+.detail {
+ font-size: 12pt;
+}
+
+.albumComment, .log {
+ font-size: 12pt;
+}
+
diff --git a/subsonic-main/src/main/webapp/style/hd1080.css b/subsonic-main/src/main/webapp/style/hd1080.css
new file mode 100644
index 00000000..b11e7f1e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hd1080.css
@@ -0,0 +1,17 @@
+/*
+ * CSS styleshet for the "HD-1080" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "hd.css";
+
+body {
+ margin:1.5em;
+ font-size: 14pt;
+}
+
+.detail, .albumComment, .log {
+ font-size: 12pt;
+}
+
diff --git a/subsonic-main/src/main/webapp/style/hd720.css b/subsonic-main/src/main/webapp/style/hd720.css
new file mode 100644
index 00000000..0edb8ee4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hd720.css
@@ -0,0 +1,16 @@
+/*
+ * CSS styleshet for the "HD-720" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "hd.css";
+
+body {
+ margin:0.5em;
+ font-size: 11pt;
+}
+
+.detail, .albumComment, .log {
+ font-size: 9pt;
+}
diff --git a/subsonic-main/src/main/webapp/style/hd768.css b/subsonic-main/src/main/webapp/style/hd768.css
new file mode 100644
index 00000000..c7b7b25a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hd768.css
@@ -0,0 +1,16 @@
+/*
+ * CSS styleshet for the "HD-768" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "hd.css";
+
+body {
+ margin:0.5em;
+ font-size: 12pt;
+}
+
+.detail, .albumComment, .log {
+ font-size: 10pt;
+}
diff --git a/subsonic-main/src/main/webapp/style/hicon.css b/subsonic-main/src/main/webapp/style/hicon.css
new file mode 100644
index 00000000..ea13f95c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hicon.css
@@ -0,0 +1,85 @@
+/*
+ * CSS styleshet for the "High Contast" theme.
+ *
+ * Author: Jeebs (Fisher Evans)
+ */
+
+@import "default.css";
+
+table {
+ color: #5c5c5c;
+}
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: white;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: white;
+}
+
+body.bgcolor2 {
+ border-bottom: 1px solid #5c5c5c;
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ color: #5c5c5c;
+ background-color: white;
+}
+
+/* The primary foreground color. */
+body {
+ color: #5c5c5c;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #5c5c5c;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #5c5c5c;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: black
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #5c5c5c;
+}
+
+/* Color for warning messages. */
+.warning {
+ color: red;
+}
+
+/* Simple border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid #5c5c5c;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: white;
+ scrollbar-highlight-color: #5c5c5c;
+ scrollbar-shadow-color: white;
+ scrollbar-3dlight-color: white;
+ scrollbar-arrow-color: #5c5c5c;
+ scrollbar-track-color: #5c5c5c;
+ scrollbar-darkshadow-color: #5c5c5c;
+}
+
+.back {
+ background-image:url("../icons/midnight/back.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+.forward {
+ background-image:url("../icons/midnight/forward.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
diff --git a/subsonic-main/src/main/webapp/style/hiconi.css b/subsonic-main/src/main/webapp/style/hiconi.css
new file mode 100644
index 00000000..81e8ed01
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hiconi.css
@@ -0,0 +1,85 @@
+/*
+ * CSS styleshet for the "High Contast (Inverted)" theme.
+ *
+ * Author: Jeebs (Fisher Evans)
+ */
+
+@import "default.css";
+
+table {
+ color: #a3a3a3;
+}
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: black;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: black;
+}
+
+body.bgcolor2 {
+ border-bottom: 1px solid #a3a3a3;
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ color: #a3a3a3;
+ background-color: black;
+}
+
+/* The primary foreground color. */
+body {
+ color: #a3a3a3;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #a3a3a3;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #a3a3a3;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: white
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #a3a3a3;
+}
+
+/* Color for warning messages. */
+.warning {
+ color: red;
+}
+
+/* Simple border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid #a3a3a3;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: black;
+ scrollbar-highlight-color: #a3a3a3;
+ scrollbar-shadow-color: black;
+ scrollbar-3dlight-color: black;
+ scrollbar-arrow-color: #a3a3a3;
+ scrollbar-track-color: #a3a3a3;
+ scrollbar-darkshadow-color: #a3a3a3;
+}
+
+.back {
+ background-image:url("../icons/midnight/back.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+.forward {
+ background-image:url("../icons/midnight/forward.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
diff --git a/subsonic-main/src/main/webapp/style/hitech.css b/subsonic-main/src/main/webapp/style/hitech.css
new file mode 100644
index 00000000..a5fc6b3e
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/hitech.css
@@ -0,0 +1,96 @@
+/*
+ * CSS styleshet for the "High Tech" theme.
+ *
+ * Author: Jeebs (Fisher Evans)
+ */
+
+@import "default.css";
+
+table {
+ color: #d2f1d5;
+}
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #090a09;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #090a09;
+}
+
+body.bgcolor2 {
+ border-bottom: 1px solid #272727;
+}
+
+.bgcolor2 {
+ background-image:url(../icons/hitech/bg.jpg);
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ color: #d2f1d5;
+ background-color: #090a09;
+}
+
+/* The primary foreground color. */
+body {
+ color: #d2f1d5;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #d2f1d5;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #d2f1d5;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #00d61b
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #ffffff;
+}
+
+/* Color for warning messages. */
+.warning {
+ color: white;
+}
+
+/* Simple border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid #272727;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #253126;
+ scrollbar-highlight-color: #00c52c;
+ scrollbar-shadow-color: #253126;
+ scrollbar-3dlight-color: #253126;
+ scrollbar-arrow-color: #00c52c;
+ scrollbar-track-color: #00c52c;
+ scrollbar-darkshadow-color: #00c52c;
+}
+
+.back {
+ background-image:url("../icons/midnight/back.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+.forward {
+ background-image:url("../icons/midnight/forward.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+
+.mainframe {
+ background-image:url( "../icons/hitech/bg2.jpg" );
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-position:bottom right;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/lowerleftfade.png b/subsonic-main/src/main/webapp/style/lowerleftfade.png
new file mode 100644
index 00000000..cc0665b4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/lowerleftfade.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/midnight.css b/subsonic-main/src/main/webapp/style/midnight.css
new file mode 100644
index 00000000..b6519bb8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/midnight.css
@@ -0,0 +1,77 @@
+/*
+ * CSS styleshet for the "2 Minutes To Midnight" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "default.css";
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #656569;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #4D4C52
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ color: black;
+ background-color: #DDDDDD;
+}
+
+/* The primary foreground color. */
+body {
+ color: #DDDDDD;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #DDDDDD;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #DDDDDD;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #BAD9F2
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: orange;
+}
+
+/* Color for warning messages. */
+.warning {
+ color: orange;
+}
+
+/* Simple border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid white;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #4D4C52;
+ scrollbar-highlight-color: #656569;
+ scrollbar-shadow-color: #4D4C52;
+ scrollbar-3dlight-color: #4D4C52;
+ scrollbar-arrow-color: #656569;
+ scrollbar-track-color: #656569;
+ scrollbar-darkshadow-color: #656569;
+}
+
+.back {
+ background-image:url("../icons/midnight/back.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+.forward {
+ background-image:url("../icons/midnight/forward.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
diff --git a/subsonic-main/src/main/webapp/style/midnightfun.css b/subsonic-main/src/main/webapp/style/midnightfun.css
new file mode 100644
index 00000000..07f69420
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/midnightfun.css
@@ -0,0 +1,158 @@
+/*
+ * CSS styleshet for the http://www.midnightfun.co.uk "MidnightFun" theme.
+ *
+ * Author: Don Pearson
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-image:url('../icons/midnightfun/midnightfun_text_back.jpg');
+ background-repeat:repeat;
+ background-color: #AAAAAA;
+}
+
+/* The secondary background colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-image:url('../icons/midnightfun/midnightfun_background.gif');
+ background-repeat:repeat;
+ background-attachment:fixed;
+ background-color: #C0C0C0;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ background-image:url('../icons/midnightfun/midnightfun_home_hover.jpg');
+ background-repeat:repeat;
+ color: #000000;
+ background-color: #C0C0C0;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #333333;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #000000;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe, .rightframe {
+ background-image:url('../icons/midnightfun/midnightfun_background.gif');
+ background-repeat:repeat;
+ background-attachment:fixed;
+ background-color: #C0C0C0;
+}
+
+/* Back image */
+.back {
+ background-image:url("../icons/midnightfun/midnightfun_back.png");
+}
+
+/* Forward image */
+.forward {
+ background-image:url("../icons/midnightfun/midnightfun_forward.png");
+}
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #656569;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #000000;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+/* Background colour for form controls. */
+input, select {
+ background-image:url('../icons/midnightfun/midnightfun_form_controls.jpg');
+ background-repeat:repeat;
+ color: #FFFFFF;
+ background-color: #AAAAAA;
+}
+
+/* Hover colour for form controls. */
+input:hover, select:hover {
+ background-image:url('../icons/midnightfun/midnightfun_form_controls_hover.jpg');
+ background-repeat:repeat;
+ color: #000000;
+ background-color: #C0C0C0;
+}
+
+/* Background colour for option controls. */
+option {
+ font-weight: bold;
+ background-color: #666666;
+ color: #FFFFFF;
+}
+
+/* Background colour for option comments. */
+textarea {
+ background-image:url('../icons/midnightfun/midnightfun_form_controls.jpg');
+ background-repeat:repeat;
+ background-color: #AAAAAA;
+ color: #FFFFFF;
+}
+
+/* The primary foreground colour. */
+body {
+ background-color: #C0C0C0;
+ scrollbar-face-color: #CCCCCC;
+ scrollbar-highlight-color: #FFFFFF;
+ scrollbar-shadow-color: #CCCCCC;
+ scrollbar-3dlight-color: #D1D7DC;
+ scrollbar-arrow-color: #000000;
+ scrollbar-track-color: #C0C0C0;
+ scrollbar-darkshadow-color: #98AAB1;
+}
+
+/* The album comments. */
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+ color: #000000;
+}
+
+/* The log. */
+.log {
+ font-weight: bold;
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+ background-image:url('../icons/midnightfun/midnightfun_table.jpg');
+ background-repeat:repeat;
+ color: #000000;
+ background-color: #C0C0C0;
+}
+
+/* The help, status & web payer settings tables. */
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+ background-image:url('../icons/midnightfun/midnightfun_table.jpg');
+ background-repeat:repeat;
+ background-attachment:fixed;
+ color: #FFFFFF;
+ background-color: #C0C0C0;
+}
+
+
+/* MidnightFun http://www.midnightfun.co.uk */
diff --git a/subsonic-main/src/main/webapp/style/monochrome.css b/subsonic-main/src/main/webapp/style/monochrome.css
new file mode 100644
index 00000000..ce1bf47d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/monochrome.css
@@ -0,0 +1,111 @@
+/*
+ * CSS styleshet for monochrome style
+ *
+ * Author: David D ddavis1@gmail.com
+ */
+
+@import "default.css";
+
+/* highlights to contrast with bgcolor2. */
+.bgcolor1 {
+ background-color: #EEE;
+}
+
+/* The primary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #CCC;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #000000;
+ background-color: #AAAAAA;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #000000;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #000000;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe, .rightframe {
+ background-color: #EEE;
+}
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #000000;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #FFFFFF;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+/* Background colour for form controls. */
+input, select {
+ color: #333;
+ background-color: #EEE;
+}
+
+/* Hover colour for form controls. */
+input:hover, select:hover {
+ color: #000000;
+ background-color: #FFF;
+}
+
+/* Background colour for option controls. */
+option {
+ font-weight: bold;
+ background-color: #666666;
+ color: #FFFFFF;
+}
+
+/* The primary foreground colour. */
+body {
+ background-color: #EEEEEE;
+}
+
+/* The album comments. */
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+ color: #000000;
+}
+
+/* The log. */
+.log {
+ font-weight: bold;
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+ color: #000000;
+ background-color: #C0C0C0;
+}
+
+/* The help, status & web payer settings tables. */
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+ color: #FFFFFF;
+ background-color: #C0C0C0;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/monochrome_black.css b/subsonic-main/src/main/webapp/style/monochrome_black.css
new file mode 100644
index 00000000..0a9f42d3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/monochrome_black.css
@@ -0,0 +1,115 @@
+/*
+ * CSS styleshet for monochrome style
+ *
+ * Author: David D ddavis1@gmail.com
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #222;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #000000;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #FFFFFF;
+ background-color: #333333;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #FFFFFF;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #FFFFFF;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe, .rightframe {
+ background-color: #222;
+}
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #FFFFFF;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #FFFFFF;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+/* Background colour for form controls. */
+input, select {
+ color: #000;
+ background-color: #EEE;
+}
+
+/* Hover colour for form controls. */
+input:hover, select:hover {
+ color: #000000;
+ background-color: #FFF;
+}
+
+/* Background colour for option controls. */
+option {
+ font-weight: bold;
+ background-color: #666666;
+ color: #FFFFFF;
+}
+
+/* The primary foreground colour. */
+body {
+ background-color: #DDDDDD;
+}
+
+/* The album comments. */
+.albumComment {
+ width: 50em;
+ font-size: 8pt;
+ line-height: 1.4em;
+ padding-top: 0.25em;
+ color: #000000;
+}
+
+/* The log. */
+.log {
+ font-weight: bold;
+ white-space: nowrap;
+ font-size: 8pt;
+ line-height: 1em;
+ color: #000000;
+ background-color: #C0C0C0;
+}
+
+/* The help, status & web payer settings tables. */
+.ruleTableHeader, .ruleTableCell {
+ margin: 5px;
+ padding: 5px;
+ color: #FFFFFF;
+ background-color: #C0C0C0;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/pinkpanther.css b/subsonic-main/src/main/webapp/style/pinkpanther.css
new file mode 100644
index 00000000..f72494d7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/pinkpanther.css
@@ -0,0 +1,91 @@
+/*
+ * CSS styleshet for the "PinkPanther" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #402c31;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #402c31;
+ color: #EFE9D9;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #EFE9D9;
+ background-color: #402c31;
+ border: 1px solid #EFE9D9;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #EFE9D9;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #EFE9D9;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #402c31;
+ background-position:right;
+}
+
+
+/* Back image */
+.back {
+ background-image:url( "../icons/pinkpanther/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/pinkpanther/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #11bdcb;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #11bdcb;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+
+/* The primary foreground colour. */
+body {
+ color: #EFE9D9;
+ background-color: #DDDDDD;
+}
+
+html {
+ background-color: transparent;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/ripserver.css b/subsonic-main/src/main/webapp/style/ripserver.css
new file mode 100644
index 00000000..9959fabe
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/ripserver.css
@@ -0,0 +1,44 @@
+/*
+ * CSS styleshet for the "Ripserver" theme.
+ *
+ * Author: Ralph Hill
+ */
+
+@import "default.css";
+
+/* The primary background color (light blue). */
+.bgcolor1 {
+ background-color: #FFFFFF;
+}
+
+/* The secondary background color (darker blue). */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #EBEFF9
+}
+
+.mainframe {
+ background-image:url('../icons/ripserver/background.png');
+ background-repeat:no-repeat;
+ background-attachment:fixed;
+ background-color: #FFFFFF
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #0076B6
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ color: orange
+}
+
+body {
+ scrollbar-face-color: #DEE3E7;
+ scrollbar-highlight-color: #FFFFFF;
+ scrollbar-shadow-color: #DEE3E7;
+ scrollbar-3dlight-color: #D1D7DC;
+ scrollbar-arrow-color: #006699;
+ scrollbar-track-color: #EFEFEF;
+ scrollbar-darkshadow-color: #98AAB1;
+}
diff --git a/subsonic-main/src/main/webapp/style/sandstorm.css b/subsonic-main/src/main/webapp/style/sandstorm.css
new file mode 100644
index 00000000..89556bc9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/sandstorm.css
@@ -0,0 +1,39 @@
+/*
+ * CSS styleshet for the "Sandstorm" theme.
+ *
+ * Author: Sindre Mehus
+ */
+
+@import "default.css";
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #F5F5D0
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #CCCC99
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #057368
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #DD6900
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #CCCC99;
+ scrollbar-highlight-color: #F5F5D0;
+ scrollbar-shadow-color: #CCCC99;
+ scrollbar-3dlight-color: #CCCC99;
+ scrollbar-arrow-color: #F5F5D0;
+ scrollbar-track-color: #F5F5D0;
+ scrollbar-darkshadow-color: #F5F5D0;
+}
diff --git a/subsonic-main/src/main/webapp/style/shadow.css b/subsonic-main/src/main/webapp/style/shadow.css
new file mode 100644
index 00000000..144a34bc
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/shadow.css
@@ -0,0 +1,107 @@
+/*
+ From http://www.positioniseverything.net/articles/dropshadows.html
+*/
+
+html>body .outerpair1 {
+ background: url( upperrightfade.png ) right top no-repeat;
+}
+
+/* .outerpair1 must be given a width contraint, via either a width,
+or by floating or absolute positioning. In this demo these are
+applied from the second class name on the .outerpair1 DIV's.
+This box also has one of the corner .png's. */
+
+html>body .outerpair2 {
+ background: url( lowerleftfade.png ) left bottom no-repeat;
+ padding-top: 8px;
+ padding-left: 8px;
+}
+
+/* .outerpair2 has padding equal to the shadow
+thickness, and also has one of the corner .png's */
+
+html>body .shadowbox {
+ background: url( shadow.png ) bottom right;
+}
+
+/* .shadowbox holds the main shadow .png */
+
+html>body .innerbox {
+ position: relative;
+ left: -8px;
+ top: -8px;
+}
+
+/* .innerbox is made "relative" and is "pulled" up and to
+the left, by a distance equal to the thickness of the shadow.
+Because this is a relative-based shift, the box retains its
+exact dimensions without change. */
+
+.shadowbox img {
+ border: 0;
+ vertical-align: bottom;
+}
+
+/* Shadowed images should not be made "block" for eliminating the baseline
+space under the images, because this may trigger IE background bugs.
+Instead, use "vertical-align: bottom;" for this purpose. */
+
+/*XXXXXXXXXXXXXXXXXX Custom width constraints and extra styling XXXXXXXXXXXXXXX*/
+
+.floatimage {
+ float: left; /* Floating causes this box to shrinkwrap around sized content elements. */
+ margin: 130px 0 0 450px;
+ display: inline; /* IE doubled margin bug is defeated via this fixer rule. */
+}
+
+.flashbox {
+/* Absolute positioning also causes the shrinkwrap behavior. */
+ position: absolute;
+ left: 377px;
+ top: 30px;
+}
+
+.flashbox .innerbox {
+ background: #eed;
+ border: 1px solid #ccb;
+}
+
+.absoluteimage {
+/* Again, absolute positioning causes shrinkwrapping. */
+ position: absolute;
+ left: 40px;
+ top: 200px;
+}
+
+.textbox {
+ position: absolute; /* AP once more... */
+ left: 20px;
+ top: 1.8em;
+}
+
+.textbox .innerbox {
+ border: 1px solid #ccc;
+ background: #e8e8e8;
+ width: 330px;
+ height: 210px;
+ overflow: auto;
+}
+
+/* Unlike the other items, the .textbox content is just text without a natural
+width, and so shrinkwrapping fails, unless .innerbox is given a specific width.
+All shadowed text elements will need a width of some kind to avoid a full-width
+shadowed box, unless that is the desired effect. The width may be appied to
+div.inner, div.outerpair1, or an external wrapper element. */
+
+.linkbox {
+ position: absolute; /* AP once more... */
+ left: 10px;
+ top: 6px;
+}
+
+.linkbox .innerbox {
+ display: block;
+ background: #fff;
+ border: 1px solid #ccc;
+ padding: 3px 5px;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/shadow.png b/subsonic-main/src/main/webapp/style/shadow.png
new file mode 100644
index 00000000..7862c9bb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/shadow.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/simplify.css b/subsonic-main/src/main/webapp/style/simplify.css
new file mode 100644
index 00000000..d3ac78c1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/simplify.css
@@ -0,0 +1,90 @@
+/*
+ * CSS styleshet for the "Simplify" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #1b1b1b;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #1b1b1b;
+ color: #FFFFFF;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #FFFFFF;
+ background-color: #1b1b1b;
+ border: 1px solid #FFFFFF;
+}
+
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #FFFFFF;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #FFFFFF;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #1b1b1b;
+ background-position:right;
+}
+
+
+/* Back image */
+.back {
+ background-image:url( "../icons/simplify/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/simplify/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #FFFFFF;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #FFFFFF;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #e7238b;
+}
+
+/* The primary foreground colour. */
+body {
+ color: #FFFFFF;
+ background-color: #DDDDDD;
+}
+
+html {
+ background-color: transparent;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/slick.css b/subsonic-main/src/main/webapp/style/slick.css
new file mode 100644
index 00000000..1ae02240
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/slick.css
@@ -0,0 +1,89 @@
+/*
+ * CSS styleshet for the "Slick" theme.
+ *
+ * Author: Jeebs (Fisher Evans)
+ */
+
+@import "default.css";
+
+table {
+ color: #0e0e0e;
+}
+
+/* The primary background color. */
+.bgcolor1 {
+ background-color: #0e0e0e;
+}
+
+/* The secondary background color. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #0e0e0e;
+}
+
+body.bgcolor2 {
+ border-bottom: 1px solid #67778c;
+}
+
+.bgcolor2 {
+ background-image:url(../icons/slick/top_bg.jpg);
+}
+
+/* Background color for selected header, log etc. */
+.headerSelected {
+ color: #0e0e0e;
+ background-color: #8eacc4;
+}
+
+/* The primary foreground color. */
+body {
+ color: #ccd5dc;
+}
+
+/* The secondary foreground color used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #ccd5dc;
+}
+
+/* Foreground color used for h2, bold and tr. */
+h2, b, tr {
+ color: #ccd5dc;
+}
+
+/* Link color */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #8a9aa7
+}
+
+/* Link hover color */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #d2d9df;
+}
+
+/* Color for warning messages. */
+.warning {
+ color: #ff4800;
+}
+
+/* Simple border. */
+.border1, .ruleTableHeader, .ruleTableCell, .headerSelected, .log {
+ border: 1px solid #67778c;
+}
+
+/* Scrollbar colors (supported on IE and Opera) */
+body {
+ scrollbar-face-color: #8eacc4;
+ scrollbar-highlight-color: #67778c;
+ scrollbar-shadow-color: #8eacc4;
+ scrollbar-3dlight-color: #8eacc4;
+ scrollbar-arrow-color: #67778c;
+ scrollbar-track-color: #67778c;
+ scrollbar-darkshadow-color: #67778c;
+}
+
+.back {
+ background-image:url("../icons/midnight/back.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
+.forward {
+ background-image:url("../icons/midnight/forward.png");background-position: 0px 3px; background-repeat:no-repeat; padding-left: 16px; line-height: 16px;
+}
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
new file mode 100644
index 00000000..5b5dab2a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
new file mode 100644
index 00000000..ac8b229a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 00000000..ad3d6346
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 00000000..42ccba26
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 00000000..5a46b47c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 00000000..86c2baa6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 00000000..4443fdc1
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 00000000..7c9fa6c6
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_222222_256x240.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_222222_256x240.png
new file mode 100644
index 00000000..b273ff11
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_2e83ff_256x240.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 00000000..09d1cdc8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_454545_256x240.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_454545_256x240.png
new file mode 100644
index 00000000..59bd45b9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_888888_256x240.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_888888_256x240.png
new file mode 100644
index 00000000..6d02426c
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_cd0a0a_256x240.png b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 00000000..2ab019b7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/style/smoothness/jquery-ui-1.8.18.custom.css b/subsonic-main/src/main/webapp/style/smoothness/jquery-ui-1.8.18.custom.css
new file mode 100644
index 00000000..4cfb50a4
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/smoothness/jquery-ui-1.8.18.custom.css
@@ -0,0 +1,565 @@
+/*
+ * jQuery UI CSS Framework 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
+.ui-helper-clearfix:after { clear: both; }
+.ui-helper-clearfix { zoom: 1; }
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.ui-widget-header a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.18
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: hidden; *overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/sonic.css b/subsonic-main/src/main/webapp/style/sonic.css
new file mode 100644
index 00000000..526ac59d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/sonic.css
@@ -0,0 +1,88 @@
+/*
+ * CSS styleshet for the "Sonic" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "default.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #e7e7e7;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #e7e7e7;
+ color: #696969;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #696969;
+ background-color: #e7e7e7;
+ border: 1px solid #696969;
+}
+
+
+/* The primary foreground color (black). */
+body {
+ color: #696969;
+ background-color: #DDDDDD;
+}
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #696969;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #696969;
+}
+
+/* Table sizing */
+table {
+ margin: 0 0 0 0;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #e7e7e7;
+}
+
+/* Back image */
+.back {
+ background-image:url( "../icons/sonic/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/sonic/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #333333;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ text-decoration: underline;
+ color: #696969;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #e7238b;
+}
+
+html {
+ background-color: transparent;
+}
+
+label, p {
+ color: #696969;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/sonic_blue.css b/subsonic-main/src/main/webapp/style/sonic_blue.css
new file mode 100644
index 00000000..7122ad59
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/sonic_blue.css
@@ -0,0 +1,77 @@
+/*
+ * CSS styleshet for the "Sonic Blue" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "sonic.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #456993;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #456993;
+ color: #EFE9D9;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #EFE9D9;
+ background-color: #456993;
+ border: 1px solid #EFE9D9;
+}
+
+/* The primary foreground color (black). */
+body {
+ color: #EFE9D9;
+ background-color: #DDDDDD;
+}
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #EFE9D9;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #EFE9D9;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #456993;
+}
+
+/* Back image */
+.back {
+ background-image:url( "../icons/sonic_blue/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/sonic_blue/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #B6BEC2;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ color: #B6BEC2;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #990099;
+}
+
+label, p {
+ color: #CCC;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/sonic_white.css b/subsonic-main/src/main/webapp/style/sonic_white.css
new file mode 100644
index 00000000..0c28f138
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/sonic_white.css
@@ -0,0 +1,78 @@
+/*
+ * CSS styleshet for the "Sonic White" theme.
+ *
+ * Author: Thomas Bruce Dyrud (thomasbrucedyrud[at]gmail.com)
+ *
+ */
+
+@import "sonic.css";
+
+/* The primary background colour. */
+.bgcolor1 {
+ background-color: #FFFFFF;
+}
+
+/* The secondary background colour, light blue, primary theme colour. */
+.bgcolor2, .ruleTableHeader, .log {
+ background-color: #FFFFFF;
+ color: #696969;
+}
+
+/* Background colour for selected header, log etc. */
+.headerSelected {
+ color: #696969;
+ background-color: #FFFFFF;
+ border: 1px solid #696969;
+}
+
+
+/* The primary foreground color (black). */
+body {
+ color: #696969;
+ background-color: #DDDDDD;
+}
+
+/* The secondary foreground colour used for h1, details etc. */
+h1, .detail, .albumComment {
+ color: #696969;
+}
+
+/* Foreground colour used for h2, bold and tr. */
+h2, b, tr {
+ color: #696969;
+}
+
+/* Main frame image & colour */
+.mainframe {
+ background-color: #FFFFFF;
+}
+
+/* Back image */
+.back {
+ background-image:url( "../icons/sonic_white/back.png" );
+}
+
+/* Forward image */
+.forward {
+ background-image:url( "../icons/sonic_white/forward.png" );
+}
+
+
+/* Link colour */
+a:link, a:active, a:visited, a:link *, a:active *, a:visited * {
+ color: #333333;
+}
+
+/* Link hover colour */
+a:hover, a:hover * {
+ color: #696969;
+}
+
+/* Colour for warning messages. */
+.warning {
+ color: #e7238b;
+}
+
+label, p {
+ color: #696969;
+} \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/style/upperrightfade.png b/subsonic-main/src/main/webapp/style/upperrightfade.png
new file mode 100644
index 00000000..cad0f2c7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/style/upperrightfade.png
Binary files differ
diff --git a/subsonic-main/src/main/webapp/xsd/albumList2_example_1.xml b/subsonic-main/src/main/webapp/xsd/albumList2_example_1.xml
new file mode 100644
index 00000000..21684156
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/albumList2_example_1.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <albumList2>
+ <album id="1768" name="Duets" coverArt="al-1768" songCount="2" created="2002-11-09T15:44:40" duration="514" artist="Nik Kershaw" artistId="829"/>
+ <album id="2277" name="Hot" coverArt="al-2277" songCount="4" created="2004-11-28T00:06:52" duration="1110" artist="Melanie B" artistId="1242"/>
+ <album id="4201" name="Bande A Part" coverArt="al-4201" songCount="14" created="2007-10-29T19:25:05" duration="3061" artist="Nouvelle Vague" artistId="2060"/>
+ <album id="2910" name="Soundtrack From Twin Peaks" coverArt="al-2910" songCount="6" created="2002-11-17T09:58:42" duration="1802" artist="Angelo Badalamenti" artistId="1515"/>
+ <album id="3109" name="Wild One" coverArt="al-3109" songCount="38" created="2001-04-17T00:20:08" duration="9282" artist="Thin Lizzy" artistId="661"/>
+ <album id="1151" name="Perleporten" coverArt="al-1151" songCount="2" created="2002-11-16T22:24:22" duration="494" artist="Magnus Gr&#248;nneberg" artistId="747"/>
+ <album id="2204" name="Wholesale Meats And Fish" coverArt="al-2204" songCount="24" created="2004-11-27T23:44:31" duration="5362" artist="Letters To Cleo" artistId="1216"/>
+ <album id="114" name="Sounds of the Seventies: AM Nuggets" coverArt="al-114" songCount="2" created="2004-03-09T07:32:46" duration="420" artist="Rubettes" artistId="97"/>
+ <album id="279" name="Waiting for the Day" coverArt="al-279" songCount="2" created="2004-11-27T17:49:19" duration="448" artist="Bachelor Girl" artistId="231"/>
+ <album id="4414" name="For Sale" songCount="14" created="2007-10-30T00:11:58" duration="2046" artist="The Beatles" artistId="509"/>
+ </albumList2>
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/albumList_example_1.xml b/subsonic-main/src/main/webapp/xsd/albumList_example_1.xml
new file mode 100644
index 00000000..d83482a9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/albumList_example_1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.6.0">
+
+ <albumList>
+ <album id="11" parent="1" title="Arrival" artist="ABBA" isDir="true" coverArt="22" userRating="4" averageRating="4.5"/>
+ <album id="12" parent="1" title="Super Trouper" artist="ABBA" isDir="true" coverArt="23" averageRating="4.4"/>
+ </albumList>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/album_example_1.xml b/subsonic-main/src/main/webapp/xsd/album_example_1.xml
new file mode 100644
index 00000000..c1a8df27
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/album_example_1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.8.0">
+
+ <album id="11053" name="High Voltage" coverArt="al-11053" songCount="8" created="2004-11-27T20:23:32" duration="2414" artist="AC/DC" artistId="5432">
+ <song id="71463" parent="71381" title="The Jack" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-08T23:36:11" duration="352" bitRate="128" size="5624132" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - The Jack.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71464" parent="71381" title="Tnt" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-08T23:36:11" duration="215" bitRate="128" size="3433798" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - TNT.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71458" parent="71381" title="It&apos;s A Long Way To The Top" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:32" duration="315" bitRate="128" year="1976" genre="Rock" size="5037357" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - It&apos;s a long way to the top if you wanna rock &apos;n &apos;roll.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71461" parent="71381" title="Rock &apos;n&apos; Roll Singer." album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:33" duration="303" bitRate="128" track="2" year="1976" genre="Rock" size="4861680" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - Rock N Roll Singer.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71460" parent="71381" title="Live Wire" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:33" duration="349" bitRate="128" track="4" year="1976" genre="Rock" size="5600206" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - Live Wire.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71456" parent="71381" title="Can I sit next to you girl" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:32" duration="251" bitRate="128" track="6" year="1976" genre="Rock" size="4028276" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - Can I Sit Next To You Girl.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71459" parent="71381" title="Little Lover" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:33" duration="339" bitRate="128" track="7" year="1976" genre="Rock" size="5435119" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - Little Lover.mp3" albumId="11053" artistId="5432" type="music"/>
+ <song id="71462" parent="71381" title="She&apos;s Got Balls" album="High Voltage" artist="AC/DC" isDir="false" coverArt="71381" created="2004-11-27T20:23:34" duration="290" bitRate="128" track="8" year="1976" genre="Rock" size="4651866" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/High voltage/ACDC - Shes Got Balls.mp3" albumId="11053" artistId="5432" type="music"/>
+ </album>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/artist_example_1.xml b/subsonic-main/src/main/webapp/xsd/artist_example_1.xml
new file mode 100644
index 00000000..86fc4e67
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/artist_example_1.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.8.0">
+
+ <artist id="5432" name="AC/DC" coverArt="ar-5432" albumCount="15">
+ <album id="11047" name="Back In Black" coverArt="al-11047" songCount="10" created="2004-11-08T23:33:11" duration="2534" artist="AC/DC" artistId="5432"/>
+ <album id="11048" name="Black Ice" coverArt="al-11048" songCount="15" created="2008-10-30T09:20:52" duration="3332" artist="AC/DC" artistId="5432"/>
+ <album id="11049" name="Blow up your Video" coverArt="al-11049" songCount="10" created="2004-11-27T19:22:45" duration="2578" artist="AC/DC" artistId="5432"/>
+ <album id="11050" name="Flick Of The Switch" coverArt="al-11050" songCount="10" created="2004-11-27T19:22:51" duration="2222" artist="AC/DC" artistId="5432"/>
+ <album id="11051" name="Fly On The Wall" coverArt="al-11051" songCount="10" created="2004-11-27T19:22:57" duration="2405" artist="AC/DC" artistId="5432"/>
+ <album id="11052" name="For Those About To Rock" coverArt="al-11052" songCount="10" created="2004-11-08T23:35:02" duration="2403" artist="AC/DC" artistId="5432"/>
+ <album id="11053" name="High Voltage" coverArt="al-11053" songCount="8" created="2004-11-27T20:23:32" duration="2414" artist="AC/DC" artistId="5432"/>
+ <album id="10489" name="Highway To Hell" coverArt="al-10489" songCount="12" created="2009-06-15T09:41:54" duration="2745" artist="AC/DC" artistId="5432"/>
+ <album id="11054" name="If You Want Blood..." coverArt="al-11054" songCount="1" created="2004-11-27T20:23:32" duration="304" artist="AC/DC" artistId="5432"/>
+ <album id="11056" name="Let There Be Rock" coverArt="al-11056" songCount="8" created="2004-11-27T20:33:40" duration="2449" artist="AC/DC" artistId="5432"/>
+ <album id="11057" name="Live - Special Collector&apos;s Edition" coverArt="al-11057" songCount="22" created="2004-11-08T23:37:09" duration="6999" artist="AC/DC" artistId="5432"/>
+ <album id="11058" name="Powerage" coverArt="al-11058" songCount="9" created="2004-11-27T20:33:41" duration="2380" artist="AC/DC" artistId="5432"/>
+ <album id="11059" name="Stiff Upper Lip" coverArt="al-11059" songCount="11" created="2004-11-08T23:41:13" duration="2595" artist="AC/DC" artistId="5432"/>
+ <album id="11060" name="The Razors Edge" coverArt="al-11060" songCount="12" created="2004-11-27T20:33:42" duration="2787" artist="AC/DC" artistId="5432"/>
+ <album id="11061" name="Who Made Who" coverArt="al-11061" songCount="9" created="2004-11-08T23:43:18" duration="2291" artist="AC/DC" artistId="5432"/>
+ </artist>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/artists_example_1.xml b/subsonic-main/src/main/webapp/xsd/artists_example_1.xml
new file mode 100644
index 00000000..0c2498b2
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/artists_example_1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.8.0">
+ <artists>
+ <artist id="5449" name="A-Ha" coverArt="ar-5449" albumCount="4"/>
+ <artist id="5421" name="ABBA" coverArt="ar-5421" albumCount="6"/>
+ <artist id="5432" name="AC/DC" coverArt="ar-5432" albumCount="15"/>
+ <artist id="6633" name="Aaron Neville" coverArt="ar-6633" albumCount="1"/>
+ <artist id="5950" name="Bob Marley" coverArt="ar-5950" albumCount="8"/>
+ <artist id="5957" name="Bruce Dickinson" coverArt="ar-5957" albumCount="2"/>
+</artists>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/chatMessages_example_1.xml b/subsonic-main/src/main/webapp/xsd/chatMessages_example_1.xml
new file mode 100644
index 00000000..b633d534
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/chatMessages_example_1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.2.0">
+
+ <chatMessages>
+ <chatMessage username="sindre" time="1269771845310" message="Sindre was here"/>
+ <chatMessage username="ben" time="1269771842504" message="Ben too"/>
+ </chatMessages>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/directory_example_1.xml b/subsonic-main/src/main/webapp/xsd/directory_example_1.xml
new file mode 100644
index 00000000..c2ec1582
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/directory_example_1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <directory id="1" name="ABBA">
+ <child id="11" parent="1" title="Arrival" artist="ABBA" isDir="true" coverArt="22"/>
+ <child id="12" parent="1" title="Super Trouper" artist="ABBA" isDir="true" coverArt="23"/>
+ </directory>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/directory_example_2.xml b/subsonic-main/src/main/webapp/xsd/directory_example_2.xml
new file mode 100644
index 00000000..246e7b51
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/directory_example_2.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <directory id="11" parent="1" name="Arrival">
+ <child id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3" duration="146" bitRate="128"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <child id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="208" bitRate="128"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </directory>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/error_example_1.xml b/subsonic-main/src/main/webapp/xsd/error_example_1.xml
new file mode 100644
index 00000000..80cafabf
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/error_example_1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="failed" version="1.1.1">
+
+ <error code="40" message="Wrong username or password."/>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/indexes_example_1.xml b/subsonic-main/src/main/webapp/xsd/indexes_example_1.xml
new file mode 100644
index 00000000..5438ad51
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/indexes_example_1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.1.1">
+
+ <indexes lastModified="237462836472342">
+ <shortcut id="11" name="Audio books"/>
+ <shortcut id="10" name="Podcasts"/>
+ <index name="A">
+ <artist id="1" name="ABBA"/>
+ <artist id="2" name="Alanis Morisette"/>
+ <artist id="3" name="Alphaville"/>
+ </index>
+ <index name="B">
+ <artist name="Bob Dylan" id="4"/>
+ </index>
+
+ <child id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3" duration="146" bitRate="128"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <child id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="208" bitRate="128"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </indexes>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/jukeboxPlaylist_example_1.xml b/subsonic-main/src/main/webapp/xsd/jukeboxPlaylist_example_1.xml
new file mode 100644
index 00000000..e214a860
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/jukeboxPlaylist_example_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <jukeboxPlaylist currentIndex="0" playing="true" gain="0.67" position="67">
+ <entry id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ duration="345" size="8421341" contentType="audio/mpeg" suffix="mp3"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <entry id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ duration="240" size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </jukeboxPlaylist>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/jukeboxStatus_example_1.xml b/subsonic-main/src/main/webapp/xsd/jukeboxStatus_example_1.xml
new file mode 100644
index 00000000..a0200494
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/jukeboxStatus_example_1.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.7.0">
+
+ <jukeboxStatus currentIndex="7" playing="true" gain="0.9" position="67"/>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/license_example_1.xml b/subsonic-main/src/main/webapp/xsd/license_example_1.xml
new file mode 100644
index 00000000..720381ab
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/license_example_1.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.1.1">
+
+ <license valid="true" email="foo@bar.com" key="ABC123DEF" date="2009-09-03T14:46:43"/>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/lyrics_example_1.xml b/subsonic-main/src/main/webapp/xsd/lyrics_example_1.xml
new file mode 100644
index 00000000..1a2831cb
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/lyrics_example_1.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.2.0">
+ <lyrics artist="Muse" title="Hysteria">
+It&apos;s bugging me
+Grating me
+And twisting me around
+Yeah I&apos;m endlessly
+Caving in
+And turning inside out
+
+Cause I want it now
+I want it now
+Give me your heart and your soul
+And I&apos;m breaking out
+I&apos;m breaking out
+That&apos;s when she&apos;ll lose control
+
+It&apos;s holding me
+Morphing me
+And forcing me to strive
+To be endlessly
+Cold within
+And dreaming I&apos;m alive
+
+Cause I want it now
+I want it now
+Give me your heart and your soul
+I&apos;m not breaking down
+I&apos;m breaking out
+That&apos;s when she&apos;ll lose control
+
+And I want you now
+I want you now
+I&apos;ll feel my heart implode
+And I&apos;m breaking out
+Escaping now
+Feeling my faith erode
+ </lyrics>
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/musicFolders_example_1.xml b/subsonic-main/src/main/webapp/xsd/musicFolders_example_1.xml
new file mode 100644
index 00000000..8531045b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/musicFolders_example_1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.1.1">
+
+ <musicFolders>
+ <musicFolder id="1" name="Music"/>
+ <musicFolder id="2" name="Movies"/>
+ <musicFolder id="3" name="Incoming"/>
+ </musicFolders>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/nowPlaying_example_1.xml b/subsonic-main/src/main/webapp/xsd/nowPlaying_example_1.xml
new file mode 100644
index 00000000..b492fbb7
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/nowPlaying_example_1.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <nowPlaying>
+ <entry username="sindre" minutesAgo="12" playerId="2"
+ id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3" path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <entry username="bente" minutesAgo="1" playerId="4" playerName="Kitchen" id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac" transcodedContentType="audio/mpeg"
+ transcodedSuffix="mp3" path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </nowPlaying>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/ping_example_1.xml b/subsonic-main/src/main/webapp/xsd/ping_example_1.xml
new file mode 100644
index 00000000..b475286d
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/ping_example_1.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.1.1">
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/playlist_example_1.xml b/subsonic-main/src/main/webapp/xsd/playlist_example_1.xml
new file mode 100644
index 00000000..58966868
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/playlist_example_1.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <playlist id="15" name="kokos" comment="fan" owner="admin" public="true" songCount="6" duration="1391"
+ created="2012-04-17T19:53:44">
+ <allowedUser>sindre</allowedUser>
+ <allowedUser>john</allowedUser>
+ <entry id="657" parent="655" title="Making Me Nervous" album="I Don&apos;t Know What I&apos;m Doing"
+ artist="Brad Sucks" isDir="false" coverArt="655" created="2008-04-10T07:10:32" duration="159"
+ bitRate="202" track="1" year="2003" size="4060113" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Brad Sucks/I Don&apos;t Know What I&apos;m Doing/01 - Making Me Nervous.mp3" albumId="58"
+ artistId="45" type="music"/>
+ <entry id="823" parent="784" title="Piano escena" album="BSO Sebastian" artist="PeerGynt Lobogris"
+ isDir="false" coverArt="784" created="2009-01-14T22:26:29" duration="129" bitRate="170" track="8"
+ year="2008" genre="Blues" size="2799954" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="PeerGynt Lobogris/BSO Sebastian/08 - Piano escena.mp3" albumId="75" artistId="54" type="music"/>
+ <entry id="748" parent="746" title="Stories from Emona II" album="Between two worlds" artist="Maya Filipi&#269;"
+ isDir="false" coverArt="746" created="2008-07-30T22:05:40" duration="335" bitRate="176" track="2"
+ year="2008" genre="Classical" size="7458214" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Maya Filipic/Between two worlds/02 - Stories from Emona II.mp3" albumId="68" artistId="51"
+ type="music"/>
+ <entry id="848" parent="827" title="Run enemy" album="Eve" artist="Shearer" isDir="false" coverArt="827"
+ created="2009-01-15T22:54:38" duration="331" bitRate="195" track="14" year="2008" genre="Rock"
+ size="8160185" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Shearer/Eve/14 - Run enemy.mp3" albumId="77" artistId="55" type="music"/>
+ <entry id="884" parent="874" title="Isolation" album="Kosmonaut" artist="Ugress" isDir="false" coverArt="874"
+ created="2009-01-14T21:34:49" duration="320" bitRate="160" track="4" year="2006" genre="Electronic"
+ size="6412176" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Ugress/Kosmonaut/Ugress-KosmonautEP-04-Isolation.mp3" albumId="81" artistId="57" type="music"/>
+ <entry id="805" parent="783" title="Bajo siete lunas (intro)" album="Broken Dreams" artist="PeerGynt Lobogris"
+ isDir="false" coverArt="783" created="2008-12-19T14:13:58" duration="117" bitRate="225" track="1"
+ year="2008" genre="Blues" size="3363271" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="PeerGynt Lobogris/Broken Dreams/01 - Bajo siete lunas (intro).mp3" albumId="74" artistId="54"
+ type="music"/>
+ </playlist>
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/playlists_example_1.xml b/subsonic-main/src/main/webapp/xsd/playlists_example_1.xml
new file mode 100644
index 00000000..9b4b60a3
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/playlists_example_1.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <playlists>
+ <playlist id="15" name="Some random songs" comment="Just something I tossed together" owner="admin" public="false" songCount="6" duration="1391" created="2012-04-17T19:53:44">
+ <allowedUser>sindre</allowedUser>
+ <allowedUser>john</allowedUser>
+ </playlist>
+ <playlist id="16" name="More random songs" comment="No comment" owner="admin" public="true" songCount="5" duration="1018" created="2012-04-17T19:55:49"/>
+ </playlists>
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/podcasts_example_1.xml b/subsonic-main/src/main/webapp/xsd/podcasts_example_1.xml
new file mode 100644
index 00000000..23b8d2ae
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/podcasts_example_1.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.6.0">
+
+ <podcasts>
+ <channel id="1"
+ url="http://downloads.bbc.co.uk/podcasts/fivelive/drkarl/rss.xml"
+ title="Dr Karl and the Naked Scientist"
+ description="Dr Chris Smith aka The Naked Scientist with the latest news from the world of science and Dr Karl answers listeners' science questions."
+ status="completed">
+ <episode id="34"
+ streamId="523"
+ title="Scorpions have re-evolved eyes"
+ description="This week Dr Chris fills us in on the UK's largest free science festival, plus all this week's big scientific discoveries."
+ publishDate="2011-02-03T14:46:43"
+ status="completed"
+ parent="11" isDir="false" year="2011" genre="Podcast" coverArt="24"
+ size="78421341" contentType="audio/mpeg" suffix="mp3" duration="3146" bitRate="128"
+ path="Podcast/drkarl/20110203.mp3"/>
+ <episode id="35"
+ streamId="524"
+ title="Scar tissue and snake venom treatment"
+ description="This week Dr Karl tells the gruesome tale of a surgeon who operated on himself."
+ publishDate="2011-09-03T16:47:52"
+ status="completed"
+ parent="11" isDir="false" year="2011" genre="Podcast" coverArt="27"
+ size="45624671" contentType="audio/mpeg" suffix="mp3" duration="3099" bitRate="128"
+ path="Podcast/drkarl/20110903.mp3"/>
+ </channel>
+ <channel id="2"
+ url="http://podkast.nrk.no/program/herreavdelingen.rss"
+ title="NRK P1 - Herreavdelingen"
+ description="Et program der herrene Yan Friis og Finn Bjelke møtes og musikk nytes."
+ status="completed">
+ </channel>
+ <channel id="3"
+ url="http://foo.bar.com/xyz.rss"
+ status="error"
+ errorMessage="Not found."/>
+ </podcasts>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/randomSongs_example_1.xml b/subsonic-main/src/main/webapp/xsd/randomSongs_example_1.xml
new file mode 100644
index 00000000..dc3a6a99
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/randomSongs_example_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <randomSongs>
+ <song id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3" duration="146" bitRate="128"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <song id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="208" bitRate="128"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </randomSongs>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/searchResult2_example_1.xml b/subsonic-main/src/main/webapp/xsd/searchResult2_example_1.xml
new file mode 100644
index 00000000..dd7a2c10
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/searchResult2_example_1.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <searchResult2>
+ <artist id="1" name="ABBA"/>
+ <album id="11" parent="1" title="Arrival" artist="ABBA" isDir="true" coverArt="22"/>
+ <album id="12" parent="1" title="Super Trouper" artist="ABBA" isDir="true" coverArt="23"/>
+ <song id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </searchResult2>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/searchResult3_example_1.xml b/subsonic-main/src/main/webapp/xsd/searchResult3_example_1.xml
new file mode 100644
index 00000000..ef1b1df9
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/searchResult3_example_1.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <searchResult3>
+ <artist id="5944" name="Black" coverArt="ar-5944" albumCount="2"/>
+ <artist id="5785" name="Black Sabbath" coverArt="ar-5785" albumCount="22"/>
+ <artist id="5945" name="Black Debbath" coverArt="ar-5945" albumCount="7"/>
+ <artist id="6063" name="Mary Black" coverArt="ar-6063" albumCount="1"/>
+ <artist id="6065" name="Frances Black" coverArt="ar-6065" albumCount="1"/>
+ <artist id="6131" name="Black Box" coverArt="ar-6131" albumCount="1"/>
+ <artist id="6973" name="The Black Crowes" coverArt="ar-6973" albumCount="2"/>
+ <artist id="6974" name="The Black Sorrows" coverArt="ar-6974" albumCount="1"/>
+ <artist id="6061" name="Eleanor Mcevoy With Mary Black" coverArt="ar-6061" albumCount="1"/>
+ <album id="11241" name="Black" coverArt="al-11241" songCount="10" created="2004-11-14T13:02:03" duration="2575" artist="Black" artistId="5944"/>
+ <album id="12768" name="Black" coverArt="al-12768" songCount="1" created="2000-07-31T15:13:50" duration="448" artist="Metallica" artistId="6308"/>
+ <album id="11242" name="Black Debbath Hyller Kvinnen!" coverArt="al-11242" songCount="11" created="2010-05-25T13:04:41" duration="2778" artist="Black Debbath" artistId="5945"/>
+ <album id="11047" name="Back In Black" coverArt="al-11047" songCount="10" created="2004-11-08T23:33:11" duration="2534" artist="AC/DC" artistId="5432"/>
+ <album id="11048" name="Black Ice" coverArt="al-11048" songCount="15" created="2008-10-30T09:20:52" duration="3332" artist="AC/DC" artistId="5432"/>
+ <album id="11615" name="The Black Parade" coverArt="al-11615" songCount="15" created="2007-06-21T07:52:46" duration="3356" artist="My Chemical Romance" artistId="6159"/>
+ <album id="12132" name="Black Celebration" coverArt="al-12132" songCount="10" created="2005-01-18T23:19:33" duration="2225" artist="Depeche Mode" artistId="6355"/>
+ <album id="12544" name="The Black Halo" coverArt="al-12544" songCount="3" created="2010-04-07T13:41:39" duration="668" artist="Kamelot" artistId="5433"/>
+ <album id="12771" name="The Black Album" coverArt="al-12771" songCount="1" created="1999-10-15T00:00:00" duration="386" artist="Metallica" artistId="6308"/>
+ <album id="13554" name="Black Angel" coverArt="al-13554" songCount="12" created="2002-11-16T15:37:20" duration="3160" artist="Savage Rose" artistId="6999"/>
+ <album id="13609" name="The Black Rider" coverArt="al-13609" songCount="20" created="2002-02-22T14:50:34" duration="3327" artist="Tom Waits" artistId="6920"/>
+ <album id="11243" name="Black Debbaths Beste-Ti &#197;r Med Rock Mot Alt Som Er Kult" coverArt="al-11243" songCount="27" created="2009-05-27T14:43:12" duration="6710" artist="Black Debbath" artistId="5945"/>
+ <album id="11250" name="Black sabbath &amp; Rob Halford live 11-15-92 Costa mesa" coverArt="al-11250" songCount="13" created="2003-07-21T13:52:14" duration="4500" artist="Black Sabbath" artistId="5785"/>
+ <album id="10490" name="The Black Halo [Bonus Track]" coverArt="al-10490" songCount="42" created="2010-04-07T12:58:16" duration="10305" artist="Kamelot" artistId="5433"/>
+ <album id="10559" name="Back to Black: 1900-1999" coverArt="al-10559" songCount="1" created="2004-03-09T07:32:42" duration="99" artist="Earth, Wind &amp; Fire, The Emotions" artistId="5489"/>
+ <album id="11087" name="Back To Black (Deluxe Edition)" coverArt="al-11087" songCount="19" created="2008-06-25T10:01:30" duration="3663" artist="Amy Winehouse" artistId="5870"/>
+ <album id="11604" name="Black Holes &amp; Revelations" coverArt="al-11604" songCount="12" created="2007-06-21T07:52:44" duration="3025" artist="Muse" artistId="6150"/>
+ <album id="12993" name="Black Market Music" coverArt="al-12993" songCount="1" created="2003-07-21T14:48:38" duration="233" artist="Placebo" artistId="6313"/>
+ <album id="13580" name="Black Rose [Remastered]" coverArt="al-13580" songCount="9" created="2009-07-06T08:55:22" duration="2324" artist="Thin Lizzy" artistId="6084"/>
+ <album id="13904" name="Down Under The Black Light" songCount="1" created="2003-10-07T07:22:26" duration="90" artist="The Molecules" artistId="7202"/>
+ <song id="77451" parent="77433" title="Black" album="Angry Machines" artist="Dio" isDir="false" coverArt="77433" created="2007-03-15T06:46:06" duration="190" bitRate="192" track="3" year="1996" genre="Hard Rock" size="4575589" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Dio/Angry Machines/Angry Machines 2.mp3" albumId="12168" artistId="6357" type="music"/>
+ <song id="84902" parent="84883" title="Black" album="Ten" artist="Pearl Jam" isDir="false" coverArt="84883" created="2001-06-11T22:15:52" duration="344" bitRate="160" year="1992" genre="Rock" size="6882991" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Pearl Jam/Ten/05 - Black.mp3" albumId="12944" artistId="6745" type="music"/>
+ <song id="84916" parent="84884" title="Black" album="Unplugged at MTV" artist="Pearl Jam" isDir="false" coverArt="84884" created="1999-07-18T13:40:38" duration="449" bitRate="128" size="7180644" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Pearl Jam/Unplugged at MTV/4- Pearl Jam Unplugged - Black.mp3" albumId="12945" artistId="6745" type="music"/>
+ <song id="85038" parent="85036" title="Black" album="Musicforthemorningafter" artist="Pete Yorn" isDir="false" coverArt="85036" created="2004-11-28T00:42:29" duration="250" bitRate="192" track="4" year="2001" genre="Rock/Pop" size="6026430" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Pete Yorn/Pete Yorn/Pete Yorn - 04 - Black.mp3" albumId="12951" artistId="6747" type="music"/>
+ <song id="73890" parent="73888" title="Black Sabbath" album="Best Of...CD1" artist="Black Sabbath" isDir="false" coverArt="73888" created="2003-07-21T12:19:32" duration="380" bitRate="160" track="1" genre="Hard Rock" size="8124439" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Black Sabbath/Best Of Black Sabbath/CD1/01 - Black Sabbath.mp3" albumId="11248" artistId="5785" type="music"/>
+ <song id="73934" parent="73875" title="Black Moon" album="Headless Cross" artist="Black Sabbath" isDir="false" coverArt="73875" created="2002-09-05T23:00:52" duration="246" bitRate="128" track="7" genre="(255)" size="3949687" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Black Sabbath/Headless Cross/07 - Black Moon.mp3" albumId="11251" artistId="5785" type="music"/>
+ <song id="73844" parent="73790" title="Datidas Black Debbath" album="Naar Vi D&#248;de Rocker" artist="Black Debbath" isDir="false" coverArt="73790" created="2010-05-03T09:40:22" duration="192" bitRate="320" track="7" year="2006" genre="Rock" size="7704160" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Black Debbath/N&#229;r vi d&#248;de rocker/Black Debbath - Datidas Black Debbath.mp3" albumId="11245" artistId="5945" type="music"/>
+ <song id="73924" parent="73874" title="Black Sabbath (with Ozzy)" album="Black sabbath &amp; Rob Halford live 11-15-92 Costa mesa" artist="Black Sabbath" isDir="false" coverArt="73874" created="2003-07-21T13:54:14" duration="407" bitRate="128" track="10" genre="Hard Rock" size="6518578" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Black Sabbath/Black sabbath &amp; Rob Halford live 11-15-92 Costa mesa/10 - Black Sabbath (with Ozzy).mp3" albumId="11250" artistId="5785" type="music"/>
+ <song id="75272" parent="75055" title="Black Sabbath - Paranoid" album="All Time Greatest Rock Songs (Disc 1)" artist="Black Sabbath" isDir="false" coverArt="75055" created="2002-11-23T19:31:34" duration="169" bitRate="160" track="18" genre="General Rock" size="3379328" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Compilations/All Time Greatest Rock Songs (CD 1)/18 - Black Sabbath - Paranoid.mp3" albumId="11482" artistId="5785" type="music"/>
+ <song id="89111" parent="89093" title="Black Moon Creeping" album="The Southern Harmony And Musical Companion" artist="The Black Crowes" isDir="false" coverArt="89093" created="2002-11-03T13:51:24" duration="294" bitRate="160" track="7" size="5891285" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="The Black Crowes/The Southern Harmony And Musical Companion/07 - Black Moon Creeping.mp3" albumId="13473" artistId="6973" type="music"/>
+ <song id="92435" parent="92347" title="Belle Epoque - Black Is Black" album="Cd4" artist="Summer Hits - Top 100" isDir="false" created="1999-07-18T10:48:14" duration="204" bitRate="160" size="4087948" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="_Various/Summer Hits - Top 100/Cd4/16. Belle Epoque - Black Is Black.mp3" albumId="13948" artistId="7223" type="music"/>
+ <song id="70221" parent="70211" title="The Black Halo" album="The Black Halo [Bonus Track]" artist="Kamelot" isDir="false" coverArt="70211" created="2010-04-07T12:58:17" duration="223" bitRate="192" track="10" year="2005" genre="Progressive Metal" size="5363128" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Incoming/Kamelot/08 - The Black Halo - 2005/10 - The Black Halo.mp3" albumId="10490" artistId="5433" type="music"/>
+ <song id="71056" parent="70678" title="Black Coffee" album="Saints &amp; Sinners" artist="All Saints" isDir="false" coverArt="70678" created="2004-03-09T07:34:28" duration="290" bitRate="192" track="5" year="2000" genre="Rock/Pop" size="6975616" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Absolute/Absolute music 36/Absolute Music 36 - 12 All Saints - Black Coffee.mp3" albumId="10811" artistId="5692" type="music"/>
+ <song id="71393" parent="71375" title="Back In Black" album="Back In Black" artist="AC/DC" isDir="false" coverArt="71375" created="2004-11-08T23:33:11" duration="268" bitRate="128" genre="Blues" size="4292608" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/Back in black/ACDC - Back In Black.mp3" albumId="11047" artistId="5432" type="music"/>
+ <song id="71415" parent="71376" title="Black Ice" album="Black Ice" artist="AC/DC" isDir="false" coverArt="71376" created="2008-10-30T10:08:28" duration="205" bitRate="320" track="15" year="2008" genre="Heavy Metal" size="8210893" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/Black Ice/15 AC-DC - Black Ice.mp3" albumId="11048" artistId="5432" type="music"/>
+ <song id="71495" parent="71385" title="Back In Black" album="Live - Special Collector&apos;s Edition" artist="AC/DC" isDir="false" coverArt="71385" created="2004-11-08T23:37:09" duration="266" bitRate="128" year="1992" genre="Metal" size="4255615" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="ACDC/Live (CD1)/ac dc - live - special collector edition - disc 1 - 03 - ba.mp3" albumId="11057" artistId="5432" type="music"/>
+ <song id="71763" parent="71758" title="Back To Black" album="Back To Black (Deluxe Edition)" artist="Amy Winehouse" isDir="false" coverArt="71758" created="2008-06-25T10:12:14" duration="241" bitRate="153" track="5" year="2007" genre="Blues" size="4639525" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Amy Winehouse/Back To Black/105-amy_winehouse-back_to_black-ukp.mp3" albumId="11087" artistId="5870" type="music"/>
+ <song id="75149" parent="75052" title="Black Sabbath" album="70" artist="Paranoid" isDir="false" coverArt="75052" created="2004-11-14T13:38:38" duration="169" bitRate="160" track="18" genre="Rock" size="3406066" suffix="wma" contentType="audio/x-ms-wma" isVideo="false" path="Compilations/70/Black Sabbath - Paranoid.wma" albumId="11396" artistId="6010" type="music"/>
+ <song id="75301" parent="75057" title="Black Betty" album="Born To Be Wild - Vol. 2" artist="Ram Jam" isDir="false" coverArt="75057" created="2006-02-04T20:30:44" duration="239" bitRate="160" track="12" year="1994" genre="Hard Rock" size="4792458" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Compilations/Born To Be Wild - Vol. 2/12 - Black Betty.mp3" albumId="11510" artistId="6091" type="music"/>
+ <song id="76048" parent="75100" title="Black Velvet" album="The Good" artist="Alannah Myles" isDir="false" coverArt="75100" created="2002-11-17T15:44:08" duration="289" bitRate="128" genre="Other" size="4628480" suffix="mp3" contentType="audio/mpeg" isVideo="false" path="Compilations/The Good/Alannah Myles - Black Velvet.mp3" albumId="11818" artistId="6286" type="music"/>
+ </searchResult3>
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/searchResult_example_1.xml b/subsonic-main/src/main/webapp/xsd/searchResult_example_1.xml
new file mode 100644
index 00000000..fb1e9c4a
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/searchResult_example_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.4.0">
+
+ <searchResult offset="0" totalHits="2">
+ <match id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <match id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </searchResult>
+
+</subsonic-response>
+
diff --git a/subsonic-main/src/main/webapp/xsd/shares_example_1.xml b/subsonic-main/src/main/webapp/xsd/shares_example_1.xml
new file mode 100644
index 00000000..1fb8732f
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/shares_example_1.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.6.0">
+
+ <shares>
+ <share id="1" url="http://sindre.subsonic.org/share/sKoYn" description="Check this out" username="sindre"
+ created="2011-06-04T12:34:56" lastVisited="2011-06-04T13:14:15" expires="2013-06-04T00:00:00" visitCount="0">
+
+ <entry id="111" parent="11" title="Dancing Queen" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="24"
+ size="8421341" contentType="audio/mpeg" suffix="mp3" duration="146" bitRate="128"
+ path="ABBA/Arrival/Dancing Queen.mp3"/>
+
+ <entry id="112" parent="11" title="Money, Money, Money" isDir="false"
+ album="Arrival" artist="ABBA" track="7" year="1978" genre="Pop" coverArt="25"
+ size="4910028" contentType="audio/flac" suffix="flac"
+ transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="208" bitRate="128"
+ path="ABBA/Arrival/Money, Money, Money.mp3"/>
+ </share>
+ </shares>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/song_example_1.xml b/subsonic-main/src/main/webapp/xsd/song_example_1.xml
new file mode 100644
index 00000000..58b8773b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/song_example_1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.8.0">
+
+ <song id="48228" parent="48203" title="You Shook Me All Night Long" album="Back In Black" artist="AC/DC"
+ isDir="false" coverArt="48203" created="2004-11-08T23:33:11" duration="210" bitRate="112" size="2945619"
+ suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="ACDC/Back in black/ACDC - You Shook Me All Night Long.mp3"/>
+
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/starred2_example_1.xml b/subsonic-main/src/main/webapp/xsd/starred2_example_1.xml
new file mode 100644
index 00000000..8c0104dd
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/starred2_example_1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <starred2>
+ <artist id="126" name="Iron Maiden" coverArt="ar-126" albumCount="1" starred="2012-04-05T19:03:31"/>
+ <artist id="133" name="Dimmu Borgir" coverArt="ar-133" albumCount="1" starred="2012-04-05T19:03:17"/>
+ <artist id="141" name="Kvelertak" albumCount="1" starred="2012-04-05T19:03:05"/>
+ <album id="180" name="Collapse Into Now" artist="R.E.M." artistId="144" songCount="12" duration="2459"
+ created="2011-03-23T09:37:55" starred="2012-04-05T19:02:02"/>
+ <album id="178" name="Postcards From A Young Man" artist="Manic Street Preachers" artistId="142"
+ coverArt="al-178" songCount="12" duration="2665" created="2011-02-26T10:47:19"
+ starred="2012-04-05T19:01:03"/>
+ <song id="143588" parent="143586" title="Born Treacherous" album="Abrahadabra" artist="Dimmu Borgir"
+ isDir="false" coverArt="143586" created="2010-09-27T20:52:23" starred="2012-04-02T17:17:01" duration="302"
+ bitRate="320" track="2" year="2010" genre="Scene-core" size="12087601" suffix="mp3"
+ contentType="audio/mpeg" isVideo="false" path="Dimmu Borgir/Abrahadabra/02 - Born Treacherous.mp3"
+ albumId="163" artistId="133" type="music"/>
+ <song id="143600" parent="143386" title="Satellite 15....The Final Frontier"
+ album="The Final Frontier (Mission Edition)" artist="Iron Maiden" isDir="false" coverArt="143386"
+ created="2010-08-16T21:08:01" starred="2012-04-02T14:12:54" duration="521" bitRate="320" track="1"
+ year="2010" genre="Heavy Metal" size="21855635" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Iron Maiden/2010 The Final Frontier/01 Satellite 15....The Final Frontier.mp3" albumId="156"
+ artistId="126" type="music"/>
+ <song id="143604" parent="143386" title="The Alchemist" album="The Final Frontier (Mission Edition)"
+ artist="Iron Maiden" isDir="false" coverArt="143386" created="2010-08-16T21:07:51"
+ starred="2012-04-02T14:12:52" duration="269" bitRate="320" track="5" year="2010" genre="Heavy Metal"
+ size="11774455" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Iron Maiden/2010 The Final Frontier/05 The Alchemist.mp3" albumId="156" artistId="126"
+ type="music"/>
+ </starred2>
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/starred_example_1.xml b/subsonic-main/src/main/webapp/xsd/starred_example_1.xml
new file mode 100644
index 00000000..916b2a7b
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/starred_example_1.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0">
+ <starred>
+ <artist name="Kvelertak" id="143408"/>
+ <artist name="Dimmu Borgir" id="143402"/>
+ <artist name="Iron Maiden" id="143403"/>
+ <album id="143862" parent="143410" title="Postcards From A Young Man" album="Postcards From A Young Man"
+ artist="Manic Street Preachers" isDir="true" coverArt="143862" created="2011-02-26T10:45:30"
+ starred="2012-04-05T18:40:08"/>
+ <album id="143888" parent="143412" title="Collapse Into Now" album="Collapse Into Now" artist="R.E.M."
+ isDir="true" created="2011-03-23T09:29:13" starred="2012-04-05T18:40:02"/>
+ <song id="143588" parent="143586" title="Born Treacherous" album="Abrahadabra" artist="Dimmu Borgir"
+ isDir="false" coverArt="143586" created="2010-09-27T20:52:23" starred="2012-04-02T17:17:01" duration="302"
+ bitRate="320" track="2" year="2010" genre="Scene-core" size="12087601" suffix="mp3"
+ contentType="audio/mpeg" isVideo="false" path="Dimmu Borgir/Abrahadabra/02 - Born Treacherous.mp3"
+ albumId="163" artistId="133" type="music"/>
+ <song id="143600" parent="143386" title="Satellite 15....The Final Frontier"
+ album="The Final Frontier (Mission Edition)" artist="Iron Maiden" isDir="false" coverArt="143386"
+ created="2010-08-16T21:08:01" starred="2012-04-02T14:12:54" duration="521" bitRate="320" track="1"
+ year="2010" genre="Heavy Metal" size="21855635" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Iron Maiden/2010 The Final Frontier/01 Satellite 15....The Final Frontier.mp3" albumId="156"
+ artistId="126" type="music"/>
+ <song id="143604" parent="143386" title="The Alchemist" album="The Final Frontier (Mission Edition)"
+ artist="Iron Maiden" isDir="false" coverArt="143386" created="2010-08-16T21:07:51"
+ starred="2012-04-02T14:12:52" duration="269" bitRate="320" track="5" year="2010" genre="Heavy Metal"
+ size="11774455" suffix="mp3" contentType="audio/mpeg" isVideo="false"
+ path="Iron Maiden/2010 The Final Frontier/05 The Alchemist.mp3" albumId="156" artistId="126"
+ type="music"/>
+ </starred>
+</subsonic-response>
diff --git a/subsonic-main/src/main/webapp/xsd/subsonic-rest-api.xsd b/subsonic-main/src/main/webapp/xsd/subsonic-rest-api.xsd
new file mode 100644
index 00000000..449b49e8
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/subsonic-rest-api.xsd
@@ -0,0 +1,434 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:sub="http://subsonic.org/restapi"
+ targetNamespace="http://subsonic.org/restapi"
+ attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ version="1.8.0">
+
+ <xs:element name="subsonic-response" type="sub:Response"/>
+
+ <xs:complexType name="Response">
+ <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:element name="musicFolders" type="sub:MusicFolders" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="indexes" type="sub:Indexes" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="directory" type="sub:Directory" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="artists" type="sub:ArtistsID3" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="artist" type="sub:ArtistWithAlbumsID3" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="album" type="sub:AlbumWithSongsID3" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="song" type="sub:Child" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="videos" type="sub:Videos" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="nowPlaying" type="sub:NowPlaying" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="searchResult" type="sub:SearchResult" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="searchResult2" type="sub:SearchResult2" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="searchResult3" type="sub:SearchResult3" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="playlists" type="sub:Playlists" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="playlist" type="sub:PlaylistWithSongs" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="jukeboxStatus" type="sub:JukeboxStatus" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="jukeboxPlaylist" type="sub:JukeboxPlaylist" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="license" type="sub:License" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="user" type="sub:User" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="chatMessages" type="sub:ChatMessages" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="albumList" type="sub:AlbumList" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="albumList2" type="sub:AlbumList2" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="randomSongs" type="sub:RandomSongs" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="lyrics" type="sub:Lyrics" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="podcasts" type="sub:Podcasts" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="shares" type="sub:Shares" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="starred" type="sub:Starred" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="starred2" type="sub:Starred2" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="error" type="sub:Error" minOccurs="1" maxOccurs="1"/>
+ </xs:choice>
+ <xs:attribute name="status" type="sub:ResponseStatus" use="required"/>
+ <xs:attribute name="version" type="sub:Version" use="required"/>
+ </xs:complexType>
+
+ <xs:simpleType name="ResponseStatus">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="ok"/>
+ <xs:enumeration value="failed"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="Version">
+ <xs:restriction base="xs:string">
+ <xs:pattern value="\d+\.\d+\.\d+"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="MusicFolders">
+ <xs:sequence>
+ <xs:element name="musicFolder" type="sub:MusicFolder" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="MusicFolder">
+ <xs:attribute name="id" type="xs:int" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="Indexes">
+ <xs:sequence>
+ <xs:element name="shortcut" type="sub:Artist" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="index" type="sub:Index" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="child" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/> <!-- Added in 1.7.0 -->
+ </xs:sequence>
+ <xs:attribute name="lastModified" type="xs:long" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="Index">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:Artist" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="Artist">
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="ArtistsID3">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:ArtistID3" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="ArtistID3">
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="coverArt" type="xs:string" use="optional"/>
+ <xs:attribute name="albumCount" type="xs:int" use="required"/>
+ <xs:attribute name="starred" type="xs:dateTime" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="ArtistWithAlbumsID3">
+ <xs:complexContent>
+ <xs:extension base="sub:ArtistID3">
+ <xs:sequence>
+ <xs:element name="album" type="sub:AlbumID3" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="AlbumID3">
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="artist" type="xs:string" use="optional"/>
+ <xs:attribute name="artistId" type="xs:string" use="optional"/>
+ <xs:attribute name="coverArt" type="xs:string" use="optional"/>
+ <xs:attribute name="songCount" type="xs:int" use="required"/>
+ <xs:attribute name="duration" type="xs:int" use="required"/>
+ <xs:attribute name="created" type="xs:dateTime" use="required"/>
+ <xs:attribute name="starred" type="xs:dateTime" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="AlbumWithSongsID3">
+ <xs:complexContent>
+ <xs:extension base="sub:AlbumID3">
+ <xs:sequence>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="Videos">
+ <xs:sequence>
+ <xs:element name="video" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Directory">
+ <xs:sequence>
+ <xs:element name="child" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="parent" type="xs:string" use="optional"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="Child">
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="parent" type="xs:string" use="optional"/>
+ <xs:attribute name="isDir" type="xs:boolean" use="required"/>
+ <xs:attribute name="title" type="xs:string" use="required"/>
+ <xs:attribute name="album" type="xs:string" use="optional"/>
+ <xs:attribute name="artist" type="xs:string" use="optional"/>
+ <xs:attribute name="track" type="xs:int" use="optional"/>
+ <xs:attribute name="year" type="xs:int" use="optional"/>
+ <xs:attribute name="genre" type="xs:string" use="optional"/>
+ <xs:attribute name="coverArt" type="xs:string" use="optional"/>
+ <xs:attribute name="size" type="xs:long" use="optional"/>
+ <xs:attribute name="contentType" type="xs:string" use="optional"/>
+ <xs:attribute name="suffix" type="xs:string" use="optional"/>
+ <xs:attribute name="transcodedContentType" type="xs:string" use="optional"/>
+ <xs:attribute name="transcodedSuffix" type="xs:string" use="optional"/>
+ <xs:attribute name="duration" type="xs:int" use="optional"/>
+ <xs:attribute name="bitRate" type="xs:int" use="optional"/>
+ <xs:attribute name="path" type="xs:string" use="optional"/>
+ <xs:attribute name="isVideo" type="xs:boolean" use="optional"/> <!-- Added in 1.4.1 -->
+ <xs:attribute name="userRating" type="sub:UserRating" use="optional"/> <!-- Added in 1.6.0 -->
+ <xs:attribute name="averageRating" type="sub:AverageRating" use="optional"/> <!-- Added in 1.6.0 -->
+ <xs:attribute name="discNumber" type="xs:int" use="optional"/> <!-- Added in 1.8.0 -->
+ <xs:attribute name="created" type="xs:dateTime" use="optional"/> <!-- Added in 1.8.0 -->
+ <xs:attribute name="starred" type="xs:dateTime" use="optional"/> <!-- Added in 1.8.0 -->
+ <xs:attribute name="albumId" type="xs:string" use="optional"/> <!-- Added in 1.8.0 -->
+ <xs:attribute name="artistId" type="xs:string" use="optional"/> <!-- Added in 1.8.0 -->
+ <xs:attribute name="type" type="sub:MediaType" use="optional"/> <!-- Added in 1.8.0 -->
+ </xs:complexType>
+
+ <xs:simpleType name="MediaType">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="music"/>
+ <xs:enumeration value="podcast"/>
+ <xs:enumeration value="audiobook"/>
+ <xs:enumeration value="video"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="UserRating">
+ <xs:restriction base="xs:int">
+ <xs:minInclusive value="1"/>
+ <xs:maxInclusive value="5"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:simpleType name="AverageRating">
+ <xs:restriction base="xs:double">
+ <xs:minInclusive value="1.0"/>
+ <xs:maxInclusive value="5.0"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="NowPlaying">
+ <xs:sequence>
+ <xs:element name="entry" type="sub:NowPlayingEntry" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="NowPlayingEntry">
+ <xs:complexContent>
+ <xs:extension base="sub:Child">
+ <xs:attribute name="username" type="xs:string" use="required"/>
+ <xs:attribute name="minutesAgo" type="xs:int" use="required"/>
+ <xs:attribute name="playerId" type="xs:int" use="required"/>
+ <xs:attribute name="playerName" type="xs:string" use="optional"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <!--Deprecated-->
+ <xs:complexType name="SearchResult">
+ <xs:sequence>
+ <xs:element name="match" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="offset" type="xs:int" use="required"/>
+ <xs:attribute name="totalHits" type="xs:int" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="SearchResult2">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:Artist" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="album" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="SearchResult3">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:ArtistID3" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="album" type="sub:AlbumID3" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Playlists">
+ <xs:sequence>
+ <xs:element name="playlist" type="sub:Playlist" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Playlist">
+ <xs:sequence>
+ <xs:element name="allowedUser" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> <!--Added in 1.8.0-->
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="comment" type="xs:string" use="optional"/> <!--Added in 1.8.0-->
+ <xs:attribute name="owner" type="xs:string" use="optional"/> <!--Added in 1.8.0-->
+ <xs:attribute name="public" type="xs:boolean" use="optional"/> <!--Added in 1.8.0-->
+ <xs:attribute name="songCount" type="xs:int" use="required"/> <!--Added in 1.8.0-->
+ <xs:attribute name="duration" type="xs:int" use="required"/> <!--Added in 1.8.0-->
+ <xs:attribute name="created" type="xs:dateTime" use="required"/> <!--Added in 1.8.0-->
+ </xs:complexType>
+
+ <xs:complexType name="PlaylistWithSongs">
+ <xs:complexContent>
+ <xs:extension base="sub:Playlist">
+ <xs:sequence>
+ <xs:element name="entry" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="JukeboxStatus">
+ <xs:attribute name="currentIndex" type="xs:int" use="required"/>
+ <xs:attribute name="playing" type="xs:boolean" use="required"/>
+ <xs:attribute name="gain" type="xs:float" use="required"/>
+ <xs:attribute name="position" type="xs:int" use="optional"/> <!--Added in 1.7.0-->
+ </xs:complexType>
+
+ <xs:complexType name="JukeboxPlaylist">
+ <xs:complexContent>
+ <xs:extension base="sub:JukeboxStatus">
+ <xs:sequence>
+ <xs:element name="entry" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:complexType name="ChatMessages">
+ <xs:sequence>
+ <xs:element name="chatMessage" type="sub:ChatMessage" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="ChatMessage">
+ <xs:attribute name="username" type="xs:string" use="required"/>
+ <xs:attribute name="time" type="xs:long" use="required"/>
+ <xs:attribute name="message" type="xs:string" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="AlbumList">
+ <xs:sequence>
+ <xs:element name="album" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="AlbumList2">
+ <xs:sequence>
+ <xs:element name="album" type="sub:AlbumID3" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="RandomSongs">
+ <xs:sequence>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Lyrics" mixed="true">
+ <xs:attribute name="artist" type="xs:string" use="optional"/>
+ <xs:attribute name="title" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="Podcasts">
+ <xs:sequence>
+ <xs:element name="channel" type="sub:PodcastChannel" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="PodcastChannel">
+ <xs:sequence>
+ <xs:element name="episode" type="sub:PodcastEpisode" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string" use="required"/>
+ <xs:attribute name="title" type="xs:string" use="optional"/>
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="status" type="sub:PodcastStatus" use="required"/>
+ <xs:attribute name="errorMessage" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="PodcastEpisode">
+ <xs:complexContent>
+ <xs:extension base="sub:Child">
+ <xs:attribute name="streamId" type="xs:string" use="optional"/> <!-- Use this ID for streaming the podcast. -->
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="status" type="sub:PodcastStatus" use="required"/>
+ <xs:attribute name="publishDate" type="xs:dateTime" use="optional"/>
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
+ <xs:simpleType name="PodcastStatus">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="new"/>
+ <xs:enumeration value="downloading"/>
+ <xs:enumeration value="completed"/>
+ <xs:enumeration value="error"/>
+ <xs:enumeration value="deleted"/>
+ <xs:enumeration value="skipped"/>
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="Shares">
+ <xs:sequence>
+ <xs:element name="share" type="sub:Share" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Share">
+ <xs:sequence>
+ <xs:element name="entry" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="id" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string" use="required"/>
+ <xs:attribute name="description" type="xs:string" use="optional"/>
+ <xs:attribute name="username" type="xs:string" use="required"/>
+ <xs:attribute name="created" type="xs:dateTime" use="required"/>
+ <xs:attribute name="expires" type="xs:dateTime" use="optional"/>
+ <xs:attribute name="lastVisited" type="xs:dateTime" use="optional"/>
+ <xs:attribute name="visitCount" type="xs:int" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="Starred">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:Artist" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="album" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="Starred2">
+ <xs:sequence>
+ <xs:element name="artist" type="sub:ArtistID3" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="album" type="sub:AlbumID3" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="song" type="sub:Child" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="License">
+ <xs:attribute name="valid" type="xs:boolean" use="required"/>
+ <xs:attribute name="email" type="xs:string" use="optional"/>
+ <xs:attribute name="key" type="xs:string" use="optional"/>
+ <xs:attribute name="date" type="xs:dateTime" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="User">
+ <xs:attribute name="username" type="xs:string" use="required"/>
+ <xs:attribute name="email" type="xs:string" use="optional"/> <!-- Added in 1.6.0 -->
+ <xs:attribute name="scrobblingEnabled" type="xs:boolean" use="required"/> <!-- Added in 1.7.0 -->
+ <xs:attribute name="adminRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="settingsRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="downloadRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="uploadRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="playlistRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="coverArtRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="commentRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="podcastRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="streamRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="jukeboxRole" type="xs:boolean" use="required"/>
+ <xs:attribute name="shareRole" type="xs:boolean" use="required"/> <!-- Added in 1.7.0 -->
+ </xs:complexType>
+
+ <xs:complexType name="Error">
+ <xs:attribute name="code" type="xs:int" use="required"/>
+ <xs:attribute name="message" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+</xs:schema> \ No newline at end of file
diff --git a/subsonic-main/src/main/webapp/xsd/user_example_1.xml b/subsonic-main/src/main/webapp/xsd/user_example_1.xml
new file mode 100644
index 00000000..9b96db67
--- /dev/null
+++ b/subsonic-main/src/main/webapp/xsd/user_example_1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<subsonic-response xmlns="http://subsonic.org/restapi"
+ status="ok" version="1.7.0">
+
+ <user username="sindre" email="sindre@activeobjects.no" scrobblingEnabled="true" adminRole="false" settingsRole="true" downloadRole="true" uploadRole="false" playlistRole="true"
+ coverArtRole="true" commentRole="true" podcastRole="true" streamRole="true" jukeboxRole="true" shareRole="false"/>
+
+</subsonic-response>
+