From 89ea01c4290f2c1973ed0a703f832072ccc0e6f2 Mon Sep 17 00:00:00 2001 From: DSB Date: Fri, 10 Jun 2011 21:28:27 +0000 Subject: [PATCH] Struggeling with relocating --- ajax/dump_ajax.php | 343 ++ ajax/install_get_language_files.php | 150 + ajax/restore_ajax.php | 369 ++ ajax/show_log_entry.php | 121 + ajax/show_table_field.php | 58 + ajax/show_tabledata_entry.php | 105 + css/msd/icons/ajax-loader.gif | Bin 0 -> 1849 bytes css/msd/icons/arrow_down.gif | Bin 0 -> 119 bytes css/msd/icons/arrow_up.gif | Bin 0 -> 117 bytes css/msd/icons/arrowleft.gif | Bin 0 -> 120 bytes css/msd/icons/browse.gif | Bin 0 -> 986 bytes css/msd/icons/close.gif | Bin 0 -> 117 bytes css/msd/icons/db.gif | Bin 0 -> 527 bytes css/msd/icons/delete.gif | Bin 0 -> 128 bytes css/msd/icons/download.png | Bin 0 -> 3418 bytes css/msd/icons/edit.gif | Bin 0 -> 976 bytes css/msd/icons/gz.gif | Bin 0 -> 990 bytes css/msd/icons/index.gif | Bin 0 -> 995 bytes css/msd/icons/key_fulltext.gif | Bin 0 -> 990 bytes css/msd/icons/key_nokey.gif | Bin 0 -> 527 bytes css/msd/icons/key_primary.gif | Bin 0 -> 987 bytes css/msd/icons/key_unique.gif | Bin 0 -> 994 bytes css/msd/icons/minus.gif | Bin 0 -> 186 bytes css/msd/icons/mysql_help.gif | Bin 0 -> 352 bytes css/msd/icons/notok.gif | Bin 0 -> 67 bytes css/msd/icons/ok.gif | Bin 0 -> 126 bytes css/msd/icons/openfile.gif | Bin 0 -> 135 bytes css/msd/icons/plus.gif | Bin 0 -> 190 bytes css/msd/icons/progressbar_dump.gif | Bin 0 -> 780 bytes css/msd/icons/progressbar_restore.gif | Bin 0 -> 680 bytes css/msd/icons/progressbar_speed.gif | Bin 0 -> 108 bytes css/msd/icons/save.png | Bin 0 -> 703 bytes css/msd/icons/search.gif | Bin 0 -> 986 bytes css/msd/icons/table_truncate.gif | Bin 0 -> 970 bytes css/msd/icons/table_truncate_reset.gif | Bin 0 -> 908 bytes css/msd/icons/truncate.gif | Bin 0 -> 128 bytes css/msd/pics/bg-body.gif | Bin 0 -> 1249 bytes css/msd/pics/bg-buttons.gif | Bin 0 -> 1298 bytes css/msd/pics/bg-headings.gif | Bin 0 -> 583 bytes css/msd/pics/h1_logo.gif | Bin 0 -> 3476 bytes css/msd/pics/loveyourdata.gif | Bin 0 -> 6340 bytes css/msd/pics/navi_bg.jpg | Bin 0 -> 4132 bytes css/msd/pics/navi_bg_expert.jpg | Bin 0 -> 5461 bytes css/msd/pics/truck_bg.png | Bin 0 -> 6539 bytes css/msd/style.css | 906 +++ docs/LICENSE.txt | 341 ++ docs/de/CHANGELOG.txt | 55 + docs/de/INSTALLATION.txt | 7 + docs/de/LIESMICH.txt | 90 + docs/en/CHANGELOG.txt | 52 + docs/en/INSTALL.txt | 7 + docs/en/README.txt | 80 + docs/fr/INSTALL.txt | 8 + docs/it/INSTALL.txt | 8 + images/logo.gif | Bin 0 -> 5219 bytes images/paypal-de.gif | Bin 0 -> 1356 bytes images/paypal-en.gif | Bin 0 -> 1352 bytes inc/classes/Log.php | 195 + inc/classes/db/MsdDbFactory.php | 249 + inc/classes/db/mysql/MsdDbMysql.php | 439 ++ inc/classes/db/mysqli/MsdDbMysqli.php | 413 ++ inc/classes/helper/File.php | 51 + inc/classes/helper/Html.php | 218 + inc/classes/helper/Sql.php | 87 + inc/classes/helper/String.php | 37 + inc/configuration.php | 94 + inc/configuration/autodelete.php | 46 + inc/configuration/config_files.php | 206 + inc/configuration/config_menu.php | 31 + inc/configuration/cronscript.php | 69 + inc/configuration/databases.php | 174 + inc/configuration/email.php | 184 + inc/configuration/footer.php | 44 + inc/configuration/ftp.php | 156 + inc/configuration/general.php | 146 + inc/configuration/interface.php | 100 + inc/define_icons.php | 69 + inc/dump.php | 31 + inc/dump/do_dump.php | 70 + inc/dump/dump_finished.php | 88 + inc/dump/dump_prepare.php | 158 + inc/dump/select_tables.php | 76 + inc/filemanagement/converter.php | 77 + inc/filemanagement/file_delete.php | 68 + inc/filemanagement/file_download.php | 41 + inc/filemanagement/file_upload.php | 49 + inc/filemanagement/files.php | 237 + inc/files.php | 37 + inc/functions/functions.php | 715 +++ inc/functions/functions_dump.php | 668 +++ inc/functions/functions_files.php | 370 ++ inc/functions/functions_global.php | 915 ++++ inc/functions/functions_restore.php | 426 ++ inc/functions/functions_sql.php | 403 ++ inc/home/home.php | 119 + inc/home/phpinfo.php | 12 + inc/home/protection_create.php | 144 + inc/home/protection_delete.php | 17 + inc/home/protection_edit.php | 57 + inc/log.php | 133 + inc/menu.php | 151 + inc/mysql.php | 225 + inc/restore.php | 19 + inc/restore/restore_finished.php | 91 + inc/restore/restore_prepare.php | 250 + inc/restore/restore_start.php | 148 + inc/restore/select_tables.php | 63 + inc/runtime.php | 229 + inc/sql.php | 61 + inc/sqlbrowser/db/list_databases.php | 185 + inc/sqlbrowser/general/footer.php | 17 + inc/sqlbrowser/general/mysql_variables.php | 65 + inc/sqlbrowser/general/process.php | 96 + inc/sqlbrowser/general/sqlbox.php | 122 + inc/sqlbrowser/general/status.php | 72 + inc/sqlbrowser/mysql_search.php | 405 ++ inc/sqlbrowser/nav/topnav_general.php | 26 + inc/sqlbrowser/sqlbox/show_results.php | 382 ++ inc/sqlbrowser/table/edit_table.php | 115 + inc/sqlbrowser/table/list_tables.php | 259 + inc/sqlbrowser/table/show_tabledata.php | 230 + inc/sqllib.php | 60 + index.php | 138 + install.php | 276 + js/growler/growler.js | 176 + js/highslide/graphics/close.png | Bin 0 -> 1910 bytes js/highslide/graphics/closeX.png | Bin 0 -> 3665 bytes .../graphics/controlbar-black-border.gif | Bin 0 -> 5109 bytes .../graphics/controlbar-text-buttons.png | Bin 0 -> 1300 bytes .../graphics/controlbar-white-small.gif | Bin 0 -> 3151 bytes js/highslide/graphics/controlbar-white.gif | Bin 0 -> 4999 bytes js/highslide/graphics/controlbar2.gif | Bin 0 -> 884 bytes js/highslide/graphics/controlbar3.gif | Bin 0 -> 838 bytes js/highslide/graphics/controlbar4-hover.gif | Bin 0 -> 2410 bytes js/highslide/graphics/controlbar4.gif | Bin 0 -> 854 bytes js/highslide/graphics/fullexpand.gif | Bin 0 -> 209 bytes js/highslide/graphics/geckodimmer.png | Bin 0 -> 2817 bytes js/highslide/graphics/icon.gif | Bin 0 -> 867 bytes js/highslide/graphics/loader.gif | Bin 0 -> 668 bytes js/highslide/graphics/loader.white.gif | Bin 0 -> 673 bytes js/highslide/graphics/outlines/beveled.png | Bin 0 -> 1848 bytes .../graphics/outlines/drop-shadow.png | Bin 0 -> 2091 bytes .../graphics/outlines/glossy-dark.png | Bin 0 -> 2793 bytes js/highslide/graphics/outlines/outer-glow.png | Bin 0 -> 3423 bytes .../graphics/outlines/rounded-black.png | Bin 0 -> 3031 bytes .../graphics/outlines/rounded-white.png | Bin 0 -> 2050 bytes js/highslide/graphics/resize.gif | Bin 0 -> 70 bytes js/highslide/graphics/scrollarrows.png | Bin 0 -> 6463 bytes js/highslide/graphics/zoomin.cur | Bin 0 -> 326 bytes js/highslide/graphics/zoomout.cur | Bin 0 -> 326 bytes js/highslide/highslide-with-html.js | 2486 +++++++++ js/highslide/highslide.css | 1047 ++++ js/jquery/jquery.min.js | 154 + js/prototype/prototype.js | 4874 +++++++++++++++++ js/script.js | 323 ++ js/scriptaculous/MIT-LICENSE | 20 + js/scriptaculous/lib/builder.js | 136 + js/scriptaculous/lib/controls.js | 965 ++++ js/scriptaculous/lib/dragdrop.js | 975 ++++ js/scriptaculous/lib/effects.js | 1130 ++++ js/scriptaculous/lib/scriptaculous.js | 60 + js/scriptaculous/lib/slider.js | 275 + js/scriptaculous/lib/sound.js | 55 + js/stereotabs/stereotabs.js | 93 + js/strength.jpg | Bin 0 -> 602 bytes language/ar/lang.php | 1100 ++++ language/ch/lang.php | 910 +++ language/cs/lang.php | 1092 ++++ language/da/lang.php | 892 +++ language/de/lang.php | 976 ++++ language/de_du/lang.php | 974 ++++ language/el/lang.php | 1313 +++++ language/en/lang.php | 899 +++ language/es/lang.php | 980 ++++ language/fa/lang.php | 1133 ++++ language/flags/ar.gif | Bin 0 -> 226 bytes language/flags/ch.gif | Bin 0 -> 1765 bytes language/flags/cs.gif | Bin 0 -> 600 bytes language/flags/da.gif | Bin 0 -> 117 bytes language/flags/de.gif | Bin 0 -> 158 bytes language/flags/de_du.gif | Bin 0 -> 158 bytes language/flags/el.gif | Bin 0 -> 696 bytes language/flags/en.gif | Bin 0 -> 206 bytes language/flags/es.gif | Bin 0 -> 141 bytes language/flags/fa.gif | Bin 0 -> 681 bytes language/flags/fr.gif | Bin 0 -> 140 bytes language/flags/index.htm | 10 + language/flags/it.gif | Bin 0 -> 140 bytes language/flags/lu.gif | Bin 0 -> 232 bytes language/flags/nl.gif | Bin 0 -> 141 bytes language/flags/pl.gif | Bin 0 -> 1121 bytes language/flags/pt.gif | Bin 0 -> 3316 bytes language/flags/pt_br.gif | Bin 0 -> 222 bytes language/flags/ro.gif | Bin 0 -> 370 bytes language/flags/ru.gif | Bin 0 -> 118 bytes language/flags/si.gif | Bin 0 -> 890 bytes language/flags/sk.gif | Bin 0 -> 132 bytes language/flags/sw.gif | Bin 0 -> 117 bytes language/flags/tr.gif | Bin 0 -> 1245 bytes language/flags/vn.gif | Bin 0 -> 928 bytes language/flags/width25/ar.gif | Bin 0 -> 134 bytes language/flags/width25/ch.gif | Bin 0 -> 1103 bytes language/flags/width25/cs.gif | Bin 0 -> 545 bytes language/flags/width25/da.gif | Bin 0 -> 97 bytes language/flags/width25/de.gif | Bin 0 -> 115 bytes language/flags/width25/de_du.gif | Bin 0 -> 115 bytes language/flags/width25/el.gif | Bin 0 -> 627 bytes language/flags/width25/en.gif | Bin 0 -> 127 bytes language/flags/width25/es.gif | Bin 0 -> 106 bytes language/flags/width25/fa.gif | Bin 0 -> 620 bytes language/flags/width25/fr.gif | Bin 0 -> 128 bytes language/flags/width25/index.htm | 10 + language/flags/width25/it.gif | Bin 0 -> 128 bytes language/flags/width25/lu.gif | Bin 0 -> 232 bytes language/flags/width25/nl.gif | Bin 0 -> 93 bytes language/flags/width25/pl.gif | Bin 0 -> 428 bytes language/flags/width25/pt.gif | Bin 0 -> 417 bytes language/flags/width25/pt_br.gif | Bin 0 -> 180 bytes language/flags/width25/ro.gif | Bin 0 -> 211 bytes language/flags/width25/ru.gif | Bin 0 -> 109 bytes language/flags/width25/si.gif | Bin 0 -> 892 bytes language/flags/width25/sk.gif | Bin 0 -> 110 bytes language/flags/width25/sw.gif | Bin 0 -> 97 bytes language/flags/width25/tr.gif | Bin 0 -> 937 bytes language/flags/width25/vn.gif | Bin 0 -> 768 bytes language/fr/lang.php | 1007 ++++ language/it/lang.php | 943 ++++ language/lang_list.php | 47 + language/lu/lang.php | 899 +++ language/nl/lang.php | 912 +++ language/pl/lang.php | 899 +++ language/pt_br/lang.php | 942 ++++ language/ro/lang.php | 907 +++ language/ru/lang.php | 965 ++++ language/si/lang.php | 899 +++ language/sk/lang.php | 927 ++++ language/sw/lang.php | 885 +++ language/tr/lang.php | 913 +++ language/vn/lang.php | 1009 ++++ lib/json.php | 804 +++ lib/phpmailer/php5/class.phpmailer.php | 2214 ++++++++ lib/phpmailer/php5/class.smtp.php | 727 +++ lib/template.php | 388 ++ msd_cron/crondump.pl | 1561 ++++++ msd_cron/perltest.pl | 203 + msd_cron/simpletest.pl | 38 + refresh_dblist.php | 58 + tpl/configuration/autodelete.tpl | 21 + tpl/configuration/configFiles.tpl | 118 + tpl/configuration/config_menu.tpl | 46 + tpl/configuration/cronscript.tpl | 43 + tpl/configuration/databases.tpl | 94 + tpl/configuration/email.tpl | 207 + tpl/configuration/footer.tpl | 20 + tpl/configuration/ftp.tpl | 102 + tpl/configuration/general.tpl | 103 + tpl/configuration/interface.tpl | 72 + tpl/credits/credits.tpl | 14 + tpl/dump/dump.tpl | 266 + tpl/dump/dump_finished.tpl | 80 + tpl/dump/dump_prepare.tpl | 221 + tpl/dump/selectTables.tpl | 51 + tpl/filemanagement/converter.tpl | 50 + tpl/filemanagement/files.tpl | 191 + tpl/globalHeader.tpl | 41 + tpl/home/home.tpl | 62 + tpl/home/protection_create.tpl | 162 + tpl/home/protection_edit.tpl | 76 + tpl/install/check_directories.tpl | 45 + tpl/install/db_parameter.tpl | 108 + tpl/install/header.tpl | 37 + tpl/install/select_language.tpl | 148 + tpl/log/log.tpl | 72 + tpl/log/log_ajax.tpl | 20 + tpl/menu/menu.tpl | 83 + tpl/restore/file_select_encoding.tpl | 24 + tpl/restore/restore.tpl | 218 + tpl/restore/restorePrepare.tpl | 106 + tpl/restore/restore_finished.tpl | 87 + tpl/restore/selectTables.tpl | 56 + tpl/sqlbrowser/db/list_databases.tpl | 111 + tpl/sqlbrowser/db/operation.tpl | 32 + tpl/sqlbrowser/general/footer.tpl | 1 + tpl/sqlbrowser/general/mysqlVariables.tpl | 28 + tpl/sqlbrowser/general/process.tpl | 59 + tpl/sqlbrowser/general/sqlbox.tpl | 52 + tpl/sqlbrowser/general/status.tpl | 31 + tpl/sqlbrowser/general/tools.tpl | 1 + tpl/sqlbrowser/mysql_search.tpl | 82 + tpl/sqlbrowser/nav/topnav_general.tpl | 8 + .../sql_record_insert_inputmask.tpl | 52 + .../sql_record_update_inputmask.tpl | 55 + tpl/sqlbrowser/sqlbox/showQueryResults.tpl | 26 + tpl/sqlbrowser/sqlbox/showResults.tpl | 57 + tpl/sqlbrowser/sqlbox/sqlbox.tpl | 66 + tpl/sqlbrowser/table/edit_field.tpl | 27 + tpl/sqlbrowser/table/edit_table.tpl | 204 + tpl/sqlbrowser/table/listTables.tpl | 252 + tpl/sqlbrowser/table/operation.tpl | 34 + tpl/sqlbrowser/table/show_tabledata.tpl | 165 + tpl/sqlbrowser/table/show_tabledata_entry.tpl | 30 + 301 files changed, 59926 insertions(+) create mode 100644 ajax/dump_ajax.php create mode 100644 ajax/install_get_language_files.php create mode 100644 ajax/restore_ajax.php create mode 100644 ajax/show_log_entry.php create mode 100644 ajax/show_table_field.php create mode 100644 ajax/show_tabledata_entry.php create mode 100644 css/msd/icons/ajax-loader.gif create mode 100644 css/msd/icons/arrow_down.gif create mode 100644 css/msd/icons/arrow_up.gif create mode 100644 css/msd/icons/arrowleft.gif create mode 100644 css/msd/icons/browse.gif create mode 100644 css/msd/icons/close.gif create mode 100644 css/msd/icons/db.gif create mode 100644 css/msd/icons/delete.gif create mode 100644 css/msd/icons/download.png create mode 100644 css/msd/icons/edit.gif create mode 100644 css/msd/icons/gz.gif create mode 100644 css/msd/icons/index.gif create mode 100644 css/msd/icons/key_fulltext.gif create mode 100644 css/msd/icons/key_nokey.gif create mode 100644 css/msd/icons/key_primary.gif create mode 100644 css/msd/icons/key_unique.gif create mode 100644 css/msd/icons/minus.gif create mode 100644 css/msd/icons/mysql_help.gif create mode 100644 css/msd/icons/notok.gif create mode 100644 css/msd/icons/ok.gif create mode 100644 css/msd/icons/openfile.gif create mode 100644 css/msd/icons/plus.gif create mode 100644 css/msd/icons/progressbar_dump.gif create mode 100644 css/msd/icons/progressbar_restore.gif create mode 100644 css/msd/icons/progressbar_speed.gif create mode 100644 css/msd/icons/save.png create mode 100644 css/msd/icons/search.gif create mode 100644 css/msd/icons/table_truncate.gif create mode 100644 css/msd/icons/table_truncate_reset.gif create mode 100644 css/msd/icons/truncate.gif create mode 100644 css/msd/pics/bg-body.gif create mode 100644 css/msd/pics/bg-buttons.gif create mode 100644 css/msd/pics/bg-headings.gif create mode 100644 css/msd/pics/h1_logo.gif create mode 100644 css/msd/pics/loveyourdata.gif create mode 100644 css/msd/pics/navi_bg.jpg create mode 100644 css/msd/pics/navi_bg_expert.jpg create mode 100644 css/msd/pics/truck_bg.png create mode 100644 css/msd/style.css create mode 100644 docs/LICENSE.txt create mode 100644 docs/de/CHANGELOG.txt create mode 100644 docs/de/INSTALLATION.txt create mode 100644 docs/de/LIESMICH.txt create mode 100644 docs/en/CHANGELOG.txt create mode 100644 docs/en/INSTALL.txt create mode 100644 docs/en/README.txt create mode 100644 docs/fr/INSTALL.txt create mode 100644 docs/it/INSTALL.txt create mode 100644 images/logo.gif create mode 100644 images/paypal-de.gif create mode 100644 images/paypal-en.gif create mode 100644 inc/classes/Log.php create mode 100644 inc/classes/db/MsdDbFactory.php create mode 100644 inc/classes/db/mysql/MsdDbMysql.php create mode 100644 inc/classes/db/mysqli/MsdDbMysqli.php create mode 100644 inc/classes/helper/File.php create mode 100644 inc/classes/helper/Html.php create mode 100644 inc/classes/helper/Sql.php create mode 100644 inc/classes/helper/String.php create mode 100644 inc/configuration.php create mode 100644 inc/configuration/autodelete.php create mode 100644 inc/configuration/config_files.php create mode 100644 inc/configuration/config_menu.php create mode 100644 inc/configuration/cronscript.php create mode 100644 inc/configuration/databases.php create mode 100644 inc/configuration/email.php create mode 100644 inc/configuration/footer.php create mode 100644 inc/configuration/ftp.php create mode 100644 inc/configuration/general.php create mode 100644 inc/configuration/interface.php create mode 100644 inc/define_icons.php create mode 100644 inc/dump.php create mode 100644 inc/dump/do_dump.php create mode 100644 inc/dump/dump_finished.php create mode 100644 inc/dump/dump_prepare.php create mode 100644 inc/dump/select_tables.php create mode 100644 inc/filemanagement/converter.php create mode 100644 inc/filemanagement/file_delete.php create mode 100644 inc/filemanagement/file_download.php create mode 100644 inc/filemanagement/file_upload.php create mode 100644 inc/filemanagement/files.php create mode 100644 inc/files.php create mode 100644 inc/functions/functions.php create mode 100644 inc/functions/functions_dump.php create mode 100644 inc/functions/functions_files.php create mode 100644 inc/functions/functions_global.php create mode 100644 inc/functions/functions_restore.php create mode 100644 inc/functions/functions_sql.php create mode 100644 inc/home/home.php create mode 100644 inc/home/phpinfo.php create mode 100644 inc/home/protection_create.php create mode 100644 inc/home/protection_delete.php create mode 100644 inc/home/protection_edit.php create mode 100644 inc/log.php create mode 100644 inc/menu.php create mode 100644 inc/mysql.php create mode 100644 inc/restore.php create mode 100644 inc/restore/restore_finished.php create mode 100644 inc/restore/restore_prepare.php create mode 100644 inc/restore/restore_start.php create mode 100644 inc/restore/select_tables.php create mode 100644 inc/runtime.php create mode 100644 inc/sql.php create mode 100644 inc/sqlbrowser/db/list_databases.php create mode 100644 inc/sqlbrowser/general/footer.php create mode 100644 inc/sqlbrowser/general/mysql_variables.php create mode 100644 inc/sqlbrowser/general/process.php create mode 100644 inc/sqlbrowser/general/sqlbox.php create mode 100644 inc/sqlbrowser/general/status.php create mode 100644 inc/sqlbrowser/mysql_search.php create mode 100644 inc/sqlbrowser/nav/topnav_general.php create mode 100644 inc/sqlbrowser/sqlbox/show_results.php create mode 100644 inc/sqlbrowser/table/edit_table.php create mode 100644 inc/sqlbrowser/table/list_tables.php create mode 100644 inc/sqlbrowser/table/show_tabledata.php create mode 100644 inc/sqllib.php create mode 100644 index.php create mode 100644 install.php create mode 100644 js/growler/growler.js create mode 100644 js/highslide/graphics/close.png create mode 100644 js/highslide/graphics/closeX.png create mode 100644 js/highslide/graphics/controlbar-black-border.gif create mode 100644 js/highslide/graphics/controlbar-text-buttons.png create mode 100644 js/highslide/graphics/controlbar-white-small.gif create mode 100644 js/highslide/graphics/controlbar-white.gif create mode 100644 js/highslide/graphics/controlbar2.gif create mode 100644 js/highslide/graphics/controlbar3.gif create mode 100644 js/highslide/graphics/controlbar4-hover.gif create mode 100644 js/highslide/graphics/controlbar4.gif create mode 100644 js/highslide/graphics/fullexpand.gif create mode 100644 js/highslide/graphics/geckodimmer.png create mode 100644 js/highslide/graphics/icon.gif create mode 100644 js/highslide/graphics/loader.gif create mode 100644 js/highslide/graphics/loader.white.gif create mode 100644 js/highslide/graphics/outlines/beveled.png create mode 100644 js/highslide/graphics/outlines/drop-shadow.png create mode 100644 js/highslide/graphics/outlines/glossy-dark.png create mode 100644 js/highslide/graphics/outlines/outer-glow.png create mode 100644 js/highslide/graphics/outlines/rounded-black.png create mode 100644 js/highslide/graphics/outlines/rounded-white.png create mode 100644 js/highslide/graphics/resize.gif create mode 100644 js/highslide/graphics/scrollarrows.png create mode 100644 js/highslide/graphics/zoomin.cur create mode 100644 js/highslide/graphics/zoomout.cur create mode 100644 js/highslide/highslide-with-html.js create mode 100644 js/highslide/highslide.css create mode 100644 js/jquery/jquery.min.js create mode 100644 js/prototype/prototype.js create mode 100644 js/script.js create mode 100644 js/scriptaculous/MIT-LICENSE create mode 100644 js/scriptaculous/lib/builder.js create mode 100644 js/scriptaculous/lib/controls.js create mode 100644 js/scriptaculous/lib/dragdrop.js create mode 100644 js/scriptaculous/lib/effects.js create mode 100644 js/scriptaculous/lib/scriptaculous.js create mode 100644 js/scriptaculous/lib/slider.js create mode 100644 js/scriptaculous/lib/sound.js create mode 100644 js/stereotabs/stereotabs.js create mode 100644 js/strength.jpg create mode 100644 language/ar/lang.php create mode 100644 language/ch/lang.php create mode 100644 language/cs/lang.php create mode 100644 language/da/lang.php create mode 100644 language/de/lang.php create mode 100644 language/de_du/lang.php create mode 100644 language/el/lang.php create mode 100644 language/en/lang.php create mode 100644 language/es/lang.php create mode 100644 language/fa/lang.php create mode 100644 language/flags/ar.gif create mode 100644 language/flags/ch.gif create mode 100644 language/flags/cs.gif create mode 100644 language/flags/da.gif create mode 100644 language/flags/de.gif create mode 100644 language/flags/de_du.gif create mode 100644 language/flags/el.gif create mode 100644 language/flags/en.gif create mode 100644 language/flags/es.gif create mode 100644 language/flags/fa.gif create mode 100644 language/flags/fr.gif create mode 100644 language/flags/index.htm create mode 100644 language/flags/it.gif create mode 100644 language/flags/lu.gif create mode 100644 language/flags/nl.gif create mode 100644 language/flags/pl.gif create mode 100644 language/flags/pt.gif create mode 100644 language/flags/pt_br.gif create mode 100644 language/flags/ro.gif create mode 100644 language/flags/ru.gif create mode 100644 language/flags/si.gif create mode 100644 language/flags/sk.gif create mode 100644 language/flags/sw.gif create mode 100644 language/flags/tr.gif create mode 100644 language/flags/vn.gif create mode 100644 language/flags/width25/ar.gif create mode 100644 language/flags/width25/ch.gif create mode 100644 language/flags/width25/cs.gif create mode 100644 language/flags/width25/da.gif create mode 100644 language/flags/width25/de.gif create mode 100644 language/flags/width25/de_du.gif create mode 100644 language/flags/width25/el.gif create mode 100644 language/flags/width25/en.gif create mode 100644 language/flags/width25/es.gif create mode 100644 language/flags/width25/fa.gif create mode 100644 language/flags/width25/fr.gif create mode 100644 language/flags/width25/index.htm create mode 100644 language/flags/width25/it.gif create mode 100644 language/flags/width25/lu.gif create mode 100644 language/flags/width25/nl.gif create mode 100644 language/flags/width25/pl.gif create mode 100644 language/flags/width25/pt.gif create mode 100644 language/flags/width25/pt_br.gif create mode 100644 language/flags/width25/ro.gif create mode 100644 language/flags/width25/ru.gif create mode 100644 language/flags/width25/si.gif create mode 100644 language/flags/width25/sk.gif create mode 100644 language/flags/width25/sw.gif create mode 100644 language/flags/width25/tr.gif create mode 100644 language/flags/width25/vn.gif create mode 100644 language/fr/lang.php create mode 100644 language/it/lang.php create mode 100644 language/lang_list.php create mode 100644 language/lu/lang.php create mode 100644 language/nl/lang.php create mode 100644 language/pl/lang.php create mode 100644 language/pt_br/lang.php create mode 100644 language/ro/lang.php create mode 100644 language/ru/lang.php create mode 100644 language/si/lang.php create mode 100644 language/sk/lang.php create mode 100644 language/sw/lang.php create mode 100644 language/tr/lang.php create mode 100644 language/vn/lang.php create mode 100644 lib/json.php create mode 100644 lib/phpmailer/php5/class.phpmailer.php create mode 100644 lib/phpmailer/php5/class.smtp.php create mode 100644 lib/template.php create mode 100644 msd_cron/crondump.pl create mode 100644 msd_cron/perltest.pl create mode 100644 msd_cron/simpletest.pl create mode 100644 refresh_dblist.php create mode 100644 tpl/configuration/autodelete.tpl create mode 100644 tpl/configuration/configFiles.tpl create mode 100644 tpl/configuration/config_menu.tpl create mode 100644 tpl/configuration/cronscript.tpl create mode 100644 tpl/configuration/databases.tpl create mode 100644 tpl/configuration/email.tpl create mode 100644 tpl/configuration/footer.tpl create mode 100644 tpl/configuration/ftp.tpl create mode 100644 tpl/configuration/general.tpl create mode 100644 tpl/configuration/interface.tpl create mode 100644 tpl/credits/credits.tpl create mode 100644 tpl/dump/dump.tpl create mode 100644 tpl/dump/dump_finished.tpl create mode 100644 tpl/dump/dump_prepare.tpl create mode 100644 tpl/dump/selectTables.tpl create mode 100644 tpl/filemanagement/converter.tpl create mode 100644 tpl/filemanagement/files.tpl create mode 100644 tpl/globalHeader.tpl create mode 100644 tpl/home/home.tpl create mode 100644 tpl/home/protection_create.tpl create mode 100644 tpl/home/protection_edit.tpl create mode 100644 tpl/install/check_directories.tpl create mode 100644 tpl/install/db_parameter.tpl create mode 100644 tpl/install/header.tpl create mode 100644 tpl/install/select_language.tpl create mode 100644 tpl/log/log.tpl create mode 100644 tpl/log/log_ajax.tpl create mode 100644 tpl/menu/menu.tpl create mode 100644 tpl/restore/file_select_encoding.tpl create mode 100644 tpl/restore/restore.tpl create mode 100644 tpl/restore/restorePrepare.tpl create mode 100644 tpl/restore/restore_finished.tpl create mode 100644 tpl/restore/selectTables.tpl create mode 100644 tpl/sqlbrowser/db/list_databases.tpl create mode 100644 tpl/sqlbrowser/db/operation.tpl create mode 100644 tpl/sqlbrowser/general/footer.tpl create mode 100644 tpl/sqlbrowser/general/mysqlVariables.tpl create mode 100644 tpl/sqlbrowser/general/process.tpl create mode 100644 tpl/sqlbrowser/general/sqlbox.tpl create mode 100644 tpl/sqlbrowser/general/status.tpl create mode 100644 tpl/sqlbrowser/general/tools.tpl create mode 100644 tpl/sqlbrowser/mysql_search.tpl create mode 100644 tpl/sqlbrowser/nav/topnav_general.tpl create mode 100644 tpl/sqlbrowser/sql_record_insert_inputmask.tpl create mode 100644 tpl/sqlbrowser/sql_record_update_inputmask.tpl create mode 100644 tpl/sqlbrowser/sqlbox/showQueryResults.tpl create mode 100644 tpl/sqlbrowser/sqlbox/showResults.tpl create mode 100644 tpl/sqlbrowser/sqlbox/sqlbox.tpl create mode 100644 tpl/sqlbrowser/table/edit_field.tpl create mode 100644 tpl/sqlbrowser/table/edit_table.tpl create mode 100644 tpl/sqlbrowser/table/listTables.tpl create mode 100644 tpl/sqlbrowser/table/operation.tpl create mode 100644 tpl/sqlbrowser/table/show_tabledata.tpl create mode 100644 tpl/sqlbrowser/table/show_tabledata_entry.tpl diff --git a/ajax/dump_ajax.php b/ajax/dump_ajax.php new file mode 100644 index 0000000..55552c1 --- /dev/null +++ b/ajax/dump_ajax.php @@ -0,0 +1,343 @@ +setConnectionCharset($dump['dump_encoding']); +// each time a new database will be dumped -> +// look for Command before dump to be executed +if ($dump['table_offset'] == - 1) + executeCommand('b'); +$dump['data'] = ''; // will hold string data to be saved to the dump file +// needed to find out if new log-messages were added +$_SESSION['temp_log'] = $_SESSION['log']; +if (! isset($dump['table_records_total'])) + $dump['table_records_total'] = 0; +$tableIndex = $dump['table_offset'] == - 1 ? 0 : $dump['table_offset']; +if ($dump['backup_done'] == 0) { + if ($dump['databases'][$dump['db_actual']]['table_count'] == 0) { + //no tables found -> prevent creation of empty backupfile + $msg = sprintf($lang['L_DUMP_NOTABLES'], $dump['db_actual']); + if ($dump['databases'][$dump['db_actual']]['prefix'] != '') { + $msg = sprintf($lang['L_DUMP_NOTABLES'], $dump['db_actual']); + $msg .= ' (' . $lang['L_WITHPRAEFIX'] . ': \'' ; + $msg .= $dump['databases'][$dump['db_actual']]['prefix']. '\')'; + } + writeToErrorLog($dump['db_actual'], '', $msg); + $dbsToBackup = array( + $dump['db_actual']); + $dump['backupdatei'] = ''; + $dump['filesize'] = 0; + checkForNextDB(); + } else { + if ($dump['table_offset'] == - 1) { + // first call for this database -> create new backup file + createNewFile(); + $dump['table_offset'] = 0; // begin with first table + $dump['table_record_offset'] = 0; + $dump['restzeilen'] = $config['minspeed']; + } + $dump['restzeilen'] = $dump['speed']; + while ($dump['restzeilen'] > 0 && $dump['table_offset'] < + $dump['databases'][$dump['db_actual']]['table_count']) { + $tableNames = $dump['databases'][$dump['db_actual']]['tables']; + $table = getValueFromIndex($tableNames, $dump['table_offset']); + if ($dump['table_record_offset'] == 0) { + $dbo->selectDb($dump['db_actual']); + // a new table begins + // optimize it? + if ($config['optimize_tables_beforedump'] == 1) { + if (true === Sql::optimizeTable($dbo, $table)) { + $dump['tables_optimized']++; + } + } + $recordOffset = 0; + $recordsTotal = 0; + $dump['progress_table_percent'] = 0; + $dump['speed'] = $config['minspeed']; + // should we dump the table structure? + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['dump_structure'] > 0) { + // get create statement of table + try { + $records = $dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records']; + $createStatement = getCreateString($dump['db_actual'], $table, $records); + $dump['data'] .= $createStatement; + } catch (Exception $e) { + // error reading table definition + writeToDumpFile(); // save data we have up to now + $logMsg = sprintf($lang['L_FATAL_ERROR_DUMP'], $table, $dump['db_actual']); + $readCreateError = $logMsg . ': ' . $e->getMessage(); + writeToErrorLog($config['db_actual'], '', $readCreateError, 0); + $log->write(Log::ERROR, $readCreateError); + $dump['errors'] ++; + //next table + $dump['table_offset'] ++; + $dump['table_record_offset'] = 0; + // set records of table not to be dumped + $dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records'] = 0; + } + } + } + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records'] ==1) { + getContent($dump['db_actual'], $table); + } else { + //jump to next table if we don't need to dump the records of this table + $dump['table_offset'] ++; + } + if (strlen($dump['data']) > $config['memory_limit']) { + writeToDumpFile(); + } + } + // create list of databases for output + $dbsToBackup = implode(', ', array_keys($dump['databases'])); + // highligth actual db + $replace = '' . $dump['db_actual'] . ''; + $dbsToBackup = str_replace($dump['db_actual'], $replace, $dbsToBackup); + // we need to get the actual table again because it might have changed + $table = getValueFromIndex($dump['databases'][$dump['db_actual']]['tables'], $dump['table_offset']); + if ($table) { + // get nr of records from dump-array + $dump['table_records_total'] = $dump['databases'][$dump['db_actual']]['tables'][$table]['records']; + if ($dump['table_records_total'] > 0) { + $percent = (100 * $dump['table_record_offset']) / $dump['table_records_total']; + $dump['progress_table_percent'] = round($percent, 2); + } else { + $dump['progress_table_percent'] = 0; + } + if ($dump['speed'] + $dump['table_record_offset'] >= + $dump['table_records_total']) { + $recordOffset = $dump['table_record_offset'] + + 1; + $recordsTotal = $dump['table_records_total']; + if ($recordsTotal == 0) { + $recordOffset = 0; + } + } else { + $recordsTotal = $dump['table_record_offset'] + $dump['speed']; + $recordOffset = $dump['table_record_offset'] + 1; + } + } else { + // looks like we've done the job + $dump['table_offset'] ++; + $dump['table_records_total'] = 0; + $table = ''; + } + writeToDumpFile(); + if ($dump['table_offset'] <= $dump['databases'][$dump['db_actual']]['table_count']) { + $dauer = time() - $dump['page_start_time']; + //Zeitanpassung + if ($dauer < $dump['max_zeit']) { + if ($dauer < $dump['max_zeit'] / 2) { + $dump['speed'] *= 1.8; + } else { + $dump['speed'] *= $config['tuning_add']; + } + if ($dump['speed'] > $config['maxspeed']) { + $dump['speed'] = $config['maxspeed']; + } + } else { + $dump['speed'] *= $config['tuning_sub']; + if ($dump['speed'] < $config['minspeed']) { + $dump['speed'] = $config['minspeed']; + } + } + $dump['speed'] = (int) $dump['speed']; + $dump['page_refreshs'] ++; + } else { + //Backup for all databases is done + $dump['data'] = "\nSET FOREIGN_KEY_CHECKS=1;"; + $dump['data'] .= "\n" . + '-- EOB' . "\n\n"; + writeToDumpFile(); + executeCommand('a'); + chmod($config['paths']['backup'] . $dump['backupdatei'], 0777); + $logMsg = sprintf($lang['L_DUMP_OF_DB_FINISHED'], $dump['db_actual']); + $log->write(Log::PHP, $logMsg); + checkForNextDB(); + } + } +} +// everything is dumped -> check for e-mail and ftp-actions +if ($dump['backup_done'] == 1) { + if (count($_SESSION['log']['files_created']) > 0) { + if (! isset($_SESSION['log']['email'])) { + // first call after backup is finished -> create todo-list + $_SESSION['log']['email'] = array(); + $_SESSION['email']['filelist'] = array(); + $_SESSION['log']['ftp'] = array(); + foreach ($_SESSION['log']['files_created'] as $file) { + if ($config['send_mail'] == 1) { + $_SESSION['log']['email'][] = $file; + } + foreach ($config['ftp'] as $index => $val) { + // build array with files to send. The key of $_SESSION['log']['ftp'] is the index of + // the ftp-connection details of the configuration profile to be used + if ($val['transfer'] == 1) { + if (! isset($_SESSION['log']['ftp'])) { + $_SESSION['log']['ftp'][$index] = array(); + } + $_SESSION['log']['ftp'][$index][] = $file; // add file to transfer + } + } + } + // don't start sending now, because we want to inform the client first and show the logentry + // log-messages will be sent to client + if ($config['send_mail'] == 1) { + $log->write(Log::PHP, $lang['L_EMAIL_START']); + } + if (count($_SESSION['log']['ftp']) > 0) { + $log->write(Log::PHP, $lang['L_FTP_START']); + } + } else { + if (count($_SESSION['log']['email']) > 0) { + // Ok we need to send an e-mail -> get index of first file + $files = $_SESSION['log']['email']; + $key = array_keys($files); + doEmail($_SESSION['log']['email'][$key[0]]); + unset($_SESSION['log']['email'][$key[0]]); // remove from array + } else { + $dump['backup_in_progress'] = 0; // all files sent + } + if ($dump['backup_in_progress'] == 0) { + // check if ftp-transfers need to be done + if (isset($_SESSION['log']['ftp']) && count($_SESSION['log']['ftp']) > 0) { + // a file needs to be transferred + $dump['backup_in_progress'] = 1; // indicate that there is still more to do + // get index of ftp-connection + $ftpConnectionIndexes = array_keys($_SESSION['log']['ftp']); + $ftpConnection = $ftpConnectionIndexes[0]; + // now get next file to be transferred + $files = $_SESSION['log']['ftp'][$ftpConnection]; + $fileKeys = array_keys($files); + if (isset($fileKeys[0])) { + $fileKey = $fileKeys[0]; + sendViaFTP($ftpConnection, $_SESSION['log']['ftp'][$ftpConnection][$fileKey]); + // remove file from todo-list + unset($_SESSION['log']['ftp'][$ftpConnection][$fileKey]); + } else { + // all files transferred for this ftp-connection -> remove connection index + unset($_SESSION['log']['ftp'][$ftpConnection]); + } + } else { + $dump['backup_in_progress'] = 0; + } + } + } + } +} +// get values to return +$r = array(); +$json = new Services_JSON(); +$r['backup_in_progress'] = $dump['backup_in_progress']; +// send vars that do not change while dumping only once +if ($dump['page_refreshs'] == 1) { + $r['tables_total'] = $dump['tables_total']; + $r['records_total'] = String::formatNumber($dump['records_total']); + $r['speed_min'] = String::formatNumber($config['minspeed']); + $r['speed_max'] = String::formatNumber($config['maxspeed']); + $r['config_file'] = $config['config_file']; + $r['dump_encoding'] = $dump['dump_encoding']; + $r['comment'] = $dump['comment'] > '' ? $dump['comment'] : '-'; +} +$r['table_records_total'] = String::formatNumber($dump['table_records_total']); +if (isset($dbsToBackup)) { + $r['dbs_to_backup'] = $dbsToBackup; +} +$r['actual_database'] = $dump['db_actual']; +$r['actual_table'] = $table; +$_SESSION['actual_table'] = $table; +$r['actual_table_nr'] = String::formatNumber($dump['table_offset_total'] + 1); +$r['page_refreshs'] = String::formatNumber($dump['page_refreshs']); +$r['filename'] = $dump['backupdatei']; +$r['filesize'] = byteOutput($dump['filesize']); +$r['record_offset_start'] = String::formatNumber($recordOffset); +$r['record_offset_end'] = String::formatNumber($recordsTotal); +$r['progressbar_table_width'] = (int) $dump['progress_table_percent'] * 3; +$r['progress_table_percent'] = String::formatNumber($dump['progress_table_percent'], 2); +$elapsed = time() - $dump['dump_start_time']; +$r['elapsed_time'] = getTimeFormat($elapsed); +if ($dump['records_total'] > 0) { + $progressOverallPercent = $dump['countdata'] * 100 / $dump['records_total']; + if ($progressOverallPercent == 0) { + $progressOverallPercent = 0.001; + } + $r['progress_overall_percent'] = String::formatNumber($progressOverallPercent, 2); + $r['progressbar_overall_width'] = $r['progress_overall_percent'] * 3; + $estimatedTime = ceil(($elapsed * 100 / $progressOverallPercent) - $elapsed); + $r['estimated_end'] = getTimeFormat($estimatedTime); +} +$r['speed'] = String::formatNumber($dump['speed']); +$r['speedbar_width'] = (int) $dump['speed'] * 100 / $config['maxspeed'] * 3; +$r['nr_of_errors'] = $dump['errors'] == 0 ? '-' : $dump['errors']; +$r['records_saved_total'] = String::formatNumber($dump['countdata']); +$r['tables_optimized'] =''; +if ($dump['tables_optimized'] > 0) { + $r['tables_optimized'] = sprintf($lang['L_NR_TABLES_OPTIMIZED'], String::formatNumber($dump['tables_optimized'])); +} +if ($msg > '') { + $r['log'] = $msg; +} +if ($config['multi_part'] == 1) { + $r['multipart_part'] = $dump['part'] - $dump['part_offset'] - 1; +} +$r['prefix'] = ''; +if (isset($dump['databases'][$dump['db_actual']]['prefix'])) { + $r['prefix'] = $dump['databases'][$dump['db_actual']]['prefix']; +} +// check if new log-messages werde added +$messages = getArrayDiffAssocRecursive($_SESSION['log'], $_SESSION['temp_log']); +if (isset($messages['actions']) && is_array($messages['actions'])) { + $r['actions'] = implode('
', $messages['actions']); +} +if (isset($messages['errors']) && is_array($messages['errors'])) { + $r['errors'] = implode('
', $messages['errors']); +} +$_SESSION['log'] = $_SESSION['log'] + $_SESSION['temp_log']; +$dump['last_db_actual'] = $dump['db_actual']; +//backup_done means that all tables are saved. The overall progress +//(emails, ftp) can still continue +if ($dump['backup_done'] == 1) { + // some values need to be decreased + $r['progressbar_table_width'] = 0; + $r['progress_table_percent'] = ''; + $r['actual_database'] = ''; + $r['actual_table'] = ''; + $r['record_offset_start'] = '-'; + $r['record_offset_end'] = '-'; + $r['table_records_total'] = '-'; + $r['actual_table_nr'] = String::formatNumber($dump['table_offset_total']); + $r['progress_overall_percent'] = String::formatNumber(100, 2); + $r['progressbar_overall_width'] = 300; + $r['speed'] = 0; + $r['speedbar_width'] = 0; +} +// save actual values to session +$_SESSION['dump'] = $dump; +echo $json->encode($r); +obend(true); \ No newline at end of file diff --git a/ajax/install_get_language_files.php b/ajax/install_get_language_files.php new file mode 100644 index 0000000..d67d048 --- /dev/null +++ b/ajax/install_get_language_files.php @@ -0,0 +1,150 @@ + 0) { + $file = $_SESSION['get_language'][count($_SESSION['get_language']) - 1]; + $call = '?a=get_language_file&v=' . $version . '&l=' . $languageToLoad ; + $call .= '&f=' . $file; + $fileData = getFileDataFromURL($updateUrl . $call); + if (false === $fileData || $fileData == '') { + $msg = 'Fatal error: error downloading file \'' . $file . '\'!'; + $msg .= ' Please try again.'; + $message[] = Html::getErrorMsg($msg); + } else { + // save file to disk + $file = $path . '/' . $file . '.php'; + $fp = @fopen($file, 'wb'); + if ($fp) { + fwrite($fp, $fileData); + fclose($fp); + if (!File::isWritable($file, 0644)) { + File::isWritable($file, 0777); + } + $msg = ' File \'' . $file . '\' saved succesfully.'; + $message[] = Html::getOkMsg($msg); + // remove file from todo list + $fileIndex = count($_SESSION['get_language']) - 1; + unset($_SESSION['get_language'][$fileIndex]); + } else { + $msg = 'Fatal error: couldn\'t write file \'' . $file; + $msg .= '\' to \'' . $path . '\''; + $message[] = Html::getErrorMsg($msg); + } + } + } else { + $inProgress = 0; + $msg = 'Finished installing language \'' . $languageToLoad; + $msg .= '\' successfully.'; + $message[] = Html::getOkMsg($msg); + } +} +$json = new Services_JSON(); +$r = array(); +$r['in_progress'] = $inProgress; // finished? 0=no +$r['error'] = 0; +if ($error) { + if (!empty($_SESSION['get_language'])) { + $msg = 'Incomplete installation of language pack: '; + $msg .= 'removing incomplete files.'; + $message[] = Html::getErrorMsg($msg); + // now we need to delete the language files + foreach ($filesToLoad as $file) { + if (file_exists($path . '/' . $file . '.php')) { + if (@unlink($path . '/' . $file . '.php')) { + $msg = 'Deleted file \'' . $file . '\' successfully.'; + $message[] = Html::getOkMsg($msg); + } else { + $msg = 'Error deleting file \'' . $file; + $msg .= '\'! Remove it using your FTP-Programm!'; + $message[] = Html::getErrorMsg($msg); + } + } + } + if (!in_array($languageToLoad, array('en', 'de'))) { + if (@rmdir($path)) { + $msg = 'Directory \'' . $languageToLoad; + $msg .= '\' deleted successfully.'; + $message[] = Html::getOkMsg($msg); + } else { + $msg = 'Error deleting directory \'' . $path . '\'!'; + $message[] = Html::getErrorMsg($msg); + } + } + } + $r['error'] = 1; // inidcate that an error occured to stop further actions + $r['in_progress'] = 0; +} +$r['message'] = implode('', $message); +if ($r['in_progress'] == 0) { + unset($_SESSION['get_language']); +} +echo $json->encode($r); diff --git a/ajax/restore_ajax.php b/ajax/restore_ajax.php new file mode 100644 index 0000000..c3b3817 --- /dev/null +++ b/ajax/restore_ajax.php @@ -0,0 +1,369 @@ + 0) +// clear temp log. Needed to find out if new log-messages were added +$_SESSION['temp_log'] = $_SESSION['log']; +if (!isset($config['language'])) { + // some server limit the number of vars that can be saved in a session + die('Incomplete session in restore_ajax.php'); +} +$restore['restore_in_progress'] = 1; +$timeElapsed = 0; +$commandsFound = 0; +try +{ + $dbo->setConnectionCharset($restore['dump_encoding']); + $dbo->selectDb($config['db_actual'], true); +} +catch (Exception $e) +{ + die($lang['L_DB_SELECT_ERROR'] . $config['db_actual'] + . $lang['L_DB_SELECT_ERROR2'] . '
' . $e->getMessage()); +} + +// open backup file +$file = $config['paths']['backup'] . $restore['filename']; +if ($restore['compressed'] == 1) { + $restore['filehandle'] = gzopen($file, 'r'); +} else { + $restore['filehandle'] = fopen($file, 'r'); +} +if (!$restore['filehandle']) { + // fatal error: we couldn't open the backup file + writeToErrorLog( + '', '', $lang['L_FILE_OPEN_ERROR'] . ': ' . $config['paths']['backup'] + . $restore['filename'], 0 + ); + die($lang['L_FILE_OPEN_ERROR'] . ': ' . $restore['filename']); +} + +$filesize = filesize($config['paths']['backup'] . $restore['filename']); +// move file pointer to the actual positon in the file +if ($restore['compressed']) { + gzseek($restore['filehandle'], $restore['offset']); +} else { + fseek($restore['filehandle'], $restore['offset']); +} +$log=new Log(); + +// Disable Keys of actual table (after page-refresh: again!) +// to speed up restoring +// but only if the table should be restored and if it already exists +$existingTables = $dbo->getTables($config['db_actual']); +$actualTable = $restore['actual_table']; +if (in_array($actualTable, $existingTables)) { + if ($restore['tables_to_restore']) { + if (in_array($actualTable, $restore['tables_to_restore'])) { + Sql::disableKeys($dbo, $restore['actual_table']); + } + } elseif ($actualTable > '' && $actualTable != 'unbekannt') { + Sql::disableKeys($dbo, $restore['actual_table']); + } +} +WHILE ($commandsFound < $restore['speed'] && + $timeElapsed < $restore['max_zeit'] && + !$restore['fileEOF'] && !$restore['EOB']) { + $sqlCommand = getQueryFromFile(); + $commandsFound++; + if ($sqlCommand > '') { + //$log->write(Log::PHP, $sqlCommand); + try + { + $res = $dbo->query($sqlCommand, MsdDbFactory::SIMPLE); + // get nr of affected rows + $command = strtoupper(substr($sqlCommand, 0, 7)); + if ($command == 'INSERT ' || $command == 'REPLACE') { + $rowsAffected = $dbo->getAffectedRows(); + $restore['records_inserted'] += $rowsAffected; + if (!isset( + $restore['records_inserted_table'][$restore['actual_table']]) + ) { + $restore['records_inserted_table'][$restore['actual_table']] = 0; + } + $restore['records_inserted_table'][$restore['actual_table']] += $rowsAffected; + } + } + catch (Exception $e) + { + // we've got a mysql error + $sqlError = $e->getMessage(); + if ($sqlError != '') { + if (strtolower(substr($sqlError, 0, 15)) == 'duplicate entry') { + writeToErrorLog( + $config['db_actual'], $sqlCommand, $sqlError, 1 + ); + $restore['notices']++; + } else { + if ($config['stop_with_error'] == 0) { + // according to the config we continue restoring + // but log the error + writeToErrorLog( + $config['db_actual'], + $sqlCommand, $sqlError, 0 + ); + $restore['errors']++; + } else { + // we should die if errors occur -> print error message + writeToErrorLog( + $config['db_actual'], + $sqlCommand, + 'Fatal error, restore failed: ' . $sqlError, 0 + ); + SQLError($sqlCommand, $sqlError); + $restore['restore_in_progress'] = 0; + die(); + // TODO clean end of process - last message is not + // logged on restore screen and + // not sent back to client. Flag missing here that + // should be handled via JSON + } + } + } + } + } + $timeElapsed = time() - $restore['page_start_time']; +} + +if ($restore['compressed']) { + $restore['offset'] = gztell($restore['filehandle']); + gzclose($restore['filehandle']); +} else { + $restore['offset'] = ftell($restore['filehandle']); + fclose($restore['filehandle']); +} +$restore['page_refreshs']++; + +// progress of actual file +if ($restore['compressed']) { + // compressed backup - there is no way to get the exact file offset, + // because gztell delivers uncompressed bytes + // so we assume the average packing factor is 11 and will divide the file + // offset by it + $restore['progress_file_percent'] = + $restore['offset'] / 11 * 100 / $filesize; + if ($restore['progress_file_percent'] > 100) + $restore['progress_file_percent'] = 100; +} else { + // uncompressed backup -> get percentage from file offset + $restore['progress_file_percent'] = 0; + if ($filesize > 0) { + $restore['progress_file_percent'] = + ($restore['offset'] * 100) / $filesize; + } + +} + +// Overall progress +if ($restore['records_total'] > 0) { + // it's a backup of MySQLDumper (the number of total records is known) -> + // calculate on count of records + $restore['progress_overall_percent'] = + $restore['records_inserted'] * 100 / $restore['records_total']; +} else { + // backup from another script. We don't know how many records will follow + $restore['progress_overall_percent'] = 0; +} +// tables to create +if ((int) $restore['tables_total'] > 0) { + // MSD-Backup + // tables to go + $tablesToDo = $restore['tables_total']; + // selected tables should be restored? Overwrite value + if (is_array($restore['tables_to_restore'])) + $tablesToDo = count($restore['tables_to_restore']); + $tablesToCreate = sprintf( + $lang['L_RESTORE_TABLES_COMPLETED'], $restore['table_ready'], $tablesToDo + ); + $recordsDone = sprintf( + $lang['L_ACTUALLY_INSERTED_RECORDS_OF'], + String::formatNumber($restore['records_inserted']), + String::formatNumber($restore['records_total']) + ); +} else { + // not a MSD-Backup + $tablesToCreate = sprintf( + $lang['L_RESTORE_TABLES_COMPLETED'], + $restore['table_ready'], + $lang['L_UNKNOWN'] + ); + $recordsDone = sprintf( + $lang['L_ACTUALLY_INSERTED_RECORDS'], + String::formatNumber($restore['records_inserted']), + $lang['L_UNKNOWN'] + ); +} + +// calculate speed for next page call +if ($timeElapsed < $restore['max_zeit']) { + // wenn wir mehr als die Haelfte der Zeit noch haetten nutzen koennen: + // Anzahl direkt um fast das Doppelte erhoehen + if ($timeElapsed < $restore['max_zeit'] / 2) + $restore['speed'] = $restore['speed'] * 1.8; + else $restore['speed'] = $restore['speed'] * $config['tuning_add']; + if ($restore['speed'] > $config['maxspeed']) + $restore['speed'] = $config['maxspeed']; +} else { + $restore['speed'] = $restore['speed'] * $config['tuning_sub']; + if ($restore['speed'] < $config['minspeed']) + $restore['speed'] = $config['minspeed']; +} + +if ($restore['fileEOF'] && $restore['part'] == 0) +$restore['EOB'] = true; //part is >0 if we have a Multipart backup +if ($restore['EOB']) { + // Done + $time = getTimeFormat(time() - $restore['restore_start_time']); + $log->write( + Log::PHP, sprintf( + $lang['L_RESTORE_DB_COMPLETE_IN'], + $config['db_actual'], + $time + ) + ); + $restore['restore_in_progress'] = 0; +} else { + if ($restore['fileEOF']) { + // let's get the next Multipart file + $restore['fileEOF'] = false; + $nextfile = getNextPart($restore['filename'], 0, true); + // there is more to do -> process the next Multipart file + if (!file_exists($config['paths']['backup'] . $nextfile)) { + writeToErrorLog( + $config['db_actual'], '', + sprintf($lang['L_ERROR_MULTIPART_RESTORE'], $nextfile) + ); + $restore['restore_in_progress'] = 0; + } else { + $restore['filename'] = $nextfile; + $restore['offset'] = 0; + $restore['part']++; + $log->write( + Log::PHP, + sprintf( + $lang['L_CONTINUE_MULTIPART_RESTORE'], + $restore['filename'] + ) + ); + } + } +} + + +// collect values to return to client +$r = array(); +$json = new Services_JSON(); +$r['restore_in_progress'] = $restore['restore_in_progress']; +if ($restore['page_refreshs'] == 1) { + // Only send on first page call because values won't change + $r['speed_min'] = String::formatNumber($config['minspeed']); + $r['speed_max'] = String::formatNumber($config['maxspeed']); + $r['dump_encoding'] = $restore['dump_encoding']; +} + +// if restore is finished and file is gzipped, the file pointer +// might not be accurate +if ($restore['restore_in_progress'] == 0) { + // correct percentage of file to now known exact value + $restore['progress_file_percent'] = 100; + $restore['speed'] = 0; +} + +$r['filename'] = $restore['filename']; +$r['nr_of_errors'] = String::formatNumber($restore['errors']); +$r['nr_of_notices'] = String::formatNumber($restore['notices']); + +$r['progress_file_percent'] = String::formatNumber( + $restore['progress_file_percent'], 2 +); +$r['progress_file_bar_width'] = round($restore['progress_file_percent'] * 3, 0); + +if ($restore['progress_overall_percent'] > 0) { + $r['progress_overall_percent'] = String::formatNumber( + $restore['progress_overall_percent'], 2 + ); + $r['progress_overall_bar_width'] = round( + $restore['progress_overall_percent'] * 3, 0 + ); +} else { + $r['progress_overall_percent'] = $lang['L_UNKNOWN']; + $r['progress_overall_bar_width'] = 0; +} +if ($restore['part'] > 0) { + $r['part'] = $restore['part']; +} +$r['tables_to_create'] = $tablesToCreate; +$r['records_done'] = $recordsDone; +$r['actual_table'] = sprintf( + $lang['L_ANALYZING_TABLE'], $restore['actual_table'] +); +$r['speed'] = String::formatNumber($restore['speed']); +$r['speedbar_width'] = round( + $restore['speed'] * 100 / $config['maxspeed'] * 3, 0 +); +$r['page_refreshs'] = String::formatNumber($restore['page_refreshs']); + +$elapsed = time() - $restore['restore_start_time']; +$r['elapsed_time'] = getTimeFormat($elapsed); + +// if we restore a MySQLDumper-Backup we know the nr of records and can +// calculate the estimated time from it +// if we restore a backup from another program we need to rely on the filesize +//which is not accurate +// when the file is gzipped, but we can't help it because there is no way to +// get the exact file pointer position in a gzipped file. +// So we give our best to guess the corect position (see line 117 above) +if ($restore['progress_overall_percent'] > 0) { + $percentageDone = $restore['progress_overall_percent']; +} else { + $percentageDone = $restore['progress_file_percent']; +} +$estimatedTime = 0; +if ($percentageDone > 0) { + $estimatedTime = (100 - $percentageDone) * ($elapsed / $percentageDone); + $r['estimated_end'] = getTimeFormat($estimatedTime); +} else { + $r['estimated_end'] = $lang['L_UNKNOWN']; +} + +// check if new log-messages have been added +$messages = getArrayDiffAssocRecursive($_SESSION['log'], $_SESSION['temp_log']); +if (isset($messages['actions']) && is_array($messages['actions'])) +$r['actions'] = implode('
', $messages['actions']); +if (isset($messages['errors']) && is_array($messages['errors'])) +$r['errors'] = implode('
', $messages['errors']); +$_SESSION['log'] = $_SESSION['log'] + $_SESSION['temp_log']; + +// save actual values to session +$_SESSION['restore'] = $restore; +echo $json->encode($r); +obend(true); diff --git a/ajax/show_log_entry.php b/ajax/show_log_entry.php new file mode 100644 index 0000000..c6fdf4f --- /dev/null +++ b/ajax/show_log_entry.php @@ -0,0 +1,121 @@ +set_filenames(array('tplLog' => 'tpl/log/log_ajax.tpl')); + +// get log filename to show +$lfile = Log::getLogfile($logType); +$tplLog->assign_vars( + array( + 'ICON_SORT' => $revers == 0 ? $icon['arrow_up'] : $icon['arrow_down'], + 'SORT_ORDER' => $revers == 0 ? 1 : 0, + 'LOG' => str_replace($config['paths']['log'], '', $lfile)) +); + +$tplLog->assign_vars( + array( + 'LOG_TYPE' => $logType, + 'REVERS' => $revers) +); + +if (file_exists($lfile)) { + $lines = ($config['logcompression'] == 1) ? gzfile($lfile) : file($lfile); + if ($revers == 1) { + $lines = array_reverse($lines); + } + $i = 1; + $entriesTotal = count($lines); + foreach ($lines as $index => $val) { + if ($index >= $offset * $entriesShown + && $index < ($offset + 1) * $entriesShown) { + // strip html in Perl-Log + $val = strip_tags($val, '
'); + $timestamp = substr($val, 0, 19); + $message = substr($val, 20); + if ($revers == 0) { + $nr = String::formatNumber($offset * $entriesShown + $i); + } else { + $nr= String::formatNumber($entriesTotal - $index); + } + $tplLog->assign_block_vars( + 'LINE', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $nr, + 'TIMESTAMP' => $timestamp, + 'MSG' => $message) + ); + $i++; + } + } + $maxOffset = floor($entriesTotal / $entriesShown); + if ($maxOffset * $entriesShown == $entriesTotal) { + $maxOffset--; + } + + $offsetForeward = $offset < $maxOffset ? $offset + 1 : 0; + $offsetBackward = $offset > 0 ? $offset - 1 : $maxOffset; + if ($revers == 0) { + $entryTo = ($offset + 1) * $entriesShown; + if ($entryTo > $entriesTotal) { + $entryTo = $entriesTotal; + } + $pagination = sprintf( + $lang['L_SHOWING_ENTRY_X_TO_Y_OF_Z'], + String::formatNumber($offset * $entriesShown + 1), + String::formatNumber($entryTo), + String::formatNumber($entriesTotal) + ); + } else { + $total = $maxOffset * $entriesShown; + $entryFrom = $entriesTotal - ($offset * $entriesShown); + if ($entryFrom > $entriesTotal) $entryFrom = $entriesTotal; + $entryTo = $entriesTotal - (($offset + 1) * $entriesShown) + 1; + if ($entryTo < 1) { + $entryTo = 1; + } + $pagination = sprintf( + $lang['L_SHOWING_ENTRY_X_TO_Y_OF_Z'], + String::formatNumber($entryFrom), + String::formatNumber($entryTo), + String::formatNumber($entriesTotal) + ); + } + + $tplLog->assign_vars( + array( + 'OFFSET_FOREWARD' => $offsetForeward, + 'OFFSET_BACKWARD' => $offsetBackward, + 'PAGINATION_ENTRIES' => $pagination) + ); +} +$tplLog->pparse('tplLog'); +obend(true); diff --git a/ajax/show_table_field.php b/ajax/show_table_field.php new file mode 100644 index 0000000..0d284d9 --- /dev/null +++ b/ajax/show_table_field.php @@ -0,0 +1,58 @@ +set_filenames( + array( + 'tpl_sqlbrowser_table_show_tabledata' => + './tpl/sqlbrowser/table/edit_field.tpl' + ) +); + +/* + * Fetch and check _GET variables + */ +$validModes = array('new'); +$db = isset($_GET['db']) ? base64_decode($_GET['db']) : $config['db_actual']; +$tablename = isset($_GET['tablename']) ? base64_decode($_GET['tablename']) : ''; +$fieldname = isset($_GET['fieldname']) ? base64_decode($_GET['fieldname']) : ''; +$rowKey = isset($_GET['key']) ? base64_decode($_GET['key']) : ''; +$mode = 'new'; +if (isset($_GET['do']) && in_array($_GET['do'], $validModes)) { + $mode = $_GET['do']; +} +$tableInfos = getExtendedFieldInfo($db, $tablename); + +$tplSqlbrowserTableShowTabledataEntry->assign_vars(array('MODE' => $mode)); + +$row = array(); +if ($mode == 'EDIT') { + //TODO implement :) +} + +$tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FOOTER_' . $mode, array() +); +$tplSqlbrowserTableShowTabledataEntry->pparse( + 'tpl_sqlbrowser_table_show_tabledata' +); +obend(true); diff --git a/ajax/show_tabledata_entry.php b/ajax/show_tabledata_entry.php new file mode 100644 index 0000000..b451070 --- /dev/null +++ b/ajax/show_tabledata_entry.php @@ -0,0 +1,105 @@ +set_filenames( + array( + 'tplSqlbrowserTableShowTabledataEntry' => + './tpl/sqlbrowser/table/show_tabledata_entry.tpl' + ) +); + +/* + * Fetch and check _GET variables + */ +$validModes = array('VIEW', 'EDIT', 'NEW'); +$db = isset($_GET['db']) ? base64_decode($_GET['db']) : $config['db_actual']; +$tablename = isset($_GET['tablename']) ? base64_decode($_GET['tablename']) : ''; +$rowKey = isset($_GET['key']) ? base64_decode($_GET['key']) : ''; +$mode = 'VIEW'; +if (isset($_GET['do'])) { + $mode =strtoupper((string) $_GET['do']); +} +if (!in_array($mode, $validModes)) { + $mode = 'VIEW'; +} +$tableInfos = getExtendedFieldInfo($db, $tablename); + +$tplSqlbrowserTableShowTabledataEntry->assign_vars( + array( + 'DB_NAME' => $db, + 'TABLE_NAME' => $tablename, + 'DB_NAME_URLENCODED' => base64_encode($db), + 'TABLE_NAME_URLENCODED' => base64_encode($tablename) + ) +); + +$row = array(); + +if ($mode == 'VIEW' || $mode == 'EDIT') { + $dbo->selectDb($db); + $query = "SELECT * FROM `$tablename` WHERE $rowKey"; + $row = $dbo->query($query, MsdDbFactory::ARRAY_ASSOC); + if (false === $row || !isset($row[0])) { + //TODO clean error handling + echo "

Keine Datensätze gefunden!

"; + obend(true); + exit(); + } elseif (count($row) > 1) { + // TODO clean error handling + echo "

Mehrere Datensätze gefunden!

"; + } + $row = $row[0]; +} elseif ($mode == 'NEW') { + foreach ($tableInfos as $key => $val) { + if (isset($val['field'])) { + $row[$val['field']] = ''; + } + } +} + +foreach ($row as $key => $value) { + $keyComment = ''; + if (isset($tableInfos[$key]['comment'])) { + $keyComment = $tableInfos[$key]['comment']; + } + $templateVars = array( + 'NAME' => htmlspecialchars($key), + 'KEY' => base64_encode($key), + 'KEY_COMMENT' => Html::getJsQuote($keyComment), + 'VALUE' => htmlspecialchars($value) + ); + $tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FIELD_' . ($mode == 'VIEW' ? 'VIEW' : 'EDIT'), $templateVars + ); +} + +$templateVars = array('RECORD_KEY' => base64_encode($rowKey)); +$tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FOOTER_' . $mode, $templateVars +); +$tplSqlbrowserTableShowTabledataEntry->pparse( + 'tplSqlbrowserTableShowTabledataEntry' +); +obend(true); \ No newline at end of file diff --git a/css/msd/icons/ajax-loader.gif b/css/msd/icons/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 diff --git a/css/msd/icons/arrow_down.gif b/css/msd/icons/arrow_down.gif new file mode 100644 index 0000000000000000000000000000000000000000..a0c49e7ed89205fa09f8376f31b8c4cd0b0926e3 GIT binary patch literal 119 zcmZ?wbhEHbHk@q-qoG>Td?C( z?37*q|Nm!z0mYvzV5Sa;1exK$qB3El>&iXcIzGMYFHP!+worHx;OD;E&oxG`CF%AN N9hO7VaXlOi)&RN5F-8CY literal 0 HcmV?d00001 diff --git a/css/msd/icons/arrow_up.gif b/css/msd/icons/arrow_up.gif new file mode 100644 index 0000000000000000000000000000000000000000..d6dd5adb5a4ba1f48f323008533de3475323a6d8 GIT binary patch literal 117 zcmZ?wbhEHbQOssDxMf{kS_#3jt FYXA=8Eaw0K literal 0 HcmV?d00001 diff --git a/css/msd/icons/arrowleft.gif b/css/msd/icons/arrowleft.gif new file mode 100644 index 0000000000000000000000000000000000000000..292440f5eeb2cd228a4b07c7e299520d876af2e4 GIT binary patch literal 120 zcmZ?wbhEHb$Rb%)guD*SE#Dl=tF~hipaYrvmcSYqFnl@$0yrhh6d1Y4Ou%@ziVP z*_E)HZLphe@WeyMk`}nAd(p&{lX_E-ZZ+`6MUHe%n}k~Fr~>-+!r`F;x|?K*aZ1vb z3FGSc)WCR`rn~5<0`SUE+vNAInr*I_YTf4d@Y{js?fTowsK29J`S|m?pKZ^$gy7l1 zy{m%o#YXJLP>`q8uer^9Uq0j7sokn1|NsA4Sy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003A3EC2ui01f~S06+-00I^_!5I}$cfd~^~abd&61^@>Nd>F7J z!VLi)5;!-S0EPk;ER3j7fQCRLQCMtn0MS4WsRjh%_^^Vhgdr13EbuW_UZwHSZ)Bi$M(Im&D6S3wQqzV`Q)f E0QE~JkpKVy literal 0 HcmV?d00001 diff --git a/css/msd/icons/db.gif b/css/msd/icons/db.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe181fb7aaa7938d3c16a6c3ea8569e64c96605e GIT binary patch literal 527 zcmZ?wbhEHb4<0OAw(QcSOS^XM3JMC^y?ghB2@{SUJz7&!)7aR! za^=dcTep7t^l9S6iFI{#XU?2izI=I4PtUt|@2aY*X3Ut8nwr|t(b3h_)!EtU=jWH6 zo_^rKf$iJ3FI>1VA|hhu%$Y?+Mf>;fcXV_-e*F0K>C>BfByXY_wPS^ z`0&S%AMNe!uV24zX=#azi`%wsTX%Q&ix)3WoH%j%^yzu?=7oiYtzNzQ>({RzKYl!N zR?|2H=NsjGW8W5(lzgcoUPpNos% z=H~wY|9?kH3IiA@{$v5uIv^5ch69W4gssO`EqN=WWt#7`qNX9yN3B7%Q(;3yV2zQP YfQ4bfrAPYmEJ_BP9EN+im6;f<0TPce_5c6? literal 0 HcmV?d00001 diff --git a/css/msd/icons/download.png b/css/msd/icons/download.png new file mode 100644 index 0000000000000000000000000000000000000000..dd525cce0505494b59be27179a0286293668d63b GIT binary patch literal 3418 zcmV-g4W;slP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=00004XF*Lt006O$eEU(80000WV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}0007YNkl%fY}^N%WIfV5-`&?IB2Uq6pCyC(3X$4E;jCab@%cC5}h;< z&?p2KM?jU4Narv;o5lOcA0$%_cOZo9p-^N4psM4G{`5|*GV*N|C+bzWJtn-O0?XoH zHt>5X1I9$Wi^LI67{)^XNi`1;XlV3F+xdz!pJ#GtZt%l|iL~y5VNo!PAj5#el@chm zLE)g-uefHMt5WyPnqdH-ZlBx{&zPvGkia;@)Pjt(Eg``~%x48y)P>p-1+`^JKQv$%Kd^jiMr`;zqL@F+)Jyl(CqTP*v)JPZgkfM9gIbq#PNVDg^TY zQhj?$FR3h6h+D$F78gz*k&$5p&prVMCkC*WL_rjmR86WD6%Q5}K`Kk&m4WA%IKmUa zb};+$Xs6|Hqts7O z9uVMAH$W_m&(RI9_4V~391d@d001_#T3OB+E8kCv*R_Tp3pVP@R7|fkwzs!$8-oJC z=mTFQy+%Xxt1yzfjk;15<=XbSH%Acj&D-lF^7^Y@7yZS0A$ra+n+5?W>m$TsG2qyv wjYQ7*j@8-I&v#R2Pd^v;x^q|W?tkIm07M}whD#`bod5s;07*qoM6N<$g7>6Yod5s; literal 0 HcmV?d00001 diff --git a/css/msd/icons/edit.gif b/css/msd/icons/edit.gif new file mode 100644 index 0000000000000000000000000000000000000000..c33293dfee97de6006cd41a317c3d62ff44e862f GIT binary patch literal 976 zcmV;>126nXNk%w1VGIBd0QY_X?Y18M`Nj6@qS~AT^xuQwp#k*cjOeHW@XAo|%2Bo_ z8H#aAu9|I=dQ-S17Ot3T^lU@?_{7Y;jJHfoYH@g)gIk+~T87VL@WeyEqg z_u`M+%BYTXPUW~hdR{&Al6fYO7m{;9^wn#)sC((-$+e+z^3-dk9v-@%ZSm7*blOdE z%}ctQWRU({d|y7Ifkn~8lfNYVg~E=)AkqmI?A=I5V6c=20x>*_Fk&l)%Kr@Wn=7rbkx5C2*ot^y7{2 z#zm`-RC2pcjqjYF*Z+YiRsGeruj)>CK-^k6@o`+qe*_qV9c*nGc$C4I@Y(+1Y z9>}?jjk30|n{D~{^Y`oPif~J;fFf+uNsw+eQ%gee*V6z0|9^jffPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW004dfEC2ui01N;R06++>0R0FYC@{^234TtH(1AvYlN>lWNcqr! zpq~~rUi>h##g7FWD+~xAfB=Ay0#!g*6u2gYl^!XINJ!FV2NM=@e8?!FAbLc6x#6cwfB<7O yJh{>(!doMC!l2+ofzHqaLk4(YgQ1B?k^l}Eg~3Bi1~*xz%zy$X&mI*40RTHsoJ(o| literal 0 HcmV?d00001 diff --git a/css/msd/icons/gz.gif b/css/msd/icons/gz.gif new file mode 100644 index 0000000000000000000000000000000000000000..803ed55ab5fa54876382012ad768d8d6a5d6b1f2 GIT binary patch literal 990 zcmV<410noJNk%w1VGsZg0Qdg@9gD~^o!D(=sIlDhHi*gO>-2o9%~XoRIG^9j;^TR< z+wbx9>i7Roqt`8z)rh&`q_(!a+SLF6i6WcYai+HmSEOad_A-CPEP}{kqO>MxyO_G> zw#nH|d$Uca;WxYT>F@QFy|hiU>Gt^fpQ*gX-QGf!%bc#Qg{;y{kd4LR-P`m0Wu4R1 z=jfxysov!5g|x7%!Q1Bh{`dL( zQ^M)-^!Zr3?!wgOWv0_;p0fY|g>$Q`RDQRVs*@{QYWUuM7x_;pX%t8kUB!uQ80!{QdoVu&&eL^2F)* zvDegpr`z`Y{coeuv*Y+;sOrt(?-ifm>ErIg+}~-G$4GOptIpDo#OSET#G$s(roGqK z=I@xYyxr{Uw$JG=cE5h9&-3~6%h&L}-rSbX^NgUst=8~&q|b4q*}K{9oUF5Xpwelg z(0!`gVy>ao*X7gP=8(zhOPbV&r^!r!!Ej)-rpx4H*Z;1}<(FNo3q|Nf-C+Jvpz(%b3L-s@J8%NZG$UysMB%Erav z^ZEbyX0G0V!Q}t`^@qaf+2HHn;_tD+h!zV)P}XZ zio3aRq^E*A^8LW00930EC2ui01yBU06+-40RIX6XDH1MDHfmBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdIQKV0e4OQz0OM$%aGtkz~R0g@>*6oYoi|TEaC+BK_PQ#iX{w zvaJdqGbT)0BBp_9iU6#)Ym zi%5q9-MT^!8Y&OdJf@o$JvcDq6XW?do_L-=3{8)cPiSXt$T3Miq~oC~TJmX0QM>mv zbw95yktf(r&ET^=a%ZLU4uigyK!q&^oC{u_;*=8-`&g8inami);qhVOhc;1J%M%+` PC?>aeSlBQzF<1it%Ii+U literal 0 HcmV?d00001 diff --git a/css/msd/icons/key_fulltext.gif b/css/msd/icons/key_fulltext.gif new file mode 100644 index 0000000000000000000000000000000000000000..80098b33c731b1db733e14913d683cc348fa6ae8 GIT binary patch literal 990 zcmZ?wbhEHbBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdISwW_WwUQz0OM$%aGtkz~R0g@>*6oYoi|TEaC+B3)+I!vgn( z?lw|OmShMpFt9T6b4*z%takRWnwgy*Pto%uoyubGGy)8m&hUsj38^(UC_0^FVv5U| z$l#>NY3{sh&qQYSV^jDm%u;?NJzy6Qk+qKtEN4<0OAw(QcSOS^XM3JMC^y?ghB2@{SUJz7&!)7aR! za^=dcTep7t^l9S6iFI{#XU?2izI=I4PtUt|@2aY*X3Ut8nwr|t(b3h_)!EtU=jWH6 zo_^rKf$iJ3FI>1VA|hhu%$Y?+Mf>;fcXV_-e*F0K>C>BfByXY_wPS^ z`0&S%AMNe!uV24zX=#azi`%wsTX%Q&ix)3WoH%j%^yzu?=7oiYtzNzQ>({RzKYl!N zBBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdISwV0e4OQz0OM$%aGtkz~R0g@>*6oYoi|TEaC+B3-U%$Knmj za=bx7CL9b6O%3cUr!H_#ewHXKEho;lv-r^wLGE%94GG4yHZDcZ$qoku8dDe<^|yI2 zT)cR8f?ocU1C55P58HU+dHygoH7!1_o%K#YvFLcq^*%4H3tK#&@=e$ApEJe5qJejc zp=?#h&fw+i<7GG(J7gFN9SEEvY@?#{=&4iSVWuqt6CNl(BBMCFOPd~&0)SZoB6}3fJZNXuHSj~|^KdT)zMH+U=(wFQ@NV zQT_YF`VY4oKVHfF`{VG76-?Op^Zbe}CwCpb^6ci+OVc>-KKuUa{jXWuUVr=f z_rsSzeT&XqzW4RP&4b6cv~?AFO)7SrkZ0PS<2qsam8v+~zdw&|I&{O-Gnru&49XBt z{K>+|z)-=U19BZGPdISwXLx(VQz0OM$%aGtkz~R0g@>*6oYoi|TEaC+BK@63K|%Xr z*;a**851Tga%5l-VX;vNX!Dz4QqCi>Tjb`Ne&c-Y-2?+{SKQ|a34rd00J!@6?Gp}DN;?*eunRy{4O zu6j!+g*nxyT`uX}6-gfn_d^;2Q8QQ;lsskO=j^W7*)U}zgD#88508q&U41es5mzQ` Nbas)EV`OBo1^~h0Nge5CsF?s_w!` z-n!=%TVJ*AeABn!LCpXEf0jN@`+tAI?ZqMc-_6|kqVCD=hX3!+zCJVW>7K^_zuz%X z3>1H|FfuS`G3bCy0{O{-t=3^`0IP;r`(cLz5iu+i8V)^~VCBQbkm#WB!G=TO(BZ?i zLX6WVy(qTiV{B$qb;;z}w8|m%aHEAApMZ?CsJ~ZxhgX2BhI(E;8#|Xg7du;p6N4lx wn;ILd<8yOfF6o0ZXGB7YR=m6P3Ssn%lCXqS)?5F4Q VFE(+w(S7jFp4Du84>&{^tO05}6G#96 literal 0 HcmV?d00001 diff --git a/css/msd/icons/ok.gif b/css/msd/icons/ok.gif new file mode 100644 index 0000000000000000000000000000000000000000..06bb815b7d3494cd5db53ef438987b302eba5d30 GIT binary patch literal 126 zcmV-^0D=EUNk%w1VGIBY0J9GOoVaAG=w`C*ZN&V3rQ=}A?wH8`fz9!px$|?;^P#`^ zdDZr(+4-yB{j=TsumAu6A^8LW000jFEC2ui01N;M06+vQ@X0v=sHI#gZAS>RaosR9 gRuZg1GLn^Hyq%&r7a$1MzPx|~fpDNzjE@KaJ0u`DZvX%Q literal 0 HcmV?d00001 diff --git a/css/msd/icons/openfile.gif b/css/msd/icons/openfile.gif new file mode 100644 index 0000000000000000000000000000000000000000..8225ba3aca590f4596b927040e9ef11b16461240 GIT binary patch literal 135 zcmZ?wbhEHbn(W>({Seym;~b`}hC<|Nr{+ z>+j#c3`junCkrD30}q1^NDO3#1B;2k2~A06rPn$W|I4ZRc}FriDv0Ks_R5s;_Kaob eEYP&k=R2Sfkfb5iJ*l~9)u literal 0 HcmV?d00001 diff --git a/css/msd/icons/plus.gif b/css/msd/icons/plus.gif new file mode 100644 index 0000000000000000000000000000000000000000..96425a5086ccd3718c78b269ac0fdef9b29cfd28 GIT binary patch literal 190 zcmZ?wbhEHbtl!odXX(?7s1O&&@Z7o_?;}e52ve!}5*S zT24K0IQX#h(wn2NfBgUdf6A__45R?XpDbYIIv^5crvt0Cf=b^K36qRlEQ=cVobQ%U zI(Wyzxx?X+{XUijE+UqF9!pj%$grE$)#PDN5wVuJ@x_K0cDuTp4QIBrFfdpH0HfJQ AZvX%Q literal 0 HcmV?d00001 diff --git a/css/msd/icons/progressbar_dump.gif b/css/msd/icons/progressbar_dump.gif new file mode 100644 index 0000000000000000000000000000000000000000..35292ff76175f27f8d1c4661cde1457c5d9ea73a GIT binary patch literal 780 zcmWkrYe-W87@eYJq+v)u!bqn^W)VT^oDW*Q%3k)cQWME3nb|`vd@FM^l`JWnT3WQ- zy1J|GwtVNF&a|>k_mI@UNTL^784-By!kQn4^W)Al*S)W-f=w%>c z8LJlb9Xx*ox&mr3sB+L%pzAQ!g)T8HO7xb3z5%KP#0luzp_8Gf89hztZp70{P(o1o zpe}>F0P-Bjvmlc|oCFaMA`Wj&u$rK4h4u+FkD-2y{)eEhfyx7w4eBDuD>rV=wF*^B`V7 zW2`C=hd}c&)CS@ph-8~ZZL?oa3_@`ieJvQFnMoZ?gD`Z1NCbHahCX!PgRuwXX&C$Q zqJyyqZ_mTD-MkD6FFCdUOi*>?aybthr4mlgi887zKfj|aBBGs=(eh3zBEMLvEKnD(DlQn- zYc(U<@{lpNvGH<~VZ!vr9AYdDnhAPW%9|;jdw*bV_WkUEcSVIo;R|64g^TlHKfV|J znV(;ncUWGIs#;q7)g~Ddxp)L;<$bzQUG44@S@>JZulGB2PcXf7rHbd3o2bwUV?=Ro zp2shrH|w3Nn>MAY1P;Ej)h#I*ul%n{zf?ThVH%a$KEQkx^7R9NqADd z&^eum-lz+zm~vXDuZrO{c3yWpzm}D#%y?b1-Y;vvcvl*6*we9a&4Um&`O}G$9FQPU z2x`){pXu~JCHYYE(KplQB|m1gw#g-XOU=f)j5-g$J>}lpj#oN}EUq_Yv?JMjdn(f4 m{;ht?!41Ayn!mi=nRU(F_^;tqlxiS1AYs}mX{Wb4E9^fpKFodq literal 0 HcmV?d00001 diff --git a/css/msd/icons/progressbar_restore.gif b/css/msd/icons/progressbar_restore.gif new file mode 100644 index 0000000000000000000000000000000000000000..b9def1801b1526cc0a7994a823272242da45c077 GIT binary patch literal 680 zcmZ?wbhEHblw%NJc*el+bMMrjXAb?`T=w(m+Mfqz{aly)^X#FY+p2%=oAUF_!JkXL zex5z}bN|erD?@)CUG;Oh|Ic-4KbQIcJhtxVk<~xfCjH!6^K*Ub&jT}mZYlq{F6rmd zbw9V&{9G0Gb94F6y;FWJ_xrib@8`a$KX-Nf+&}B*&W@jJlYbst`*Ta#&#l!zkFNf? z)br=M)SoNEeqOxrbCCnX$ODQ$S-_sw0g<3MVPOB?pxe~k(%RPE#Kz6Y&BoZr-OI_@ z&B@5f$u_Nj*39WrY75nvnHQ@G2`yTC~i}fQ)Jk?bI*PTjvd=I zq&3(@Pn^~`d*T%Pxie=^YN)baXT6~+a$WWQ9m)H*@7$KWp&+jy_=^9P{JWR@Z(b{W ze);m%OT9mT|NUoR=92OFFu$vrLv%&Jih@Mv76DnM6vvH8&1|BYNn1`dIlGBTN3Lnu zuqefoL&bO9h6S5cyeH}S&a+T_;KDx3xcJ(V&W9{*bFEmfh&VL0a4dFr)rg(-WyQrs z9t*`}9UES8tmcu3Q7w4*>Y8BG;h?8p3P)Q+GA>(L%s%9JYkMy1r103j@eHpfP{N#HAU4?f@Cb01hpn$AAt2I|*nDBjfMie}KXO0mK57{QH;T zKiDWnD8&GB1~@t(1S81J|9<}l#R)(Ffi(R2`wtRTK*PayfWbdVIR6Lv5fqvKe*Oju z`~wIepoaf{K~fBLkunUpLFw$PfBt~90|XGzgN%P6;qaef;;|I0s`D&o|NZ;t7dS!y z0*D3dVxUVvz)B_-mks~^{`>nI6vF@k1alJD567M?#;V#x*6#1$zrdIQDgy{07I8t& zUw{4?D{}#(xWIZ2E*r#o|NZ^@pO1?TAb=SAR^NE^>g%O@A3lHm#Q;hHpr8PH4o*S> ljgj#`7YB=~tRO&u0RRgje4$oD=>h-%002ovPDHLkV1jACLLvYF literal 0 HcmV?d00001 diff --git a/css/msd/icons/search.gif b/css/msd/icons/search.gif new file mode 100644 index 0000000000000000000000000000000000000000..af7bf1c64a37bf2c639b4591ce7754dc62630115 GIT binary patch literal 986 zcmV<0110=NNk%w1VGaNe0QXn`dR{$~qPMP^Z=!)k{rSb^xIT(-ORRt*nuA;Q_xzQ8 zRpFrl`~3d0!r1ofqU!JZ$FzppoCEaVgYC8+*y8r^&}yxiYO9Y_l5;`y$Rb%)guD*SE#Dl=tF~hipaYrvmcSYqFnl@$0yrhh6d1Y4Ou%@ziVP z*_E)HZLphe@WeyMk`}nAd(p&{lX_E-ZZ+`6MUHe%n}k~Fr~>-+!r`F;x|?K*aZ1vb z3FGSc)WCR`rn~5<0`SUE+vNAInr*I_YTf4d@Y{js?fTowsK29J`S|m?pKZ^$gy7l1 zy{m%o#YXJLP>`q8uer^9Uq0j7sokn1|NsA4Sy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003A3EC2ui01f~S06+-00I^_!5I}$cfd~^~abd&61^@>Nd>F7J z!VLi)5;!-S0EPk;ER3j7fQCRLQCMtn0MS4WsRjh%_^^Vhgdr13EbuW_Uw5(`S=`t|YDY5MiT`S|lpecFz6PWtn;x6b?Y)oZMPBDc-= zkZv_KXwCii!N-yoDqh9r*_G(10@K~`V36r&mhQ;8jE8JR(v}H)Uq0~Lfv(5+KyuZh zfkms2RQ&kD+s~cHw1)HSx#zt^=%)hdVZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW003G(K!<=Z6J)S~QDn`+GF4g_Igx`5niysl0=Thb$(Aev7&3I=1po#C9&Eh0 zU?szuHb+n#piv+JMFkCJ5CMQh%?b!Od{D`$vkia^Lw*Q=!o>uS8VeZ&002fnfVXhF sw6S0SKm`F4D?R`+rcH{6BAPyxa3x3^CLA<;(2_<)h$vWGJP8N@JHm)g8~^|S literal 0 HcmV?d00001 diff --git a/css/msd/icons/table_truncate_reset.gif b/css/msd/icons/table_truncate_reset.gif new file mode 100644 index 0000000000000000000000000000000000000000..da1f273745a9ce3d5a0fa3d9e5384e6931672fe3 GIT binary patch literal 908 zcmZ?wbhEHbIQPYgG4nf6m|kK6%mhu#_^8Wuss;1O`(GDE?$&WMEKX&;dCLlqVcG zG8tG4ZfsyU$jHpfF(JYu$)$lo!L8wkKvK)mZZXyni31yw-31jKt3(R?|2H=NsjGW8W5(lzgcoUPpNos% z=H~wY|9?kH3IiA@{$v5uIv^5ch69W4gssO`EqN=WWt#7`qNX9yN3B7%Q(;3yV2zQP YfQ4bfrAPYmEJ_BP9EN+im6;f<0TPce_5c6? literal 0 HcmV?d00001 diff --git a/css/msd/pics/bg-body.gif b/css/msd/pics/bg-body.gif new file mode 100644 index 0000000000000000000000000000000000000000..4e94b13d6f230a62d25ed3a0c69a8d6f6fc42425 GIT binary patch literal 1249 zcmbu7=}(gf0Easz$kb&n8YBxk79CV5#7yV0`GG|PRq#k=lq@J1jg~V;3=(4&6r>0! zCxtJU-K^IEM|rBZRZT%Atm@pwX^kX$Yo3WW-Vg3sqmrP4qk;B-2@Ua!yR zvsf%*v3PTHb9#DOAP__%5!nCOkHM(_Y{VCSX@W&Z(i`p~5#KlR`|iyY|Jpr=zyF5t zCq+&w7g`@qXs8w0X>TO}{h_FIl=wI6(FKaa9$^0^v43fgG;gbnO5_y0^9sEF}4>6dm;gM1H*!Toz^5Ya? zKbOa!5eR2RV#(b6!s3#2S@zxM6@^lzUe&B=b$Wx*1c2rwi`8a#I9+a!*XIueL*ey} zEfLt(?eR!z&tz51?&MrPwRZ}=D;3#+9DFxjA9tis$sfe<8WT>}#h>i$n`wssLCBr? z@Lt$<_@O`4YIYYxqBHig#+xMNL%Z%%K&k z1iw_uDEU)$`?46M&otB`;hK;^RxtNz1)W*U0o7=Q+pH2YDXSBeTX!U;v#4uRwYyWU z&$0$}yru(LUD?Afw6Oh1k$QHRW|H)ruHS!tgbps^zr|k{jSN9D>P=zS`B8>d!778) z*Ls=uRZi88A1<(2P948FB~Q#AF55bX$?Co^HsXckw4zn<*r?yGnaYd1IL;2bfkk4T zWPB{_cj$!O7xSt%!T~q5Dw&x0_bAbDO=VOpO7l48AYSui=P^K2wJQU@R=wv6YVB#< zZTyWb@#R2d4V_Q}*VZ0vMQNYGv3PAAf`RAMALhb!4M&$zy5}d1c-@OrK0w!)9-E+V zIul!2I`wHWgIfJ?73WEc%5^*-_V*{4jS6>YYrLPZ?zU+zeZvEjc zpz-Zp?jcjh{pA8vXN57y>3ZY?P2H8z$v{taY$4EFn@9lOHKb_(OjAZO*w=EU5PaW$ zn*d@v%FQ6|Ky|Vi-`84b?#E*ZW&(*}HWR7bWMUM3xey|=j09+a?K49ZPIQWe`YCpF za*&@$w0xL723crx87Wr!(v>3Xko;ATm7y$$tV~T!ijAdjEwT**SmLH_}=0+k$o&+B-+QYMDWzKRw?YUa)oQ}Iga&qGz zSe(2BG{VI{SeEaaf#XOn0fK392@mrS?%AWVVz=lUJ;^OT<+r#c>Dy90b7%IJc;=Bw z9KBI7C-%2sl>QmgsB>0v0 zWhH)9g^BD}Kk{4stCic*0-EZ*rGd5Dq=A6;9z_$-HD#s+^(|LRgNF7y13_cQ16$D4 fgH8(peQl*75RV%OnMq7r2%_@R!lvJ0u;2ax1LX2@ literal 0 HcmV?d00001 diff --git a/css/msd/pics/bg-buttons.gif b/css/msd/pics/bg-buttons.gif new file mode 100644 index 0000000000000000000000000000000000000000..1fb2865814990a6453937dc131de1a143d711a42 GIT binary patch literal 1298 zcmZ?wbhEHb+{d8K@STC-?c2BS-@pIx;lsy|A3uNo{N>A+uV24@|Ni~Qj~_pO{`~dp z*YDrI|NQy$_wV0-|Ni~||DRzLjE2B43IQFEUqE?*f#W{|BZrK~h6M+kIfS)hPHb3s zxLrWmYmUdpMMt|OjI-`&7&IL3S8(o<@!YiJM*De2T4&&|uu&bBCi zb?4;f<>%)+G;_&1Y*}$}vBzYs*i&0pUS1xs*lVuWR;|X>5u3B_p4z(l`uc>!U9#TW z*4*5j!M!5(^tQFPw--Dk^2y`?H_fM0Lc%hiPK`)g`E(+G z$;+qH6WTPN%}AM+`D|v!vX#$fC46E@(a9XE`4?7{c&&|EenZz=*>R=VoTLo zw;lIC?5OcufBXH9J$2{b|NhU|*woz8+ScCD+11_C+t)u~;w1JdQ>RUzF>}`JIdkXD zU$Ah|;w4KNmaka3YW146>(+1BxM}m2t=qQmVBWQR&)$9e4;(yn_{h;?$4{I*b(;I^ zx$_q;Ub=kc>b2`PZr-|m=k7h82M-@Te)9C$^A|5)y?*od-TMz8KYjl4_1pI!KOeCD z`TOrb12dP5$A$$5n>mEFVoq#W*wikd>@~+@FEZ^r{>&Tuib#-;b=B&G?wywUuKH+eetoODxH#cWoUKM+K+uGaP3pU!#@ZP@e?(T}uukN1S zzW)CH24-$KpB)<>9_|py)!#EKR>^~xm(V6*Or%; zR|Kz)JG*P^>+2hmPtWt+z3uJo9mTKjo!!0t{rv;Y-12^Vc6@w%VzPGpxjj2SKfkcp Nd%oY^s&@hm)&Rz)PJaLZ literal 0 HcmV?d00001 diff --git a/css/msd/pics/h1_logo.gif b/css/msd/pics/h1_logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..6dc5b429eb93dfe3a46528448c70a1b27efc7f82 GIT binary patch literal 3476 zcmV;F4Quj8Nk%w1VZHz|0QUd@Ff1+Rq)nQbnLRo?(%RgZmY1Zw!|Jzn=&)y5iI+=A zNd5i&`1ttF&d&My`TP6(#>U3T$jJQs{L9PB$H&L|`uak7iP6!~)YR0^&(G4*(&pyo z?(Xj5;^OV??d$97%E`*{@$vBR@bdEV?Ck8v#>V60gw$6+Tr8m=;`zG^Xc2i=c-!o@9)jl z+27ya^6>8O>f`O>*XQTw*4EZ+8L|yz=qz@$&N7 z*x2KjGt<)2>%)zlxWLNS+vm)w?djsf(A2WT$#9va>D9cbrl;}m?XRw`>f_n%?(TAM zapmXe<(@!fk)G`C?&O|8>9~09=ig>yW9r_{>)_GC!NJ|)g(<5fh1nv1sbp!KbCAWnyE$zP{+&$?);BgAu@9*5<;@{%qg(at>(a2y%*^WS?6|kLi;0Qr$eF~_*4x_Ja&U0#!i~MWz2M~K@$T#L@bAjl z+-75BdZ4TBc+*!$I{iOzQonk)V#a9N`H>X)7RGB-{$G+ z?daj^y@9gF&9%nM?&;#m$;s;F-{$A&&1 zEiLWJo4&oi-QeNH#Khd&+pMdr*Vfj@)!C$@qot#ymz9>?cNcU=I6S(xw^T!+uGZ;w6vwY#Cv&q!NI}k-qG#d$?nv% zfPQ{+aB*{San#)2#?;uy)Y#3<&Gz>8-QC^w_4V}h^x}~w=;-L_>FMR=<^KNu#l^+S z%E}`jApigWA^8LV00000EC2ui0KNb+000R80O<)FNU)&6g9sBUT*$DY!-o(fN}R~A zqQ#3CGiuz(v7^V2AVZ2ANwTELlPFWFT*({Vj%brcUw(Z-vbL-yCyLX1)z=I1PPQ1AB;qA;l6 z&%eL_|Kn*O;D7`cXyAbeCaBER7GdBj}8w9DZYHjp6$1i{D)Yb1?KB?-^d+kwIf@&T4pa%*z&^sOgPgi2Fhpu?>0~DCxc=t%&n!<;n zCD=fK8|Yr>GNc6>px_6+Sk?gS!7Lp_FAe1chR_`nc)a6SQu@CEtcfF72mhY^%8dQnIrhFH)C$n^jpA6TJ3h%k+1 zB?ATZpduAHAc7nI@R5*&qzfiVNlKC+RrYYAsfu_+53nLtbtr-eG-&}d||0A}&>n%A`9vPe+LZhF(39SEm5#Ti6Yi9mMn;Gd~hF-<8D1Cwh_fm5Lpb){-V(o3&e)T5>prY+rSOaTc~5|3d`8W zI+k*fBY|TlOW7rD;If#_tY$aMSwOjr^M{)H_j?1K-~A`Ku;!zc)` zgA$wg#Cw1PgRcRHJ)C$4hG2t?*>DLsoR|;_*2fr1z~dhKxC9+g@DHl+V;;+c2@C%5 z2Z-F`aQe6kKbV4)r?8A7k3k;{9&(YDtYijz`O9A>ftbfk<{FsU%p`EcnQJfyKdjmR z4@Iit)O~{84nYO#4g(2n(C&8w zm)`20wZa$9a1l8C;SkS(#3e3*KukObanQuYH4%r4-vZ(mpLiTdv4WDDyyS!b2!hIk zzzq?sT;<((_zGTL@|O?71LLFt#JMm|58z{*_;@%8#;Fexgj57P7w1aH$pxH?lM4`6 zl+yE&bNU*6oDk2t*0&ylu6zCKHwb&!LtqE7-(UujFnc6sV0Jrvz3gJ20m)BJ_mhW! z?q7Gq$=#rLlarzc=>5RgKY-pI5dORz@VW@n8v@23-n=~k{CPhhdGiY1>yOVz4?OVS z#-mpRUk|ei5r@ed$?012B962)GAADUiVX-uHe4xyL;c z&UX9Uf1&ueFGUCxUiu!)It0#_bqGq&e1${*`68G#=w}_pvqeAFA;`Y}tb3q%=6|1& zrcb~6&t{MIyZ`<0kH7rqPyhPc|Ni*Tzy9~n|Ni^GrvMm$0yuyKSbzq2fC!j?3b=p_ z*nkfBfIT%25;%brSb-LJff$&98n}TR*nuASfgl)yA~=E!P=Y3Sf+(1RD!76y*n%$j zf-o3^GB|@YSc5j`063U~I=F*8*n>X!gFqOBLO6s(ScFD+gh-f#6|jU%*o02_gisiT zQaFWFScO)2g;|EYPg1M*oJQSh7u5mayW-{ zSci6ahj^HWdbo#t*oS`jhkzJ}g17*LScryrh=`boinxf3*ocmC_=u1giIO;plvs)T zaEX|hiJG{HoY;w;_=%txilR7*q*#ikc#5c)imJGZtk{aK_=>Q2iT^N*v{;L_c#F80 zi@Laryx5Dr_=~_8jKVmK#8`~Rc#O!HjLNu-%-D?1_>9mPjnX)c)L4zyc#Xsu5CA)u C3UQMF literal 0 HcmV?d00001 diff --git a/css/msd/pics/loveyourdata.gif b/css/msd/pics/loveyourdata.gif new file mode 100644 index 0000000000000000000000000000000000000000..ddcc647c48663b5e8b7be0ee7599df489028ba5f GIT binary patch literal 6340 zcmW-ki$7D3~~Ms9Pz zvr%qQqmo-$$fd-TB4OXp@Av!@UXRD~@pwIrO;73QI^F<6ffW(Z-1EviyZAy_LRQO* zXinYTymIpZc=B!Ir=(*)mX}%Cg$~iF%fEla6%E(o*#QOR2k$&O8xVE+QS|I+*F>E5 zxu94hzwq6zUL!B^9zEj}abL_%je2Gjj1_tu^$PyIx~g|Ku=>sWnQz}t`A6PN&D-bx zz$>fBDK4XZ;GJD$^2PAPk@sD~pFb|a=|he2A;ndrp9QHkO`m6`*Ecpkr=0Z5D~;z= z#aA_b`1Dc7^YPl>zrMMpj|(fNzkZ#p@}=JNW2Wa%wLF=anF%kg`8f9OxKG%9_VYVW zpV1zKx<4(vlb)Yi&*cvdG2nE+e2#ZUesED0GcEsmVpe(sZ(&L3o}BZzpdzuRC8MUH zckumWr0V{geqR^9*L3%dO-v-$whootFKlf);Tt|RH}~bavs+SjRzu7EoYGIZm!0D? zH9UOBr>3qvNf{dd|7j zzpo68D6Q=t9J-hJJm`7ltHHsypQrq?3MXG?e5#N5m~P>nTQ=8UaXYKTIVtxeH~C8J z)APY`@5V+QW730*s}9^|xg@Y(kBsiW;p>r__b{*QdfKzt%0{P{)Io06rwZSHLlb;* zN^)8|?q?RzyhAMlqOC*YQmX2!I=d}H;sf)Ghq^0#b4vzlVf)A=tB7RP>)xB2n|s|J z{8?L@^#Rm9d_O%W;`G?XPa8`6-Zl` z(M;0($L4I$REWE{yn_0F51dp6oYduN0`S$&N{QX-m#n4_4t6+E)lUSyZbPuADz!U2 z-88yRW4FfgBm=)1*Ax#|NJz@4gx!5cwpRvwuf$CjfAl}OFr*r0hAk@5^YQ%>;;cSw zswBVq=*P3AOoi(NnrhmKV;B6R1m*{|b)?c1ogKHO47Tz1o+UOFsv?T!N!soySa81o z!oO`2L}>MGVePGPWB|#Peu^;sfj2a*mHbURam?wPj+ZcRElF*t%yaZp$$&iBwHy|C zya=c<($NHEg1K3>!WG{4uFQWjxmAN33zi=b`LN3!)&VZshGYFR;NIBX|Jnmy*-x+^ ziyOJ3_O70Up70dYJ~&5T;z%XbbSt&uMteWTfn7`%c6AunKS6^3%}l@V&OrzT6#>V2ImD{fI*m zLE;c;i-Fi)rx=f(&-wDcFS9p2gXF~C62Rm{t9 z{-p)=PcrDTFtyMFx*w^9yvWfIAjo19C38`T?FnzH7VvUI{u9o}&+8@J1 z>(7u+(KzAY)6!S_E0=yy%eMVG%3SPNv6?9FJoJucjQe|yH)VY2%CSLW^$v4Z+w2zo zUho@xU+waOdNNkIzwk#V*E6F=P)6O8AJL&Nm7X3o>sc zFUv1?p2|$TH|o*)y-ouDyHocdUx$BM;==Wg81njyfj9bA%YDDbSr>h8HygZj4|-YD zX{XmL3q{=o!`NdEc!qn{ji`DT7XyuzK$JdS5M>PUVe&dDN-p%SE9|8S~JzUZpMh0q- z*=H6n4Vo!*%F+gMaI)Xej!Gi3NV;5EYee zvBq7v4(~U&Z`#=nL|Q#Q?V%pjhqvudT|LqR-U=r5W)ZzjLKHVS1XrUR$-z|>=;(J! z`oA_A$C;u^9@t|m&ARa#ZE{H;8642ovtoou1ta4Y!MpQ<$6jn z|6G)NuamNNx9RVgm#Q|JT_(Heh&>|2I_HolcK=8W^R!cv zR_#`}v?VTeN_me0t@mK=XR$qGmU?d++P{iNsitP5=qdUaDk>82_7tS^>}l8aezA0p z%KlF8;8S-1?0c^9_E(f3KkR?WMCxv>nXq2n-YisX`76@ul8Eq_ZsrWtO)G8nIx0It z4v39s9BKmtm3-UN1Xa%1^<(|vR`F7y%ScQ#Impy1i;xyMqdDh|@OkYh5y1>pYT{vW zT}b2mb0O#Acabj(l^Du&in#kMEin9CfRnnCf(bn;cn2>HV1=RP(i`-&cvr7bDz zbtL!F%IW?VDFK16bNtXzS)7wC5*NoP_E@~aJ0+3~RNeqiIX?^=?rk}+<)t}NQ-Ra* zI+li$r}DNNnT-q_O+cQe9NU&ljoRhmxUD|53ldtD=%G z`J9HJFu7Qear3`3K4@ZK4q+d_BfX;|<}WcCfrb6YnED7u^oOS9s;?Kydl(5BTrftp zo&H3296`;g-()XJoTogIr(J1X6#m1iRTDfC3?h^@3IpUK-|w!g!)b(;AHFsguv_N} z@w)XbpYaG2f@lJcr_v-000igkt{1}!OCDzGNJoS4oA{^Bxx4-cMtzX|sY<8T~ zB_|Qj36^&H`I=k+P7sW=wPHm@PQzM%M2dCJFCwmvA=ED0yz=U}ga6$-BdN~lh}Jo# zJme#$v|LwjxSyq?OhJ1~{=pba-AM~ zzA_W_8DV(s940>Q$`_Mc^#f{*sC@nAxowO(U|s&U-jMyLklwmV=f0mAad|0k)UT+2 zg_rXVY|ObHW{rF(z2|P^w{`A!N3R5Ut5})~MBb}PRr&tQe_kaX<&8G{%D+?f?CJMV zmqszEe?Vm2#@JhH+6#%Ln>XRX?IGp?;Oq2jCM!vUM?|`32?O!^2ahZjbm~uDe6#ja zzXidxI0g;*1)ezTStg^wntr4?W&AT~@yNyVLx&G8zo^|@<(jnoNXlIAuzM_#7V5k8 zECV+hr@dkdbFsnmpEvydM~+yW6?v;}p)lkxBYG`F*ugiOzw8)d(@Ct+tF6GPq4u@9 zH%-4|=phTD9qT(>Zq%qqoQc8AJ@ftk!C#8QB6P7Fe)%uW+(XboDS)tY5vc979Qxp~ zKJzg1!+k?oH*HDP1ZFAmS*k-9_SFSkp^KfQpcfA#qV_obprE;YL`apT&lgJ+jE($_ z+gNer2p?TCwrbxap`=MX$!Do|1?*o9G$99_4;A}I=J5_bL=k`|sfg#4%jV9uznRx) zLw+Yh9WJ>XTc`ai(T{xcgLr7@g2imWzd9l3RY~_;Lo$W9lkCeC0BBPe-I}(^GPLiOiFUZpjTAg5LEu2R6h}sDHCqO03(?u(lr-f4*9GkMvfcbNNy*w zRwPQvTag2J3M;A(i&#y5LZ;%17*ZcsBm7mvq$psI)w!^I{HU?hTtOs&Wkhz7?&fUc z$n4#q;$ZlR%rL|_sdazp$#^m{*7dO|aI1zUs7u7lZ*G?+M${cD_ zbNNhE5piQG?t(>vC^j}Wlkr$u=WK{4aU>*KqZ^JxE7dWz>_F*$Yj=aWI2ef6b`|$D zj-TNpj0<7Y`smYA8m}C8Ea`ZjAC7CaPPj{s=d$sV#__|ov0bYV#rN9v$L^EeMF6%? zZ)hi6zn(BCOkAOft8|NpSpk12Ns6bEl&;;_7*H0`G}alrfkZXeKVfcva8k@i!vcbS zsL69ybOi2y%@~=AOhJ_IOPEVitWQL5bHdW@XQefn`qC>L5=|3mHh$Ye9Jk*y8DXc_)<^z5wWaY-dZFmL z(#WBw4|O9@^%oy8O~SZOBfG7s6VVgfC71xEl4>^{mZA#>%@sZhi6>njj9uJo)t?>w z)0F(6o3x|Ndb$wJ3k%x8czW14a%A`+S(wmkDs|tRy>%x+(=qOfb4n2%@xM7Kh2pCp z>{Y$*Q}}*%?D+frs_Ows^0E@8fHb@%JJANg;h=c+nPoP>Fbwr2_{?yh#sSdt*{2)i zI2&5dA-5bS1i*Kcp3=(Zuu`n8F-q4Rje0C!!{Q^u+0ytcW+SlXaQwM&t(M^!f17MF zCv%WxGE7F4n?n68lTenJ+=`LSl2_+MlA`7NKSwVz^WP3eOAHt4@&Lut zF;2JhT9We&DG@)zJ3B*blG@I<>?~b+d1I*yo-9z-2^!; zYXz&WQQgmDUg_aVpNDV$y4;t%bKrEGvV_W5L6Jdfi6J>120(5q^0!-=U?divey$=Y zKD{e*?zCL!gUs*yo|gzqGlz;`Y8jc&A#*ru4P}e`vLbrv()J9nXzQ30 z8;z4ItDsj%G?qOzDc}6HYhBBKyZeqdKpLQ+r><~RL_#7(ts+j4v$Te^qv9q~EB2+S zY5qz6$1JgyhdyLiek3JM2ZDAMq0d9rI%#oJ4HbcUiN;WhJ8cV|UYR`-SHk4z+SFM2 zRDaorzQ8NAgHkL4PzpXJ=K8gscIAhPYF~H#^*aDZOI+y;)YlrF&trdo&G6miB?rud-9~ZG3AsMxX3Dc6V zJZmkts^@Zf39m{u-6Sn!8wYxVPps5me_mVHm@fxq-#=c^^0(1F71A<`NwaP0Xw0uU zr%^G|WH(x^va^05t@NLBTR${5kBv5e`r9lJ$+b-Aw@lf!O#8IVx;GWWPy_Wrc@eS! zhGdp2^q7Do7iu*_ZDS!8^vT6kDCOV3!zRG8@bjBK>FtHlKilfj$85&L`-yaH6R*+5=2^%)PUtl*;$Jd)2Lk9dle3&hJ(DL9sOTU%R)>XI7C;ASL^UD40LBfl zQGr5G#73_1Nva%l91AfGBflD;Jy^I)WYB^mJ}MNG5TGm={N3q*Hy3@C4Y8;%aFp)v zWKco?WmKZ&%n1%`K$lNwpk!E%p{>VmHL+fn*kjd1EP@f0CkelmB#W*0;_b=!JSu63 zO!`6wsp9=NxO}2`{}u(1l|&%)LB$m033Y@wfFLUjn9W1g0BI1w*T6{ezJaZguf3T3 zAQrlZgYe>DZgU39Si~iY*bx{wKq0rmeI5Xj$v~ri-)_c^3z`TYGUS)$D95jymzYBnk;_`c7;5;D(Y0hKYV`XSPTLoKeSs08EY+|8p zS>P|Sn3pc{Dx>TO8`;4CJp`|~=BRlgGMmnqfP2|&0x|{y_@n`Oye=D5<&%Cgh$k8E z{P?773QikFUK4SMhJ31;!&+DOgdXv!)=Q~9jcC3*BG7n3}6%>4l#(k=$!!! z|A-D9FH-$() z3hrLi^u*rwYHs(8FiL45JR3-(3}ktoh27@Au%qZ zm`MPNw^dWYqk@VsGeQ}S1odu7E}aw8M2d=u^#bS-zf~~{`Adl3=%{ZI;!~Kf40`dU zTtp8?T#t6{cm}~z2xB!80mN|h}p zOC?UiM4S+Ock_E04csq4bn!t;D)<=21+jo0pa)AM-J*gs`^Bf&s0wv5RvyZsi?wPX z?w1hS?Rz`T33D*A)d9Z&0Hai4@y$0(5f|md!n~lNj0A`vA%2P3V?=J#qllH!dW?ji z%K~&l9m!^6dZroty#4A$ z@3Dk{Z=ARbA#z{6r>V>N!Pu*Fg5&k#)7TqkoRrRuGw?BrV0D|bg!CAqs{Zq=GnggdZE~+ S`sSCB_unpZ?2rgR|Nj8-YN{3h literal 0 HcmV?d00001 diff --git a/css/msd/pics/navi_bg.jpg b/css/msd/pics/navi_bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..018cbddfd5c5ca4be04bc9756063476d3db42c5d GIT binary patch literal 4132 zcma)9c{Cf^)=v=8)U2qXlNzd~OU*NFxhSe;f=kRX&y}i{8cNYBQS((ahKi|#2wL-8 za|lu~q!mLNHB?cb`@ZkH@Aa+s$J=L}z4qCAuf6vo_+1S}wnYp;mo#WyX6c7*)y!1B#!C){8Gs}5)_VfJQ z9NheW8~$72y2fI*Dlf0+M@z`+2z!YRweXyz;g)(L!EBzN!Ji(1{Fs~=p_ zZ`|bmIu`TV945?knixpOKo4RBG5lX-ARPz&6gbn!xtlN2@`DvikL zw~|$jFb-FE`of%s=Z)LQFjDAbZ0rKbExOmGWb#Mqd_W9E<{dG+Mbb|*L${9T8mg&{ ztcxyv^3_!+K7Y$XHD%o_9I}J%)IrU>o34eI?;)#y-<4JqPptEp?v>Wo5%6=75hTLw z==;5jCaY2xs^aF%91K2|S_{Ivdi?AJkm&xM2LJN02}pn%rR(j#?}wQ;d{6NXj4CbE z=$gjSvsN(ESwMN-lf_}pAK+wR-?uC-l3O&~7y6KO2pRnIjq|ZY;E)0};47QgT{OI^ zS0;P!{_2tmrl?u+r{&yg;p2;~>psKXxASCY^}jqQth}-pVW&aC?kxwd{vOdT(^XrK zvA3OeJp1vh>i3blx^%|IqisKab$_@4EO95vB5ChZL-@?XA5&saa7_Dq%_tieoQYoW zFw`wMpXDuEj`KGNAc_!~lu)tD(&Wr-WilQawAyVe{{lRCkU5#nGMs7#KXb%ELpjSA z9da|G5beQfhVqfV38ru!Z1(vA@)GH&ebUDD-%vwr^Gsm!)3QWi(KI$^E4!#q=5TS#poq-K{9NO3 zUVcqD+i2dh`pjF4LD&$dxqcTl@rRZ%QI=}Gr=g#)t(#UqctfRZXY~@zq!RP-s@3P= zJ1?nKbN-%#2#>zeEnnyo;nCK)9qx+x&4jIk(vlRcI8@m>Q|h`}XQp29#C4n5{OCQB0qu!X3p0fpe zywA_g8U{rej868z1zG6r*1JPE$8sdpp4vB8l|*l=ruY*)c|{m$%7z?F0;l%jLxf_F!RGKY8`03aHMqHHAbGiOMyz z(WLqhQB2GuP_%71`vt-oL4os6A8`$ZLbu@ygtm?yxS^S#hqpzQcfSc+kz97U5Vjd! zyT`0njObnOQ?u(VjtJD;4J@8TQ}{F5jH2g%Ui`#aXp6I-K%GDO_H&B1_nl{8{?(&j znhfLwFmnH!RjZ81){U~<7{#F%)c9vUS+{_V%B9lqnXTbG?@}+$pyHHhA;!g(BAdZC z1tNxhSUvIypwub0dwy5ZZ*4?He+5bdO}?4yr)5BNd-E}V>qdGo;;Hb#q=r3p~pDN)} zUwz0TB^|B8AGc;7G^V$a8;6(lN-ECe;UmoqOXm-H{D_tTH5wjIxUBfZq>1X)!@ODB zL`Ns$lT%A)@m`$|jHCY*2-_j;S>lXg( z>3a;M%E*I#qggD;Srd^h&x7(Te(Pv%PsoR`cvS2tl8YEWr%JiDzxWq?PD%aNb2v&$w)$5` z;=HkWpi1Ix+;&f{UhJI7b+u;Ea_AYzym^(Z0$RFibGFS(KmY;NKEz8XiUp2eM+snx zLAI(qy~rqqN4YHUykfsC^8C%bo3k9SLC;b71r%4s+{WSU$^dI&ENZTE{+T1K#UXE@ z39A#dOcPtz^@#cP1%kAUpkURtp9R7bQz=$@)RO*zB^F%0{NozAv?D-H{+pbbN-C9e zeBMB>5^~s#=$~#>hD#}li%3RB9UV_ymV7B2<(ypK_A+Nf;a%8X)t{cv6cu~IJc(D{ zl~2AUE@Ky(#m5F;8yd=e%IqF_dH^-`&KW5&uJtjfrEe zsor+BFGUwdXSmuM<3 ztZ$UGb&a|2SA20jTD^|8Gz(Lx8R~-)Wn>S|aUIkqBxkE2H4WUKrP#o2X@mA$T2YM#JY2$ljhwU{qHO}^`{y|i1I79 zZjup3DONDc=zwW-`R<6u)17@umiROS%oiu)wnU>`c;ymN^w95e=I{f*1v65Fg4f-4 zd^3}R$k!!1iJF(jT2C>os!JuAfQua;&gbC%hs|nd0Ylw&yDWSPy>`35ffph11a`d@CS6z?c*5>`l*M6!&Vn z1~nE9LASa_Ulh$(?5Y~IZ|Lqy_}*7I_}W|3FtP5_D!;Q^icd?5j@b{(*qq8=D0w6C z%85DlD~@Bok_q~(&dq0YK&7~JeB_NmC;4e75mLI#kIwy(MRttzov=KXM3TC32Yj~b zn=vwRbxjsv1uP3|W_Y zk6To=`{W;p=}&ZTOJG=fc(^PbpuBR`jg286$to=?&(rzAemJ@v;j*$cEk86W7`4)z zAP&ma8wqrlGQp z+7R`D#pNc*g(_zSe5?E0jqs9%hqnoyRBD(@%|W5_$hRB)_r<;>oB%v!+JY^#twu^N6sm{j2k0KUKEApowZkgs{rG3dfE~Pjqoxxq#yh8>jh+j zSdUa+^WVl;FE}T)1fxkYN(Lz$>7Tc?y+VS(5cUt)TvOIvxsj?44S9)B0q>UJ>WVn2 zv~~4Cp&y8zdU0>%&JZPRA@&52Of^Ej!?%@ANJm$11jY@!=dMKu3AUTgZ@9=gT$P&1 z5!2!g(ea82#zI=^wB=6#mfU*lwVztNIympyE|YPExaQeo{WlHwO;(^HNWO@J>cHmU zDCcKsCjj3`$zWWl&*Q_`hDxt{`sHp#jZWqPcSCIu>M0Gw%f@?Cy~;bdD5R@^lx*`5 zv?(XS&~DDh&B?uHigxoVEm02%nm(%jw5y%%71lwb$=|&_zetyzm}0uP;ZK z!+x%$g^fZ6rG_}(vEmH@H%aR&noq!8H986^wT;H@tmFjqDGTt-=~wPg!GAl#zm`{R zO{G-X!~@oI=S3>FE`}T2;CDgfMP#W8b(mHGyD0u5k!oTS6U(w;Vc#p81@Ya_If6@_ z;g7Ed=Ur;()K!u`y$(ty9S+kIRzBt-Nwp2~RQ`Q0rr0JW;%MTPMByg0%%k{t6xHI2^R5XuUG=pm%zRKinxx)b`y&4@;QOC2 z{4WWtq&A_4=F^A0Q$fLJ0}ICZVK|HsSsA&#d)k)~uP?cYWvVbMHC#?C4LqZ5We5Pu9f59|_5EF`p3?%I*v!aJqN z?%l$>MMXqK|ALr=xTu(fn5d}u9&riD9T8|!dnBcH8aprf=g?gu!onhwVxnSyb@@LP z{I7tFH~;}~!a_%ZT{1$#GD7?oKt(`6E8i!*dypry-#4iu&}^r z5rM^m-qC{Rz-}24*(0aUipn{9iX9E!um2$Bo%peHAHT_8?WL+3c!fNakW^6o^?;Jv zadiz%Ekh$?6VuaY&R?*)c~C{BqsK0 zTzqO;dPZi}iKUG#$*VNY4H#9Z3w6?YX+0oh8KQK5nJTgikk*8;7 z=jIm{muTx7n_F}SleN9WO9&ADCoMtw-|~_X@Y=QG1F;=mLc8t>AS|<6?^>H_o3{lF) zB2*s<<^vCtdHuO^Gf}S6`}x4l5mOdy6H#MKq!(bn8xRke8mI^DFFmv2ez*$He4Wp5 zeRMZc-g9DJ|K7eK|uCU zuG)5V<*Kc0S!>FNQN14ftyQD#i!LucL+|W0cT%=-%yzi=x&rGPgehTV`e%CUBcp#m ze*71MM}gM|S60KL`xmyK?00mZf3x4w3ivzZdzd}#JaHx(28)``Ra}JVC}su1e2p>h zt?tyy;m9A}rNk^A^k<2wi^=~4$MeRCAJNUkv9765SWl^x`e4S{_j^OVj# z8STkxytC0@!k(l)NRyO{AIiKLw+e$bPw*n=B zP`*?d+d>>OSvq+q>{nbLi;;qk$fX{apYhP=R`K)^ugpC}sB<}_9OzU%o)7$9k7kN> zDWRvgXwpgOspnAYb?6B3`yl8R=$XQ#+0VD*9eI(Vfh(PmXP3dJn^uw~e)6PVfN{d!|2iZ4S1lNm&s$KlvG9zDlS>mPX*|LlFrFU7T#5MR_ zB&9W%sj0qtgl2B%QyBPh!AbC`>*$C&V;L?rBtd{szrb5iM`*h9^x`<9oPvt22R(D62TVE}zU+Pes&(p|n z45~^CofcqT~4Dc6UZWqRf+um-PkVps29%20qZx?a}6% z*!{`o8BU?`eTORLfNAa6?X=}_RvBr=We|k%`ar9wsmd!zTMjJh-}tm;+0OJ8c->jw z{|&gU=kZnld#;wYN%|Da&)py2*rDK*2eIoTiAO{P4-@-4fid4rO}(b zY|S}B`s{Ds6_rrO2gu&{>)0I}AYtOeAmoV(zdMkB2kEA4xguK17ITn)A>E;!Py-PIB5`UwoWu#gweYXUuQir09% z>u-=w67cv0nQw!N@wynf`%p5d(JM$Tm{QvsdEWliS^42O=cBBMnp(S~ws4JOduG-r zeT`TY??PS&N|z>ZjGZkV!IUN^KU0x0S}w_L59OTy{MwKx>+xEHHH5x1p!g@ea@?S6 zy_oZfO<4yM&{UTa+#${ds#LB-qYv9+k+3y^d#OhkOWHCkcKV48$_t08p1Y8{-f`%W zNjlGYGC7{5-ziuVMvLAqgxuF$86$XYH+8I8A=~50z9(L;^0v;fk>>T{NoP77{(AXq zH}WRw%`aVrHhmbuuAq5NJ0(yt$lvAk#ceb9^03}RFVPQ?H`-V&!LsQZBwukGBUNru zf3kjJ(6oJ!S*U73c|^qzNe8By5`sdW{75a0jXZD&2g!h-s{a2adws@C(z%QsN#y5*H? zoQ&nGi?YtPGvlpCL0m&t;Iko|2J;fhw_R7b#qv}t^h0~zWV31}9T(`@SPrj#37#8c z*J3-7AewLj$t#Yc3-kTISp?TN}=lM8_PhCyZyctL_h#u&pN$?RGgX)YcUfNQg8s z;Oa!F>qY#kehrbmi0Qn|tuVJxjGNonF$gK_hN)N4!BTmaK zB%deAV9|aHY5tGwQeg?|IXZS5$YNgP@FL5kH{o!zSE-kQTQp>;CsQFwwIbVXwZ7iy zOMeUwqLS_lwZ$E6y}3vVxlrNQGJavK** zS~hcf5u~`6J;w*4=2##aVW^GL^SDUpzNcyf2w7g=5P40t2rty2;{xJzRa?Z14))e2z#M#eb@a;GXg_R+mL6LKyz3@}Azt zdwCna<%>=XdK8b5VHkLn4@{Rs3FcLVv@AgeBQn{5iahq)l8H zbI*r;y6|1)&Qn@VUqoY2tW=^{o*?hM#Q{|ygSdqp>#1CEX8vW|;AXDGBHf9n#QS;u z%Z16>M7B$8=cEs^xD@FyY^0qWM{sEEI?!lVf=Km0QI#a;lvw!cq?J#Z!_23Z%QJ9G zznYL|!)cd7;|?8yZgl=8^PFbsnH&R?xKgf#Z$L{MfqV|Z^g?W|K`h*7dhji@qVVR^ z5m58I;Fc>%DwEiRR18MDjmDTFiPq;wK{U4K|1n^>4+-hz1Jx)M#D?9#TXy&EG;J1| ztv~#NxiKaEl^AzF@q=53Qo!YN(HPqS%R3Q^^#^$@tV}QG;1oI*uOfGzdgy2B(0p^P z>mQ%xg0ADU%_0Sj|L1MSjzJ4Dn^>XLamSN_PvgOBHgH4 zQuwoUzw1@aJeH;S2hLL3*a97!s`viGfxmM)O$7yX$&Tp*u&mpQ{m6p$t z^SNBns>ajOD}&Ul6t`x4153HPDrU^?9UekJi3-3%vOyVg6hN#e+v zjJhm?atF!OE03>xD`f{w%ZqmoLF#J80+zlrrR=4f_<%4iec>+d+C}H0nfcQ;*Irvh zBo@qTOtLjE4%z=$V80K90G9##N&D zz#$0BWJ>fM*-nfclQCuHjLaC?(x_ro*5!1@pZ2|#Gx%|?uDOSq%+jo&#E=c6pOs!+e4Xw z5y=B}QEk5^7W;Ii{`+^Li+nUh|lllWt}gOi9f68$aj&`{XeYi+l_ z3h0jy1G-9@-V@w~vAs|{!lmg&g)AvNw#`awOHK2~4?6rpMO8$+tA9k#JXVfbxu@oP_g0lf*L3>`A0R2m3~8WI42|anXd-sOVG8uP8VYcv zslE@w6Qe6HP>Ws{PojsNVX4M^pgj|>f}To4f5>f>CX7wv;@D?79@N3X;^rO+=<#sf1usgiyK^&~j8i9PeGVS*gc|r}a$0#4sM|;ox6<-Vi*_L0O^VXd! z^z|FM)^mj2t(w_=&@8I@eu5F^n$(Ap_ZO=T%koW&QrAa|=U#5~k-n>$|Nar7mG;iV zuH(lm0t~lUtI(JY+Q*aOoad&7_sE~X=S@s9<(v_3G!4sx;3oFtd|;yP1`D)W)VaX9 zG>sPG9PJmRaZ?D&g_=zCW)gk-QxE+yIwveD%%)?N#_Hjl;#NGOZCHr57o1}*VDL97Z!?2ZK*fq2sb+A6M0v>3VMD<;*(}Vc5LPr*Ex%-MwIG|uMk+AUO&l3%QZ5A1hrRchTIbpy53zmWHbq{jaY zQ@v*@m3m)61c!elG((kna(-{LD{E8U3>qbfWURVv`Q2jEMUJP~-gep&IF`>F^V1K!095pz+^;Kl*Qb_58sA literal 0 HcmV?d00001 diff --git a/css/msd/pics/truck_bg.png b/css/msd/pics/truck_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..3b81b9ff16197fcd885c737b5d73517462f86e6a GIT binary patch literal 6539 zcmW+*c~}zb+a4AXWkk%QLYATx?utu`X##4tS!t)#785tDj;U=fr2?YlQmK`xWop~W zPL`Zj%LNcKbFHkzGKEY%r3J2_BJ=V4{+Mg7dFP#(=X##^zTf+PCMRIWCOzGyx&Q#^ zZQ1O*OLO+s94=@S0KhKBZ&zwAkTJCFGyte+U8|M*Lvu|yxiRGApYe>7N#Vz%0a{di zL^N^B(eRk)UD4rDDaYPKdurw^-{MQ#o%}gM1wj;q1%RX46TkI+8+SZrQ<_D|@66Sn zfLtDt$q1AU_=(4MshaZ32Zq6P0V-6e+Z3Gafu8R$zm^;vNp*j;g50~1e!=H z_-QG%`aBqYh1@ZWrQ=>Q+T^mC)x3eP( zKYbjGeP%PZ>HN2LnWj)9lTCCCKo2SER>gT=hEc8hJPGm5Dd8$&|dtyxh(dZk+lda zAPj~3JA_jIQ9o7wY>A;L@jL#_acg7EX_Si7cnW$)*MJYKWo5r_2|3xBu+T-5nNt z9Drr6&4qp+EuNo}>=q8jm-{2h5}B^q6R|b&77RpxwM49OS3PBtx8{8#7Cq zfm5qb6$I_a!EjW%Px<0EMr){|%fd`kjd4~H_N!VVO@jfiFGpnFNxB{UE#}xD8p6U?%*VsdR4cmpw0+ym~jBKwY3G; z#(za#*@Y3zF@{>s$FM#SqPD>_6w!K7>%?cs@73mp4T-@484ha?PW{$HrTJ~{n;cq5 z%J^tV1*HD7g@Mk`ZdztEGS%v=(Vzo>hM{uqp>trBsV%Jz%bW)BF@KvGsZPjnK@H~O+XQ6#hF2sl+vyTBCaCk7POGbLV zJy~m5xei}j3Xv1ki}30h<}Kq;ikGXZ@nwz_>aM~6&9S{0=6?Y6WDXd_F|0!nQ)&!% zCzE9p5Q5xg18aw|0B;=wdJ52nZX~BaTVg2kJzuj3*@u!98k1_i6}t&Qj=$f|B%T!dQdCgo*d6fbuz5%-qsoIb%JxSE&N{90i68c{;hs_ugxhgjY_=qbz>-Tp z!zVR}OKPpSuJ>b9-)z^FKZ7Hcv%lRrPw>3u6ABX%U!f3MBxH9M2p21~d7oh;qvjL> zK2uBZ)D5ki;)jO_>p0Sxoo2O>YE?``tzu&d?Yv^OXraO$B)on_Sh;c~KBO;%5Oq5m zU%uf?i+ogOp8O1jCK@MO9*-}1!3;j$%lXqET;{(EyvHnesX8*|O5Qf+qJrkjg|Ltp zswi=XfQa<34LhhAo4v}Wp`=3!{yKt}>Kh$52c45@@&v{n9|4cV3Q&6FPR=se4iozh z@E_ZM3L-A$=S#mXB*{ru8bV!uoIH4QTj3_QRH&cGHHYv75(W4?DRBrGaq!7+PUKDr z{2u(fhFk=xbz?2yF~(b<{vAtTo(z z^4&ZPo_+JPLRPq?5u!Bk7SoRDi6BbVDZ?sClYXq&)milgtp1KQbioi=y!FVCsf^Pb z%LMBs1n#i*=~T8>nM0L(PMmvnY3HQO3bDYC7mt6@JNY5*$<^R%eJ>_$H6U5{eeq-k zPjPV#DYm`|?6-&T$?9}Z(a+t_bYthjDu+jdfw6*};#;Wy6y4Pkpo$-cGrAhfHIPA# z^(5uoFH@A+r;0iaAN603518-5)z~%*L@mt%z3~^17GG5#gZe$N)&DsT#WLlKr?I)H z`YWzFZLZ{;Dsp{V@+J000DsDJ9IPlGK)HW+)QFH}aI*uf%R&$wJFj+D=Bk^ox2siA z$`cC!4|}>*e!kTF&|Dbm0>9SI!_PPf#n^a;8{b`nIP;l8JbKd1fXg%w1Fq?2fJ}4N zrEY?TR~+}!epn)Qd$P2|1cGBJ6!rGoT%O0>)RcC)Z1@y?(|=1Dh|PVlkR~QwIeRus z`ntCUytt1GGR?}6YX&G(m3@4tTprk}>Il~hK&MM3i7+1quL!tf67J;>OyL|E;0_9A@a+ys>zz)8b^hj~6UtBI6 zi94RnT1KDbKd%J$$vhE>&3Be~z4Y2Yw|80pnz_Trb?_jBS2NSQfFndm)!im9+gn82 z<5Mb?UFwmHz^ndhMvi}gUYx-V-12!-$lQxZZrwrOjnJqtWd6cH#-7$9^8$)g#$<49 z|HfQ%nv_Mg8l}I&C5GilhZi49){GvJ;!rp8_kxCQc7(WrMy*6-^`g$wYfU5B+F@CE zvoyozrJWi`EwgN++AeaHzdQ8_%CupEm-ms7zjuOs^<&p4Rbqtvgf24gPK}namP&AX zWKla|%s#E%iz{oIni#q?PuG2Dy5uz{9=BI2N?^H`S=GX@_I$WhT?O!j_`sA7U;akMF2>K>g9&^p7 zZl9K50!{Aa1Pvj64uiM$>N3MhN*3!)egY4tHR62%`*k-phaQT zy^-;c-F&jQB6TPR7Y?-vVB_W0jorHcmK7Dz{Qb|djY{rlrvFlCP}y&>bIr^zW68)# zqutM==uwQU?_j{?iLDu%F8)>xb_b|GmW4kcQV^oLd{Eu*B>1%(pWNP}g*7s`qL92V z9;5J5VbAu`sR026?DOZZ(*mdkQbl~nKN_DP!Ol$&KmThX(t*CLfzJB|wc`GM{YGGa z0~^a!M=K$R=YeT~75!mhG|pf*+^YZRPT~W1 z$2don1&x;6gINCmVKW<7mfDs44065o?X#@c6!{Z>B2x z5KsTCd=174Ic>RLlaN%7ojmAsS8qM&l|jKrB_r|4g3#Ua60K^(Xn5`gh4}zy`P46u zT87_`G7Wgv_15kPy`i$=v4wjs5bm!SNR5f4hpRdT|46ZM4Pgr{?FrMQCW8)tT-yGD zJh;Q0))CuJkFDbE5m(CYuZinNJ5~(Trz3(8r^NfGV%==fv+K> z^gE6vxTG%37L9*0)4U;9nPYU{m%@K(I-vr6K6QTq&BHt+6U3*^(xw~jL|cm2b2+js zyye6875#eSM0T52f*qz+Ju&1_eRCy7Q?^fkTk`5E?d?}qPRk&^YmfT}>Z;1iBacn# z?&1<->$HU9$6d1Xa$HranLO#-RLmcztN?hnIQ_icTMG&+?W%~n$(Xf_WNV4W8ZqcE z;aTWp-~5hg7#x842HBZ4a5mmsLDguysx>8>Fo0Rj&=NN_1XXnLgOiX@Lv0(&Eg;!M zyrN{wQ4bjWPyiC$Is}gQx11}3uZPORj8qHbE<8f&@*1;%x<$&0KkE(+zfgQQiJ=1T zR#PQrF{7X66eDa2KlnL8?d5UyDL?y0aiNy@>>muJeyr1kDg{w-{FO`Y8RP7l%AKP) zj3#zE3-AzDsxxQTVz&&Ldrr1NFEa7#_?`=0>X`|~@9OI@38%|SKb92k4;Y2+24^s7 zX3SLGvCYn2z|xKCHp?c#tjrEET9J>eD@+Eg6Q~YVk9ijHJW~Sx90V;!Ps+YVb}?mU ztn~-}w?%#Uid+2zIaO*ZkWJRk9Q?TTfilpPlIFBh}e*Rym&q>GXCXpPI6 zhpdthmHnXXljU=YrO@4CkQq0QoK+vZbUl~4S8C1-Ob#c^!Ie`8c-3OT3?BIsbPq-hP#2^f- zhFYixpUo?b#hxi=Dx`yF7uG%2I<#`g?moGI!H~>PVet~a^^~W_TEyeqUcAB1E;;mN zKetA9&{-ArJ)ABzrb|XfqBL$4ncpG$i1@N#E(=5I`LQrZG#g;twPW7eq;u>Lf{2vp z$F)92;<}YqfNS9VH;Lp!H-|^3#z1pQS~z`f4vR;dw|41D3XKC?%d^f)2d{krD_Di9 z;-X@9)}>+^)mv9AOKz4!3VjqQ-G*+FX4DTub)IFxGq(hAHP+Ep@JWFQ!5lH-1)eb? zvd~v71T6UQ>bK3ebb_=z{lTXKb!?SVzn)`l&7NGa5(h%eRHVM0;LrB^V}kKn-M^|i z^CbU_WXbf^v%{mr$Nz*2o+yE*LsSn26gUv!?0YF2fy?%_P;@Zrl$do7=1gj8Ya%){ zy_Q40R`gS|ShuU3kAaoId^1Tvo>Q$xp59Yxw8*^Y!%9xogWWW}whGas zfk>3jvq8|$Q%107{nAelv}pzGdBPb*pAolP7Ursw7HW-PvGUX$`zaGHnrjM5JG2RE zNPEqX8=PGg&@q=X$e~+4yEdwNOhNHOxsTL<-Vl!6-ZEWG<4&0%40#KJl{YVZZc01< zpWe*&h>Aceh{V9;e=ZbSwZ5Ox?TwJbfbHr9}ox`^!igvwmLWNtvxJ(Fg zE~5h}RJ8q(H;OD;#nft1LyaZ)KI0tNF97IEf)Fu%5TAA0Anl%O@c{c8qU^ulsS5RS z-gqQpUnknE^Yp^V>f{i;-6!CGYJai``(J~kK=jwB?PKc_J6HY&t?H$3?$0(tjBq8q zI%s3&96?;&gHQh*hnRIZS#UF_^hT`qO0sX37D26Az+l0X2`WSVby@QmFW8m6DnVi% z{^qe6g-KO$n!ekNwz0cJ-}ZyVj^@RjZx*z_*6y;*ukdOqIf6p9#2xsp=_XI#^OakM zq3BlDduf@%(6Vc^t3O;tvQ49ehm_=331b+$tnG&VXWCoj^Jf8026BZchD1v+ z&C(aYT_nae@n#({qVrbSSGCrlbl@uL>#2!JPUNKMtiB-Xyvv>K)4)AGpHij&x0Wrr zPb|(%PD#;nU*}HmoL)ga(S>tCXjYN#9y#EdY+Rtzf7tuvZ!z9`cq86BT8aP8juzHz zGQmIeRHL!CgXSU&fH?uCgI^7H9Pk7w_!l^@)0VH~zcDvYXWjt=JYY}SblkRr*Pg&S zi|bLOU$ShY!tYL03xr07`4Qkh?^CrCq;`{P-f{8)$)$*pv?&kK&;2~V3-;uK%LMOJ zzW8UYO4rTIhSx;-{4xae%g^b`!;q1nx51@=kDE{zA(5U;;jni zR5*D5v1{BnFE70GL7?0$tzI>C*sro~&9CQ865NOnvEMJFsVo=}74!t1 V`&MkH`I`c4*|@{^p*KC_{{RV=2}u9| literal 0 HcmV?d00001 diff --git a/css/msd/style.css b/css/msd/style.css new file mode 100644 index 0000000..874e841 --- /dev/null +++ b/css/msd/style.css @@ -0,0 +1,906 @@ +/************************************************************************************************************************ + @MySQLDumper STYLESHEET + @name msd + @author Ingo Wagener , Daniel Schlichtholz, Christian Gresshoener + @copyright MySQLDumper - Daniel Schlichtholz + @media screen and projection +************************************************************************************************************************/ + /**************************/ /*Basics, Colors & Typography*/ + /**************************/ +* { + margin: 0px; + padding: 0px; + border: 0px; +} + +html { + text-align: left; + overflow: auto; +} + +body { + min-width: 782px; + min-height: 100%; + font-size: 62.5%; /*Resets 1em to 10px*/ + font-family: Verdana, Helvetica, Sans-Serif; + font-weight: normal; + color: #000; + text-align: left; +} + +h1 { + margin-bottom: 20px; + font-size: 2.4em; + font-weight: normal; + color: #256777; +} + +h2 { + margin-bottom: 0.92em; + padding: 4px 0px 3px 0px; + font-size: 2.2em; + font-weight: normal; + color: #256777; + border-bottom: 1px solid #c7c7c7; +} + +h3 { + margin-bottom: 12px; + padding: 3px 0px 0px 4px; + font-size: 1.8em; + font-weight: normal; + color: #256777; + background: url(pics/bg-headings.gif) repeat-x; +} + +h4 { + margin-bottom: 0.8em; + padding: 0px 4px; + font-size: 1.5em; + color: #256777; +} + +p { + margin-bottom: 1.5em; + padding-left: 4px; + font-size: 1.2em; + line-height: 1.5em; +} + +a { + color: #256777; + text-decoration: none; +} + +a:hover { + color: #e87b00; + text-decoration: none; +} + +img { + display: inline; +} + +/*IE4, IE5, IE5.5, IE6 PNG-Fix +* html img { + behavior: url(pngfix/iepngfix.htc); + } +*/ /*IE7 PNG-Fix +* + html img { + behavior: url(pngfix/iepngfix.htc); + } +*/ /**********/ /*Container*/ /**********/ +#container { + float: left; + min-width: 100%; + width: auto; + padding-bottom: 10px; + border-top: 5px solid #256777; + background: #fff url(pics/bg-body.gif) repeat-x 0 0; +} + +#header { + width: auto; + margin: 0px auto; + padding: 0px 18px; + text-align: center; +} + +#sidebar { + float: left; + min-width: 190px; + width: 19em; +} + +#content { + clear: right; + width: auto; + margin-left: 19em; + padding: 0px 18px; +} + +* html #content { + float: left; + margin-left: 0px; +} + +*+html #content { + float: left; + margin-left: 0px; +} + +*+html #content h2 { + min-width: 100%; +} + +#fullcontent { + width: auto; + margin: 0px auto; + padding: 0px 18px; + text-align: center; +} + +#footer { + clear: both; + width: auto; + margin: 0px auto; + text-align: center; +} + +#footer h4 { + margin: 0px; +} + +/*******/ /*Menu*/ /*******/ +ul.menu { + margin: 0px; + padding: 0px; + font-size: 1em; + list-style: none; +} + +ul.menu li { + border-bottom: 1px solid #ddd; +} + +ul.menu a { + padding: 0px 0.85em; + font-size: 1.2em; + line-height: 2.0em; + outline: none; + display: block; +} + +ul.menu a:hover { + background: #eee; +} + +ul.menu li.active { + border-bottom: 1px solid #256777; +} + +ul.menu li.active a,ul.menu li.active a:hover { + font-weight: bold; + color: #e87b00; + background: transparent; +} + +/******/ /*Lists*/ /******/ +ul { + margin-bottom: 1.5em; + padding-left: 18px; + font-size: 1.2em; + line-height: 1.5em; + list-style-type: disc; +} + +/********/ /*Tables*/ /********/ +table { + margin-bottom: 1.5em; + color: #000; +} + +tr.dbrow { + background-color: #fcfdfd; +} + +tr.dbrow1 { + background-color: #f8fafb; +} + +/************/ /*Formbuttons*/ /************/ +a.Formbutton { + display: inline-block; + width: auto; + height: 16px; + margin: 0px 6px 20px 0px; + padding: 3px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + white-space: nowrap; + text-decoration: none; +} + +button.Formbutton { + width: auto; + height: 24px; + margin: 0px 6px 20px 0px; + padding: 0px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + vertical-align: top; + overflow: visible; + white-space: nowrap; + cursor: pointer; +} + +input.Formbutton { + width: auto; + height: 24px; + margin: 0px 6px 20px 0px; + padding: 0px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + vertical-align: top; + overflow: visible; + white-space: nowrap; + cursor: pointer; +} + +ul.Formbutton { + margin: 0px; + padding: 0px; + font-size: 1em; + list-style: none; +} + +ul.Formbutton li { + display: inline; +} + +select.Formbutton { + width: auto; + margin: 0px 6px 0px 0px; +} + +td .Formbutton { + margin: 0px 6px 0px 0px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,a.Formbutton { + margin: 0px 6px 1.5em 0px; +} + +/*IE7*/ +*+html a.Formbutton { + margin: 0px 6px 20px 0px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,button.Formbutton { + margin: 0px 6px 1.5em 0px; + padding: 0px 3px 2px 3px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html button.Formbutton { + padding: 0px 5px; +} + +/*IE7*/ +*+html button.Formbutton { + margin: 0px 6px 20px 0px; + padding: 0px 5px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,input.Formbutton { + margin: 0px 6px 1.5em 0px; + padding: 0px 3px 2px 3px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html input.Formbutton { + padding: 0px 5px; +} + +/*IE7*/ +*+html input.Formbutton { + margin: 0px 6px 20px 0px; + padding: 0px 5px; +} + +.Formbutton img { + max-height: 1.2em; + margin: 0px 6px 0px 1px; + padding: 0px 0px 3px 0px; + border: 0px; + vertical-align: middle; + display: inline; +} + +button.Formbutton img { + padding: 0px 0px 1px 0px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html .Formbutton img { + padding: 0px; +} + +/*IE7*/ +*+html .Formbutton img { + padding: 0px; +} + +a.Formbutton:hover,button.Formbutton:hover,input.Formbutton:hover { + color: #256777 !important; +} + +.Formbutton:disabled { + color: #888 !important; + cursor: default; +} + +/**********/ /*Messages*/ /**********/ +.message { + display: inline-block; + padding: 6px; + font-size: 1.2em; + border: 1px solid #ff0000; + background: #ffff00 url(pics/truck_bg.png) no-repeat; + background-position: right; + z-index: 1; +} + +.message div.Growler-notice-exit { + float: right; + cursor: pointer; + margin: 0px; + background: transparent; +} + +.message div.Growler-notice-body { + padding: 6px 82px 6px 6px; +} + +.message div.Growler-notice-head { + padding: 6px; +} + +/*------------------*/ /*old CSS Build*/ /*-----------------*/ +#version { + font-size: 1.1em; + color: #4E5665; + text-align: center; +} + +#version a:hover { + color: #4E5665; +} + +#menu { + margin: 1px 0px 23px 0px; +} + +#server0 { + position: fixed; + bottom: 4px; + text-align: center; + left: 10px; + color: #000; +} + +#server1 { + position: absolute; + right: 16px; + text-align: center; + top: 10px; + z-index: 1; +} + +label { + cursor: pointer; +} + +a.ul { + text-decoration: underline; +} + +.small { + font-size: 0.9em !important; +} + +.ssmall { + font-size: 0.8em !important; +} + +td.small { + font-size: 11px !important; +} + +td.ssmall { + font-size: 10px !important; +} + +.success { + color: green; + font-weight: bold; +} + +.error { + color: #E87B00; + background-color: yellow; + font-weight: bold; +} + +.explain { + text-decoration: none; + border-bottom: 1px dotted; +} + +.explain:hover { + cursor: help; +} + +.active_db { + font-weight: bold; + border-bottom: 1px dotted; + color: #9AA2B1; +} + +table { + color: #000; +} + +table.bdr,.bdr { + border: 1px solid #ddd !important; + /* border-collapse: collapse !important; */ +} + +#fullcontent table.bdr { + margin: 0px auto; +} + +table td { + text-align: left; + vertical-align: top; + padding: 0 6px; + font-size: 12px; +} + +table th { + padding: 0 6px; +} + +fieldset { + margin: 0px; + padding: 5px; + border: 1px solid #ddd; + color: #256777; +} + +body.content legend { + font-weight: bold; + color: #256777; +} + +body.menu fieldset p { + margin: 5px 0 0; + text-align: center; +} + +/* MAIN */ +#topnavi { + list-style: none; + margin: 10px 0 20px; +} + +#topnavi li { + float: left; + margin-right: 6px; +} + +#topnavi li a { + float: left; + font: 1.1em verdana, arial, sans-serif; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + color: #E87B00; + padding: 3px 6px; + vertical-align: bottom; + cursor: pointer; + text-decoration: none; + white-space: nowrap; +} + +#topnavi li a span { + color: #256777; +} + +#topnavi li a:hover { + color: #256777; +} + +#topnavi li a:hover span { + color: #E87B00; +} + +/*Tabellen */ +table tr.dbrow { + background: #FCFDFD; +} + +table tr.dbrow1 { + background: #F8FAFB; +} + +table tr.dbrow a,table tr.dbrow1 a,table tr.dbrowsel a { + margin: 0px 3px 0px 0px; +} + +table tr.dbrowsel { + background: #F9F3ED; + color: #000; +} + +table tr.dbrowsel a:hover { + color: #E87B00; +} + +table td.treffer { + background: #000; +} + +/* Treffer bei der MySQL-Suche */ +table tr.dbrow .treffer,table tr.dbrow1 .treffer { + color: yellow; + background-color: #E87B00; +} + +table.border { + border: 1px solid #738C88; +} + +table td.sum { + background-color: red; + font-weight: bold; + text-align: right; +} + +table tr.thead th,table tr.thead td { + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; +} + +.tdcompact { + width: 100px; + height: 16px; + overflow: hidden; + font-size: 11px; +} + +.tdnormal { + white-space: nowrap; + padding: 1px; +} + +.sqlheadmenu a { + +} + +td a.Formbutton,td button.Formbutton,td input.Formbutton { + float: none; + margin: 0px 6px 0px 0px; + display: inline-block; +} + +input.Formtext { + background: url(pics/bg-buttons.gif) repeat-x; + float: left; + width: auto; + margin: 2px 6px 0px 0px; + padding: 1px 2px; + border: 1px solid #ddd; + color: #000; + overflow: hidden; + cursor: text; +} + +#content .SQLbutton { + font-size: 11px; + background: #E4E9E8; + cursor: pointer; +} + +/* htaccess edit area */ +#content textarea { + width: 100%; + background: #FFF; + border: 1px solid #ddd; + overflow: auto; +} + +#content textarea.hta_content { + border: 0px; +} + +input.radio,input.checkbox { + background-color: transparent; +} + +/* margins and paddings for input elements */ +.radio,.checkbox,.text,.select,.margin { + margin-right: 4px; + margin-left: 10px; +} + +.noleftmargin { + margin-left: 0px !important; +} + +/* options in select lists */ +option { + padding: 0px 6px 0 6px; + border-bottom: 1px solid #eee; +} + +/* save button at left site in configuration screen */ +.save-button { + float: left; + padding-top: 12px; +} + +/* Colors for Formelements */ +input.text,input.small { + padding: 1px 2px; + border: 1px solid #256777; + background: #fff; + color: #000; +} + +input.text:disabled { + border-color: #cccccc; +} + +select { + padding: 1px; + border: 1px solid #7F9DB9; + background: #FFF; + color: #000; +} + +textarea { + background: #B3C2C0; + color: #4E5665; +} + +/* disabled textarea when editign rows in SQLBrowser */ +.off { + background-color: #ccc !important; +} + +/* for Geckos */ +input[disabled] { + color: #888 !important; +} + +/* special elements */ +.MySQLbox { + font-size: 10pt; + padding: 0px; + background: #000; + color: #fff; + border: thin solid #999999; + height: 200px; + width: 100%; + text-align: left; + overflow: auto; +} + +#content #sqlheaderbox,.sqlbox-warning { + width: 100%; + padding: 6px 0px; + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; + white-space: nowrap; + vertical-align: top; +} + +#sqlheaderbox .Formbutton { + line-height: 14px; + margin: 0px 6px 0px 0px; +} + +#sqltextarea { + width: 100% !important; + margin-right: 30px !important; + border: 1px solid #ddd; + overflow: auto; +} + +#content #sqleditbox { + border: 1px solid #738C88; + background: #EEEEEE; + margin-bottom: 10px; +} + +#content #sqleditbox form { + margin: 10px; +} + +#content #sqleditbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqlnewbox { + border: 1px solid #738C88; + background: #E4E9E8; +} + +#content #sqlnewbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqloutbox { + font-size: 11px; + width: 700px; + padding: 6px; + background: #D5DDDC; + border: 1px solid #738C88; + overflow: auto; +} + +#content p.autodel { + font-size: 11px; + border-bottom: 1px dashed #fff; + margin-bottom: 12px; +} + +#content .Logbox { + font: 12px/ 1.2 "Courier New", Courier, monospace; + padding: 6px; + border: 1px solid #ddd; + height: 320px; + width: 90%; + text-align: left; + overflow: auto; +} + +#content .Logbox span { + color: #738C88; +} + +#content .backupmsg { + padding-left: 20px; + font-size: 11px; +} + +#content .backupmsg .success,#content .backupmsg a { + color: #999; + font-size: 11px; +} + +#content .backupmsg .error { + color: red; +} + +.panel { + display: block; +} + +#selectConfig { + margin: 0px 6px; + width: 180px; + height: 196px; +} + +#ilog { + border: 1px solid #ddd !important; + padding: 12px; + background-color: #fcfcfc; +} + +.center { + margin: 0px auto; + text-align: center; +} + +.left { + text-align: left; +} + +.right { + text-align: right; +} + +.middle { + vertical-align: middle; +} + +.inputsize-middle { + width: 90px; +} + +.nowrap { + white-space: nowrap; +} + +.nodisplay { + display: none; +} + +.blend-in { + border: 3px outset #DDD; + padding: 6px; + z-index: 1; + background: #FFF url(pics/bg-buttons.gif) repeat-x; +} + +/* creaet directory protection */ +#psContainer { + height: 18px; + width: 100px; + border: none; + float: left; + margin: 0; + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + cursor: default; +} + +#psStrength { + background-image: url('./../../js/strength.jpg'); + height: 18px; + width: 0px; + cursor: default; +} + +/* Installation */ +#fullcontent .bdr td { + border: 1px solid #ddd; + vertical-align: middle; +} + +#download { + position: absolute; + top: 50%; + left: 50%; + width: 500px; + height: 300px; + margin-top: -150px; + margin-left: -250px; + border: 4px outset #E29126; + background-color: #EEEEEE; + padding: 12px; +} + +#download-messages { + width: 480px; + height: 220px; + overflow: auto; + top: 6px; + background-color: #ffffff; + border: 1px inset #cccccc; + padding: 10px; +} + +#close_button { + position: absolute; + width: 100px; + left: 230px; + bottom: 4px; +} \ No newline at end of file diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt new file mode 100644 index 0000000..be8167b --- /dev/null +++ b/docs/LICENSE.txt @@ -0,0 +1,341 @@ +$Id$ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/docs/de/CHANGELOG.txt b/docs/de/CHANGELOG.txt new file mode 100644 index 0000000..6d29f90 --- /dev/null +++ b/docs/de/CHANGELOG.txt @@ -0,0 +1,55 @@ +$Id$ + +Changelog der wichtigsten Änderungen im Vergleich zur Version 1.22 (1.23 hat die Beta-Phase nie verlassen): + +- MySQLDumper 1.24 funktioniert nach wie vor sowohl mit PHP4 als auch mit PHP5 +- neue, helle und freundliche Optik. Der "alte" Style ist für "Nostaligiker" ebenfalls enthalten. +- bessere Speicherausnutzung +- Backup und Wiederherstellung per PHP sind im Schnitt ca. 25 Prozent schneller +- Möglichkeit beim Sichern und auch beim Wiederherstellen nur bestimmte Tabellen auszuwählen +- Nutzen von mehreren MySQL-Servern und -Usern über Konfigurationsprofile. +Damit kann man mehrere Server über eine MySQLDumper-Installation warten und sichern. +- der interne SQL-Parser ist an vielen Stellen verbessert worden (noch mehr Fremdbackups können importiert werden) +- SQLBrowser: jede Menge Bugfxies und kleinere Erweiterungen (dennoch ist der SQLBrowser noch als experimentell einzustufen) +- SQLBrowser: über die Lupe kommt man zu einer durchdachten Vollextsuche. Editiert man einen Datensatz, kommt man zur Trefferliste zurück. + Das ist recht komfortabel wenn man Stellen finden muss, von denen man nicht genau weiß in welchen Spalten sie vorkommen können. +- Tools: der Export von Daten als Datei funktionierte in 1.22 nicht. Jetzt klappt das wieder. +- die Konfiguration in der WEB-GUI wurde an einigen Stellen nochmals vereinfacht und überflüssige Parameter entfernt +(Du hast kaum noch eine Chance etwas "falsch" einzustellen. :) ) +- FTP-Übertragung kann nun optional auf bis zu 3 unterschiedliche Server gleichzeitig erfolgen +- Tabellen vom Typ VIEW und MEMORY werden nun automatisch erkannt und deren Daten korrekterweise nicht mitgesichert, wohl aber deren Struktur. +- das Verzeichnis work/structure wird nicht mehr benötigt +- die automatisch immer mit angelegten Struktur-Backups wurden entfernt +- noch besseres, internes Handling der Kodierung von Backups (Umlautproblematik) +- Fehler (auch beim Sichern) werden noch zuverlässig abgefangen und aussagekräftig im Log notiert +- Konverter: wurde neu geschrieben. Jetzt werden große Dateien beim Konvertieren automatisch in Multipart-Dateien aufgeteilt +- keine Notices in Server-Logs mehr +- Beim Anlegen von gespeicherten SQL-Befehlen können nun mehrere Queries angegeben werden, die bei Nutzung von "Command before/after backup" +nacheinander ausgeführt werden. Der Erfolg oder Mißerfolg wird im Logfile notiert. +- Beim Anlegen des Verzeichnisschutzes wird die Stärke des Kennworts visualisiert. + +crodump.pl: +- wenn die crondump.pl im Standardordner "msd_cron" aufgerufen werded kann, braucht man den "$absolute_path_of_configdir" nicht mehr editieren. +Hier findet nun eine automatische Erkennung statt. +- besseres Abfangen von Fehlern +- es wird präziser mit aussagekräftigen Nachrichten geloggt +- automatisches Löschen betrachtet Multipart-Dateien nun korrekt als 1 vollständiges Backup und funktioniert wie erwartet +- das automatische Löschen wird nicht mehr zu Beginn ausgeführt, sondern erst nach Beenden des Sicherungsvorgangs + (somit bleiben im Fehlerfall alte Backups erhalten) +- der Parameter der zu nutzenden Konfigurationsdatei kann im Aufruf nun auf 3 Arten angegeben werden. +Die fehlende Endung ".conf.php" wird bei Bedarf automatisch ergänzt: +1. config=mysqldumper.conf.php +2. config=mysqdumper.conf +3. config=mysqldumper + +- Signalhandler entfernt: +Dieser sollte eigentlich einen Abbruch des Users melden und das Skript beenden. Über einen Cronjob aufgerufen führte dies bei einigen, wenigen +Servern zu der Fehlfunktion, dass mehrere Instanzen des Skripts gestartet wurden, die lange in der Prozesliste standen und manuell beendet +werden mussten. + +... und viele weitere kleine und große Bugfixes und jede Menge Aufräumarbeiten im Code. + +Wenn Du es ganz genau wissen möchtest, dann schaue Dir das changelog auf Sourceforge an. Hier ist akribisch +jede Änderung am Code dokumentiert: + +http://mysqldumper.svn.sourceforge.net/viewvc/mysqldumper/trunk/?view=log diff --git a/docs/de/INSTALLATION.txt b/docs/de/INSTALLATION.txt new file mode 100644 index 0000000..43d3116 --- /dev/null +++ b/docs/de/INSTALLATION.txt @@ -0,0 +1,7 @@ +$Id$ + +Installation: + +- lade den Ordner mysqldumper in einen beliebigen Ordner auf Deinen Webspace hoch +- Starte das Script im Browser (http://www.deineDomain.de/DeinOrdner/mysqldumper/) +- Folge den Installationsanweisungen diff --git a/docs/de/LIESMICH.txt b/docs/de/LIESMICH.txt new file mode 100644 index 0000000..41db2ef --- /dev/null +++ b/docs/de/LIESMICH.txt @@ -0,0 +1,90 @@ +$Id$ + +MySQLDumper - Readme_de +==================== + + MySQLDumper ist ein Sicherungsprogramm für MySQL-Datenbanken, + geschrieben in PHP und Perl. Damit können Sicherungskopien der + Daten (Forum, Shop, Blog, usw.) erstellt und bei Bedarf auch + wieder hergestellt werden. Besonders bei Web-Space ohne Shell-Zugang + bietet sich MySQLDumper als sinnvolle Alternative an. + + Version 1.25 + --------------- + http://www.MySQLDumper.de/ + + Copyright (C) 2004-2010 Daniel Schlichtholz (admin@MySQLDumper.de) und mehr + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program 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. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Voraussetzungen: + PHP 4 oder höher + MySQL 4.1 oder höher + einen Web-Browser + optional Perl für das Cron-Script + + Übersicht: + Das Problem: + PHP-Skripte werden nach einer bestimmten Laufzeit abgebrochen + (normalerweise nach 30 Sekunden), und so funktioniert ein Backup mit diversen + Tools nur bis zu einer bestimmten Größe. + + Braucht das Skript länger als die vom Server zugestandene Ausführungszeit, + so wird es einfach abgebrochen! Man erhält damit keine vollständige + Sicherungskopie. Die gleiche Problematik besteht beim Wiedereinspielen eines + Backups. + + Wer einmal ein Dumpfile von Hand in viele kleinere Einzelabschnitte zerlegt hat, + um eine Datenbank wieder herzustellen, der hat leidvoll und arbeitsintensiv + erfahren, wovon wir reden. Mit MySQLDumper gehört dieser manuelle Aufwand + glücklicherweise der Vergangenheit an. + + MySQLDumper umgeht den Timeout-Error mit Hilfe eines kleinen Tricks: Er liest + nur eine bestimmte Anzahl von Datensätzen aus der Tabelle aus, merkt sich, wie + weit er gekommen ist, und ruft sich anschließend selbst auf. Dadurch erhält das + Skript bei jedem Aufruf wieder die volle Ausführungszeit und umgeht so geschickt + das Problem des Abbruchs durch den Server. Das gleiche Prinzip benutzt MySQLDumper + auch beim Wiederherstellen der Daten. + + MySQLDumper kann die Daten beim Sichern sofort packen. Auch das Wiederherstellungs- + skript kann direkt aus dieser gepackten Datei lesen, ohne dass sie auf dem Server + entpackt werden muss! Das spart Zeit, Platz und Nerven. Natürlich kann man die Datei + auch ungepackt lassen, aber spätestens beim Hochladen eines Backups weiß man dies + zu schätzen. + + Download: + Die neuste Version gibt es unter http://www.MySQLDumper.de/ + + Credits: + Finden Sie auf http://www.MySQLDumper.de/credits/ + + Installation: + Siehe INSTALL_de.txt. + + Sicherheit: + Um Ihren MySQLDumper vor unberechtigten Zugriff zu schützen, müssen Sie einen sog. + Verzeichnisschutz einrichten. Öffnen Sie dazu bitte mit dem Browser die Startseite + der MySQLDumper-Installation. Dort drücken Sie den Button 'Verzeichnisschutz erstellen' + (funktioniert nur mit dem Apache-Webserver) oder erstellen Sie den Schutz manuell. + + Änderungen: + Siehe changelog_deutsch.txt + + Support: + Hilfe finden Sie im Forum unter http://forum.MySQLDumper.de/ + + Viel Spaß! + Ihr MySQLDumper-Team \ No newline at end of file diff --git a/docs/en/CHANGELOG.txt b/docs/en/CHANGELOG.txt new file mode 100644 index 0000000..958b6b6 --- /dev/null +++ b/docs/en/CHANGELOG.txt @@ -0,0 +1,52 @@ +$Id$ + +Changelog of the most important changes compared to version 1.22 (1.23 never left the beta status): + +- MySQLDumper 1.24 is still working on PHP4 and PHP5 +- new, light and friendly style. The "old" style is still included. +- better use of RAM +- backup and restore via PHP is about 25 percent faster +- possibility to select tables when doing backup or restore +- use different configuration profiles to manage different MySQL-Server or -user. + This way you can administrate different MySQL-Server with a single MySQLDumper-Installation. +- the internal SQL-Parser has been improved (more backups from other programms can be imported) +- SQLBrowser: a lot of bugfixes and some improvements (nevertheless it must still be regarded as experimental) +- SQLBrowser: a comfortable fulltext-search lets you find text even when you don't know in which column it can occur + After editing a record you get back to the hitlist. That is very comfortable if you need to change some data. +- Tools: in version 1.22 the export of data as file didn't work. Now it is working again. +- the Web-GUI has been simplified. Some parameters have been removed. (You nearly have no chance to configure something incorrectly :) ) +- FTP-Transfer: address up to 3 ftp configurations simultaneously in one backup process +- Tables of type VIEW or MEMORY are now detected and data is not saved but the structure of the table is +- the directory work/structure is no longer needed +- the automatic "structure only" backup has been removed +- better and safer handling of encodings of backup files +- better and safer error-handling +- the backup converter has been rewritten. Now it also automatically converts big files into Multipart files. +- no notices in server-logs +- when adding SQL-Queries to the SQL-Library you can now enter more than one query. If using "command before/after backup" + these queries will be executed in a row. Success or failure is written into the log file. +- When creating a password protection the password strength is visualized. + +crodump.pl: +- when you can call crondump.pl in the standard directory "msd_cron" you no longer need to enter the + "$absolute_path_of_configdir" manually. An automatic detection was added. +- better and safer catching of errors +- logging of events is much more precise and gives you clear information what happened +- automatic deletion now regards Multipart files as one complete backup and works the way you expect it to work +- automatic deletion is done after the backup process. In case of errors this retains your old backups you might need. +- the config parameter - which configuration profile is to be used - can be set in 3 ways. The missing suffix + ".conf.php" will be added dynamically. +1. config=mysqldumper.conf.php +2. config=mysqdumper.conf +3. config=mysqldumper + +- removed signalhandler: +When crondump.pl was started via a cronjob there was a malfunction. On some, rare server this signalhandler caused a second +or third instance of the script that never stopped and stuck in the process list. In this case the process must be killed manually. + +... and many more small or big bugfixes and cleaning up of the code + +When you want to know more, just take a look at the changelog of code changes at Sourceforge. Each change +of the code is documented here: + +http://mysqldumper.svn.sourceforge.net/viewvc/mysqldumper/trunk/?view=log diff --git a/docs/en/INSTALL.txt b/docs/en/INSTALL.txt new file mode 100644 index 0000000..98464f2 --- /dev/null +++ b/docs/en/INSTALL.txt @@ -0,0 +1,7 @@ +$Id$ + +Installation: + +- upload the folder mysqldumper to any folder on your webspace +- start the script in your browser (http://www.yourDomain.de/yourFolder/mysqldumper/) +- Follow the Installation assistent diff --git a/docs/en/README.txt b/docs/en/README.txt new file mode 100644 index 0000000..9b0dd59 --- /dev/null +++ b/docs/en/README.txt @@ -0,0 +1,80 @@ +$Id$ + +MySQLDumper - Readme +==================== + + MySQLDumper is a PHP and Perl based tool for backing up MySQL databases. + You can easily dump your data into a backup file and - if needed - restore it. + It is especially suited for shared hosting webspaces, where you don't have + shell access. + + Version 1.25 + --------------- + http://www.MySQLDumper.net/ + + Copyright (C) 2004-2010 Daniel Schlichtholz (admin@MySQLDumper.de) and more + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program 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. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Requirements: + PHP 4 or later + MySQL 4.1 or later + a web-browser + optional Perl for cronscript + + Summary: + The problem: + A PHP script has a maximum execution time that is usually set to 30 seconds on + most server installations. A script running longer than this limit will simply + stop working. This behavior makes backing up large databases impossible. Maybe + you already had this specific problem when using other tools. + + MySQLDumper uses a proprietary technique to avoid this problem. It only reads + and saves a certain amount of data, then calls itself recursively via JavaScript + and remembers how far in the backup process it was. The script then resumes + backing up from that point. + + The restore process is similar. Unlike other tools, splitting and splicing of + large backup files is no longer necessary. + + MySQLDumper can write the data directly into a compressed .gz file. The restore + script is able to read this file directly without unpacking it. You can also + use the script without compression, but using Gzip saves a lot of bandwidth. + You can even configure the script to automatically send the backup file to an + FTP account or your email adress. + + Download: + You can get the newest version at http://www.MySQLDumper.net/ + + Credits: + Please see http://www.MySQLDumper.net/credits/ + + Installation: + Please see the install_english.txt file. + + Security: + To protect MySQLDumper, you have to create a directory protection. Point your web + browser to your MySQLDumper installation and push the button 'Create + directory protection' (works only with apache) or create it manually + + Changelog: + Please see changelog_english.txt + + Support: + See support forum under http://forum.MySQLDumper.de/ + + Enjoy! + The MySQLDumper team \ No newline at end of file diff --git a/docs/fr/INSTALL.txt b/docs/fr/INSTALL.txt new file mode 100644 index 0000000..578448d --- /dev/null +++ b/docs/fr/INSTALL.txt @@ -0,0 +1,8 @@ +$Id$ + +Installation: + +- Envoyer le répertoire "mysqldumper" sur votre serveur +- Chmoder le fichier "config.php" en 777 +- D�buter le script en saisissant l'adresse suivante dans votre navigateur (http://www.votre_domaine.fr/mysqldumper/) +- Suivez les instructions d'installation du script diff --git a/docs/it/INSTALL.txt b/docs/it/INSTALL.txt new file mode 100644 index 0000000..0f19bbb --- /dev/null +++ b/docs/it/INSTALL.txt @@ -0,0 +1,8 @@ +$Id$ + +Installazione: + +- carica la cartella mysqldumper sul tuo spazio web +- metti i diritti del file config.php a 777 +- fai partire lo script nel tuo Browser (http://www.il tuo dominio.it/mysqldumper/) +- segui la installazione \ No newline at end of file diff --git a/images/logo.gif b/images/logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..fa6d5c0dbdcbbcfdfeeb135c2bd63fc1f0280b59 GIT binary patch literal 5219 zcmWlb_aoGg0FcIH-t|)FhXZ`pi3NWpAq%Jd zaYa9RXaFfOv*p3R=T-dr%OTy9>uc-HBi!XMMc4E~ev-nw<&V43(zUgx{W@s2?RDUZqzwGUG{dz)ChW~DX>D1KJ9B=7efE3>! zI(<#7G{TbL=Ra&K@-0{_tfJY`$#JAGfaYo#78YS=dx2Ea{CA;uifI?+W3v&m_jiE6 zd_}mso3oqwN&Z!!`0?28H(pWct+%xlegbAG-DJgQ?{b#2us5A|X>Vsor)z9HpG{Pb zFZ}C^=&oR?UbuAWyeTyF=32G)j{x|WyOC=YwFSzl&y9&&p~#))V16Ch+S&a%ZW$V9wJG(eL-sODv6>)HI@V$0zcXQp&-X5Ud ziHMBk^l|{dw3gZmGBx~hpxQ=jTta>sDcF+|?Mm_>ZFs^ryam?NPbvCDt;C_EB_#QL zzWf#p_}|q2hyOJJkbKJZL>fIQIVJT5gPE3|k;%%+&dI%*mtRm=R9sSetE@mw`x^Uh zWmR=eZC!oCy~d{I8)5?Dg0e0x54s=rJbKLO?W=F=kUeZ@c&Y#C__OESiOH$NL1*aj z$lUb&!r~j=(&UWdrMZtI+HY6CeEs%)jq!eY<=5x+KU;tQZQq+Y^Yimm0c`I-1 zU}_y?CYE~PgDcgJ|NN9YTM?8Hy!7MqLac8(sN@w6LwgP~MByn3+gPz*i2?{vtvTx+zkjl-7uaVQv5+tl zwe(*QMEpI3s>tYkMHTqGVIri~v4Uf&`{E#o6Ei*v>7sX5L}@}NeN06aY@C-^zb;C$ ztPXsuD&>nV#Ow5?D&^@e0{Wjr2y)KeHA;C%GDwjlC95bG0QOrVLWY$$IGGxlPu}t2 zOZjODGQT(FB#MJI+1Jk;w)HHF3iS3?a|-nJ$uEx6TQ0vMJeLJKrFPppGj4cdxws(W z>culMF;TaJ)W66B5(<9Ad_c*wqYx1B`EdA1l60+7dC}nq)dlH~<|f3CTI#ESQfX{x zWzZ#`EVmb*p4^E9Q!R0IiZI4!K+y+NjVb-~`AmIF$fpktUdy!s7Q$T8KqDsu18)x8 zy_z9mq2Inx>cr!!<-W*n`uqs$-ldAx%;*;AT$n7n(ZKsFe~)u;i}S9LaBnOyDM7SA zo`3F^Fk4D^A91}5urJW(JW`A$t9$P0G;XR#cqM9d306I|l{!_zJj&XKA9Bl;yzo8= z`A~CJjVhg{sy<@rWyE;e6oFsuMCXjOSv>s9xR+_=e&+o%gPf?~y>nSa35my_nEk0+ ze#t-Qsz)Z!1)3rSOsv$Wp}Sm zWLw`ej~KY#Om zeq+3KaL}s|c}ak``)_UN`|h6NBWkIzjj{6(h5hg4uK!G75%}8{9PmLV2%LcJ?inU* zd0<6_2*yyfbD|}RoVaF5id)tPr*HpvgOerrc4^wc{Bw-z%Nv0#Q9AyLWPalrDAQnh~w|`v~ zkj3_bgxvo2b~Y*tfMTjF$6PN23nu&E)NdD{R3=*(N!5V(pygAwhVu}Tnz!ai_294e z;H+!@)5j}*KQVi$>5;ylj$&I`ig2@v{Y(>a6yTZLMRsDU4+nd{@T@fjlKr9Om1th4 zsIC1ijTmXLb6T6JEK0JTohaIQ8s=$3HyXCP-R2SoUoZ z)Bmc{Vom3XEfVI}cK=>k0$zdBD-7ZG>xI3e%F~zJ$^1VYQAchfJ;niODK0fRAWwBU z^RwQ9&Jjfh+aB`%i8&DNX>n6vGCO&Kq}YB$^ZWxdW1F0+G|_?odL7Q10lys6Z4^iz z%d>-vqg8aG+W85q)POv@>TI&}q7M&1PdbS1WK<5pmL z6vZEU7<1{D@Y_c`0Vw(O?MfpJ6e2m6!lx^x(blJr)U_J+i?TVcRde+gtrtPr2QhAU z)K4HJESnj44JfwWp`ycnRn9+Je@R*sdaNT@B;PG{S-QDG8}&}IIuE(5Fb{RDQXMvBxa zds+mkAJHe{(w2_H`iBmuYDrJM{1ti}m#6N3ys`7;b;4RXqBq#+1oqjLb1!d-RhyeK z;%ua05z|L`@BdE3i3y|{(>UrrBR7i|k{!PyC4mRw`wj}2V>wZ>>2IxGA9%C5*dgz~ zHyibm? z+oG?(Ki<3Dkes<`KMg4DTW+P-kn$b#UIQjNKm7#blk4OudtsIz}cqi17T_gr?XeZft^v1*ek&{S$Fo zEOIQu?kVzU!3(rDS<2?aRj4S$m_+;+f(+nzkSXzdn&S6x<5wnKVL*_-0$y<{>{oIS z9s}aZLB81VFHZdH-z3O*OY4n~jz14M{f@lO-$dR^(UT!Y>}(>%^Ysono$j2a6e|QU z+2#}dl)n5p7fTAl5_J$0=~)@)p+!yo5O>cO-r9UF_XG$QC{noPZ$G%&X9wcHttvfrf-VKI zM;rehNs}P?9&3UIVnMB~^prm!^hSp9O6ob&42u(}D;TeTGhW{%1;Ssbdl69U1!gyC zDDNtz)~n{%G0AAVfD|AuuU${t0(Aj>G%%Uf*u`A=5YeB9qUc0+SDFhtqW9eh6;y)l z7|S#FrA9$ED-l{MIsajye%Oo@4Sw!gXO8}> z98kimCOOVJESRz)>9=M7 z-j)-{^DxdTA;O&~L7FV^PIeY~vgDZ_OqC124?Jy=f@t_2B>P6K!QY^7 z$&-CWqHond$uXzjF|$W0Qyk;Cw(DDWOf2awT9;EWaQn|EDhu^!++u2{MkLZ1YIVC) zrQBaJnPhyE#sx%Flf8>5wM_^$4!orE|1E&V>@)k{mS6Nb(?f%*Y^BVRD`0P{Xj@^Y z|CZix$qC{Xyx%HVLDyKhqJ*1n=|_c|6y9wC!De$*0V=|64!PQTlhUpsgsD6po^$yD zqirj*WB9Dj{YtnBRd|$H%Ldf2z^6!GO%3GeNco^7B-I3$@Icjx1*)taXfwgbNS^7i ztFF@uI`^@H5L58RnRbj9ow1ehefAPm!i(0#&&aQ>am8IsBSP+=YoUKHrVv3C>TI|P zNhcg)++Mr1QQ0+OcaTtS$zdD(s#L5++p+4p8RaUxyaZ2S8x6euTK%B3??Ftn2(x7D zK_bY@v&E_7^&7U9OL43M<`(EjL49>;QFd$j?(27`yCrn6 zG>5o%w>uWwbwstx7PYg8AVBEwt7DS{+g<;m9XQ|xViU0wdWa|K!vbMs(1`;V(ZE&a zf|}T7`hn*27+`G90dU~s{)Iq){tm-qp@gCWEn?1vRpHMr*8CZvPjg6-Sv}Y=VtE~^ zhV2X!3|HYvT3|aB`{7Z2a2XziX5Do@GDx$jzQ_ZwCk-L2btSo^Ch6YE_hx)qYp^qY z$mQNIyQa6tMMi)Hnv%<_VuNbxge&{TjqkzMJAbGDZ zr{a${0*}c!C7nB=VK|)wTqf0+%|-$gpSvOoR+VF}fZ zfj$_BI0bHqQG#Hbk6LwZ+N;jpwUZf(Hd7Tco%c158;~y<7`K)=3c&UgAZImTp%`ct zesF{gi>pWaV6+UIxe+VmbvBk@5c%(MLZ zk@xP;7{o9RIJzG@dZ)fGrUZGnd$et8FzMf*2eBcS8FbbE+4lVNGKz9k_0!NjPzUnR zG4@bg9Bjh?-x|xA{#!b$KeB5uv40#*!m7Y|uyYzJWd=iMNl+NEr!E#0JPXimx;MoU zM$!}c%j04@lNt*sHxek?J>i%AbZn1Djm-G80e)sXY~lW-7Q650!720j=_D+az6z6@ zQ8;*Z=p0&LVs-0tr1cNv~&KFbcG=4{>g@sb@K}%IxV&aY3GjDhUT) z!HG!!uNAVz1s?3ae;QGhYYLUlP<6O(qD>_w6hy#L@CIq(nqTYK+X=>V=zB?aRQe+5xy2w zn?9*kFx;%auqZUuWsedib|sf2L}|>vDP7!Ox}YO7@Ofu3wE;oxsY@4l^LzZo6#$q^ b4~oJpZ0@|-jWbHy2@51Z#ODR50D%8Lv`eIHFGIblw+i>mxQZnTDa;;(k^*bl&Yr?14|NEo zxjb+4ka@U5HmopD(KO8CZV$E@qnVfDUi}aE`2*g+d`|nJ-H(N102@Gx2flpyGB!3g zK0e+f=n)8dluD&Sp^(XBQ&UqjGc%KulVY)0DwRqkl8)Yq)PmMOGwaR~o?E!xizc#% zMAG;a=F>9%iL2QQ+LgU1%H8z(L%*b5kJKp?1Z6T-d8h@VsLr`Niiq4D_rrM2(sTl@3NxP=vObBkNCiNy!c(vDq9&wSn( zdYh5<>~&>b=WnD+xHl=5${H9Jdt$Q4Pil*+I|YNI38^ewZ*o>)Q%n26(5LA{8aqCf zK{D0Z5f(-xw41Vd(OV*$cYEK z?JzBOPb0R(_TKf0ot9ttr{=iVgx5-(g|LlmVsp+*-*tG~9AT6bD<@~Mv#MD)r>t%>c2d=JK-ZW@91@FEuLN#NRowvENzEDd7`tqBIg+Vv z7{8-bH%>;`&hMOk>{$gB{T*D@G*@a8)9^0bR@1yJysl}{PcV&I)^wiLO&Gnhx*5G#8NNfG<7@RwW3<;M?#!$52&=PQ~jrGwzUn;d@tsF@CSvFh69&ajxfwM z_GjVMc=hLzuw-Yq5fUw2CnA)wb)(UZYTejP<|Bm5?U5TR;s#&p%EY}D=N^ERKza`^ naF5&Mkj{wc+LyX(d#YW&Lxf&Jr=Rq*FNHnmvQ2^*0-pZ?5LKO{ literal 0 HcmV?d00001 diff --git a/images/paypal-en.gif b/images/paypal-en.gif new file mode 100644 index 0000000000000000000000000000000000000000..ad9ce6910e78ae82e6e17b5206bb7102c1f7e345 GIT binary patch literal 1352 zcmb`^{Xf$Q0KoCDwv6;DQfrQ5gmbow)OC`&d11*VC22jlXpD2X@~Y6)ojrLNG45t0 zn!CxvHhGwb94-oJcKoJ4ybiNpYQfC>kUjEp$N zB+F#79&wLY+@sU!v|6oNtyU-$N~LmibW|>vPfSdxRI0Atk69({zf&7e$Cg?iz8jC{ z3`$kWnXK=x(26QW4!)#0{nCNpoIANq=!id+s@Z^R^zd61A#o1^ql(>vvMafLRrLZm zilF%wXyQr4Vlmu@__+S%z1;f!rzprDGMn1_A5`)MqJHKRA>4<+7xtx6IMmV>I+1j$)!;ve-l$n2{(895K1oy5|;!t#z{@(13@``=2AoX^EvXABOF z<(IYg4UV*R4v60jr)06QsZ4g0$SsIcR^1U8RfLYD{X(d@mcm?HTU%LKF>X%gd{) ztFyDS)6>(#!^4Y;r!2g&13fKq$Z~*c%`S}R| z*-XQML0Bva3(m^SB!S7~-1MBhY%+*+KZBY_PX}d{QnDXCEGWy*aKT}tIkivf>Khul zP0cNhWKPtJw)PGlU(nel6t#0m(J#flef^!$-Qn-vOFj%qhldV2esmleQ%c{m0k!7X z9-Z!6&Fq}oeQL^m9)LqFpiWC0o4^~|flzds57FP<6QhanBiTXr1@7iyxA;PKGGs+V z4!%|pxHHA(WXJ?+OEk19w0PVXX=W1`K#c%{GfLnEviLEekhseR7YJDR`?4TGVXoob z-0E``(KfWRD4Oei2>YGE3(muczK(hm*@3tSdx_~-Z$Ge=owdocjg28n#v zuLEFjC}SC-!OM}cx9}AGf@Y9)HSagZsPUZjkf#lwnBj`3hGi7SuYoaM zc;&YCAA0z+3uvP#D8Mf^Z#{!Ea$}@A?-% zPE$^vNn?Qa2H-V+Q_-$kWsImxOM zx$4+lNgRS*SU4}NePNi?J>eN6rs}$&tE@vVspV4*;={VB#sM^9n)}Xc7mJdgXWN!V)+p%eOpjZ~`Uaw|V?$LU zY`Wmco)c{UZfdU6{3{#Y9?RfU^A*DJ5Ls>4t{9v8L)Aao>t8w2handle = array(); + $this->handle[self::PHP] = false; + $this->handle[self::PERL] = false; + $this->handle[self::PERL_COMPLETE] = false; + $this->handle[self::ERROR] = false; + } + /** + * Close all open file handles on destruct + * + * @return void + */ + public function __destruct() + { + if ($this->handle[self::PHP]) { + $this->_close(self::PHP); + } + if (is_resource($this->handle[self::PERL])) { + $this->_close(self::PERL); + } + if (is_resource($this->handle[self::PERL_COMPLETE])) { + $this->_close(self::PERL_COMPLETE); + } + if (is_resource($this->handle[self::ERROR])) { + $this->_close(self::ERROR); + } + } + + /** + * Open file and store file handle + * + * @param string $file + * + * @return bool + */ + private function _getFileHandle($file) + { + $filename=$this->getLogfile($file); + if ($_SESSION['config']['logcompression'] == 1) { + $fileHandle = @gzopen($filename, 'a'); + } else { + $fileHandle = @fopen($filename, 'ab'); + } + if ($fileHandle) { + $this->handle[$file] = $fileHandle; + return $this->handle[$file]; + } else { + return false; + } + } + /** + * Close a filehandle + * + * @param Loge $file The file to close + */ + private function _close($file) + { + $filename=$this->getLogfile($file); + $extension = pathinfo($filename, PATHINFO_EXTENSION); + if ($extension == 'gz') { + gzclose($this->handle[$file]); + } else { + fclose($this->handle[$file]); + } + } + /** + * Write to log file + * + * @param string $file + * @param string $message + * + * @return bool + */ + public function write($file, $message) + { + // TODO get $config-values from a config class and delete global + global $config; + if (!$this->handle[$file]) { + $this->handle[$file] = $this->_getFileHandle($file); + } + $message = strip_tags($message); + // we don't need linebreaks in the log + $search = array("\n","\r"); + $replace = array ('',''); + $message = str_replace($search, $replace, $message); + $logMessage = date('d.m.Y H:i:s') . ' '. $message . "\n"; + $_SESSION['log']['actions'][] = $logMessage; + $filename = $this->getLogfile($file); + if (@filesize($filename) > $config['log_maxsize']) { + Log::delete($file); + } + //save to log file + if ($_SESSION['config']['logcompression'] == 1) { + $res = @gzwrite($this->handle[$file], $logMessage); + } else { + $res = @fwrite($this->handle[$file], $logMessage); + } + return $res; + } + + /** + * Get log file name + * + * @param const $file + * + * @return string Filename of logfile + */ + public function getLogfile($file) + { + switch ($file) + { + case Log::PHP: + $filename = self::PHP_FILE; + break; + case Log::PERL: + $filename = self::PERL_FILE; + break; + case Log::PERL_COMPLETE: + $filename = self::PERL_COMPLETE_FILE; + break; + case Log::ERROR: + $filename = self::ERROR_FILE; + break; + default: + echo "Unknown $file: ".$file; + return; + } + if ($_SESSION['config']['logcompression'] == 1) { + $filename .= '.gz'; + } + return $filename; + } + + /** + * Delete log file and recreates it. + * + * @param string $file Filename + * + * @return void + */ + public function delete($file) + { + $filename=Log::getLogfile($file); + @unlink($filename); + if ($file == Log::PHP) { + // re-create main log + if (substr($filename, -3) == '.gz') { + $log = date('d.m.Y H:i:s') . " Log created.\n"; + if ($_SESSION['config']['logcompression'] == 1) { + $fp = @gzopen($filename, "wb"); + @gzwrite($fp, $log); + @chmod($file . '.gz', 0777); + $this->handle[$file]=$fp; + } else { + $fp = @fopen($filename, "wb"); + @fwrite($fp, $log); + @chmod($file, 0777); + $this->handle[$file]=$fp; + } + } + } + } +} \ No newline at end of file diff --git a/inc/classes/db/MsdDbFactory.php b/inc/classes/db/MsdDbFactory.php new file mode 100644 index 0000000..b36b381 --- /dev/null +++ b/inc/classes/db/MsdDbFactory.php @@ -0,0 +1,249 @@ +server = $server; + $this->user = $user; + $this->password = $password; + $this->port = $port; + $this->socket = $socket; + $this->connectionCharset = 'utf8'; + $this->connectionHandle = false; + $this->dbSelected = ''; + $this->tables = array(); + $this->metaTables = array(); + } + + /** + * Create database adapter + * + * @param string $server + * @param string $user + * @param string $password + * @param string $port + * @param string $socket + * + * @return MsdDbFactory + */ + static function getAdapter($server, $user, $password, $port = 3306, $socket = '') + { + if (function_exists('mysqli_connect') && !self::FORCE_MYSQL) { + include_once ('./inc/classes/db/mysqli/MsdDbMysqli.php'); + $db = new MsdDbMysqli($server, $user, $password, $port, $socket); + } else { + include_once ('./inc/classes/db/mysql/MsdDbMysql.php'); + $db = new MsdDbMysql($server, $user, $password, $port, $socket); + } + return $db; + } + + /** + * Establish a connection to SQL-Server. + * + * Doesn't need to be called directly because of lazy loading. + * Will be called automatically if any other method is called that + * needs a connection. + * Creates a connection to the database and stores the connection handle + * in $this->connection_handle. + * If $select_database is set, the database is selected for next queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connection_charset + * + * @return bool + * */ + abstract public function dbConnect($selectDatabase = false); + + /** + * Get selected database + * + * @return string + */ + abstract public function getSelectedDb(); + + /** + * Get version nr of sql server + * + * @return string + */ + abstract public function getServerInfo(); + + /** + * Get version nr of sql client + * + * @return string + */ + abstract public function getClientInfo(); + + /** + * Get all known character sets of this SQL-Server. + * + * @return array + */ + abstract public function getCharsets(); + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + abstract public function setConnectionCharset($charset = 'utf8'); + /** + * Get list of databases + * + * Gets list of all databases that the actual SQL-Server-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return bool + */ + abstract public function getDatabases(); + + /** + * Select the given database to use it as the target for following queries. + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database + * + * @return bool + */ + abstract public function selectDb($database); + + /** + * Execute a SQL-Server-Query + * + * @param $query The query to execute + * @param $kind Type of result set + * + * @return array + */ + abstract public function query($query, $kind = self::ARRAY_OBJECT); + + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * + * @return array + */ + abstract public function getTables($database = false); + + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * + * @return array + */ + abstract public function getTableStatus($table = false, $database = false); + + /** + * Returns the CREATE Statement of a table. + * + * @throws Exception + * @param string $table Get CREATE-Statement for this table + * @param string $database Database holding the table + * + * @return string Create statement + */ + abstract public function getTableCreate($table, $database = false); + + /** + * Gets the full description of all columns of a table + * + * Saves list to $this->metaTables[$database][$table]. + * + * @param string $table Table to read meta info from + * @param string $database Database holding the table + * + * @return array + */ + abstract public function getTableColumns($table, $database = false); + + /** + * Gets the number of affected rows of the last query + * + * @return int + */ + abstract function getAffectedRows(); + + /** + * Escape a value for inserting it in query + * + * @param string $val + * + * @return string + */ + abstract function escape($val); + + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param $table string Name of table + * + * @return string|bool Returned optimize message or false on error + */ + abstract function optimizeTable($table); + + /** + * Handles a SQL-Error + * + * @param string $errmsg + * @param int $errno + * @throws MsdEception + * + * @return void + */ + public function sqlError($errmsg, $errno) + { + throw new Exception($errmsg, $errno); + } + +} diff --git a/inc/classes/db/mysql/MsdDbMysql.php b/inc/classes/db/mysql/MsdDbMysql.php new file mode 100644 index 0000000..de823ae --- /dev/null +++ b/inc/classes/db/mysql/MsdDbMysql.php @@ -0,0 +1,439 @@ +charsets)) return $this->charsets; + if (false === $this->connectionHandle) $this->dbConnect(); + $result = $this->query('SHOW CHARACTER SET', self::ARRAY_ASSOC); + $this->charsets = array(); + foreach ($result as $r) { + $this->charsets[$r['Charset']] = $r; + } + @ksort($this->charsets); + return $this->charsets; + } + /** + * Establish a connection to MySQL. + * + * Creates a connection to the database and stores the connection handle in + * $this->connectionHandle. + * If $select_database is set, the database is selected for further queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connectionCharset + * + * @return bool + **/ + public function dbConnect($selectDatabase = false) + { + if (is_resource($this->connectionHandle)) return true; + + $connectionString = $this->server . ':' . $this->port; + if ($this->socket > '') $connectionString .= ':' . $this->socket; + if (false === $this->connectionHandle) { + $this->connectionHandle = @mysql_connect( + $connectionString, + $this->user, $this->password + ); + if (false === $this->connectionHandle) { + return '(' . mysql_errno() . ') ' . mysql_error(); + } + } + $this->setConnectionCharset($this->connectionCharset); + if (false === $selectDatabase && $this->dbSelected > '') { + $selectDatabase = $this->dbSelected; + } + if ($selectDatabase) { + try + { + $this->selectDb($selectDatabase, $this->connectionHandle); + $this->dbSelected = $selectDatabase; + return true; + } catch (Exception $e) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + return false; + } + } + return true; + } + + /** + * Get version nr of sql server + * + * @return string + */ + public function getServerInfo() + { + if (false === $this->connectionHandle) { + $this->dbConnect(); + } + $version = mysql_get_server_info($this->connectionHandle); + return $version; + } + /** + * Get version nr of sql client + * + * @return string + */ + public function getClientInfo() + { + if (false === $this->connectionHandle) $this->dbConnect(); + $version = mysql_get_client_info(); + return $version; + } + + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + public function setConnectionCharset($charset = 'utf8') + { + if (false === $this->connectionHandle) $this->dbConnect(); + if (function_exists('mysql_set_charset') + && @mysql_set_charset($charset, $this->connectionHandle)) { + $this->connectionCharset = $charset; + return $this->connectionCharset; + } else { + $this->query('SET NAMES \'' . $charset . '\'', self::SIMPLE); + $this->connectionCharset = $charset; + return $charset; + } + } + + /** + * Get list of databases + * + * Gets list of all databases that the actual MySQL-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return array List of found database names + */ + public function getDatabases() + { + if (false === $this->connectionHandle) $this->dbConnect(); + $this->databases = array(); + $databases = @mysql_list_dbs($this->connectionHandle); + if (is_resource($databases)) { + WHILE ($row = mysql_fetch_array($databases, MYSQL_ASSOC)) { + $this->databases[] = $row['Database']; + } + } else { + //mysql_list_dbs seems not to be allowed for this user + // try to get list via "SHOW DATABASES" + $res = $this->query('SHOW DATABASES', self::ARRAY_ASSOC); + foreach ($res as $r) { + $this->databases[] = $r['Database']; + } + } + sort($this->databases); + return $this->databases; + } + /** + * Select the given database to use it as the target for following queries + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database + * + * @return bool + */ + public function selectDb($database) + { + if (!is_resource($this->connectionHandle)) { + $this->dbConnect(); + } + $res=@mysql_select_db($database, $this->connectionHandle); + if ($res===false) { + return mysql_error(); + } else { + $this->dbSelected = $database; + return true; + } + } + + /** + * Get selected database + * + * @see inc/classes/db/MsdDbFactory#getSelectedDb() + * + * @return string + */ + public function getSelectedDb() + { + return $this->dbSelected; + } + + /** + * Execute a query + * + * @param $query The query to execute + * @param $kind Type of result set + * + * @return array + */ + public function query($query, $kind = self::ARRAY_OBJECT) + { + if (false === $this->connectionHandle) { + $this->dbConnect(); + } + $res = @mysql_query($query, $this->connectionHandle); + if (false === $res) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + if ($kind === self::SIMPLE || is_bool($res)) { + return $res; + } + $ret = array(); + if ($kind === self::ARRAY_OBJECT) { + WHILE ($row = mysql_fetch_object($res)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_NUMERIC) { + WHILE ($row = mysql_fetch_array($res, MYSQL_NUM)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_ASSOC) { + WHILE ($row = mysql_fetch_array($res, MYSQL_ASSOC)) { + $ret[] = $row; + } + } + return $ret; + } + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * + * @return array + */ + public function getTables($database = false) + { + if (false === $this->connectionHandle) $this->dbConnect(); + if (!isset($this->tables)) $this->tables = array(); + if ($database !== false) { + //list tables of selected database + if (is_array($database)) { + $databases = $database; + } else { + $databases = array(); + $databases[0] = $database; + } + } else { + //list tables for all databases + $this->getDatabases(); + $databases = $this->databases; + } + // get tablenames inside each database + foreach ($databases as $db) { + $this->tables[$db] = array(); + $sql = 'SHOW TABLES FROM `' . $db . '`'; + $res = $this->query($sql, self::ARRAY_NUMERIC); + foreach ($res as $val) { + $this->tables[$db][] = $val[0]; + } + } + return is_string($database) ? $this->tables[$database] : $this->tables; + } + + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * + * @return array + */ + public function getTableStatus($table = false, $database = false) + { + if ($database !== false && $database != $this->dbSelected) { + $this->selectDb($database); + } elseif (!$this->_mysqli instanceof mysqli) { + $this->selectDb($this->dbSelected); + } + if (!isset($this->tableinfo)) { + $this->tableinfo = array(); + } + if (!isset($this->tableinfo[$this->dbSelected])) { + $this->tableinfo[$this->dbSelected] = array(); + } + $sql = 'SHOW TABLE STATUS'; + if ($table !== false) { + $sql .= ' LIKE \'' . $table . '\''; + } + $res = $this->query($sql, self::ARRAY_ASSOC); + if (is_array($res)) { + foreach ($res as $r) { + $tablename = $r['Name']; + unset($r['Name']); + $this->tableinfo[$this->dbSelected][$tablename] = $r; + } + } elseif ($res === false) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + if ($table !== false) { + if (isset($this->tableinfo[$this->dbSelected][$table])) { + return $this->tableinfo[$this->dbSelected][$table]; + } + } + return $this->tableinfo[$this->dbSelected]; + } + + /** + * Returns the CREATE-Statement for the given table or false on error. + * + * @throws Exception + * @param string $table + * @param string $database + * + * @return string + */ + public function getTableCreate($table, $database = false) + { + if (false === $database) $database = $this->dbSelected; + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + if (!is_resource($this->connectionHandle)) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW CREATE TABLE `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (isset($res[0]['Create Table'])) { + return $res[0]['Create Table']; + } else { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + + /** + * Gets the full description of all columns of a table + * + * Saves it to $this->metaTables[$database][$table]. + * + * @param string $table + * @param string $database + * @return array + */ + public function getTableColumns($table, $database = false) + { + if (false === $database) $database = $this->dbSelected; + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + if (!is_resource($this->connectionHandle)) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW FULL COLUMNS FROM `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (!isset($this->metaTables[$database])) { + $this->metaTables[$database] = array(); + } + if ($res) { + $this->metaTables[$database][$table] = array(); + foreach ($res as $r) { + $this->metaTables[$database][$table][$r['Field']] = $r; + } + } + return $this->metaTables[$database][$table]; + } + + /** + * Gets the number of affected rows for the last query + * + * @see inc/classes/db/MsdDbFactory#affectedRows() + * + * @return int + */ + public function getAffectedRows() + { + return mysql_affected_rows($this->connectionHandle); + } + + /** + * Escape a value to use it in a query + * + * @see inc/classes/db/MsdDbFactory#escape($val) + * @param mixed $val The value to escape + * @return mixed + */ + public function escape($val) + { + return mysql_real_escape_string($val, $this->connectionHandle); + } + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param string $table Name of table + * + * @return string|bool Returned optimize message or false on error + */ + function optimizeTable($table) + { + $sql = 'OPTIMIZE TABLE `' . $this->dbSelected . '`.`' . $table . '`'; + $res = $this->query($sql, MsdDbFactory::ARRAY_ASSOC); + if (isset($res[0]['Msg_text'])) { + return $res[0]; + } else { + return false; + } + } + +} diff --git a/inc/classes/db/mysqli/MsdDbMysqli.php b/inc/classes/db/mysqli/MsdDbMysqli.php new file mode 100644 index 0000000..64da069 --- /dev/null +++ b/inc/classes/db/mysqli/MsdDbMysqli.php @@ -0,0 +1,413 @@ +charsets)) return $this->charsets; + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + $result = $this->query('SHOW CHARACTER SET', self::ARRAY_ASSOC); + $this->charsets = array(); + foreach ($result as $r) { + $this->charsets[$r['Charset']] = $r; + } + @ksort($this->charsets); + return $this->charsets; + } + /** + * Establish a connection to MySQL. + * + * Create a connection to MySQL and store the connection handle in + * $this->connectionHandle. + * If $select_database is set, the database is selected for further queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connectionCharset + * @return boolean + **/ + public function dbConnect($selectDatabase = false) + { + if ($this->_mysqli instanceof mysqli) { + return true; + } + $this->_mysqli = @new mysqli( + $this->server, + $this->user, + $this->password, + $this->dbSelected, + $this->port, + $this->socket + ); + if ($this->_mysqli->connect_errno) { + $error = $this->_mysqli->connect_error; + $errno = $this->_mysqli->connect_errno; + $this->_mysqli = null; + return '(' . $errno . ') ' . $error; + } + $this->setConnectionCharset($this->connectionCharset); + if (false === $selectDatabase && $this->dbSelected > '') { + $selectDatabase = $this->dbSelected; + } + if ($selectDatabase) { + if ($this->selectDb($selectDatabase)) { + $this->dbSelected = $selectDatabase; + return true; + } else { + $this->sqlError(mysqli_error(mysqli_error(), mysqli_errno())); + } + } + return true; + } + /** + * Get version nr of sql server + * + * @return string + */ + public function getServerInfo() + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + return $this->_mysqli->server_info; + } + /** + * Get version nr of sql client + * + * @return string + */ + public function getClientInfo() + { + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + return $this->_mysqli->client_info; + } + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + public function setConnectionCharset($charset = 'utf8') + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + if (!@$this->_mysqli->set_charset($charset)) { + $this->sqlError( + $charset . ' ' . $this->_mysqli->error, + $this->_mysqli->errno + ); + } + $this->connectionCharset = $this->_mysqli->character_set_name(); + return $this->connectionCharset; + } + /** + * Get list of databases + * + * Gets list of all databases that the actual MySQL-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return boolean + */ + public function getDatabases() + { + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + $res = $this->query('SHOW DATABASES', self::ARRAY_ASSOC); + $this->databases = array(); + foreach ($res as $r) { + $this->databases[] = $r['Database']; + } + sort($this->databases); + return $this->databases; + } + /** + * Select the given database to use it as the target for following queries. + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database Database to select + * + * @return boolean + */ + public function selectDb($database) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + $res=@$this->_mysqli->select_db($database); + if ($res===false) { + return $this->_mysqli->error; + } else { + $this->dbSelected = $database; + return true; + } + } + /** + * Get selected database + * + * @see inc/classes/db/MsdDbFactory#getSelectedDb() + * @return string + */ + public function getSelectedDb() + { + return $this->dbSelected; + } + /** + * Execute a MySQL-Query + * + * @throws Exception + * @param string $query The query to execute + * @param const $kind Type of result set + * + * @return array + */ + public function query($query, $kind = self::ARRAY_OBJECT) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + //echo "
Mysqli: executing query: " . $query; + $result = $this->_mysqli->query($query); + if (false === $result) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + if (!$result instanceof mysqli_result || $kind === self::SIMPLE) { + return $result; + } + $ret = array(); + if ($kind === self::ARRAY_OBJECT) { + WHILE ($row = $result->fetch_object()) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_NUMERIC) { + WHILE ($row = $result->fetch_array(MYSQLI_NUM)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_ASSOC) { + WHILE ($row = $result->fetch_assoc()) { + $ret[] = $row; + } + } + if ($result instanceof mysqli) { + $result->close(); + } + return $ret; + } + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * @return array + */ + public function getTables($database = false) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + if (!isset($this->tables)) { + $this->tables = array(); + } + if ($database !== false) { + //only list tables of selected database + if (is_array($database)) { + $databases = $database; + } else { + $databases = array(); + $databases[0] = $database; + } + } else { + //list tables for all databases + $databases = $this->databases; + } + // get tablenames for each database + foreach ($databases as $db) { + $this->tables[$db] = array(); + $sql = 'SHOW TABLES FROM `' . $db . '`'; + $res = $this->query($sql, self::ARRAY_NUMERIC); + foreach ($res as $val) { + $this->tables[$db][] = $val[0]; + } + } + return is_string($database) ? $this->tables[$database] : $this->tables; + } + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * @return array + */ + public function getTableStatus($table = false, $database = false) + { + if ($database !== false && $database != $this->dbSelected) { + $this->selectDb($database); + } + if (!$this->_mysqli instanceof mysqli) { + $this->selectDb($this->dbSelected); + } + if (!isset($this->tableinfo)) { + $this->tableinfo = array(); + } + if (!isset($this->tableinfo[$this->dbSelected])) { + $this->tableinfo[$this->dbSelected] = array(); + } + $sql = 'SHOW TABLE STATUS'; + if ($table !== false) { + $sql .= ' LIKE \'' . $table . '\''; + } + $res = $this->query($sql, self::ARRAY_ASSOC); + if (is_array($res)) { + foreach ($res as $r) { + $tablename = $r['Name']; + unset($r['Name']); + $this->tableinfo[$this->dbSelected][$tablename] = $r; + } + } elseif ($res === false) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + if ($table !== false) { + if (isset($this->tableinfo[$this->dbSelected][$table])) { + return $this->tableinfo[$this->dbSelected][$table]; + } + } + return $this->tableinfo[$this->dbSelected]; + } + /** + * Returns the CREATE Statement of a table. + * + * @throws Exception + * @param string $table + * @param string $database + * + * @return string + */ + public function getTableCreate($table, $database = false) + { + if (false === $database) { + $database = $this->dbSelected; + } + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW CREATE TABLE `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (isset($res[0]['Create Table'])) { + return $res[0]['Create Table']; + } else { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + /** + * Gets the full description of all columns of a table + * + * Saves it to $this->metaTables[$database][$table]. + * + * @param string $table + * @param string $database + * + * @return array + */ + public function getTableColumns($table, $database = false) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + if (false === $database) { + $database = $this->dbSelected; + } + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + $sql = 'SHOW FULL FIELDS FROM `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (!isset($this->metaTables[$database])) $this->metaTables[$database] = array(); + if (is_array($res)) { + $this->metaTables[$database][$table] = array(); + foreach ($res as $r) { + $this->metaTables[$database][$table][$r['Field']] = $r; + } + } + return $this->metaTables[$database][$table]; + } + /** + * Gets the number of affected rows for the last query + * + * @see inc/classes/db/MsdDbFactory#affectedRows() + * @return integer + */ + public function getAffectedRows() + { + return $this->_mysqli->affected_rows; + } + /** + * Escape a value to use it in a query + * + * @see inc/classes/db/MsdDbFactory#escape($val) + * @param mixed $val The value to escape + * + * @return mixed + */ + public function escape($val) + { + return $this->_mysqli->real_escape_string($val); + } + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param string $table Name of table + * + * @return boolean or string true on success or mysql_error() on failure + */ + function optimizeTable($table) + { + $sql = 'OPTIMIZE TABLE `' . $this->dbSelected . '`.`' . $table . '`'; + $res = $this->query($sql, MsdDbFactory::ARRAY_ASSOC); + if (isset($res[0]['Msg_text'])) { + return $res[0]; + } else { + return false; + } + } + +} diff --git a/inc/classes/helper/File.php b/inc/classes/helper/File.php new file mode 100644 index 0000000..7cee0b8 --- /dev/null +++ b/inc/classes/helper/File.php @@ -0,0 +1,51 @@ + $val) { + $r .= '' . "\n"; + } + return $r; + } + + /** + * Get HTML output for attribute "checked" + * + * @param string $val The current value + * @param string $checked The value for the element if it should be checked + * @return string Html attribute "checked" or empty string + */ + public static function getChecked($val, $checked) + { + return $val == $checked ? ' checked="checked"' : ''; + } + + /** + * Get HTML output for attribute "disable" + * + * @param string $val The current value + * @param string $disabled The value for the element if disabled + * + * @return string Html attribute "disabled" or empty string + */ + public static function getDisabled($val, $disabled) + { + return $val == $disabled ? ' disabled="disabled"' : ''; + } + /** + * Get HTML output for attribute "selected" + * + * @param string $val The current value + * @param string $selected The value for the element if selected + * + * @return string Html attribute "selected" or empty string + */ + public static function getSelected($val, $selected) + { + return $val == $selected ? ' selected="selected"' : ''; + } + + /** + * Convert HTML br's to new lines. + * + * @param string $str The string to convert + * @return string Converted string + */ + public static function br2nl($str) + { + $search = array('
', '
', '
'); + $replace = array("\n", "\n", "\n"); + return str_replace($search, $replace, $str); + } + + /** + * Escape quotes and/or slashes and double quotes. + * + * Used for escaping strings in JS-alerts and config-files. + * + * @param string $string String to escape + * @param boolean $escapeSlashes Escape slashes and double quotes + * + * @return string Escaped string + */ + public static function getJsQuote($string, $escapeSlashes = false) + { + if ($escapeSlashes) { + $string = str_replace('/', '\/', $string); + $string = str_replace('"', '\"', $string); + } + $string = str_replace("\n", '\n', $string); + return str_replace("'", '\\\'', $string); + } + + /** + * Replace quotes with their Html entity. + * + * Used for outputing values in HTML attributes without breaking HTML + * + * @param string $string String with quotes + * + * @return string String with replaced quotes + */ + public static function replaceQuotes($string) + { + $string = str_replace("\n", '\n', $string); + return str_replace('"', '"', $string); + } + + /** + * Escape special chars according to Perl syntax. + * + * @param string $text The string to escape + * + * @return string Escaped string + */ + public static function escapeSpecialchars($string) + { + $search = array('@', '$', '\\\\', '"'); + $replace = array('\@', '\$', '\\', '\"'); + return str_replace($search, $replace, $string); + } + + /** + * Remove added slashes recursively. + * + * @param mixed $values Array or string to remove added slashes from + * + * @return mixed Array or String with recursively removed slashes + */ + public static function stripslashesDeep($values) + { + if (is_array($values)) { + $values = array_map('Html::stripslashesDeep', $values); + } else { + $values = stripslashes($values); + } + return $values; + } + + /** + * Remove whitespaces before and after string or array. + * + * @param mixed $values Array or string to remove whitespaces from + * + * @return mixed Recursively trimmed array or string + */ + public static function trimDeep($values) + { + if (is_array($values)) { + $values = array_map('Html::trimDeep', $values); + } else { + $values = trim($values); + } + return $values; + } + /** + * Build HTML-Message-String for success messages + * + * @param string $text Message to print + * + * @return string Message surrounded by HTML-Container + */ + public static function getOkMsg($text) + { + $html= sprintf('%s', $text); + $html .= ''; + $html .= ''; + $html .= '
'; + return $html; + } + /** + * Build HTML-Message-String for error messages + * + * @param string $text Message to print + * + * @return string Message surrounded by HTML-Container + */ + public static function getErrorMsg($text) + { + $html= sprintf('%s', $text); + $html .= ''; + $html .= ''; + $html .= '
'; + return $html; + } + /** + * Create IMG-Tag + * + * @param string $pic Filename of picture + * @param string $title Title for the picture (will alsobe used for alt-tag) + * + * @return string Built img-tag + */ + public static function getIcon($pic, $title = '') + { + //TODO get value from a config class and get rid of global + global $config; + $img = '%s'; + return sprintf($img, $config['files']['iconpath'], $pic, $title, $title); + } +} diff --git a/inc/classes/helper/Sql.php b/inc/classes/helper/Sql.php new file mode 100644 index 0000000..2d13a78 --- /dev/null +++ b/inc/classes/helper/Sql.php @@ -0,0 +1,87 @@ +optimizeTable($table); + if (false !== $res) { + $success=array('status', 'info','warning','note'); + if (in_array($res['Msg_type'], $success)) { + $logMsg = $lang['L_OPTIMIZE'].' `'.$dbo->dbSelected. '`.`'; + $logMsg .= $table.'`: '.$res['Msg_text']; + $log->write(Log::PHP, $logMsg); + return true; + } else { + $logMsg = sprintf($lang['L_OPTIMIZE_TABLE_ERR'], $table); + writeToErrorLog($dbo->dbSelected, $logMsg, $res['msg_text'], 0); + return false; + } + } else { + $logMsg = sprintf($lang['L_OPTIMIZE_TABLE_ERR'], $table); + writeToErrorLog($dbo->dbSelecte, $logMsg, $res['msg_text'], 0); + return false; + } + } + /** + * Creates a INSERT INTO-string + * + * "INSERT INTO (`field1`,`field2`,..)"-Command for the given table + * by looking up the meta-information. Table must exists. + * Note: Only used when restoring a backup not from MySQLDumper containing + * extended inserts. + * + * @param MsdDbFactory Database object + * @param string $table Name of table to analyze + * + * @return string $insert The name of the table extracted from the Query + **/ + public static function getInsertSyntax(MsdDbFactory $dbo, $table) + { + $insert = ''; + $columns=$dbo->getTableColumns($table); + $fields=array_keys($columns); + $insert = 'INSERT INTO `' . $table . '` (`'; + $insert .=implode('`,`', $fields) . '`)'; + return $insert; + } + /** + * Disables keys for given table + * + * @param MsdDbFactory $dbo Database object + * @param string $table Name of table + * + * @return bool + */ + public static function disableKeys(MsdDbFactory $dbo, $table) + { + $query = '/*!40000 ALTER TABLE `' . $table . '` DISABLE KEYS */'; + return $dbo->query($query, MsdDbFactory::SIMPLE); + } +} diff --git a/inc/classes/helper/String.php b/inc/classes/helper/String.php new file mode 100644 index 0000000..0fc00a4 --- /dev/null +++ b/inc/classes/helper/String.php @@ -0,0 +1,37 @@ + '') { + $tmpConfigfilename = utf8_decode(trim($_POST['new_configurationfile'])); + if (!preg_match("/^[a-z.-_]+$/i", $tmpConfigfilename, $matches)) { + $msg = sprintf( + $lang['L_ERROR_CONFIGFILE_NAME'], + $_POST['new_configurationfile'] + ); + $msg = Html::getErrorMsg($msg); + } else { + $config['config_file'] = $_POST['new_configurationfile']; + $config['cron_configurationfile'] = $_POST['new_configurationfile'] + . ".conf.php"; + $saveConfig = true; + } + + if ($saveConfig) { + if (saveConfig() == true) { + $saveConfig = false; + $msg = sprintf( + $lang['L_SUCCESS_CONFIGFILE_CREATED'], + $_POST['new_configurationfile'] + ); + $msg = Html::getOkMsg($msg); + } else { + $msg = Html::getErrorMsg($lang['L_SAVE_ERROR']); + $saveConfig = false; + } + } + } else { + $msg = Html::getErrorMsg($lang['L_NO_NAME_GIVEN']); + } +} + +if ($saveConfig) { + // validation was fine, we can write the values to the actual config file + if (saveConfig() == true) { + getConfig($config['config_file']); + if ($config['logcompression'] != $oldlogcompression) { + deleteLog(); + } + $msg = sprintf($lang['L_SAVE_SUCCESS'], $config['config_file']); + $msg = Html::getOkMsg($msg); + } else { + $msg = Html::getErrorMsg($lang['L_SAVE_ERROR']); + } +} +include ('./inc/configuration/config_files.php'); +include ('./inc/configuration/config_menu.php'); +include ('./inc/configuration/footer.php'); diff --git a/inc/configuration/autodelete.php b/inc/configuration/autodelete.php new file mode 100644 index 0000000..b064fc4 --- /dev/null +++ b/inc/configuration/autodelete.php @@ -0,0 +1,46 @@ +set_filenames( + array( + 'tplConfigurationAutodelete' => 'tpl/configuration/autodelete.tpl') +); + +$tplConfigurationAutodelete->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'AUTODELETE_ENABLED_SELECTED' => + Html::getChecked($config['auto_delete']['activated'], 1), + 'AUTODELETE_DISABLED_SELECTED' => + Html::getChecked($config['auto_delete']['activated'], 0), + 'MAX_BACKUP_FILES' => + (int) $config['auto_delete']['max_backup_files'], + 'MAX_BACKUP_FILES_DISABLED' => + Html::getDisabled($config['auto_delete']['activated'], 0)) +); \ No newline at end of file diff --git a/inc/configuration/config_files.php b/inc/configuration/config_files.php new file mode 100644 index 0000000..fa3a331 --- /dev/null +++ b/inc/configuration/config_files.php @@ -0,0 +1,206 @@ +' + . sprintf($lang['L_CONFIG_LOADED'], $config['config_file']) + . '

'; + } else { + getConfig($oldConfig); + $databases = $oldDatabases; + $msg = '

' + . sprintf( + $lang['L_ERROR_LOADING_CONFIGFILE'], + $config['config_file'] + ) . '

'; + } +} + +if (isset($_GET['config_delete'])) { + $deleteConfig = urldecode($_GET['config_delete']); + if ($deleteConfig == $config['config_file']) { + //actaul configuration was deleted, fall back to mysqldumper-conf + $config['config_file'] = 'mysqldumper'; + $_SESSION['config_file'] = $config['config_file']; + getConfig($config['config_file']); + } + $del = @unlink('./' . $config['paths']['config'] . $deleteConfig . '.php'); + if ($del) { + // delete Perl config file + $delFile = $config['paths']['config'] . $deleteConfig . '.conf.php'; + $del = @unlink('./' . $delFile); + } + if ($del === false) { + $msg = '

' + . sprintf($lang['L_ERROR_DELETING_CONFIGFILE'], $deleteConfig) + . '

'; + } else { + $msg = '

' + . sprintf($lang['L_SUCCESS_DELETING_CONFIGFILE'], $deleteConfig) + . '

'; + } + $sel = 'configs'; +} + +$tplConfigurationConfigFiles = new MSDTemplate(); +$tplConfigurationConfigFiles->set_filenames( + array( + 'tplConfigurationConfigFiles' => 'tpl/configuration/configFiles.tpl' + ) +); +$tplConfigurationConfigFiles->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_SEARCH' => $icon['search'], + 'ICON_EDIT' => $icon['edit'], + 'ICON_DELETE' => $icon['delete'] + ) +); +$i = 0; +$configs = getConfigFilenames(); +// iterate config files and print settings to screen +foreach ($configs as $c) { + $i++; + unset($databases); + $databases = array(); + getConfig($c); + $rowclass = ($i % 2) ? 'dbrow' : 'dbrow1'; + if ($oldConfig == $c) { + $rowclass = 'dbrowsel'; // highlight active configuration + } + // Generate configuration output + $outputstringMultisettings = ''; + $dbsToBackup = array(); + // look up which databases are set to be dumped + prepareDumpProcess(); + $dbs = array_keys($dump['databases']); + $dbsToBackup = implode(', ', $dbs); + + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW', + array( + 'ROWCLASS' => $rowclass, + 'NR' => $i, + 'CONFIG_ID' => sprintf("%03d", $i), + 'CONFIG_NAME' => $c, + 'CONFIG_NAME_URLENCODED' => urlencode($c), + 'DB_HOST' => $config['dbhost'], + 'DB_USER' => $config['dbuser'], + 'NR_OF_DATABASES' => sizeof($databases), + 'DBS_TO_BACKUP' => $dbsToBackup . ' ', + 'ATTACH_BACKUP' => + $config['email']['attach_backup'] == 1 ? + $lang['L_YES'] : $lang['L_NO'] + ) + ); + + if (count($databases) > 0) { + $a = 1; + foreach ($databases as $dbName => $val) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.LIST_DBS', + array( + 'ROWCLASS' => $a % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $a, + 'DB_NAME_URLENCODED' => base64_encode($dbName), + 'DB_NAME' => $dbName + ) + ); + $a++; + } + } + + // is Multipart used? + if ($config['multi_part'] == 1) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.USE_MULTIPART', + array( + 'MULTIPART_FILESIZE' => + byteOutput($config['multipart_groesse']) + ) + ); + } + + // send mail after backup? + if ($config['send_mail'] == 1) { + $recipientsCc = implodeSubarray( + $config['email']['recipient_cc'], + 'address' + ); + if ($config['email']['recipient_name'] > '') { + $recipient = $config['email']['recipient_name']; + } else { + $recipient = $config['email']['recipient_address']; + } + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_EMAIL', + array( + 'RECIPIENT' => $recipient, + 'RECIPIENT_CC' => + $recipientsCc > '' ? $recipientsCc : $lang['L_NO'] + ) + ); + $bytes = $config['email_maxsize1'] * 1024; + if ($config['email_maxsize2'] == 2) $bytes = $bytes * 1024; + if ($config['email']['attach_backup'] == 1) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_EMAIL.EMAIL_MAX_SIZE', + array( + 'SIZE' => byteOutput($bytes) + ) + ); + } + } + + // FTP settings + foreach ($config['ftp'] as $ftp) { + if ($ftp['transfer'] > 0) { + $ftpSettings = sprintf( + $lang['L_FTP_SEND_TO'], $ftp['server'], $ftp['dir'] + ); + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_FTP', + array( + 'FTP_SETTINGS' => Html::replaceQuotes($ftpSettings) + ) + ); + } + } + + // Show delete-button if it is not the standard config file + if ($c != 'mysqldumper') { + $confirmDelete = sprintf($lang['L_CONFIRM_CONFIGFILE_DELETE'], $c); + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.DELETE_CONFIG', + array( + 'CONFIRM_DELETE' => Html::getJsQuote($confirmDelete) + ) + ); + } +} + +unset($databases); +$databases = array(); +$_SESSION['config_file'] = $oldConfig; +$config['config_file'] = $oldConfig; +// reload actual configuration +getConfig($oldConfig); diff --git a/inc/configuration/config_menu.php b/inc/configuration/config_menu.php new file mode 100644 index 0000000..41667ad --- /dev/null +++ b/inc/configuration/config_menu.php @@ -0,0 +1,31 @@ +set_filenames( + array( + 'tplConfigurationConfigMenu' => 'tpl/configuration/config_menu.tpl' + ) +); + +$msdMode = $lang['L_MODE_EXPERT']; +if ($config['msd_mode'] == 0) { + $msdMode = $lang['L_MODE_EASY']; +} +$tplConfigurationConfigMenu->assign_vars( + array( + 'CONFIGURATION_NAME' => $config['config_file'], + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_OPEN_FILE' => $icon['small']['open_file'], + 'MSD_MODE' => $msdMode + ) +); diff --git a/inc/configuration/cronscript.php b/inc/configuration/cronscript.php new file mode 100644 index 0000000..1817c10 --- /dev/null +++ b/inc/configuration/cronscript.php @@ -0,0 +1,69 @@ + 1 + && substr($config['cron_execution_path'], -1) != '/') { + $config['cron_execution_path'] .= '/'; + } + if (isset($_POST['cron_printout'])) { + $config['cron_printout'] = (int) $_POST['cron_printout']; + } + if (isset($_POST['cron_completelog'])) { + $config['cron_completelog'] = (int) $_POST['cron_completelog']; + } + if (isset($_POST['compression'])) { + $config['cron_compression'] = (int) $_POST['compression']; + } + if (isset($_POST['cron_completelog'])) { + $config['cron_completelog'] = (int) $_POST['cron_completelog']; + } +} + +$tplConfigurationCronscript = new MSDTemplate(); +$tplConfigurationCronscript->set_filenames( + array('tplConfigurationCronscript' => 'tpl/configuration/cronscript.tpl') +); + +$tplConfigurationCronscript->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'EXTENSION_PL_SELECTED' => + Html::getChecked($config['cron_extender'], 0), + 'EXTENSION_CGI_SELECTED' => + Html::getChecked($config['cron_extender'], 1), + 'EXEC_PATH' => $config['cron_execution_path'], + 'CRON_PRINTOUT_ENABLED_SELECTED' => + Html::getChecked($config['cron_printout'], 1), + 'CRON_PRINTOUT_DISABLED_SELECTED' => + Html::getChecked($config['cron_printout'], 0), + 'CRON_COMPLETELOG_ENABLED_SELECTED' => + Html::getChecked($config['cron_completelog'], 1), + 'CRON_COMPLETELOG_DISABLED_SELECTED' => + Html::getChecked($config['cron_completelog'], 0), + 'CRON_COMMENT' => + htmlspecialchars($config['cron_comment'], ENT_COMPAT, 'UTF-8') + ) +); diff --git a/inc/configuration/databases.php b/inc/configuration/databases.php new file mode 100644 index 0000000..fe344b3 --- /dev/null +++ b/inc/configuration/databases.php @@ -0,0 +1,174 @@ + 0) { + $i = 0; + foreach ($databases as $dbName => $val) { + $databases[$dbName]['prefix'] = ''; + if (isset($_POST['dbpraefix_' . $i])) { + $databases[$dbName]['prefix'] = $_POST['dbpraefix_' . $i]; + } + $databases[$dbName]['command_before_dump'] = ''; + if (!empty($_POST['command_before_' . $i])) { + $databases[$dbName]['command_before_dump'] = + getQueryFromSqlLibrary($_POST['command_before_' . $i]); + } + $databases[$dbName]['command_after_dump'] = ''; + if (!empty($_POST['command_after_' . $i])) { + $databases[$dbName]['command_after_dump'] = + getQueryFromSqlLibrary($_POST['command_after_' . $i]); + } + if (isset($_POST['db_multidump_' . $i]) + && $_POST['db_multidump_' . $i] == "db_multidump_$i") { + $databases[$dbName]['dump'] = 1; + } else { + $databases[$dbName]['dump'] = 0; + } + $i++; + } + } + if ($config['dbhost'] != $_POST['dbhost'] + || $config['dbuser'] != $_POST['dbuser'] + || $config['dbpass'] != $_POST['dbpass'] + || $config['dbport'] != $_POST['dbport'] + || $config['dbsocket'] != $_POST['dbsocket']) { + //neue Verbindungsparameter + $blendInConnectionParams = true; + //alte Parameter sichern + $old['dbhost'] = $config['dbhost']; + $old['dbuser'] = $config['dbuser']; + $old['dbpass'] = $config['dbpass']; + $old['dbport'] = $config['dbport']; + $old['dbsocket'] = $config['dbsocket']; + //neu setzen + $config['dbhost'] = $_POST['dbhost']; + $config['dbuser'] = $_POST['dbuser']; + $config['dbpass'] = $_POST['dbpass']; + $config['dbport'] = $_POST['dbport']; + $config['dbsocket'] = $_POST['dbsocket']; + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + // try to connect with new params + $res = $dbo->dbConnect(); + if ($res === true) { + // ok - get list of databases + $dbo->getDatabases(); + setDefaultConfig(); + } else { + //something went wrong - resume old values + $config['dbhost'] = $old['dbhost']; + $config['dbuser'] = $old['dbuser']; + $config['dbpass'] = $old['dbpass']; + $config['dbport'] = $old['dbport']; + $config['dbsocket'] = $old['dbsocket']; + $msg .= '

' . $lang['L_WRONG_CONNECTIONPARS'] . ': ' . $res . '

'; + $saveConfig = false; + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + } + } + // manual adding of a database + if ($_POST['add_db_manual'] > '') { + $saveConfig = false; + $blendInConnectionParams = true; + $dbToAdd = trim($_POST['add_db_manual']); + $found = false; + // Check if we already have this one in our db list + if (isset($databases[$dbToAdd])) { + $addDbMessage = sprintf($lang['L_DB_IN_LIST'], $dbToAdd); + } else { + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + try { + $dbo->selectDb($dbToAdd, true); + addDatabaseToConfig($dbToAdd); + $saveConfig = true; + } catch (Exception $e){ + $addDbMessage = $lang['L_ERROR'] . ': (' . $e->getCode() . ') '; + $addDbMessage .= $e->getMessage(); + } + } + } +} +$tplConfigurationDatabases = new MSDTemplate(); +$tplConfigurationDatabases->set_filenames( + array('tplConfigurationDatabases' => 'tpl/configuration/databases.tpl') +); +$tplConfigurationDatabases->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'DB_HOST' => $config['dbhost'], + 'DB_USER' => $config['dbuser'], + 'DB_PASS' => $config['dbpass'], + 'DB_PORT' => $config['dbport'], + 'DB_SOCKET' => $config['dbsocket'], + 'ICON_EDIT' => $icon['edit'], + 'ICON_DOWN' => $icon['arrow_down'], + 'ICON_PLUS' => $icon['plus'], + 'ICON_MINUS' => $icon['minus']) +); +if (isset($addDbMessage) && $addDbMessage > '') + $tplConfigurationDatabases->assign_block_vars( + 'MANUAL_DB_ADD', array('MESSAGE' => $addDbMessage) +); + //Wenn Datenbanken vorhanden sind +if (count($databases) > 0) { + $dbCount = count($databases); + $tplConfigurationDatabases->assign_block_vars( + 'DBS', array('DB_COUNT' => $dbCount) + ); + $i = 0; + foreach ($databases as $dbName => $val) { + if (!isset($val['dump'])) { + $val['dump'] = 0; + } + if (!isset($val['prefix'])) { + $val['prefix'] = ''; + } + $rowclass = $i % 2 ? 'dbrow' : 'dbrow1'; + if ($dbName == $config['db_actual']) { + $rowclass = 'dbrowsel'; + } + $tplConfigurationDatabases->assign_block_vars( + 'DBS.ROW', array( + 'ROWCLASS' => $rowclass, + 'ID' => $i, + 'NR' => $i + 1, + 'DB_NAME' => $dbName, + 'DB_MULTIDUMP_ENABLED' => Html::getChecked($val['dump'], 1), + 'DB_PREFIX' => $val['prefix'], + 'COMMAND_BEFORE_BACKUP_COMBO' => getCommandDumpComboBox(0, $i, $dbName), + 'COMMAND_AFTER_BACKUP_COMBO' => getCommandDumpComboBox(1, $i, $dbName)) + ); + $i++; + } +} else { + $tplConfigurationDatabases->assign_block_vars('NO_DB', array()); +} diff --git a/inc/configuration/email.php b/inc/configuration/email.php new file mode 100644 index 0000000..dd870e6 --- /dev/null +++ b/inc/configuration/email.php @@ -0,0 +1,184 @@ + $val) { + $config['email']['recipient_cc'][$i] = array(); + $config['email']['recipient_cc'][$i]['name'] = $val['name']; + $config['email']['recipient_cc'][$i]['address'] = $val['address']; + $i++; + } + } + if (isset($_POST['email_sender_name'])) { + $config['email']['sender_name'] = $_POST['email_sender_name']; + } + if (isset($_POST['email_sender_address'])) { + $config['email']['sender_address'] = $_POST['email_sender_address']; + } + if (isset($_POST['attach_backup'])) { + $config['email']['sender_address'] = $_POST['attach_backup']; + } + if (isset($_POST['email_maxsize1'])) { + $config['email_maxsize1'] = floatval($_POST['email_maxsize1']); + } + if (isset($_POST['email_maxsize2'])) { + $config['email_maxsize2'] = $_POST['email_maxsize2']; + } + $config['email_maxsize'] = $config['email_maxsize1'] + * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (isset($_POST['use_mailer'])) { + $config['use_mailer'] = $_POST['use_mailer']; + } + if (isset($_POST['sendmail_call'])) { + $config['sendmail_call'] = $_POST['sendmail_call']; + } + if (isset($_POST['smtp_server'])) { + $config['smtp_server'] = $_POST['smtp_server']; + } + if (isset($_POST['smtp_user'])) { + $config['smtp_user'] = $_POST['smtp_user']; + } + if (isset($_POST['smtp_pass'])) { + $config['smtp_pass'] = $_POST['smtp_pass']; + } + if (isset($_POST['smtp_useauth'])) { + $config['smtp_useauth'] = $_POST['smtp_useauth']; + } + if (isset($_POST['smtp_usessl'])) { + $config['smtp_usessl'] = $_POST['smtp_usessl']; + } + if (isset($_POST['smtp_port'])) { + $config['smtp_port'] = (int) $_POST['smtp_port']; + } + if (isset($_POST['smtp_pop3_server'])) { + $config['smtp_pop3_server'] = (string) $_POST['smtp_pop3_server']; + } + if (isset($_POST['smtp_pop3_port'])) { + $config['smtp_pop3_port'] = (int) $_POST['smtp_pop3_port']; + } +} + +// backwards compatibilty with older configurations +if (!isset($config['email_maxsize1'])) { + $config['email_maxsize1'] = 0; +} +if (!isset($config['email_maxsize2'])) { + $config['email_maxsize2'] = 1; +} +$tplConfigurationEmail = new MSDTemplate(); +$tplConfigurationEmail->set_filenames( + array('tplConfigurationEmail' => 'tpl/configuration/email.tpl') +); + +$tplConfigurationEmail->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_PLUS' => $icon['plus'], + 'ICON_DELETE' => $icon['delete'], + 'SEND_MAIL_ENABLED_SELECTED' => Html::getChecked($config['send_mail'], 1), + 'EMAIL_DISABLED' => Html::getDisabled($config['send_mail'], 0), + 'SEND_MAIL_DISABLED_SELECTED' => Html::getChecked($config['send_mail'], 0), + 'EMAIL_RECIPIENT_NAME' => + Html::replaceQuotes($config['email']['recipient_name']), + 'EMAIL_RECIPIENT_ADDRESS' => + Html::replaceQuotes($config['email']['recipient_address']), + 'EMAIL_SENDER_NAME' => Html::replaceQuotes($config['email']['sender_name']), + 'EMAIL_SENDER_ADDRESS' => + Html::replaceQuotes($config['email']['sender_address']), + 'ATTACH_BACKUP_ENABLED_SELECTED' => + Html::getChecked($config['email']['attach_backup'], 1), + 'ATTACH_BACKUP_DISABLED_SELECTED' => + Html::getChecked($config['email']['attach_backup'], 0), + 'MAXSIZE_DISABLED' => + Html::getDisabled($config['email']['attach_backup'], 0), + 'EMAIL_MAXSIZE' => $config['email_maxsize1'], + 'EMAIL_UNIT_SIZE_KB_SELECTED' => + Html::getSelected($config['email_maxsize2'], 1), + 'EMAIL_UNIT_SIZE_MB_SELECTED' => + Html::getSelected($config['email_maxsize2'], 2), + 'EMAIL_USE_PHPMAIL_SELECTED' => Html::getChecked($config['use_mailer'], 0), + 'EMAIL_USE_SENDMAIL_SELECTED' => Html::getChecked($config['use_mailer'], 1), + 'EMAIL_USE_SMTP_SELECTED' => Html::getChecked($config['use_mailer'], 2), + 'SENDMAIL_CALL' => Html::replaceQuotes($config['sendmail_call']), + 'SMTP_SERVER' => $config['smtp_server'], + 'SMTP_USER' => $config['smtp_user'], + 'SMTP_PASS' => $config['smtp_pass'], + 'SMTP_AUTH_DISABLED' => Html::getDisabled($config['smtp_useauth'], 0), + 'SMTP_AUTH_SELECTED' => Html::getChecked($config['smtp_useauth'], 1), + 'SMTP_AUTH_NOT_SELECTED' => Html::getChecked($config['smtp_useauth'], 0), + 'SMTP_SSL_SELECTED' => Html::getChecked($config['smtp_usessl'], 1), + 'SMTP_SSL_NOT_SELECTED' => Html::getChecked($config['smtp_usessl'], 0), + 'SMTP_PORT' => $config['smtp_port'], + 'SMTP_POP3_SERVER' => $config['smtp_pop3_server'], + 'SMTP_POP3_PORT' => $config['smtp_pop3_port']) +); +if ($config['smtp_useauth'] == 0) { + $tplConfigurationEmail->assign_block_vars('HIDE_SMTP_AUTH_FIELDS', array()); +} +if (is_array($config['email']['recipient_cc']) + && sizeof($config['email']['recipient_cc']) > 0) { + foreach ($config['email']['recipient_cc'] as $key => $val) { + $confirmRecDelete = sprintf( + $lang['L_CONFIRM_RECIPIENT_DELETE'], + $val['name'] . ' ' . $val['address'] + ); + $confirmRecDelete = Html::replaceQuotes($confirmRecDelete); + + $tplConfigurationEmail->assign_block_vars( + 'EMAIL_RECIPIENT_CC', + array( + 'NR' => $key, + 'CONFIRM_RECIPIENT_DELETE' => $confirmRecDelete, + 'EMAIL_RECIPIENT_CC_NAME' => Html::replaceQuotes($val['name']), + 'EMAIL_RECIPIENT_CC_ADDRESS' => + Html::replaceQuotes($val['address']) + ) + ); + } +} \ No newline at end of file diff --git a/inc/configuration/footer.php b/inc/configuration/footer.php new file mode 100644 index 0000000..0003929 --- /dev/null +++ b/inc/configuration/footer.php @@ -0,0 +1,44 @@ +set_filenames( + array('tplConfigurationFooter' => 'tpl/configuration/footer.tpl') +); + +$tplConfigurationFooter->assign_vars( + array( + 'SELECTED_DIV' => $sel, + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +//output notification message if we have one +if ($msg > '') { + $tplConfigurationFooter->assign_block_vars( + 'MESSAGE', array('TEXT' => Html::getJsQuote($msg, true)) + ); +} +// if something went wrong with the change of a user or no database was found +// -> blend in connection params and let the user correct it +if ( (isset($blendInConnectionParams) && $blendInConnectionParams) + || count($databases) == 0) { + $tplConfigurationFooter->assign_block_vars( + 'SWITCH_TO_CONNECTION_PARAMETER', array() + ); +} \ No newline at end of file diff --git a/inc/configuration/ftp.php b/inc/configuration/ftp.php new file mode 100644 index 0000000..27dd91b --- /dev/null +++ b/inc/configuration/ftp.php @@ -0,0 +1,156 @@ + $val) { + // set default values if this connection is a new one + if (!isset($config['ftp'][$key]) || !is_array($config['ftp'][$key])) { + $config['ftp'][$key] = array(); + } + if (!isset($config['ftp'][$key]['server'])) { + $config['ftp'][$key]['server'] = ''; + } + if (!isset($config['ftp'][$key]['transfer'])) { + $config['ftp'][$key]['transfer'] = 0; + } + if (!isset($config['ftp'][$key]['timeout'])) { + $config['ftp'][$key]['timeout'] = 10; + } + if (!isset($config['ftp'][$key]['ssl'])) { + $config['ftp'][$key]['ssl'] = 0; + } + if (!isset($config['ftp'][$key]['mode'])) { + $config['ftp'][$key]['mode'] = 0; + } + if (!isset($config['ftp'][$key]['port'])) { + $config['ftp'][$key]['port'] = 21; + } + if (!isset($config['ftp'][$key]['user'])) { + $config['ftp'][$key]['user'] = ''; + } + if (!isset($config['ftp'][$key]['pass'])) { + $config['ftp'][$key]['pass'] = ''; + } + if (!isset($config['ftp'][$key]['dir'])) { + $config['ftp'][$key]['dir'] = '/'; + } + if (isset($val['transfer'])) { + $config['ftp'][$key]['transfer'] = (int) $val['transfer']; + } + if (isset($val['server'])) { + $config['ftp'][$key]['server'] = (string) $val['server']; + } + if (isset($val['timeout'])) { + $config['ftp'][$key]['timeout'] = (int) $val['timeout']; + } + if (isset($val['ssl'])) { + $config['ftp'][$key]['ssl'] = (int) $val['ssl']; + } + if (isset($val['mode'])) { + $config['ftp'][$key]['mode'] = (int) $val['mode']; + } + if (isset($val['port'])) { + $config['ftp'][$key]['port'] = (int) $val['port']; + } + if (isset($val['user'])) { + $config['ftp'][$key]['user'] = (string) $val['user']; + } + if (isset($val['pass'])) { + $config['ftp'][$key]['pass'] = (string) $val['pass']; + } + if (isset($val['dir'])) { + $config['ftp'][$key]['dir'] = (string) $val['dir']; + } + if ($config['ftp'][$key]['dir'] == '' + || (strlen($config['ftp'][$key]['dir']) > 1 + && substr($config['ftp'][$key]['dir'], -1) != '/')) { + $config['ftp'][$key]['dir'] .= '/'; + } + if (isset($_POST['ftp'][$key]['test'])) { + $ftpConnectionCheck[$key] = testFTP($key); + // don't save values - we just want to test the ftp connection data + $saveConfig = false; + } + } +} + +if (isset($_GET['del_ftp'])) { + $index = intval($_GET['del_ftp']); + if (isset($config['ftp'][$index])) { + unset($config['ftp'][$index]); + } + sort($config['ftp']); +} + +$tplConfigurationFtp = new MSDTemplate(); +$tplConfigurationFtp->set_filenames( + array('tplConfigurationFtp' => 'tpl/configuration/ftp.tpl') +); + +$tplConfigurationFtp->assign_vars( + array( + 'ICON_ADD' => $icon['plus'], + 'ICON_DELETE' => $icon['delete'], + 'ICON_SAVE' => $icon['small']['save'] + ) +); + +foreach ($config['ftp'] as $key => $val) { + // if ftp extension is not loaded -> disable ftp transfer + if (!extension_loaded('ftp')) { + $config['ftp'][$key]['transfer'] = 0; + } + + $tplConfigurationFtp->assign_block_vars( + 'FTP', + array( + 'NR' => $key + 1, + 'ID' => $key, + 'FTP_DISABLED' => Html::getDisabled(!extension_loaded("ftp"), true), + 'FTP_ENABLED_SELECTED' => Html::getChecked($val['transfer'], 1), + 'FTP_DISABLED_SELECTED' => Html::getChecked($val['transfer'], 0), + 'FTP_FIELDS_DISABLED' => Html::getDisabled($val['transfer'], 0), + 'FTP_TIMEOUT' => $val['timeout'], + 'FTP_PASSIVE_MODE_SELECTED' => Html::getChecked($val['mode'], 1), + 'FTP_SSL_DISABLED' => + Html::getDisabled(extension_loaded('openssl'), false), + 'FTP_SSL_ENABLED_SELECTED' => Html::getChecked($val['ssl'], 1), + 'FTP_SERVER' => $config['ftp'][$key]['server'], + 'FTP_PORT' => $val['port'], + 'FTP_USER' => Html::replaceQuotes($val['user']), + 'FTP_PASSWORD' => Html::replaceQuotes($val['pass']), + 'FTP_DIRECTORY' => $val['dir'], + 'FTP_CONFIRM_DELETE' => + Html::replaceQuotes($lang['L_FTP_CONFIRM_DELETE']) + ) + ); + if (isset($ftpConnectionCheck[$key])) { + $tplConfigurationFtp->assign_block_vars( + 'FTP.CHECK', array('RESULT' => $ftpConnectionCheck[$key]) + ); + } +} diff --git a/inc/configuration/general.php b/inc/configuration/general.php new file mode 100644 index 0000000..e45d174 --- /dev/null +++ b/inc/configuration/general.php @@ -0,0 +1,146 @@ + delete old log file + $oldlogcompression = $config['logcompression']; + if (isset($_POST['logcompression'])) { + $config['logcompression'] = $_POST['logcompression']; + } else { + $config['logcompression'] = 0; + } + // if zlib-extension is not installed de-activate compression + // for log and dump file automatically + if (!$config['zlib']) { + $config['logcompression'] = 0; + $config['compression'] = 0; + } + // max size of logfiles + if (isset($_POST['log_maxsize1'])) { + $config['log_maxsize1'] = (int) $_POST['log_maxsize1']; + } + if (isset($_POST['log_maxsize2'])) { + $config['log_maxsize2'] = (int) $_POST['log_maxsize2']; + } + if ($config['log_maxsize1'] < 1) { + $config['log_maxsize1'] = 1; + } + if ($config['log_maxsize1'] < 100 && $config['log_maxsize2'] == 1) { + $config['log_maxsize1'] = 100; + } + if ($config['log_maxsize2'] == 1) { + $config['log_maxsize'] = 1024 * $config['log_maxsize1']; // kB + } else { + $config['log_maxsize'] = 1024 * 1024 * $config['log_maxsize1']; // MB + } + if (isset($_POST['empty_db_before_restore'])) { + $config['empty_db_before_restore'] = + (int) $_POST['empty_db_before_restore']; + } + if (isset($_POST['optimize_tables'])) { + $config['optimize_tables_beforedump'] = (int) $_POST['optimize_tables']; + } + //if we are not in mode expert -> reset hidden settings to safe defaults + if ($config['msd_mode'] == 0) { + $config['empty_db_before_restore'] = 0; + } +} + +$tplConfigurationGeneral = new MSDTemplate(); +$tplConfigurationGeneral->set_filenames( + array('tplConfigurationGeneral' => 'tpl/configuration/general.tpl') +); +if ($config['msd_mode'] > 0) { + $tplConfigurationGeneral->assign_block_vars('MODE_EXPERT', array()); +} +$logGz = !$config['zlib'] ? '' : Html::getChecked($config['logcompression'], 1); + +$tplConfigurationGeneral->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'MSD_MODE_EASY_SELECTED' => Html::getChecked($config['msd_mode'], 0), + 'MSD_MODE_EXPERT_SELECTED' => Html::getChecked($config['msd_mode'], 1), + 'GZ_DISABLED' => Html::getDisabled($config['zlib'], 0), + 'LOG_GZ_SELECTED' => $logGz, + 'LOG_MAXSIZE1' => $config['log_maxsize1'], + 'LOG_UNIT_KB_SELECTED' => Html::getSelected($config['log_maxsize2'], 1), + 'LOG_UNIT_MB_SELECTED' => Html::getSelected($config['log_maxsize2'], 2), + 'PHP_MEMORY_LIMIT' => $config['memory_limit'], + 'MIN_SPEED' => $config['minspeed'], + 'MAX_SPEED' => $config['maxspeed'], + 'DUMP_GZ_ENABLED_SELECTED' => + Html::getChecked($config['compression'], 1), + 'DUMP_GZ_DISABLED_SELECTED' => + Html::getChecked($config['compression'], 0), + 'MULTIPART_ENABLED_SELECTED' => + Html::getChecked($config['multi_part'], 1), + 'MULTIPART_DISABLED_SELECTED' => + Html::getChecked($config['multi_part'], 0), + 'MULTIPART_FILE_SIZE' => $config['multipartgroesse1'], + 'MULTIPART_DISABLED' => Html::getDisabled($config['multi_part'], 0), + 'MULTIPART_FILE_SIZE_DISABLED' => + Html::getDisabled($config['multi_part'], 0), + 'MULTIPART_FILE_UNIT_KB_SELECTED' => + Html::getSelected($config['multipartgroesse2'], 1), + 'MULTIPART_FILE_UNIT_MB_SELECTED' => + Html::getSelected($config['multipartgroesse2'], 2), + 'OPTIMIZE_TABLES_ENABLED_SELECTED' => + Html::getChecked($config['optimize_tables_beforedump'], 1), + 'OPTIMIZE_TABLES_DISABLED_SELECTED' => + Html::getChecked($config['optimize_tables_beforedump'], 0), + 'TRUNCATE_DB_ENABLED_SELECTED' => + Html::getChecked($config['empty_db_before_restore'], 1), + 'TRUNCATE_DB_DISABLED_SELECTED' => + Html::getChecked($config['empty_db_before_restore'], 0), + 'STOP_ON_ERROR_ENABLED_SELECTED' => + Html::getChecked($config['stop_with_error'], 1), + 'STOP_ON_ERROR_DISABLED_SELECTED' => + Html::getChecked($config['stop_with_error'], 0) + ) +); + diff --git a/inc/configuration/interface.php b/inc/configuration/interface.php new file mode 100644 index 0000000..cb27346 --- /dev/null +++ b/inc/configuration/interface.php @@ -0,0 +1,100 @@ + $lang['L_POSITION_TL'], + 'tc' => $lang['L_POSITION_TC'], + 'tr' => $lang['L_POSITION_TR'], + 'ml' => $lang['L_POSITION_ML'], + 'mc' => $lang['L_POSITION_MC'], + 'mr' => $lang['L_POSITION_MR'], + 'bl' => $lang['L_POSITION_BL'], + 'bc' => $lang['L_POSITION_BC'], + 'br' => $lang['L_POSITION_BR'] +); + +if (isset($_POST['save'])) { + if (isset($_POST['interface_server_caption'])) { + $config['interface_server_caption'] = 1; + } else { + $config['interface_server_caption'] = 0; + } + if (isset($_POST['interface_server_caption_position_1'])) { + $config['interface_server_caption_position'] = 1; + } else { + $config['interface_server_caption_position'] = 0; + } + if (isset($_POST['sqlboxsize'])) { + $config['interface_sqlboxsize'] = (int) $_POST['sqlboxsize']; + } + if (isset($_POST['theme'])) { + $config['theme'] =(string) $_POST['theme']; + } + // make sure that the theme exists! If not fall back to standard theme + if (!is_dir('./css/' . $config['theme']) + || !is_readable('./css/' . $config['theme'])) { + $config['theme'] = 'msd'; + } + if (isset($_POST['notification_position'])) { + $config['notification_position'] = $_POST['notification_position']; + } + if (isset($_POST['interface_table_compact'])) { + $config['interface_table_compact'] + = (int) $_POST['interface_table_compact']; + }; + if (isset($_POST['resultsPerPage'])) { + $config['resultsPerPage'] = (int) $_POST['resultsPerPage']; + } + if (isset($_POST['refresh_processlist'])) { + $config['refresh_processlist'] = (int) $_POST['refresh_processlist']; + } + if ($config['refresh_processlist'] < 2) { + $config['refresh_processlist'] = 2; + } +} + +$tplConfigurationInterface = new MSDTemplate(); +$tplConfigurationInterface->set_filenames( + array('tplConfigurationInterface' => 'tpl/configuration/interface.tpl') +); + +$tplConfigurationInterface->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'SEL_LANGUAGES' => getLanguageCombo(), + 'LINK_DOWNLOAD_LANGUAGE_PACKS' => + 'http://forum.mysqldumper.de/downloads.php?cat=9', + 'LANGUAGE' => $config['language'], + 'SERVER_CAPTION' => $config['interface_server_caption'], + 'INTERFACE_SERVER_CAPTION_ACTIVATED' => + Html::getChecked($config['interface_server_caption'], 1), + 'INTERFACE_SERVER_CAPTION_DISABLED' => + Html::getDisabled($config['interface_server_caption'], 0), + 'SERVER_CAPTION_POS_MAINFRAME_SELECTED' => + Html::getChecked($config['interface_server_caption_position'], 1), + 'SERVER_CAPTION_POS_MENUE_SELECTED' => + Html::getChecked($config['interface_server_caption_position'], 0), + 'SEL_THEME' => getThemes(), + 'LINK_DOWNLOAD_THEMES' => + 'http://forum.mysqldumper.de/downloads.php?cat=3', + 'SQLBOX_HEIGHT' => intval($config['interface_sqlboxsize']), + 'RESULTS_PER_PAGE' => intval($config['resultsPerPage']), + 'SEL_NOTIFICATION_POSITION' => + Html::getOptionlist($positions, $config['notification_position']), + 'REFRESH_PROCESSLIST' => (int) $config['refresh_processlist'], + 'SQL_GRID_TYPE_COMPACT_SELECTED' => + $config['interface_table_compact'] == 1 ? ' checked="checked"' : '', + 'SQL_GRID_TYPE_NORMAL_SELECTED' => + $config['interface_table_compact'] == 0 ? ' checked="checked"' : '' + ) +); diff --git a/inc/define_icons.php b/inc/define_icons.php new file mode 100644 index 0000000..546f5d1 --- /dev/null +++ b/inc/define_icons.php @@ -0,0 +1,69 @@ + clear arrays with values from last run + $_SESSION['dump'] = array(); + $_SESSION['log'] = array(); + $_SESSION['log']['actions'] = array(); + $_SESSION['log']['errors'] = array(); + $_SESSION['log']['files_created'] = array(); + $dump['comment'] = ''; + $action = 'prepare_dump'; +} +if ($action == 'prepare_dump') include ('./inc/dump/dump_prepare.php'); +if ($action == 'select_tables') include ('./inc/dump/select_tables.php'); +if ($action == 'do_dump') include ('./inc/dump/do_dump.php'); +if ($action == 'done') include ('./inc/dump/dump_finished.php'); +$_SESSION['dump'] = $dump; diff --git a/inc/dump/do_dump.php b/inc/dump/do_dump.php new file mode 100644 index 0000000..061864c --- /dev/null +++ b/inc/dump/do_dump.php @@ -0,0 +1,70 @@ + set start values +$dump = $_SESSION['dump']; +$dump['tables_total'] = 0; +$dump['records_total'] = 0; +$dump['tables_optimized'] = 0; +$dump['part'] = 1; +$dump['part_offset'] = 0; +$dump['errors'] = 0; +//-1 instead of 0 is needed for the execution of command before backup +$dump['table_offset'] = -1; +$dump['table_record_offset'] = 0; +$dump['filename_stamp'] = ''; +$dump['speed'] = $config['minspeed'] > 0 ? $config['minspeed'] : 50; +$dump['max_zeit'] = + (int) $config['max_execution_time'] * $config['time_buffer']; +$dump['dump_start_time'] = time(); +$dump['countdata'] = 0; +$dump['table_offset_total'] = 0; +$dump['page_refreshs'] = 0; +// used as overall flag including e-mail and ftp-actions +$dump['backup_in_progress'] = 1; +// used to determine id databases still need to be dumped +$dump['backup_done'] = 0; +$dump['selected_tables'] = FALSE; +if (isset($_POST['sel_tbl'])) { + $dump['selected_tables'] = $_POST['sel_tbl']; +} +// function was called in dump_prepare +// -- maybe get rid of this second call later on +prepareDumpProcess(); +// last_db_actual is used to detect if db changed in multidump-mode +// -> set to first db +$dump['last_db_actual'] = $dump['db_actual']; + +$_SESSION['config_file'] = $config['config_file']; +$_SESSION['dump'] = $dump; + +$tplDoDump = new MSDTemplate(); +$tplDoDump->set_filenames(array('tplDoDump' => 'tpl/dump/dump.tpl')); +$gzip = $config['compression'] == 1 ? $icon['gz'] : $lang['L_NOT_ACTIVATED']; +$tplDoDump->assign_vars( + array( + 'ICONPATH' => $config['files']['iconpath'], + 'GZIP' => $gzip, + 'SESSION_ID' => session_id(), + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +$sizeUnits = array(1, 1024, 1024 * 1024, 1024 * 10242 * 1024); +$size = $config['multipartgroesse1'] * $sizeUnits[$config['multipartgroesse2']]; +if ($config['multi_part'] > 0) { + $tplDoDump->assign_block_vars( + 'MULTIPART', array('SIZE' => byteOutput($size)) + ); +} + +$tplDoDump->assign_var('TABLES_TO_DUMP', $dump['tables_total']); + diff --git a/inc/dump/dump_finished.php b/inc/dump/dump_finished.php new file mode 100644 index 0000000..4b88b73 --- /dev/null +++ b/inc/dump/dump_finished.php @@ -0,0 +1,88 @@ +set_filenames( + array('tplDumpFinished' => 'tpl/dump/dump_finished.tpl') +); +$recordsTotal = String::formatNumber((int)$dump['records_total']); +$tablesTotal = $dump['tables_total']; +$msg = sprintf($lang['L_DUMP_ENDERGEBNIS'], $tablesTotal, $recordsTotal); +$tplDumpFinished->assign_vars( + array( + 'ICON_OPEN_FILE' => $icon['small']['open_file'], + 'ICON_EDIT' => $icon['small']['edit'], + 'ICON_VIEW' => $icon['small']['view'], + 'SESSION_ID' => session_id(), + 'BACKUPPATH' => $config['paths']['backup'], + 'PAGE_REFRESHS' => $dump['page_refreshs'], + 'TIME_ELAPSED' => getTimeFormat(time() - $dump['dump_start_time']), + 'MSG' => $msg + ) +); + +if (count($dump['databases']) > 1) { + $msg = sprintf($lang['L_MULTIDUMP_FINISHED'], count($dump['databases'])); + $tplDumpFinished->assign_block_vars('MULTIDUMP', array('MSG' => $msg)); +} +$i = 1; +foreach ($_SESSION['log']['files_created'] as $file) { + $fileSize = @filesize($config['paths']['backup'] . $file); + $tplDumpFinished->assign_block_vars( + 'FILE', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'FILENAME' => $file, + 'FILENAME_URLENCODED' => urlencode($file), + 'FILESIZE' => byteOutput($fileSize) + ) + ); + $i++; +} + +$i = 1; +foreach ($_SESSION['log']['actions'] as $message) { + $timestamp = substr($message, 0, 19); + $message = substr($message, 20); + $tplDumpFinished->assign_block_vars( + 'ACTION', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'TIMESTAMP' => $timestamp, + 'ACTION' => $message + ) + ); + $i++; +} + +if (sizeof($_SESSION['log']['errors']) > 0) { + $tplDumpFinished->assign_block_vars('ERROR', array()); + $i = 1; + foreach ($_SESSION['log']['errors'] as $error) { + $timestamp = substr($error, 0, 19); + $error = substr($error, 20); + $tplDumpFinished->assign_block_vars( + 'ERROR.ERRORMSG', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'TIMESTAMP' => $timestamp, + 'MSG' => $error + ) + ); + $i++; + } +} diff --git a/inc/dump/dump_prepare.php b/inc/dump/dump_prepare.php new file mode 100644 index 0000000..e436ab1 --- /dev/null +++ b/inc/dump/dump_prepare.php @@ -0,0 +1,158 @@ + reset hidden settings to safe defaults +if ($config['msd_mode'] == 0) { + $config['replace_command'] = 0; +} + +$tplDumpPrepare = new MSDTemplate(); +$tplDumpPrepare->set_filenames( + array('tplDumpPrepare' => 'tpl/dump/dump_prepare.tpl') +); +if ($config['msd_mode'] > 0) { + $tplDumpPrepare->assign_block_vars('MODE_EXPERT', array()); +} +// set charset-selectbox to standard-encoding of MySQL-Server as initial value +$dump['sel_dump_encoding'] = 'utf8'; +$dump['dump_encoding'] = 'utf8'; +$charsets=$dbo->getCharsets(); +foreach ($charsets as $name=>$val) { + $charsetsDescription[$name]=$name.' - '.$val['Description']; +} +// tables will be selected on next page, but we need a dummy val +// for prepareDumpProcess() +$dump['selected_tables'] = false; +$dbsToBackup = array(); +// look up which databases need to be dumped +prepareDumpProcess(); +$dbs = array_keys($dump['databases']); +$dbsToBackup = implode(', ', $dbs); + +$cext = ($config['cron_extender'] == 0) ? 'pl' : 'cgi'; +$scriptName = $_SERVER['SCRIPT_NAME']; +$serverName = $_SERVER['SERVER_NAME']; +$url = substr($scriptName, 0, strrpos($scriptName, '/') + 1); +if (substr($url, -1) != '/') $url .= '/'; +if (substr($url, 0, 1) != '/') $url = '/' . $url; +$refdir = (substr($config['cron_execution_path'], 0, 1) == '/') ? '' : $url; +$scriptdir = $config['cron_execution_path'] . 'crondump.' . $cext; +$perlModultest = $config['cron_execution_path'] . "perltest.$cext"; +$perlSimpletest = $config['cron_execution_path'] . "simpletest.$cext"; +$scriptentry = myRealpath('./') . $config['paths']['config']; +$cronabsolute = myRealpath('./') . $scriptdir; +if (substr($config['cron_execution_path'], 0, 1) == '/') { + $cronabsolute = $_SERVER['DOCUMENT_ROOT'] . $scriptdir; +} +$confabsolute = $config['config_file']; +$perlHttpCall = getServerProtocol() . $serverName . $refdir; +$perlHttpCall .= $config['cron_execution_path'] . 'crondump.' . $cext; +$perlHttpCall .= '?config=' . $confabsolute; +$perlCrontabCall = 'perl ' . $cronabsolute . ' -config=' . $confabsolute; +$perlCrontabCall .= ' -html_output=0'; +$tplDumpPrepare->assign_vars( + array( + 'SESSION_ID' => session_id(), + 'CONFIG_FILE' => $config['config_file'], + 'POSSIBLE_DUMP_ENCODINGS' => + Html::getOptionlist($charsetsDescription, 'utf8'), + 'DBS_TO_BACKUP' => $dbsToBackup, + 'TABLES_TOTAL' => $dump['tables_total'], + 'RECORDS_TOTAL' => String::formatNumber(intval($dump['records_total'])), + 'DATASIZE_TOTAL' => byteOutput($dump['datasize_total']), + 'NR_OF_DBS' => count($dump['databases']), + 'DUMP_COMMENT' => Html::replaceQuotes($dump['comment']), + 'PERL_TEST' => $perlSimpletest, + 'PERL_MODULTEST' => $perlModultest, + 'PERL_HTTP_CALL' => $perlHttpCall, + 'PERL_CRONTAB_CALL' => $perlCrontabCall, + 'PERL_ABSOLUTE_PATH_OF_CONFIGDIR' => $scriptentry, + 'TIMESTAMP' => time() + ) +); + +if (count($dump['databases']) == 1 && $config['db_actual'] == $dbsToBackup) { + $tplDumpPrepare->assign_block_vars('TABLESELECT', array()); +} +if ($config['compression'] == 1) { + $tplDumpPrepare->assign_block_vars('GZIP_ACTIVATED', array()); +} else { + $tplDumpPrepare->assign_block_vars('GZIP_NOT_ACTIVATED', array()); +} + +if ($config['multi_part'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'MULTIPART', + array('SIZE' => byteOutput($config['multipart_groesse'])) + ); +} else { + $tplDumpPrepare->assign_block_vars('NO_MULTIPART', array()); +} + +if ($config['send_mail'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL', + array('RECIPIENT' => $config['email']['recipient_address']) + ); + $recipients = $config['email']['recipient_cc']; + $recipientsCc = implodeSubarray($recipients, 'address'); + if ($recipientsCc > '') { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.CC', array('EMAIL_ADRESS' => $recipientsCc) + ); + } + if ($config['email']['attach_backup'] == 1) { + $bytes = $config['email_maxsize1'] * 1024; + if ($config['email_maxsize2'] == 2) { + $bytes = $bytes * 1024; + } + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.ATTACH_BACKUP', array('SIZE' => byteOutput($bytes)) + ); + } else { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.DONT_ATTACH_BACKUP', array() + ); + } +} else { + $tplDumpPrepare->assign_block_vars('NO_SEND_MAIL', array()); +} + +$i = 1; +foreach ($config['ftp'] as $ftp) { + if ($ftp['transfer'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'FTP', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow1' : 'dbrow' + ) + ); + + $tplDumpPrepare->assign_block_vars( + 'FTP.CONNECTION', + array( + 'SERVER' => $ftp['server'], + 'PORT' => $ftp['port'], + 'DIR' => $ftp['dir'] + ) + ); + $i++; + } +} diff --git a/inc/dump/select_tables.php b/inc/dump/select_tables.php new file mode 100644 index 0000000..da33467 --- /dev/null +++ b/inc/dump/select_tables.php @@ -0,0 +1,76 @@ +set_filenames( + array('tplDumpSelectTables' => 'tpl/dump/selectTables.tpl') + ); + $dbo->selectDb($dump['db_actual']); + $tables=$dbo->getTableStatus(); + $i=0; + foreach ($tables as $tableName => $row) { + $klasse = ($i % 2) ? 1 : ''; + $tableSize = byteOutput($row['Data_length'] + $row['Index_length']); + $tableType = $row['Engine']; + $nrOfRecords = String::formatNumber($row['Rows']); + if (substr($row['Comment'], 0, 4) == 'VIEW' && $row['Engine'] == '') { + $tableType = 'View'; + $tableSize = '-'; + $nrOfRecords = '-'; + } + $tplDumpSelectTables->assign_block_vars( + 'ROW', + array( + 'CLASS' => 'dbrow' . $klasse, + 'ID' => $i, + 'NR' => $i + 1, + 'TABLENAME' => $tableName, + 'TABLETYPE' => $tableType, + 'RECORDS' => $nrOfRecords, + 'SIZE' => $tableSize, + 'LAST_UPDATE' => $row['Update_time'] + ) + ); + $i++; + } + + $tplDumpSelectTables->assign_vars( + array( + 'PAGETITLE' => $lang['L_TABLESELECTION'], + 'SESSION_ID' => session_id(), + 'DATABASE' => $config['db_actual'], + 'ICON_OK' => $icon['ok'], + 'ICON_DELETE' => $icon['delete'], + 'ICON_DB' => $icon['db'], + 'L_NO_MSD_BACKUP' => $lang['L_NOT_SUPPORTED'] + ) + ); +} diff --git a/inc/filemanagement/converter.php b/inc/filemanagement/converter.php new file mode 100644 index 0000000..c66a6f8 --- /dev/null +++ b/inc/filemanagement/converter.php @@ -0,0 +1,77 @@ +pparse('tplGlobalHeader'); +$tplMenu->pparse('tplMenu'); + +// Konverter +$selectfile = (isset($_POST['selectfile'])) ? $_POST['selectfile'] : ''; +$destfile = (isset($_POST['destfile'])) ? $_POST['destfile'] : ''; +$compressed = (isset($_POST['compressed'])) ? $_POST['compressed'] : ''; +include ('./inc/define_icons.php'); +$doConversion = false; +if ($selectfile != '' & file_exists($config['paths']['backup'] . $selectfile) + && strlen($destfile) > 2) { + $doConversion = true; +} + +$tpl = new MSDTemplate(); +$tpl->set_filenames(array('show' => 'tpl/filemanagement/converter.tpl')); + +$tpl->assign_vars( + array( + 'SELECTBOX_FILE_LIST' => getFilelisteCombo($selectfile), + 'NEW_FILE' => Html::replaceQuotes($destfile), + 'NEW_FILE_COMPRESED' => $compressed == 1 ? ' checked="checked"' : '', + 'ICON_GZ' => $icon['gz'] + ) +); + +if ($doConversion) { + $tpl->assign_block_vars('AUTOSCROLL', array()); +} + +$tpl->pparse('show'); +flush(); +if (isset($_POST['startconvert'])) { + echo $lang['L_CONVERTING'] . ' ' . $selectfile + . ' ==> ' . $destfile . '.sql'; + if ($compressed == 1) { + echo '.gz'; + } + if ($doConversion) { + convert($selectfile, $destfile, $compressed); + } else { + echo '

' . $lang['L_CONVERT_WRONG_PARAMETERS'] + . '

'; + } +} + +if ($doConversion) { + ?> + + + + + + '') { + $msg .= $deleteResult; + } +} + +if (isset($_POST['deleteall']) || isset($_POST['deleteallfilter'])) { + if (isset($_POST['deleteall'])) { + $del = deleteMultipartFiles($config['paths']['backup'], '', '.sql'); + $del = array_merge( + $del, deleteMultipartFiles($config['paths']['backup'], '', '.gz') + ); + } else { + $del = deleteMultipartFiles($config['paths']['backup'], $dbactive); + } +} + +// print file-delete-messages +if (is_array($del) && sizeof($del) > 0) { + foreach ($del as $file => $success) { + if ($success) { + $msg .= $lang['L_FM_DELETE1'] . ' \'' . $file . '\' '; + $msg .= $lang['L_FM_DELETE2']; + $msg = Html::getOkMsg($msg); + $log->write(Log::PHP, "Deleted '$file'."); + } else { + $msg .= $lang['L_FM_DELETE1'] . ' \'' . $file . '\' '; + $msg .= $lang['L_FM_DELETE3']; + $msg = Html::getErrorMsg($msg); + $log->write(Log::PHP, "Deleted '$file'."); + } + } +} diff --git a/inc/filemanagement/file_download.php b/inc/filemanagement/file_download.php new file mode 100644 index 0000000..5fb003d --- /dev/null +++ b/inc/filemanagement/file_download.php @@ -0,0 +1,41 @@ +' . $lang['L_FM_UPLOADFAILED'] + ); +} else { + if (!file_exists($config['paths']['backup'] . $_FILES['upfile']['name'])) { + $extension = strrchr($_FILES['upfile']['name'], '.'); + $allowedExtensions = array('.gz', '.sql', 'txt'); + if (!in_array($extension, $allowedExtensions)) { + $msg .= Html::getErrorMsg( + $lang['L_FM_UPLOADNOTALLOWED1'] . '
' + . $lang['L_FM_UPLOADNOTALLOWED2'] + ); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } else { + $upfile = $config['paths']['backup'] . $_FILES['upfile']['name']; + if (@move_uploaded_file($_FILES['upfile']['tmp_name'], $upfile)) { + @chmod($upfile, 0777); + $msg = Html::getOkMsg( + sprintf( + $lang['L_FILE_UPLOAD_SUCCESSFULL'], + $_FILES['upfile']['name'] + ) + ); + } else { + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADMOVEERROR']); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } + } + } else { + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFILEEXISTS']); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } +} diff --git a/inc/filemanagement/files.php b/inc/filemanagement/files.php new file mode 100644 index 0000000..6e0f085 --- /dev/null +++ b/inc/filemanagement/files.php @@ -0,0 +1,237 @@ +set_filenames(array('tplFiles' => 'tpl/filemanagement/files.tpl')); + +$dbactualOutput = $dbactive; +// replace internal keys for backups of other programs and converted files +// with human readable output +if ($dbactive == '~unknown') { + $dbactualOutput = '' . $lang['L_UNKNOWN'] . ''; +} +if ($dbactive == '~converted') { + $dbactualOutput = '' . $lang['L_CONVERTED_FILES'] . ''; +} +$autoDelete = $lang['L_NOT_ACTIVATED']; +if ($config['auto_delete']['activated'] > 0) { + $autoDelete = $lang['L_ACTIVATED'] . ' (' + . $config['auto_delete']['max_backup_files'] . ' ' + . $lang['L_MAX_BACKUP_FILES_EACH2'] . ')'; +} + +$tplFiles->assign_vars( + array( + 'BACKUP_PATH' => $config['paths']['backup'], + 'ICON_DOWNLOAD' => $icon['open_file'], + 'ICON_VIEW' => $icon['view'], + 'ICON_DELETE' => $icon['delete'], + 'DB_ACTUAL' => $dbactive, + 'DB_ACTUAL_OUTPUT' => $dbactualOutput, + 'UPLOAD_MAX_SIZE' => $config['upload_max_filesize'], + 'AUTODELETE_ENABLED' => $autoDelete, + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +if ($msg > '') { + $tplFiles->assign_block_vars( + 'MESSAGE', array('TEXT' => Html::getJsQuote($msg, true)) + ); +} +$backups = getBackupfileInfo(); +$i = 0; +if (!isset($backups['databases'][$dbactive])) { + $tplFiles->assign_block_vars('NO_FILE_FOUND', array()); +} +if ($dbactive != '~unknown' && $dbactive != '~converted') { + $tplFiles->assign_block_vars('DELETE_FILTER', array()); +} +// show detailed file info of the selected database at top +foreach ($backups['files'] as $backup) { + if ($backup['db'] == $dbactive) { + // get MySQL Version from which the backup was taken + $mysqlVersion = ''; + if (isset($backup['mysqlversion'])) { + $mysqlVersion = $backup['mysqlversion']; + } + // get grouping name of database + $dbName = $backup['name']; + if (!in_array($backup['db'], array('~unknown', '~converted'))) { + $dbName = $backup['db']; + } + // with which MSD-Version was the backup made? + $scriptVersion = $lang['L_UNKNOWN']; + if ($backup['script'] > '') { + $scriptVersion = $backup['script']; + } + // show Gzip-Icon? + $compressed = substr($backup['name'], -3) == '.gz' ? $icon['gz'] : '-'; + // is a commetn given? + $comment = ''; + if ($backup['comment'] > '') { + $comment = nl2br(wordwrap($backup['comment'], 50)); + } + // number of tables + $nrOfTables = $lang['L_UNKNOWN']; + if ($backup['tables'] > -1) { + $nrOfTables = String::formatNumber($backup['tables']); + } + // number of records + $nrOfRecords = $lang['L_UNKNOWN']; + if ($backup['records'] > -1) { + $nrOfRecords = String::formatNumber($backup['records']); + } + // charset of bakup file + $charset = $lang['L_UNKNOWN']; + if ($backup['charset'] != '?') { + $charset = $backup['charset']; + } + + $tplFiles->assign_block_vars( + 'FILE', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'FILE_INDEX' => $i, + // expand or unexpand multipart list on next click + 'FILE_EXPAND_INDEX' => $expand == $i ? -1 : $i, + 'FILE_NAME' => $backup['name'], + 'FILE_NAME_URLENCODED' => urlencode($backup['name']), + 'DB_NAME' => $dbName, + 'ICON_COMPRESSED' => $compressed, + 'SCRIPT_VERSION' => $scriptVersion, + 'COMMENT' => $comment, + 'FILE_CREATION_DATE' => $backup['date'], + 'NR_OF_TABLES' => $nrOfTables, + 'NR_OF_RECORDS' => $nrOfRecords, + 'FILESIZE' => byteOutput($backup['size']), + 'FILE_CHARSET' => $charset, + 'NR_OF_MULTIPARTS' => $backup['multipart'], + 'MYSQL_VERSION' => $mysqlVersion + ) + ); + + if ($backup['multipart'] > 0) { + $mpFileHeadline = $lang['L_FILES']; + if ($backup['multipart'] == 1) { + $mpFileHeadline = $lang['L_FILE']; + } + $tplFiles->assign_block_vars( + 'FILE.IS_MULTIPART', array('FILES' => $mpFileHeadline) + ); + } else { + $tplFiles->assign_block_vars('FILE.NO_MULTIPART', array()); + } + if ($backup['multipart'] > 0) { + // show all files of a Multipart-backup + $tplFiles->assign_block_vars( + 'FILE.EXPAND_MULTIPART', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow1' : 'dbrow' + ) + ); + // expand multipartlist if click came from restore screen + if ($expand == $i) { + $tplFiles->assign_block_vars( + 'EXPAND_MP_FILE', array('FILEINDEX' => $i) + ); + } + + $partPosition = strrpos($backup['name'], 'part_'); + $fileBase = substr($backup['name'], 0, $partPosition) . 'part_'; + $fileExtension = '.sql'; + if (substr($backup['name'], -2) == 'gz') { + $fileExtension = '.sql.gz'; + } + for ($x = 0; $x < $backup['multipart']; $x++) { + $fileName = $fileBase . ($x + 1) . $fileExtension; + $fileSize = $lang['L_UNKNOWN']; + $file = $config['paths']['backup'] . $fileName; + if (!is_readable($file)) { + $fileName = $backup['name']; + $fileSize = byteOutput(@filesize($file)); + } + $tplFiles->assign_block_vars( + 'FILE.EXPAND_MULTIPART.MP_FILE', + array( + 'ROWCLASS' => $x % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $x + 1, + 'FILE_NAME' => $fileName, + 'FILE_NAME_URLENCODED' => urlencode($fileName), + 'FILE_SIZE' => $fileSize + ) + ); + } + } + $i++; + } +} + +//sort databases according to the databasenames ascending +ksort($backups['databases']); +// list summary of other backup files grouped by databases +if (count($backups['databases']) > 0) { + $i = 0; + foreach ($backups['databases'] as $db => $info) { + $rowclass = $i % 2 ? 'dbrow' : 'dbrow1'; + if ($db == $dbactive) { + $rowclass = 'dbrowsel'; + } + $dbNameOutput = $db; + if ($db == '~unknown') { + $dbNameOutput = '' . $lang['L_NO_MSD_BACKUPFILE'] . ''; + } + if ($db == '~converted') { + $dbNameOutput = '' . $lang['L_CONVERTED_FILES'] . ''; + } + + $nrOfBackups = isset($info['backup_count']) ? $info['backup_count'] : 0; + $latestBackup = '-'; + if (isset($info['latest_backup_timestamp'])) { + $latestBackup = $info['latest_backup_timestamp']; + } + $fileSizeTotal = 0; + if (isset($info['backup_size_total'])) { + $fileSizeTotal = byteOutput($info['backup_size_total']); + } + + $tplFiles->assign_block_vars( + 'DB', + array( + 'ROWCLASS' => $rowclass, + 'DB_NAME_LINK' => $db, + 'DB_NAME' => $dbNameOutput, + 'NR_OF_BACKUPS' => $nrOfBackups, + 'LATEST_BACKUP' => $latestBackup, + 'SUM_SIZE' => $fileSizeTotal + ) + ); + $i++; + } +} + +$tplFiles->assign_vars( + array( + 'SUM_SIZE' => byteOutput($backups['filesize_total']), + 'FREESPACE_ON_SERVER' => getFreeDiskSpace() + ) +); diff --git a/inc/files.php b/inc/files.php new file mode 100644 index 0000000..aea505e --- /dev/null +++ b/inc/files.php @@ -0,0 +1,37 @@ += 1024; $level++) { + $bytes /= 1024; + } + switch ($level) + { + case 0: + $suffix = 'B'; + break; + case 1: + $suffix = 'KB'; + break; + case 2: + $suffix = 'MB'; + break; + case 3: + $suffix = 'GB'; + break; + case 4: + $suffix = 'TB'; + break; + case 5: + $suffix = 'PB'; + break; + case 6: + $suffix = 'EB'; + break; + case 7: + $suffix = 'ZB'; + break; + + default: + $suffix = ''; + break; + } + if (!$useHTML) { + $suffix = strip_tags($suffix); + } + $ret = sprintf("%01." . $precision . "f", round($bytes, $precision)); + return $ret . ' ' . $suffix; +} + +/** + * Delete Multipart files + * + * @param string $dir Directory to check + * @param string $prefix Only check files with this prefix + * @param string $suffix Only check files with this suffix + * + * @return array Array $file['filename']=true | false + */ +function deleteMultipartFiles($dir, $prefix = '', $suffix = '') +{ + $deleted = array(); + if (substr($dir, -1) != '/') { + $dir .= '/'; + } + if (is_dir($dir)) { + $d = opendir($dir); + while ($file = readdir($d)) { + if (is_file($dir . $file)) { + $del = false; + if ($prefix > '' && substr($file, 0, strlen($prefix)) == $prefix) $del = true; + if ($suffix > '' && substr($file, strlen($file) - strlen($suffix), strlen($suffix)) == $suffix) $del = true; + if ($del) + { + if (unlink($dir . $file)) $deleted[$file] = true; + else $deleted[$file] = false; + } + } + } + closedir($d); + return $deleted; + } +} + +/** + * Add a database to the global $databases-Array and set necessary indexes + * + * @param string $db_name Databaase-Name + * @param string $praefix Table-Prefix + * @param string $cbd Command before Dump + * @param string $cad Command after Dump + * + * @return void + */ +function addDatabaseToConfig($db_name) +{ + global $databases; + if (!isset($databases)) $databases = array(); + if (!isset($databases[$db_name])) $databases[$db_name] = array(); + if (!isset($databases[$db_name]['dump'])) $databases[$db_name]['dump'] = 0; + if (!isset($databases[$db_name]['prefix'])) $databases[$db_name]['prefix'] = ''; + if (!isset($databases[$db_name]['command_before_dump'])) $databases[$db_name]['command_before_dump'] = ''; + if (!isset($databases[$db_name]['command_after_dump'])) $databases[$db_name]['command_after_dump'] = ''; +} + +/** + * Check settings of language, config file, reloads list of databases and save result to configuration + * + * @return string String with checked and added databases + */ +function setDefaultConfig() +{ + global $config, $databases, $out, $lang, $dbo; + + // check language and fallback to englisch if language file is not readable + $lang_file = './language/' . $config['language'] . '/lang.php'; + if (!file_exists($lang_file) || !is_readable($lang_file)) + { + $config['language'] = 'en'; + } + include ('./language/' . $config['language'] . '/lang.php'); + + getConfig($config['config_file']); // falls back to config mysqldumper if something is wrong + // get list of databases for this user + $dbUser = $dbo->getDatabases(); + foreach ($dbUser as $db) + { + // new found db? -> add it + if (!isset($databases[$db])) $databases[$db] = array(); + } + ksort($databases); + foreach ($databases as $db_name => $val) + { + if ($dbo->selectDb($db_name, true)) + { + addDatabaseToConfig($db_name); + $out .= $lang['L_SAVING_DB_FORM'] . " " . $db_name . " " . $lang['L_ADDED'] . "\n"; + } + else + unset($databases[$db_name]); + } + saveConfig(); + return $out; +} + +/** + * Save actual configuration to file + * + * @return boolean + */ +function saveConfig() +{ + global $config, $databases, $configDontsave; + + $config['multipart_groesse'] = $config['multipartgroesse1'] * (($config['multipartgroesse2'] == 1) ? 1024 : 1024 * 1024); + if (!isset($config['email_maxsize'])) $config['email_maxsize'] = $config['email_maxsize1'] * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (!isset($config['cron_execution_path'])) $config['cron_execution_path'] = "msd_cron/"; + + $config2 = $config; + foreach ($config2 as $var => $val) + { + if (in_array($var, $configDontsave)) unset($config2[$var]); + } + + $t = '$config=array_merge($config,unserialize(base64_decode(\'' . base64_encode(serialize($config2)) . "')));\r\n"; + if (isset($databases)) $t .= '$databases=array_merge($databases,unserialize(base64_decode(\'' . base64_encode(serialize($databases)) . "')));\r\n"; + + $pars_all = '"; + + $ret = true; + $file = './' . $config['paths']['config'] . $config['config_file'] . '.php'; + if ($fp = @fopen($file, "wb")) + { + if (!fwrite($fp, $pars_all)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod($file, 0777); + } + else + $ret = false; + if ($ret) + { + $_SESSION['config'] = $config; + $ret = writeCronScript(); + } + return $ret; +} + + +/** + * Build string of array according to Perl syntax (needed to save Perl configuration file) + * + * @param array $arr Array to build the string from + * @param string $mode 0 for strings, 1 for int values + * + * @return string The converted Perl string + */ +function myImplode($arr, $mode = 0) // 0=String, 1=intval +{ + if (!is_array($arr)) return false; + foreach ($arr as $key => $val) + { + if ($mode == 0) $arr[$key] = Html::escapeSpecialchars($val); + else $arr[$key] = intval($val); + } + if ($mode == 0) $ret = '("' . implode('","', $arr) . '")'; + else $ret = '(' . implode(',', $arr) . ')'; + return $ret; +} + +/** + * Build and save the actual configuration file for Perl (used by crondump.pl) + * + * @return boolean true on success or false on failure + */ +function writeCronScript() +{ + global $config, $databases, $cron_db_array, $cron_dbpraefix_array, $cron_db_cbd_array, $cron_db_cad_array; + if (!isset($config['email_maxsize'])) $config['email_maxsize'] = $config['email_maxsize1'] * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (isset($config['db_actual'])) $cron_dbname = $config['db_actual']; + else + { + //get first database name from database-array (this is the case at fresh installing) + $dbs = array_keys($databases); + $cron_dbname = $dbs[0]; + } + // -2 = Multidump configuration + // -3 = all databases - nothing to do + // get standard values for all databases + $cron_db_array = ''; //$databases['Name']; + $cron_dbpraefix_array = ''; //$databases['praefix']; + $cron_command_before_dump = ''; //$databases['command_before_dump']; + $cron_command_after_dump = ''; //$databases['command_after_dump']; + + + $cron_ftp_server = ''; + $cron_ftp_port = ''; + $cron_ftp_mode = ''; + $cron_ftp_user = ''; + $cron_ftp_pass = ''; + $cron_ftp_dir = ''; + $cron_ftp_timeout = ''; + $cron_ftp_ssl = ''; + $cron_ftp_transfer = ''; + + //build db-arrays + foreach ($databases as $k => $v) + { + //should we dump this database + if (isset($databases[$k]['dump']) && $databases[$k]['dump'] === 1) + { + $cron_db_array[] .= $k; + $cron_dbpraefix_array[] .= $databases[$k]['prefix']; + $cron_command_before_dump[] .= $databases[$k]['command_before_dump']; + $cron_command_after_dump[] .= $databases[$k]['command_after_dump']; + } + } + + //build ftp-arrays + foreach ($config['ftp'] as $k => $v) + { + $cron_ftp_server[] .= $config['ftp'][$k]['server']; + $cron_ftp_port[] .= $config['ftp'][$k]['port']; + $cron_ftp_mode[] .= $config['ftp'][$k]['mode']; + $cron_ftp_user[] .= $config['ftp'][$k]['user']; + $cron_ftp_pass[] .= $config['ftp'][$k]['pass']; + $cron_ftp_dir[] .= $config['ftp'][$k]['dir']; + $cron_ftp_timeout[] .= $config['ftp'][$k]['timeout']; + $cron_ftp_ssl[] .= $config['ftp'][$k]['ssl']; + $cron_ftp_transfer[] .= $config['ftp'][$k]['transfer']; + } + + $r = str_replace("\\\\", "/", $config['paths']['root']); + $r = str_replace("@", "\@", $r); + + //built recipient_cc-arrays + $recipients_cc = ''; + foreach ($config['email']['recipient_cc'] as $k => $v) + { + $recipients_cc .= '"' . $config['email']['recipient_cc'][$k]['name'] . '" <' . $config['email']['recipient_cc'][$k]['address'] . '>, '; + } + $recipients_cc = substr($recipients_cc, 0, -2); + + // auf manchen Server wird statt 0 ein leerer String gespeichert -> fuehrt zu einem Syntax-Fehler + // hier die entsprechenden Ja/Nein-Variablen sicherheitshalber in intvalues aendern + $int_array = array( + 'dbport', + 'cron_compression', + 'cron_printout', + 'multi_part', + 'multipart_groesse', + 'email_maxsize', + //'auto_delete][activated', + //'auto_delete][max_backup_files', + 'perlspeed', + 'optimize_tables_beforedump', + 'logcompression', + 'log_maxsize', + 'cron_completelog', + 'use_sendmail', + 'smtp_port', + 'smtp_useauth', + 'smtp_usessl'); + + foreach ($int_array as $i) + { + if (is_array($i)) + { + foreach ($i as $key => $val) + { + $int_array[$key] = intval($val); + } + } + else + { + if (!isset($config[$i])) $config[$i] = 0; + $config[$i] = intval($config[$i]); + } + } + if ($config['dbport'] == 0) $config['dbport'] = 3306; + + $cronscript = "') . '";' . "\n"; + $cronscript .= '$cronmailto_cc="' . Html::escapeSpecialchars($recipients_cc) . '";' . "\n"; + $cronscript .= '$cronmailfrom="' . Html::escapeSpecialchars('"' . $config['email']['sender_name'] . '" <' . $config['email']['sender_address'] . '>') . '";' . "\n"; + $cronscript .= '$cron_use_sendmail=' . $config['use_sendmail'] . ';' . "\n"; + $cronscript .= '$cron_smtp="' . Html::escapeSpecialchars($config['smtp_server']) . '";' . "\n"; + $cronscript .= '$smtp_port=' . $config['smtp_port'] . ';' . "\n"; + $cronscript .= '$smtp_useauth=' . $config['smtp_useauth'] . ';' . "\n"; + $cronscript .= '$smtp_usessl=' . $config['smtp_usessl'] . ';' . "\n"; + $cronscript .= '$smtp_user="' . $config['smtp_user'] . '";' . "\n"; + $cronscript .= '$smtp_pass="' . $config['smtp_pass'] . '";' . "\n"; + $cronscript .= '@cron_db_array=' . myImplode($cron_db_array) . ';' . "\n"; + $cronscript .= '@cron_dbpraefix_array=' . myImplode($cron_dbpraefix_array) . ';' . "\n"; + $cronscript .= '@cron_command_before_dump=' . myImplode($cron_command_before_dump) . ';' . "\n"; + $cronscript .= '@cron_command_after_dump=' . myImplode($cron_command_after_dump) . ';' . "\n"; + + $cronscript .= '@ftp_server=' . myImplode($cron_ftp_server) . ';' . "\n"; + $cronscript .= '@ftp_port=' . myImplode($cron_ftp_port, 1) . ';' . "\n"; + $cronscript .= '@ftp_mode=' . myImplode($cron_ftp_mode, 1) . ';' . "\n"; + $cronscript .= '@ftp_user=' . myImplode($cron_ftp_user) . ';' . "\n"; + $cronscript .= '@ftp_pass=' . myImplode($cron_ftp_pass) . ';' . "\n"; + $cronscript .= '@ftp_dir=' . myImplode($cron_ftp_dir) . ';' . "\n"; + $cronscript .= '@ftp_timeout=' . myImplode($cron_ftp_timeout, 1) . ';' . "\n"; + $cronscript .= '@ftp_useSSL=' . myImplode($cron_ftp_ssl, 1) . ';' . "\n"; + $cronscript .= '@ftp_transfer=' . myImplode($cron_ftp_transfer, 1) . ';' . "\n"; + + $cronscript .= '$mp=' . $config['multi_part'] . ';' . "\n"; + $cronscript .= '$multipart_groesse=' . $config['multipart_groesse'] . ';' . "\n"; + $cronscript .= '$email_maxsize=' . $config['email_maxsize'] . ';' . "\n"; + $cronscript .= '$auto_delete=' . $config['auto_delete']['activated'] . ';' . "\n"; + $cronscript .= '$max_backup_files=' . $config['auto_delete']['max_backup_files'] . ';' . "\n"; + $cronscript .= '$perlspeed=' . $config['perlspeed'] . ';' . "\n"; + $cronscript .= '$optimize_tables_beforedump=' . $config['optimize_tables_beforedump'] . ';' . "\n"; + $cronscript .= '$logcompression=' . $config['logcompression'] . ';' . "\n"; + + //add .gz to logfiles? + if ($config['logcompression'] === 1) + { + $cronscript .= '$logdatei="' . $config['paths']['root'] . $config['files']['perllog'] . '.gz";' . "\n"; + $cronscript .= '$completelogdatei="' . $config['paths']['root'] . $config['files']['perllogcomplete'] . '.gz";' . "\n"; + } + else + { + $cronscript .= '$logdatei="' . $config['paths']['root'] . $config['files']['perllog'] . '";' . "\n"; + $cronscript .= '$completelogdatei="' . $config['paths']['root'] . $config['files']['perllogcomplete'] . '";' . "\n"; + } + $cronscript .= '$log_maxsize=' . $config['log_maxsize'] . ';' . "\n"; + $cronscript .= '$complete_log=' . $config['cron_completelog'] . ';' . "\n"; + $cronscript .= '$cron_comment="' . Html::escapeSpecialchars(stripslashes($config['cron_comment'])) . '";' . "\n"; + $cronscript .= "?>"; + + // Save config + $ret = true; + $sfile = './' . $config['paths']['config'] . $config['config_file'] . '.conf.php'; + if (file_exists($sfile)) @unlink($sfile); + + if ($fp = @fopen($sfile, "wb")) + { + if (!fwrite($fp, $cronscript)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod("$sfile", 0777); + } + else + $ret = false; + + // if standard config was deleted -> restore it with the actual values + if (!file_exists('./' . $config['paths']['config'] . "mysqldumper.conf.php")) + { + $sfile = './' . $config['paths']['config'] . 'mysqldumper.conf.php'; + if ($fp = fopen($sfile, "wb")) + { + if (!fwrite($fp, $cronscript)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod("$sfile", 0777); + } + else + $ret = false; + } + return $ret; +} + +/** + * Collect log file information and return it as assoziative array + * + * @param boolean $logcompression Watch for compressed (true) or uncompressed files + * + * @return array Associative array with information + */ +function getLogFileInfo($logcompression) +{ + global $config; + + $l = Array(); + $sum = $s = $l['log_size'] = $l['perllog_size'] = $l['perllogcomplete_size'] = $l['errorlog_size'] = $l['log_totalsize'] = 0; + if ($logcompression == 1) + { + $l['log'] = $config['files']['log'] . ".gz"; + $l['perllog'] = $config['files']['perllog'] . ".gz"; + $l['perllogcomplete'] = $config['files']['perllogcomplete'] . ".gz"; + $l['errorlog'] = $config['paths']['log'] . "error.log.gz"; + } + else + { + $l['log'] = $config['files']['log']; + $l['perllog'] = $config['files']['perllog']; + $l['perllogcomplete'] = $config['files']['perllogcomplete']; + $l['errorlog'] = $config['paths']['log'] . "error.log"; + } + $l['log_size'] += @filesize($l['log']); + $sum += $l['log_size']; + $l['perllog_size'] += @filesize($l['perllog']); + $sum += $l['perllog_size']; + $l['perllogcomplete_size'] += @filesize($l['perllogcomplete']); + $sum += $l['perllogcomplete_size']; + $l['errorlog_size'] += @filesize($l['errorlog']); + $sum += $l['errorlog_size']; + $l['log_totalsize'] += $sum; + + return $l; +} + +/** + * Delete log file and recreates it. + * + * @return void + */ +function deleteLog() +{ + global $config; + $log = date('d.m.Y H:i:s') . " Log created.\n"; + if (file_exists($config['files']['log'] . '.gz')) @unlink($config['files']['log'] . '.gz'); + if (file_exists($config['files']['log'] . '.gz')) @unlink($config['files']['log']); + if ($config['logcompression'] == 1) + { + $fp = @gzopen($config['files']['log'] . '.gz', "wb"); + @gzwrite($fp, $log); + @gzclose($fp); + @chmod($config['files']['log'] . '.gz', 0777); + } + else + { + $fp = @fopen($config['files']['log'], "wb"); + @fwrite($fp, $log); + @fclose($fp); + @chmod($config['files']['log'], 0777); + } +} + +/** + * Detect accessable databases for the current SQL-User in $config-array and returns output-string with all dbs + * Additionally it adds all found databases in the global var $databases + * + * @param boolean $printout Wether to return the output string or not + * @param string $db Optional name of a database to add manually + * + * @return string Output string containing all found dbs + */ +function searchDatabases($printout = 0, $db = '') +{ + global $config, $lang, $dbo, $databases; + $databases = array(); + $ret = ''; + $db_list = $dbo->getDatabases(); + // add manual added db to array, but only if it was not detected before + if ($db > '' && !in_array($db, $db_list)) $db_list[] = $db; + // now check if we can select the db - if not, we can't access the database + if (sizeof($db_list) > 0) + { + foreach ($db_list as $db) + { + $res = $dbo->selectDb($db, true); + + if ($res === true) + { + addDatabaseToConfig($db); + if ($printout == 1) $ret .= Html::getOkMsg($lang['L_FOUND_DB'] . ' `' . $db); + } elseif ($printout == 1) { + $ret .= Html::getErrorMsg($lang['L_ERROR'].' : '.$res); + } + } + } + return $ret; +} + +/** + * realpath implementation working on any server + * + * @return $dir string The application path + */ +function myRealpath() +{ + $dir = dirname(__FILE__); + $dir = str_replace('\\', '/', $dir); + $dir = str_replace('//', '/', $dir); + if (substr($dir, -14) == '/inc/functions') $dir = substr($dir, 0, -13); + if (substr($dir, -1) != '/') $dir .= '/'; + return $dir; +} + +/** + * Remove tags recursivly from array or from string + * + * @param $value string | array Value/s to strip + * + * @return string|array Cleaned values + */ +function myStripTags($value) +{ + global $dont_strip; + if (is_array($value)) + { + foreach ($value as $key => $val) + { + if (!in_array($key, $dont_strip)) $ret[$key] = myStripTags($val); + else $ret[$key] = $val; + } + } + else + $ret = trim(strip_tags($value)); + return $ret; +} + +/** + * First start output buffering then start SESSION + * Reads configuration and main-language file lang.php and creates + * Database-Object $dbo + * + * @param string $json Return JSON-Encoded answer + * @param string $send_header If set to false headers are completely skipped + * @return void + */ +function obstart($json = false, $send_header = true) +{ + global $dbo, $config, $databases, $dump, $lang; + if ($config['ob_gzhandler']) + { + if (!@ob_start("ob_gzhandler")) @ob_start(); + } + + // if default config file doesn't exists, it is a new installation -> redirect to installation + if (!$json && !file_exists('./work/config/mysqldumper.php')) + { + header("location: install.php"); + die(); + exit(); + } + + session_name('MySQLDumper'); + $res = session_start(); + if (false === $res) die("Error starting session! Check server."); + if (isset($_SESSION['config_file'])) $config['config_file'] = $_SESSION['config_file']; + else $config['config_file'] = 'mysqldumper'; + if ($send_header) + { + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); // HTTP/1.1 + header('Expires: -1'); // Datum in der Vergangenheit + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s") . ' GMT'); + if (!$json) header('Content-Type: text/html; charset=UTF-8'); + else header('Content-type: application/x-json'); + } + // get config from configuration file if not set + if (!isset($_SESSION['config'])) getConfig($config['config_file']); + else + { + // otherwise get parameters from session + $config = array_merge($config, $_SESSION['config']); + if (isset($_SESSION['databases'])) $databases = $_SESSION['databases']; + if (isset($_SESSION['dump'])) $dump = $_SESSION['dump']; + } + // special case -> configuration is set to a language that was deleted meanwhile + if (!is_readable('./language/' . $config['language'] . '/lang.php')) $config['language'] = 'en'; + + include ('./language/' . $config['language'] . '/lang.php'); + // create database object + $dbo = MsdDbFactory::getAdapter($config['dbhost'], $config['dbuser'], $config['dbpass'], $config['dbport'], $config['dbsocket']); + if (!isset($_SESSION['databases'])) setDefaultConfig(); +//echo $config['db_actual']; +// die(); + if (isset($config['db_actual']) && $config['db_actual'] > '') $dbo->selectDb($config['db_actual']); + else + { + if (!isset($databases)) { + // no config loaded -> SetDefault-Values + setDefaultConfig(); + } + // get first DB-Name and set as actual db + $dbNames = array_keys($databases); + $config['db_actual'] = $dbNames[0]; + $dbo->selectDb($config['db_actual']); + } + //$_SESSION['config'] = $config; + //$_SESSION['databases'] = $databases; +} + +/** + * Add end of body/html, end output buffering and output the buffer + * + * @param boolean $ajax + * @return void + */ +function obend($ajax = false) +{ + global $config, $databases; + $_SESSION['databases'] = $databases; + $_SESSION['config'] = $config; + if (!$ajax) { + echo '' . "\n\n" . '' . "\n\n" . ''; + // close HTML-page + } + @ob_end_flush(); +} + +/** + * Extract unique prefixes from an array + * + * and return new array containing the different prefixes + * + * @param array $array Array to scan for prefixes + * @param boolean $addNoneOption Wether to add a first entry '---' + * + * @return $prefix_array array The array conatining the unique prefixes + */ +function getPrefixArray($array, $addNoneOption = true) +{ + $prefixes = array(); + $keys = array_keys($array); + foreach ($keys as $k) { + $pos = strpos($k, '_'); // find '_' + if ($pos !== false) { + $prefix = substr($k, 0, $pos); + if (!in_array($prefix, $prefixes)) { + $prefixes[$prefix] = $prefix; + } + } + } + if ($addNoneOption) { + $prefixes['-1'] = '---'; + } + ksort($prefixes); + return $prefixes; +} + +/** + * Implode the given keys of multidimensional array using the implode string + * + * @param array $array The array to implode + * @param string $key The values that should be imploded + * @param string $implodeString The string to concatenate the values with + * + * @return string The impoloded valuies as string + */ +function implodeSubarray($array, $key, $implodeString = ', ') +{ + $ret = ''; + foreach ($array as $k => $v) { + $ret .= $v[$key] . $implodeString; + } + if (strlen($ret) > 1) { + $ret = substr($ret, 0, -(strlen($implodeString))); + } + return $ret; +} + diff --git a/inc/functions/functions_dump.php b/inc/functions/functions_dump.php new file mode 100644 index 0000000..d8f9ec1 --- /dev/null +++ b/inc/functions/functions_dump.php @@ -0,0 +1,668 @@ +write( + Log::PHP, + sprintf( + $lang['L_SAVING_DATA_TO_FILE'], + $dump['db_actual'], + $dump['backupdatei'] + ) + ); + if ($dump['part'] == 1) { + $dump['table_offset'] = 0; + $dump['countdata'] = 0; + } + // Seitenerstaufruf -> Backupdatei anlegen + $dump['data'] = $statuszeile . '-- Dump created: ' . $curTime; + } else { + if ($config['multi_part'] != 0) { + $log->write( + Log::PHP, + sprintf( + $lang['L_SAVING_DATA_TO_MULTIPART_FILE'], + $dump['backupdatei'] + ) + ); + $dump['data'] = $statuszeile . '-- ' . ' This is part ' + . ($dump['part'] - $dump['part_offset']) . ' of the backup.' + . "\n\n" . $dump['data']; + } + } + writeToDumpFile(); + $dump['part']++; +} + +/** + * Creates the first statusline with information for backup files + * + * @return string + */ +function getStatusLine() +{ + // strcuture of status line: + // -- Status:nrOfTables:nrOfRecords:Multipart:database:script: + // scriptversion:comment:MySQL-Version:Backupflags(unused):SQLBefore: + //SQLAfter:Charset:CharsetEXTINFO + + global $databases, $config, $dump; + if (!defined('MSD_MYSQL_VERSION')) { + GetMySQLVersion(); + } + $tline = "-- \n-- TABLE-INFO\r\n"; + + foreach ($dump['databases'][$dump['db_actual']]['tables'] + as $tableName => $val) { + $tline .= "-- TABLE|" . $tableName . '|' . $val['records'] + . '|' . $val['data_length'] . '|' . $val['update_time'] . '|' + . $val['engine'] . "\n"; + } + $flags = 1; + $mp = 'MP_0'; + if ($config['multi_part'] == 1) { + $mp = "MP_" . ($dump['part'] - $dump['part_offset']); + } + $statusline = "-- Status:" + . $dump['databases'][$dump['db_actual']]['table_count'] + . ':' . $dump['databases'][$dump['db_actual']]['records_total'] + . ":$mp:" . $dump['db_actual'] . ":PHP:" . MSD_VERSION . ":" + . $dump['comment'] . ":"; + $statusline .= MSD_MYSQL_VERSION . ":$flags:::" . $dump['dump_encoding'] + . ":EXTINFO\n" . $tline . "-- " . "EOF TABLE-INFO\n-- "; + return $statusline; +} + +/** + * Build the DROP and CREATE TABLE string for dump file. + * + * Parameter $withdata decides if we should add the + * "ALTER TABLE DISABLE KEYS"-query. + * + * @param string $db The database + * @param string $table The table + * @param integer $withdata Add DISABLE KEYS + * @return string $def Created Query-string or false on error + */ +function getCreateString($db, $table, $withdata = 1) +{ + global $dbo, $config, $dump, $lang, $log; + + $def = "\n\n--\n-- Table structure for table `$table`\n--\n"; + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['engine'] + == 'VIEW') { + $def .= "DROP VIEW IF EXISTS `$table`;\n"; + $withdata = 0; + } else { + $def .= "DROP TABLE IF EXISTS `$table`;\n"; + } + $createStatement = $dbo->getTableCreate($table, $db); + $def .= $createStatement . ';' . "\n\n"; + if ($withdata == 1) { + $def .= "--\n-- Dumping data for table `$table`\n--\n"; + $def .= "/*!40000 ALTER TABLE `$table` DISABLE KEYS */;\n"; + } + $log->write(Log::PHP, sprintf($lang['L_CREATE_TABLE_SAVED'], $table)); + return $def; +} + +/** + * Read records from table, build query-strings and write them to dump file + * + * @param string $db The database to read from + * @param string $table The table to read from + * @return void + */ +function getContent($db, $table) +{ + global $dbo, $config, $dump, $lang, $log; + + $content = ''; + $fields = $dbo->getTableColumns($table, $db); + // TODO decide if the type of field needs to be escaped and placed between quotes + // also handle NULL-values very strict for MySQL-servers running with sql-mod=STRICT + $fieldNames = array_keys($fields); + $fieldList = '`' . implode('`,`', $fieldNames) . '`'; + // indicator if the actual table is fully dumped in this call + $tableDone = 0; + $sql = 'SELECT * FROM `' . $db . '`.`' . $table . '` LIMIT ' + . $dump['table_record_offset'] . ',' . ($dump['restzeilen'] + 1); + $result = $dbo->query($sql, MsdDbFactory::ARRAY_NUMERIC); + $numRows = @count($result); + if ($numRows > 0) { + // we've got records - get fields + $numfields = count($result[0]); + if ($numRows > $dump['restzeilen']) { + // there are more records to get - table is not fully dumped + $dump['table_record_offset'] += $dump['restzeilen']; //set table record offset for next call + $numRows--; // correct counter - we only used the last record to find out if there is more to fetch + unset($result[$numRows]); + } else { + // table is done -> increase table offset + $recordsSaved = $dump['table_record_offset'] + $numRows; + $log->write( + Log::PHP, + sprintf( + $lang['L_BACKUP_TABLE_DONE'], + $table, + String::formatNumber($recordsSaved) + ) + ); + $dump['table_offset']++; + $dump['table_offset_total']++; + $dump['table_record_offset'] = 0; + $tableDone = 1; + } + foreach ($result as $row) { + //if($config['backup_using_updates']==1){ + $insert = 'INSERT INTO `' . $table . '` (' . $fieldList + . ') VALUES ('; + //TODO implement REPLACE INTO for expert mode + // } + //else{ + //$insert='REPLACE INTO `'.$table.'` '.$complete.' VALUES ('; + // } + + foreach ($row as $field => $val) { + if ($val != '') $insert .= '\'' . $dbo->escape($val) . '\','; + else $insert .= '\'\','; + } + $insert = substr($insert, 0, -1) . ');' . "\n"; + $dump['data'] .= $insert; + $dump['restzeilen']--; + $dump['countdata']++; + if (strlen($dump['data']) > $config['memory_limit'] + || ($config['multi_part'] == 1 + && strlen($dump['data']) + MULTIPART_FILESIZE_BUFFER + > $config['multipart_groesse'])) { + writeToDumpFile(); + } + } + if ($tableDone == 1) { + // check if records have been saved and add "enable keys" + $tables = $dump['databases'][$dump['db_actual']]['tables']; + if ($tables[$table]['dump_records'] == 1) { + $dump['data'] .= "/*!40000 ALTER TABLE `$table`" + ." ENABLE KEYS */;"; + } + } + } else { + // table corrupt -> skip it + $dump['table_offset']++; + $dump['table_offset_total']++; + $dump['table_record_offset'] = 0; + $dump['restzeilen'] = $dump['restzeilen'] - $numRows; + $dump['data'] .= "/*!40000 ALTER TABLE `$table` ENABLE KEYS */;\n"; + if (strlen($dump['data']) > $config['memory_limit'] + || ($config['multi_part'] == 1 && strlen($dump['data']) + + MULTIPART_FILESIZE_BUFFER > $config['multipart_groesse'])) { + writeToDumpFile(); + } + } +} + +/** + * Saves the created data of global var $dump['data'] to the dump file. + * + * If Multipart is used and the maximum filesize is reached a new file is + * created. Sets global var $dump['filesize'] to new vaule for printing + * on sccreen. + * + * @return void + */ +function writeToDumpFile() +{ + global $config, $dump; + $file = $config['paths']['backup'] . $dump['backupdatei']; + + if ($config['compression'] == 1) { + if ($dump['data'] != '') { + $fp = gzopen($file, 'ab'); + gzwrite($fp, $dump['data']); + gzclose($fp); + } + } else { + if ($dump['data'] != '') { + $fp = fopen($file, 'ab'); + fwrite($fp, $dump['data']); + fclose($fp); + } + } + $dump['data'] = ''; + clearstatcache(); + $dump['filesize'] = intval(@filesize($file)); + // if Multipart is used and maximum filesize is reached -> create new file + if ($config['multi_part'] == 1) { + if ($dump['filesize'] + MULTIPART_FILESIZE_BUFFER + > $config['multipart_groesse']) { + @chmod($file, 0777); + createNewFile(); + } + } +} + +/** + * Checks if there is a next db to be dumped + * + * Sets the global flag $dump['backup_done'] + * + * @return void + */ +function checkForNextDB() +{ + global $dump; + // a check, if another db should be saved is at the end of the script + // backup of actual db is done -> lets check if there is more to do + $nextDb = getNextKey($dump['databases'], $dump['db_actual']); + if ($nextDb !== false) { + $dump['backup_done'] = 0; + //-1 instead of 0 is needed for the execution of command before backup + $dump['table_offset'] = -1; + $dump['db_actual'] = $nextDb; + $dump['part_offset'] = $dump['part'] - 1; + } else { + $dump['backup_done'] = 1; + $dump['table_offset_total']--; + } +} + +/** + * Execute queries before and after the backup process + * + * Queries are saved in the configuration profile + * + * @param string $when Before (b) or after backup process + * + * @return void + */ +function executeCommand($when) +{ + // TODO implement execution of command before/after backup + return; +} + +/** + * Send e-mail and attach file + * + * @param string $file + * @return boolean + */ +function doEmail($file) +{ + global $config, $dump, $lang, $log; + include ('lib/phpmailer/php5/class.phpmailer.php'); + include ('inc/classes/helper/Html.php'); + // get some status info from actual file + $rootpath = $config['paths']['root'] . $config['paths']['backup']; + $fileInfo = ReadStatusline($file); + $fileInfo['size'] = @filesize($rootpath . $file); + $database = $fileInfo['dbname']; + $tablesSaved = $fileInfo['tables']; + $recordsSaved = $fileInfo['tables']; + + if (sizeof($_SESSION['email']['filelist']) == 0) { + // first call after backup -> create file list of all files for each database + $_SESSION['email']['filelist'] = array(); + foreach ($_SESSION['log']['email'] as $filename) { + $statusInfo = ReadStatusline($filename); + if (!isset($_SESSION['email']['filelist'][$statusInfo['dbname']])) { + $_SESSION['email']['filelist'][$statusInfo['dbname']] = array(); + } + $_SESSION['email']['filelist'][$statusInfo['dbname']][] = $filename; + } + } + // create file list for specific database + $filelist = ''; + foreach ($_SESSION['email']['filelist'][$database] as $filename) { + $phpSelf = $_SERVER['PHP_SELF']; + $linkToFile = '' + . $filename . ''; + $filelist .= $linkToFile; + if ($file == $filename && $config['email']['attach_backup']) { + $filelist .= ' (' . $lang['L_ATTACHED_AS_FILE'] . ')'; + } + $filelist .= '
' . "\n"; + } + + $mail = new PHPMailer(); + $mail->CharSet = 'utf-8'; + $mail->PlugInDir = 'lib/phpmailer/php5/'; + $mail->From = $config['email']['sender_address']; + $mail->FromName = $config['email']['sender_name']; + $mail->AddAddress($config['email']['recipient_address'], $config['email']['recipient_name']); + + // add cc-recipients + foreach ($config['email']['recipient_cc'] as $recipient) { + if ($recipient['address'] > '') { + $mail->AddCC($recipient['address'], $recipient['name']); + } + } + //build subject + $subject = $lang['L_DUMP_FILENAME'] . ': ' . $file; + if ($fileInfo['comment'] > '') { + $subject = $fileInfo['comment'] . ', ' . $subject; + } + $mail->Subject = $subject; + + $mail->Timeout = 60; + // set used mail-method + $mail->IsMail(); //defaults to php-mail-function + if ($config['use_mailer'] == 1) { + $mail->IsSendmail(); + $mail->Sendmail = $config['sendmail_call']; + } elseif ($config['use_mailer'] == 2) { + $mail->IsSMTP(); + //debug + //$mail->SMTPDebug = PHP_INT_MAX; + $mail->Host = $config['smtp_server']; + $mail->Port = $config['smtp_port']; + // auth? + if ($config['smtp_useauth']) { + $mail->SMTPAuth = true; + $mail->Username = $config['smtp_user']; + $mail->Password = $config['smtp_pass']; + } + //use ssl? + if ($config['smtp_usessl']) { + $mail->SMTPSecure = 'tls'; + } + } + + //build mail body + $body = ''; + + //add attachement? + if ($config['email']['attach_backup']) { + //check if file is bigger than allowed max size + if ($config['email_maxsize'] > 0 + && $fileInfo['size'] > $config['email_maxsize']) { + // attachement too big -> don't attach and paste message to body + $body .= sprintf( + $lang['L_EMAILBODY_TOOBIG'], + byteOutput($config['email_maxsize']), + $database, + $file . ' (' . byte_output( + filesize($config['paths']['backup'] . $file) + ) + . ')
' + ); + } else { + // add file as attachement + $mail->AddAttachment($rootpath . $file); + $body .= sprintf($lang['L_EMAILBODY_ATTACH'], $database, $filelist); + } + } else { + // don't attach backup file according to configuration + $body .= sprintf( + $lang['L_EMAILBODY_TOOBIG'], + byteOutput($config['email_maxsize']), + $database, + "$file (" . byteOutput( + filesize($config['paths']['backup'] . $file) + ) + . ")
" + ); + } + + //set body + $mail->MsgHTML($body); + //build alternative-body without tags for mail-clients blocking HTML + $altBody = strip_tags(Html::br2nl($body)); + $mail->AltBody = $altBody; + + $mail->Timeout = 30; + $ret = $mail->Send(); + if (!$ret) { + writeToErrorLog( + '', '', $lang['L_MAILERROR'] . ' -> ' . $mail->ErrorInfo, 0 + ); + $log->write(Log::PHP, $lang['L_MAILERROR']); + } else { + $msg = $lang['L_EMAIL_WAS_SEND'] . "`" + . $config['email']['recipient_address']; + $log->write(Log::PHP, $msg); + } + return $ret; +} + +/** + * Transfers a file via FTP and logs each action + * + * @param integer $ftpConnectionIndex Index of FTP-Connection in configuration + * @param string $sourceFile File to transfer + * @return void + */ +function sendViaFTP($ftpConnectionIndex, $sourceFile) +{ + global $config, $lang, $log; + + $upload = false; + $i = $ftpConnectionIndex; // I am lazy ;) + // connect to ftp server + if ($config['ftp'][$i]['ssl'] == 0) { + $connId = @ftp_connect( + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'], + $config['ftp'][$i]['timeout'] + ); + } else { + $connId = @ftp_ssl_connect( + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'], + $config['ftp'][$i]['timeout'] + ); + } + + if (is_resource($connId)) { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . + sprintf( + $lang['L_FTP_CONNECTION_SUCCESS'], + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'] + ) + ); + } else { + $msg = sprintf( + $lang['L_FTP_CONNECTION_ERROR'], + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'] + ); + writeToErrorLog('', '', $lang['L_FTP'] . ': ' . $msg, 0); + } + + // login using user and password + $loginResult = @ftp_login( + $connId, + $config['ftp'][$i]['user'], + $config['ftp'][$i]['pass'] + ); + if (!$loginResult) { + writeToErrorLog( + '', + '', + $lang['L_FTP'] . ': ' . sprintf( + $lang['L_FTP_LOGIN_ERROR'], + $config['ftp'][$i]['user'] + ), + 0 + ); + } else { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . sprintf( + $lang['L_FTP_LOGIN_SUCCESS'], + $config['ftp'][$i]['user'] + ) + ); + } + if ($config['ftp'][$i]['mode'] == 1) { + if (@ftp_pasv($connId, true)) { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . $lang['L_FTP_PASV_SUCCESS'] + ); + } else { + writeToErrorLog( + '', '', + $lang['L_FTP'] . ': ' . $lang['L_FTP_PASV_ERROR'], 0 + ); + } + } + + // Upload der Datei + $dest = $config['ftp'][$i]['dir'] . $sourceFile; + $source = $config['paths']['backup'] . $sourceFile; + $upload = @ftp_put($connId, $dest, $source, FTP_BINARY); + // Upload-Status überprüfen + if (!$upload) { + writeToErrorLog( + '', '', sprintf($lang['L_FTP_FILE_TRANSFER_ERROR'], $sourceFile), 0 + ); + } else { + $log->write( + Log::PHP, + sprintf($lang['L_FTP_FILE_TRANSFER_SUCCESS'], $sourceFile) + ); + } + + // Schließen des FTP-Streams + @ftp_quit($connId); + $log->write(Log::PHP, $lang['L_FTP_CONNECTION_CLOSED']); +} + +/** + * Gets all information about a dump process and stores it in global $dump-Array + * + * @return void + */ +function prepareDumpProcess() +{ + global $databases, $dump, $config, $tableInfos; + $dump['databases'] = array(); + $dump['records_total'] = 0; + $dump['tables_total'] = 0; + $dump['datasize_total'] = 0; + // make copy of database-array to make changes for value "dump" just here + $dbs = $databases; + // first check if any db is marked to be dumped + $dbToDumpExists = false; + foreach ($dbs as $val) { + if (isset($val['dump']) && $val['dump'] == 1) { + // Db should be saved + $dbToDumpExists = true; + break; + } + } + // no db selected for dump -> set actual db to be dumped + if (!$dbToDumpExists) { + $dbs[$config['db_actual']]['dump'] = 1; + } + + // find out which databases and tables should be saved + // dump=0 -> don't dump records + // dump=1 -> dump records using "INSERT INTO" + foreach ($dbs as $dbName => $val) { + if (isset($val['dump']) && $val['dump'] > 0) { + // db should be dumped + // now lets check which tables should be saved + // for now we save all tables -> later check prefixes etc... + $tableInfos = getTableInfo($dbName); + if (isset($tableInfos[$dbName])) { + if (!isset($dump['databases'][$dbName])) { + $dump['databases'][$dbName] = array(); + } + // calculate sums + $dump['databases'][$dbName] = $tableInfos[$dbName]; + $dump['databases'][$dbName]['prefix'] = ''; + if (isset($databases[$dbName]['prefix'])) { + $dump['databases'][$dbName]['prefix'] = + $databases[$dbName]['prefix']; + } + $dump['records_total'] += + $dump['databases'][$dbName]['records_total']; + $dump['tables_total'] += + $dump['databases'][$dbName]['table_count']; + $dump['datasize_total'] += + $dump['databases'][$dbName]['datasize_total']; + + // check if tables are selected -> + // then remove all others from array and correct sums + if ($dbName == $_SESSION['config']['db_actual'] + && isset($dump['selected_tables']) + && is_array($dump['selected_tables']) + && count($dump['selected_tables']) > 0) { + foreach ($dump['databases'][$dbName]['tables'] + as $tablename => $val) { + if (!in_array($tablename, $dump['selected_tables'])) { + $dump['databases'][$dbName]['tables'][$tablename]['dump_structure'] = 0; + $dump['databases'][$dbName]['tables'][$tablename]['dump_records'] = 0; + + // remove table from todo-list + unset($dump['databases'][$dbName]['tables'][$tablename]); + // substract values of table from sums + $dump['tables_total']--; + $dump['databases'][$dbName]['table_count']--; + $dump['databases'][$dbName]['records_total'] -= $val['records']; + $dump['databases'][$dbName]['datasize_total'] -= $val['data_length']; + $dump['databases'][$dbName]['size_total'] -= $val['data_length'] + $val['index_length']; + $dump['datasize_total'] -= $val['data_length']; + $dump['records_total'] -= $val['records']; + } + } + } + } + } + } + // set db to be dumped first -> start index is needed + $dbNames=array_keys($dump['databases']); + $dump['db_actual'] = $dbNames[0]; +} + diff --git a/inc/functions/functions_files.php b/inc/functions/functions_files.php new file mode 100644 index 0000000..c69caa6 --- /dev/null +++ b/inc/functions/functions_files.php @@ -0,0 +1,370 @@ +getFilename(); + if ($file[0] != '.' && $fileinfo->isFile()) + { + $size = byteOutput($fileinfo->getSize()); + $files["$file"] = $file . ' (' . $size . ')'; + } + } + ksort($files); + $r = Html::getOptionlist($files, $selected); + return $r; +} + +/** + * Reformat a date from format dd.mm.yyyy hh:mm to yyyy.mm.dd hh:mm to make it sortable. + * + * @param string $datum Datetime taken from filename + * @return string + */ +function getSortableDate($datum) +{ + $p = explode(' ', $datum); + $uhrzeit = $p[1]; + $p2 = explode('.', $p[0]); + $day = $p2[0]; + $month = $p2[1]; + $year = $p2[2]; + return $year . '.' . $month . '.' . $day . ' ' . $uhrzeit; +} + +/** + * Checks a dump file and converts it. + * + * Places field names in backticks and splitts files into 10 MB-Parts. + * + * @param string $filesource File to convert + * @param string $db_name Database name will be used to create filenames and statusline + * @param string $cp Target copy file + * @return void + */ +function convert($filesource, $db_name, $cp) +{ + global $config, $lang; + @ini_set('max_input_time', '0'); // for real big dumps + + + $filesize = 0; + $max_filesize = 1024 * 1024 * 10; //10 MB splitsize + $part = 1; + $cps = (substr(strtolower($filesource), -2) == "gz") ? 1 : 0; + // we compare the string size with this value (not the real filesie) + //so if file is compressed we need to adjust it + if ($cps == 1) $max_filesize *= 7; + + $filedestination = $db_name . '_' . date("Y_m_d_H_i", time()); + echo "
" . sprintf($lang['L_CONVERT_FILEREAD'], $filesource) . ".....
"; + if (file_exists($config['paths']['backup'] . $filedestination)) unlink($config['paths']['backup'] . $filedestination); + $f = ($cps == 1) ? gzopen($config['paths']['backup'] . $filesource, "r") : fopen($config['paths']['backup'] . $filesource, "r"); + $z = ($cp == 1) ? gzopen($config['paths']['backup'] . $filedestination . '_part_1.sql.gz', "w") : fopen($config['paths']['backup'] . $filedestination . '_part_1.sql', "w"); + + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + ($cp == 1) ? gzwrite($z, $zeile) : fwrite($z, $zeile); + $zeile = ''; + flush(); + + $insert = $mode = ""; + $n = 0; + $eof = ($cps == 1) ? gzeof($f) : feof($f); + $splitable = false; // can the file be splitted? Try to avoid splitting before a command is completed + WHILE (!$eof) + { + flush(); + $eof = ($cps == 1) ? gzeof($f) : feof($f); + $zeile = ($cps == 1) ? gzgets($f, 5144000) : fgets($f, 5144000); + + $t = strtolower(substr($zeile, 0, 10)); + if ($t > '') + { + switch ($t) + { + case 'insert int': + { + // eine neue Insert Anweisung beginnt + if (strpos($zeile, '(') === false) + { + //Feldnamen stehen in der naechsten Zeile - holen + $zeile .= "\n\r"; + $zeile .= ($cps == 1) ? trim(gzgets($f, 8192)) : trim(fgets($f, 8192)); + $zeile .= ' '; + } + + // get INSERT-Satement + $insert = substr($zeile, 0, strpos($zeile, '(')); + if (substr(strtoupper($insert), -7) != 'VALUES ') $insert .= ' VALUES '; + $mode = 'insert'; + $splitable = false; + break; + } + + case 'create tab': + { + $mode = 'create'; + WHILE (substr(rtrim($zeile), -1) != ';') + { + $zeile .= fgets($f, 8192); + } + $zeile = setBackticks($zeile) . "\n\r"; + $splitable = true; + break; + } + } + } + + if ($mode == 'insert') + { + if (substr(rtrim($zeile), strlen($zeile) - 3, 2) == ');') $splitable = true; + + // Komma loeschen + $zeile = str_replace('),(', ");\n\r" . $insert . ' (', $zeile); + } + + if ($splitable == true && $filesize > $max_filesize) // start new file? + { + $part++; + if ($mode == 'insert') // Insert -> first complete Insert-Statement, then begin new file + { + if ($cp == 1) + { + gzwrite($z, $zeile); + gzclose($z); + $z = gzopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql.gz', "w"); + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + gzwrite($z, $zeile); + $zeile = ''; + } + else + { + fwrite($z, $zeile); + fclose($z); + $z = fopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql', "w"); + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + gzwrite($z, $zeile); + $zeile = ''; + } + } + else // first close last file, then begin new one and write new beginning command + { + if ($cp == 1) + { + gzclose($z); + $z = gzopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql.gz', "w"); + $zeile = getPseudoStatusline($part, $filedestination) . "\r\n" . $zeile; + gzwrite($z, $zeile); + } + else + { + fclose($z); + $z = fopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql', "w"); + $zeile = getPseudoStatusline($part, $filedestination) . "\r\n" . $zeile; + fwrite($z, $zeile); + } + $n = 0; + } + $filesize = 0; + $splitable = false; + } + else // no, append to actual file + { + $filesize += strlen($zeile); + if ($n > 200) + { + $n = 0; + echo '
'; + } + echo '.'; + if ($cps == 1) gzwrite($z, $zeile); + else fwrite($z, $zeile); + flush(); + } + $n++; + } + $zeile = "\n-- EOB"; + if ($cps == 1) + { + gzwrite($z, $zeile); + gzclose($z); + } + else + { + fwrite($z, $zeile); + fclose($z); + } + + if ($cps == 1) gzclose($f); + else fclose($f); + echo '
' . sprintf($lang['L_CONVERT_FINISHED'], $filedestination) . '
'; +} + +/** + * Create a dummy statusline. + * + * Used when converting a file, to create a legal MSD-Multipart-File. + * + * @param integer $part + * @param string $db_name + * @return string + */ +function getPseudoStatusline($part, $db_name) +{ + if ($part > 1) echo '
Continue with part: ' . $part . '
'; + $ret = '-- Status:-1:-1:MP_' . ($part) . ':' . $db_name . ":php:converter2:converted:unknown:1:::latin1:EXTINFO\r\n" . "-- TABLE-INFO\r\n" . "-- TABLE|unknown|0|0|2009-01-24 20:39:39\r\n" . "-- EOF TABLE-INFO\r\n"; + return $ret; +} + +/** + * Read information from all backup files in folder work/backup and return multidimensional array + * containing all info. + * + * @return array + */ +function getBackupfileInfo() +{ + global $config; + clearstatcache(); + $files = Array(); + $dh = opendir($config['paths']['backup']); + while (false !== ($filename = readdir($dh))) + { + if ($filename != '.' && $filename != '..' && !is_dir($config['paths']['backup'] . $filename)) + { + $files[]['name'] = $filename; + } + } + $arrayindex = 0; + $total_filesize = 0; + $db_backups = array(); + $db_summary_anzahl = array(); + $db_summary_last = array(); + if (count($files) > 0) + { + for ($i = 0; $i < sizeof($files); $i++) + { + // filesize + $size = filesize($config['paths']['backup'] . $files[$i]['name']); + $file_datum = date("d\.m\.Y H:i", filemtime($config['paths']['backup'] . $files[$i]['name'])); + $statusline = ReadStatusline($files[$i]['name']); + $backup_timestamp = GetTimestampFromFilename($files[$i]['name']); + $pathinfo = pathinfo($files[$i]['name']); + $file_extension = $pathinfo['extension']; + if ($backup_timestamp == '') $backup_timestamp = $file_datum; + $database_name = $statusline['dbname']; + + // check for some special cases + if ($database_name == 'unknown') $database_name = '~unknown'; // needed for sorting - place unknown files at the end + if ($statusline['comment'] == 'converted') $database_name = '~converted'; // converted fiels + + + //jetzt alle in ein Array packen + if ($statusline['part'] == 'MP_0' || $statusline['part'] == '') + { + $db_backups[$arrayindex]['name'] = $files[$i]['name']; + $db_backups[$arrayindex]['db'] = $database_name; + $db_backups[$arrayindex]['extension'] = $file_extension; + $db_backups[$arrayindex]['size'] = $size; + $db_backups[$arrayindex]['date'] = $backup_timestamp; + $db_backups[$arrayindex]['sort'] = getSortableDate($backup_timestamp); + $db_backups[$arrayindex]['tables'] = $statusline['tables']; + $db_backups[$arrayindex]['records'] = $statusline['records']; + $db_backups[$arrayindex]['multipart'] = 0; + $db_backups[$arrayindex]['comment'] = $statusline['comment']; + $db_backups[$arrayindex]['script'] = ($statusline['script'] != '') ? $statusline['script'] . '(' . $statusline['scriptversion'] . ')' : ''; + $db_backups[$arrayindex]['charset'] = $statusline['charset']; + $db_backups[$arrayindex]['mysqlversion'] = $statusline['mysqlversion']; + if (!isset($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + $db_summary_anzahl[$database_name] = (isset($db_summary_anzahl[$database_name])) ? $db_summary_anzahl[$database_name] + 1 : 1; + $db_summary_size[$database_name] = (isset($db_summary_size[$database_name])) ? $db_summary_size[$database_name] + $size : $size; + if (getSortableDate($backup_timestamp) > getSortableDate($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + } + else + { + //v($statusline); + //list multipart files only once but keep info how many files belong to this backup + $done = 0; + if (!isset($db_summary_size[$database_name])) { + $db_summary_size[$database_name] = 0; + } + for ($j = 0; $j < $arrayindex; $j++) + { + if (isset($db_backups[$j])) + { + if (($db_backups[$j]['date'] == $backup_timestamp) && $db_backups[$j]['db'] == $database_name && $db_backups[$j]['extension'] == $file_extension) + { + $db_backups[$j]['mysqlversion'] = $statusline['mysqlversion']; + $db_backups[$j]['multipart']++; + $db_backups[$j]['size'] += $size; // calculate size for this multipart backup + $db_summary_size[$database_name] += $size; // calculate total size for this database + $done = 1; + break; + } + } + } + if ($done == 1) $arrayindex--; + + if ($done == 0) + { + //new entry for this backup with this timestamp + $db_backups[$arrayindex]['name'] = $files[$i]['name']; + $db_backups[$arrayindex]['db'] = $database_name; + $db_backups[$arrayindex]['extension'] = $file_extension; + $db_backups[$arrayindex]['size'] = $size; + $db_backups[$arrayindex]['date'] = $backup_timestamp; + $db_backups[$arrayindex]['sort'] = getSortableDate($backup_timestamp); + $db_backups[$arrayindex]['tables'] = $statusline['tables']; + $db_backups[$arrayindex]['records'] = $statusline['records']; + $db_backups[$arrayindex]['multipart'] = 1; + $db_backups[$arrayindex]['comment'] = $statusline['comment']; + $db_backups[$arrayindex]['script'] = ($statusline['script'] != "") ? $statusline['script'] . "(" . $statusline['scriptversion'] . ")" : ""; + $db_backups[$arrayindex]['charset'] = $statusline['charset']; + + if (!isset($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + $db_summary_anzahl[$database_name] = (isset($db_summary_anzahl[$database_name])) ? $db_summary_anzahl[$database_name] + 1 : 1; + $db_summary_size[$database_name] = (isset($db_summary_size[$database_name])) ? $db_summary_size[$database_name] + $size : $size; + if (getSortableDate($backup_timestamp) > getSortableDate($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + } + } + $arrayindex++; + $total_filesize += $size; // calculate overall file size + } + } + if ((isset($db_backups)) && (is_array($db_backups))) $db_backups = arfsort($db_backups, get_orderarray('sort,d|name,A')); + // merge infos into one array + $info = array(); + $info['filesize_total'] = $total_filesize; // total size of all files + $info['files'] = $db_backups; // info per file + unset($db_backups); + $info['databases'] = array(); + foreach ($db_summary_anzahl as $db => $count) + { + $info['databases'][$db]['backup_count'] = $count; + $info['databases'][$db]['backup_size_total'] = $db_summary_size[$db]; + $info['databases'][$db]['latest_backup_timestamp'] = $db_summary_last[$db]; + } + return $info; +} diff --git a/inc/functions/functions_global.php b/inc/functions/functions_global.php new file mode 100644 index 0000000..4f36d22 --- /dev/null +++ b/inc/functions/functions_global.php @@ -0,0 +1,915 @@ +'; + if (is_array($t) || is_object($t)){ + echo '
';
+        print_r($t);
+        echo '
'; + }else + echo $t; +} + } +/** + * Detect server protocol + * + * @return string 'https://' || 'http://' + */ +function getServerProtocol() +{ + return (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? 'https://' : 'http://'; +} +/** + * Build order array from string 'col1,A|col2,d' for function arfosort + * + * @param string $order Orderstring + * + * @return array + */ +function get_orderarray($order) +{ + $order_arr = array(); + $orders = explode('|', $order); + foreach ($orders as $o){ + $d = explode(',', $o); + if (isset($d[0]) && isset($d[1])) + $order_arr[] = array($d[0], $d[1]); + } + return $order_arr; +} +/** + * Order multidimensial array + * + * @param array $a Array to sort + * @param string $fl Order-string to define what keys should be sortet in what direction + * + * @return array Sorted array + */ +function arfsort($a, $fl) +{ + $GLOBALS['__ARFSORT_LIST__'] = $fl; + usort($a, 'arfsort_func'); + return $a; +} +/** + * Sort a multidimenional array no matter in which depth the key is + * + * @param array $a Array to sort + * @param string $b Sortstring containing keys and kind of sorting + * + * @return mixed + */ +function arfsort_func($a, $b) +{ + foreach ($GLOBALS['__ARFSORT_LIST__'] as $f){ + if (isset($b[$f[0]]) && isset($a[$f[0]])){ + switch ($f[1]){ // switch on ascending or descending value + case 'd': + $strc = strcmp(strtolower($b[$f[0]]), strtolower($a[$f[0]])); + if ($strc != 0) + return $strc; + break; + case 'a': + $strc = strcmp(strtolower($a[$f[0]]), strtolower($b[$f[0]])); + if ($strc != 0) + return $strc; + break; + case 'D': + $strc = (floatval($b[$f[0]]) < floatval($a[$f[0]])) ? -1 : 1; + if ($b[$f[0]] != $a[$f[0]]) + return $strc; + break; + case 'A': + $strc = (floatval($b[$f[0]]) > floatval($a[$f[0]])) ? -1 : 1; + if ($b[$f[0]] != $a[$f[0]]) + return $strc; + break; + } + } + } + return 0; +} +/** + * Read table information about nr of records and more for given database name. + * Return array with advsanced information. + * + * @param string $db The database name to read info from + * @param string $table If set, only information for this table is filled + * + * @return array + */ +function getTableInfo($db, $table = '') +{ + global $dbo, $config; + $tableInfos=array(); + $res = $dbo->selectDb($db); + if ($res){ + $query = 'SHOW TABLE STATUS FROM `' . $db . '`'; + if ($table > '') { + $query .= ' LIKE \'' . $table . '\''; + } + $res = $dbo->query($query, MsdDbFactory::ARRAY_ASSOC); + // init index if not set + if (!isset($tableInfos[$db])) + $tableInfos[$db] = array(); + if (!isset($tableInfos[$db]['tables'])) + $tableInfos[$db]['tables'] = array(); + $tableInfos[$db]['table_count'] = sizeof($res); + $tableInfos[$db]['records_total'] = 0; + $tableInfos[$db]['datasize_total'] = 0; + $tableInfos[$db]['size_total'] = 0; + if ($tableInfos[$db]['table_count'] > 0){ + for ($i = 0, $max = $tableInfos[$db]['table_count']; $i < $max; $i++){ + $row = $res[$i]; + $n = $row['Name']; + if (!isset($tableInfos[$db]['tables'][$n])) + $tableInfos[$db]['tables'][$row['Name']] = array(); + if (isset($row['Type'])) + $row['Engine'] = $row['Type']; + $tableInfos[$db]['tables'][$n]['name'] = $row['Name']; + $tableInfos[$db]['tables'][$n]['engine'] = $row['Engine']; + $tableInfos[$db]['tables'][$n]['dump_structure'] = 1; + $tableInfos[$db]['tables'][$n]['dump_records'] = 1; + // if we have a VIEW or a table of Type MEMORY -> don't save records + if (strtoupper($row['Comment']) == 'VIEW' || (isset($row['Engine']) && in_array(strtoupper($row['Engine']), array( + 'MEMORY')))){ + $tableInfos[$db]['tables'][$n]['dump_records'] = 0; + } + if (!isset($row['Update_time'])) + $row['Update_time'] = ''; + $tableInfos[$db]['tables'][$n]['update_time'] = $row['Update_time']; + $tableInfos[$db]['tables'][$n]['data_free'] = isset($row['Data_free']) ? $row['Data_free'] : 0; + $tableInfos[$db]['tables'][$n]['collation'] = isset($row['Collation']) ? $row['Collation'] : ''; + $tableInfos[$db]['tables'][$n]['comment'] = isset($row['Comment']) ? $row['Comment'] : ''; + $tableInfos[$db]['tables'][$n]['auto_increment'] = isset($row['Auto_increment']) ? $row['Auto_increment'] : ''; + $tableInfos[$db]['tables'][$n]['records'] = (int) $row['Rows']; + $tableInfos[$db]['tables'][$n]['data_length'] = (float) $row['Data_length']; + $tableInfos[$db]['tables'][$n]['index_length'] = $row['Index_length']; + $tableInfos[$db]['records_total'] += (int) $row['Rows']; + $tableInfos[$db]['datasize_total'] += (float) $row['Data_length']; + $tableInfos[$db]['size_total'] += (float) $row['Data_length'] + (float) $row['Index_length']; + } + } + } + return $tableInfos; +} +/** + * Returns microtime of now as float value + * + * @return float microtime + */ +function getMicrotime() +{ + list ($usec, $sec) = explode(' ', microtime()); + return ((float) $usec + (float) $sec); +} +/** + * Detect free diskspace + * + * @return string Space in human readable Bytes or message if not available + */ +function getFreeDiskSpace() +{ + global $lang; + $dfs = @diskfreespace("../"); + return ($dfs) ? byteOutput($dfs) : $lang['L_NOTAVAIL']; +} +/** + * Extract timestamp informations from a filename and return formatted string YYYY.MM.DD HH:MM + * + * @param string $s Filename as input 'dbname_2009_10_18_16_22_part1_sql-gz' + * + * @return string Formated timestamp YYYY.MM.DD HH:MM + */ +function getTimestampFromFilename($s) +{ + $i = strpos(strtolower($s), 'part'); + if ($i > 0) + $s = substr($s, 0, $i - 1); + $i = strpos(strtolower($s), 'crondump'); + if ($i > 0) + $s = substr($s, 0, $i - 1); + $i = strpos(strtolower($s), '.sql'); + if ($i > 0) + $s = substr($s, 0, $i); + $sp = explode('_', $s); + $anz = count($sp) - 1; + if (strtolower($sp[$anz]) == 'perl') + $anz--; + if ($anz > 4){ + return $sp[$anz - 2] . '.' . $sp[$anz - 3] . '.' . $sp[$anz - 4] . ' ' . $sp[$anz - 1] . ':' . $sp[$anz]; + }else{ + //no MySQLDumper file + return ''; + } +} +/** + * Writes errors or notices to the corresponding error log + * + * @param string $db Affected database + * @param string $sql The executed query + * @param string $error The error message to log + * @param int $art The kind of error (0=error, 1=notice) + * + * @return void + */ +function writeToErrorLog($db = '', $sql = '', $error = '', $art = 1) +{ + global $config, $lang, $log; + $sql = str_replace("\r", '', $sql); + $sql = str_replace("\n\n", '
', $sql); + $sql = str_replace("\n", '
', $sql); + $sql = trim($sql); + $error = str_replace("\r", '', $error); + if ($art == 0) { + $errormsg = $lang['L_ERROR'] . ': ' . $error; + } else { + $errormsg = $lang['L_NOTICE'] . ': ' . $error; + } + // append query if set + if ($sql > '') { + $errormsg .= '
SQL: ' . $sql; + } + $time = date('d.m.Y H:i:s') . ' '; + if ($art == 0) { + $_SESSION['log']['errors'][] = $time.$errormsg; + } else { + $_SESSION['log']['notices'][] = $time.$errormsg; + } + $log->write(Log::ERROR, $errormsg); +} +/** + * Checks if the directories work, config, backup and log are writable + * Returns concatenated string with warnings or empty string if every directory is writable + * + * @return string + */ +function checkDirectories() +{ + global $config, $lang; + $warn = ''; + if (!is_writable($config['paths']['work'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['work'], '0777'); + if (!is_writable($config['paths']['config'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['config'], '0777'); + if (!is_writable($config['paths']['backup'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['backup'], '0777'); + if (!is_writable($config['paths']['log'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['log'], '0777'); + if ($warn != '') + $warn = '' . $warn . ''; + return $warn; +} +/** + * Deletes every table or view in a database + * + * @param string $dbn Databasename + * + * @return void + */ +function truncateDb($dbn) +{ + global $dbo; + $t_sql = array(); + $dbo->query('SET FOREIGN_KEY_CHECKS=0', MsdDbFactory::SIMPLE); + $res = $dbo->query('SHOW TABLE STATUS FROM `' . $dbn . '`', MsdDbFactory::ARRAY_ASSOC); + foreach ($res as $row){ + if (substr(strtoupper($row['Comment']), 0, 4) == 'VIEW'){ + $t_sql[] = 'DROP VIEW `' . $dbn . '``' . $row['Name'] . '`'; + }else{ + $t_sql[] = 'DROP TABLE `' . $dbn . '`.`' . $row['Name'] . '`'; + } + } + if (sizeof($t_sql) > 0){ + for ($i = 0; $i < count($t_sql); $i++){ + try{ + $dbo->query($t_sql[$i]); + }catch (Excption $e){ + //TODO create clean error handling depending on context + writeToErrorLog($e->getMessage()); + die($e->getMessage()); + } + } + } + $dbo->query('SET FOREIGN_KEY_CHECKS=1', MsdDbFactory::SIMPLE); +} +/** + * Delete old backups from folder work/backup according to configuration + * + * @return string Outputstring with messages about deleted files + */ +function doAutoDelete() +{ + global $config, $lang, $out; + $out = ''; + if ($config['auto_delete']['max_backup_files'] > 0){ + //Files einlesen + $dh = opendir($config['paths']['backup']); + $files = array(); + // Build assoc Array $db=>$timestamp=>$filenames + if (!function_exists('ReadStatusline')) + include ('./inc/functions/functions_files.php'); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '..' && !is_dir($config['paths']['backup'] . $filename)){ + $statusline = readStatusline($filename); + if ($statusline['dbname'] != 'unknown'){ + $dbName = $statusline['dbname']; + $datum = substr($filename, strlen($dbName) + 1); + $timestamp = substr($datum, 0, 16); + if (!isset($files[$dbName])) + $files[$dbName] = array(); + if (!isset($files[$dbName][$timestamp])) + $files[$dbName][$timestamp] = array(); + $files[$dbName][$timestamp][] = $filename; + } + } + } + $out = ''; // stores output messages + // Backups per DB and Timestamp + foreach ($files as $db => $val){ + if (sizeof($val) > $config['auto_delete']['max_backup_files']){ + $db_files = $val; + krsort($db_files, SORT_STRING); + //now latest backupfiles are on top -> delete all files with greater index + $i = 0; + foreach ($db_files as $timestamp => $filenames){ + if ($i >= $config['auto_delete']['max_backup_files']){ + // Backup too old -> delete files + foreach ($filenames as $f){ + if ($out == '') + $out .= $lang['L_FM_AUTODEL1'] . '
'; + if (@unlink('./' . $config['paths']['backup'] . $f)){ + $out .= '' . sprintf($lang['L_DELETE_FILE_SUCCESS'], $f) . '
'; + }else{ + $out .= $lang['L_ERROR'] . ':

' . sprintf($lang['L_DELETE_FILE_ERROR'], $f) . '


'; + } + } + } + $i++; + } + } + } + } + return $out; +} +/** + * Analyzes the first line of a MSD-Backup. Expects it as string parameter. + * + * @param string $line The statusline from the backup to analyze + * + * @return array Extracted information as array + */ +function readStatusline($filename) +{ + global $config; + /*AUFBAU der Statuszeile: + -- Status:nr of tables:records:Multipart:Databasename:script:scriptversion:Comment: + MySQL-Version:flags (unused):SQLCommandBeforeBackup:SQLCommandAfterBackup:Charset:EXTINFO + */ + $gz = substr($filename, -3) == '.gz' ? true : false; + $fh = $gz ? gzopen($config['paths']['backup'] . $filename, 'r') : fopen($config['paths']['backup'] . $filename, 'r'); + if (!$fh){ + v(debug_backtrace()); + die(); + } + $line = $gz ? gzgets($fh) : fgets($fh); + $gz ? gzclose($fh) : fclose($fh); + $statusline = array(); + $line = removeBom($line); + if ((substr($line, 0, 8) != "# Status" && substr($line, 0, 9) != "-- Status") || substr($line, 0, 10) == '-- StatusC'){ + //Fremdfile + $statusline['tables'] = -1; + $statusline['records'] = -1; + $statusline['part'] = 'MP_0'; + $statusline['dbname'] = 'unknown'; + $statusline['script'] = ''; + $statusline['scriptversion'] = ''; + $statusline['comment'] = ''; + $statusline['mysqlversion'] = 'unknown'; + $statusline['flags'] = '2222222'; + $statusline['sqlbefore'] = ''; + $statusline['sqlafter'] = ''; + $statusline['charset'] = '?'; + }else{ + // MySQLDumper-File - Informationen extrahieren + $s = explode(':', $line); + if (count($s) < 12){ + //fehlenden Elemente auffüllen + $c = count($s); + array_pop($s); + for ($i = $c - 1; $i < 12; $i++){ + $s[] = ''; + } + } + $statusline['tables'] = $s[1]; + $statusline['records'] = $s[2]; + $statusline['part'] = ($s[3] == '' || $s[3] == 'MP_0') ? 'MP_0' : $s[3]; + $statusline['dbname'] = $s[4]; + $statusline['script'] = $s[5]; + $statusline['scriptversion'] = $s[6]; + $statusline['comment'] = $s[7]; + $statusline['mysqlversion'] = $s[8]; + if ((isset($s[12])) && trim($s[12]) != 'EXTINFO'){ + $statusline['charset'] = $s[12]; + }else{ + $statusline['charset'] = '?'; + } + } + return $statusline; +} +/** + * Reads Head-Information about tables from MSD-Backup and returns it as array + * + * @param string $filename Filename of MSD-Backup to analyze + * + * @return array Detailed information about tables n MSD-Backup + */ +function getTableHeaderInfoFromBackup($filename) +{ + global $config; + // Get Tableinfo from file header + $tabledata = array(); + $i = 0; + $gz = substr($filename, -3) == '.gz' ? true : false; + $fh = $gz ? gzopen($config['paths']['backup'] . $filename, 'r') : fopen($config['paths']['backup'] . $filename, 'r'); + $eof = false; + WHILE (!$eof){ + $line = $gz ? gzgets($fh, 40960) : fgets($fh, 40960); + $line = trim($line); + if (substr($line, 0, 9) == '-- TABLE|'){ + $d = explode('|', $line); + $tabledata[$i]['name'] = $d[1]; + $tabledata[$i]['records'] = $d[2]; + $tabledata[$i]['size'] = $d[3]; + $tabledata[$i]['update'] = $d[4]; + $tabledata[$i]['engine'] = isset($d[5]) ? $d[5] : ''; + $i++; + }elseif (substr($line, 0, 6) == '-- EOF') + $eof = true; // End of Table-Info - >done + elseif (substr(strtolower($line), 0, 6) == 'create') + $eof = true; // we have found the first CREATE-Query -> done + } + $gz ? gzclose($fh) : fclose($fh); + return $tabledata; +} +/** + * Calculate next Multipart-Filename + * + * @param string $filename The filename to calculate the next name from + * + * @return string Filename of next Multipart-File + */ +function getNextPart($filename) +{ + $nf = explode('_', $filename); + $i = array_search('part', $nf) + 1; + $part = substr($nf[$i], 0, strpos($nf[$i], '.')); + $ext = substr($nf[$i], strlen($part)); + $nf[$i] = ++$part . $ext; + $filename = implode('_', $nf); + return $filename; +} +/** + * Formats seconds to human readable output + * + * @param integer $time Time in seconds + * + * @return string Time as human readable formatted string + */ +function getTimeFormat($time) +{ + global $lang; + $d = floor($time / 86400); + $h = floor(($time - $d * 86400) / 3600); + $m = floor(($time - $d * 86400 - $h * 3600) / 60); + $s = $time - $d * 86400 - $h * 3600 - $m * 60; + $ret = sprintf('%02d', $s) . ' ' . ($s == 1 ? $lang['L_SECOND'] : $lang['L_SECONDS']); + if ($m > 0){ + $ret = $m . ' ' . ($m == 1 ? $lang['L_MINUTE'] : $lang['L_MINUTES']) . ' ' . $ret; + } + if ($h > 0){ + $ret = $h . ' ' . ($h == 1 ? $lang['L_HOUR'] : $lang['L_HOURS']) . ' ' . $ret; + } + if ($d > 0){ + $ret = $d . ' ' . ($d == 1 ? $lang['L_DAY'] : $lang['L_DAYS']) . ' ' . $ret; + } + return $ret; +} +/** + * Tests a ftp-connection and returns messages about uccess or failure + * + * @param integer $i The index of the connection profile to test + * + * @return array Array with messages + */ +function testFTP($i) +{ + global $lang, $config; + if (!isset($config['ftp'][$i]['timeout'])) + $config['ftp'][$i]['timeout'][$i] = 30; + $ret = array(); + if ($config['ftp'][$i]['port'] == '' || $config['ftp'][$i]['port'][$i] == 0) + $config['ftp'][$i]['port'] = 21; + $pass = -1; + if (!extension_loaded("ftp")){ + $ret[] = '' . $lang['L_NOFTPPOSSIBLE'] . ''; + }else + $pass = 0; + if ($pass == 0){ + if ($config['ftp'][$i]['server'] == '' || $config['ftp'][$i]['user'] == ''){ + $ret[] = '' . $lang['L_WRONGCONNECTIONPARS'] . ''; + }else + $pass = 1; + } + if ($pass == 1){ + if ($config['ftp'][$i]['ssl'] == 0) + $conn_id = @ftp_connect($config['ftp'][$i]['server'], $config['ftp'][$i]['port'], $config['ftp'][$i]['timeout']); + else + $conn_id = @ftp_ssl_connect($config['ftp'][$i]['server'], $config['ftp'][$i]['port'], $config['ftp'][$i]['timeout']); + if (is_resource($conn_id)){ + $ret[] = sprintf($lang['L_FTP_CONNECTION_SUCCESS'], $config['ftp'][$i]['server'], $config['ftp'][$i]['port']); + }else + $ret[] = sprintf($lang['L_FTP_CONNECTION_ERROR'], $config['ftp'][$i]['server'], $config['ftp'][$i]['port']); + if ($conn_id){ + $login_result = @ftp_login($conn_id, $config['ftp'][$i]['user'], $config['ftp'][$i]['pass']); + if ($login_result) + $ret[] = sprintf($lang['L_FTP_LOGIN_SUCCESS'], $config['ftp'][$i]['user']); + else + $ret[] = sprintf($lang['L_FTP_LOGIN_ERROR'], $config['ftp'][$i]['user']); + } + if ($conn_id && $login_result){ + $pass = 2; + if ($config['ftp'][$i]['mode'] == 1){ + if (ftp_pasv($conn_id, true)) + $ret[] = $lang['L_FTP_PASV_SUCCESS']; + else + $ret[] = $lang['L_FTP_PASV_ERROR']; + } + } + } + if ($pass == 2){ + $dirc = @ftp_chdir($conn_id, $config['ftp'][$i]['dir']); + if (!$dirc){ + $ret[] = $lang['L_CHANGEDIR'] . ' \'' . $config['ftp'][$i]['dir'] . '\' -> ' . $lang['L_CHANGEDIRERROR'] . ''; + }else{ + $pass = 3; + $ret[] = $lang['L_CHANGEDIR'] . ' \'' . $config['ftp'][$i]['dir'] . '\' -> ' . $lang['L_OK'] . ''; + } + @ftp_close($conn_id); + } + if ($pass == 3) + $ret[] = '' . $lang['L_FTP_OK'] . ''; + return implode('
', $ret); +} +/** + * Returns list of configuration profiles as HTML-Optionlist + * + * @param string $selected_config The actual configuration to pre-select in option list + * + * @return string HTML-option-string + */ +function getConfigFilelist($selected_config) +{ + $configs = getConfigFilenames(); + $options = Html::getOptionlist($configs, $selected_config); + return $options; +} +/** + * Returns list of installed themes as HTML-Optionlist + * + * @return string HTML-option-string + */ +function getThemes() +{ + global $config; + $themes = array(); + $dh = opendir($config['paths']['root'] . "css/"); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '..' && is_dir($config['paths']['root'] . 'css/' . $filename) && substr($filename, 0, 1) != '.' && substr($filename, 0, 1) != '_'){ + $themes[$filename] = $filename; + } + } + @ksort($themes); + $options = Html::getOptionlist($themes, $config['theme']); + return $options; +} +/** + * Detects all language-directories and builds HTML-Optionlist + * + * @return string HTML-option-string + */ +function getLanguageCombo() +{ + global $config, $lang; + $default = $config['language']; + $dh = opendir('./language/'); + $r = ""; + $lang_files = array(); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '.svn' && $filename != '..' && $filename != 'flags' && is_dir('./language/' . $filename)){ + if (isset($lang[$filename])) + $lang_files[$lang[$filename]] = $filename; + } + } + @ksort($lang_files); + foreach ($lang_files as $filename){ + $style = 'background:url(language/flags/width25/' . $filename . '.gif) 4px;background-repeat:no-repeat;padding:2px 6px 2px 36px !important;'; + $r .= '' . "\n"; + for ($i = 0; $i < count($SQL_ARRAY); $i++) + { + $s = trim(getQueryFromSqlLibrary($i)); + $r .= '
', + creditsTitle : 'Go to the Highslide JS homepage', + previousText : 'Previous', + nextText : 'Next', + moveText : 'Move', + closeText : 'Close', + closeTitle : 'Close (esc)', + resizeTitle : 'Resize', + playText : 'Play', + playTitle : 'Play slideshow (spacebar)', + pauseText : 'Pause', + pauseTitle : 'Pause slideshow (spacebar)', + previousTitle : 'Previous (arrow left)', + nextTitle : 'Next (arrow right)', + moveTitle : 'Move', + fullExpandText : '1:1', + restoreTitle : 'Click to close image, click and drag to move. Use arrow keys for next and previous.' +}, +// See http://highslide.com/ref for examples of settings +graphicsDir : 'highslide/graphics/', +expandCursor : 'zoomin.cur', // null disables +restoreCursor : 'zoomout.cur', // null disables +expandDuration : 250, // milliseconds +restoreDuration : 250, +marginLeft : 15, +marginRight : 15, +marginTop : 15, +marginBottom : 15, +zIndexCounter : 1001, // adjust to other absolutely positioned elements +loadingOpacity : 0.75, +allowMultipleInstances: true, +numberOfImagesToPreload : 5, +outlineWhileAnimating : 2, // 0 = never, 1 = always, 2 = HTML only +outlineStartOffset : 3, // ends at 10 +padToMinWidth : false, // pad the popup width to make room for wide caption +fullExpandPosition : 'bottom right', +fullExpandOpacity : 1, +showCredits : true, // you can set this to false if you want +creditsHref : 'http://highslide.com/', +creditsTarget : '_self', +enableKeyListener : true, +openerTagNames : ['a'], // Add more to allow slideshow indexing + +allowWidthReduction : false, +allowHeightReduction : true, +preserveContent : true, // Preserve changes made to the content and position of HTML popups. +objectLoadTime : 'before', // Load iframes 'before' or 'after' expansion. +cacheAjax : true, // Cache ajax popups for instant display. Can be overridden for each popup. +dragByHeading: true, +minWidth: 200, +minHeight: 200, +allowSizeReduction: true, // allow the image to reduce to fit client size. If false, this overrides minWidth and minHeight +outlineType : 'drop-shadow', // set null to disable outlines +skin : { + contentWrapper: + '
'+ + '
'+ + '' +}, +// END OF YOUR SETTINGS + + +// declare internal properties +preloadTheseImages : [], +continuePreloading: true, +expanders : [], +overrides : [ + 'allowSizeReduction', + 'useBox', + 'outlineType', + 'outlineWhileAnimating', + 'captionId', + 'captionText', + 'captionEval', + 'captionOverlay', + 'headingId', + 'headingText', + 'headingEval', + 'headingOverlay', + 'creditsPosition', + 'dragByHeading', + + 'width', + 'height', + + 'contentId', + 'allowWidthReduction', + 'allowHeightReduction', + 'preserveContent', + 'maincontentId', + 'maincontentText', + 'maincontentEval', + 'objectType', + 'cacheAjax', + 'objectWidth', + 'objectHeight', + 'objectLoadTime', + 'swfOptions', + 'wrapperClassName', + 'minWidth', + 'minHeight', + 'maxWidth', + 'maxHeight', + 'slideshowGroup', + 'easing', + 'easingClose', + 'fadeInOut', + 'src' +], +overlays : [], +idCounter : 0, +oPos : { + x: ['leftpanel', 'left', 'center', 'right', 'rightpanel'], + y: ['above', 'top', 'middle', 'bottom', 'below'] +}, +mouse: {}, +headingOverlay: {}, +captionOverlay: {}, +swfOptions: { flashvars: {}, params: {}, attributes: {} }, +timers : [], + +pendingOutlines : {}, +sleeping : [], +preloadTheseAjax : [], +cacheBindings : [], +cachedGets : {}, +clones : {}, +onReady: [], +uaVersion: /Trident\/4\.0/.test(navigator.userAgent) ? 8 : + parseFloat((navigator.userAgent.toLowerCase().match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]), +ie : (document.all && !window.opera), +safari : /Safari/.test(navigator.userAgent), +geckoMac : /Macintosh.+rv:1\.[0-8].+Gecko/.test(navigator.userAgent), + +$ : function (id) { + if (id) return document.getElementById(id); +}, + +push : function (arr, val) { + arr[arr.length] = val; +}, + +createElement : function (tag, attribs, styles, parent, nopad) { + var el = document.createElement(tag); + if (attribs) hs.extend(el, attribs); + if (nopad) hs.setStyles(el, {padding: 0, border: 'none', margin: 0}); + if (styles) hs.setStyles(el, styles); + if (parent) parent.appendChild(el); + return el; +}, + +extend : function (el, attribs) { + for (var x in attribs) el[x] = attribs[x]; + return el; +}, + +setStyles : function (el, styles) { + for (var x in styles) { + if (hs.ie && x == 'opacity') { + if (styles[x] > 0.99) el.style.removeAttribute('filter'); + else el.style.filter = 'alpha(opacity='+ (styles[x] * 100) +')'; + } + else el.style[x] = styles[x]; + } +}, +animate: function(el, prop, opt) { + var start, + end, + unit; + if (typeof opt != 'object' || opt === null) { + var args = arguments; + opt = { + duration: args[2], + easing: args[3], + complete: args[4] + }; + } + if (typeof opt.duration != 'number') opt.duration = 250; + opt.easing = Math[opt.easing] || Math.easeInQuad; + opt.curAnim = hs.extend({}, prop); + for (var name in prop) { + var e = new hs.fx(el, opt , name ); + + start = parseFloat(hs.css(el, name)) || 0; + end = parseFloat(prop[name]); + unit = name != 'opacity' ? 'px' : ''; + + e.custom( start, end, unit ); + } +}, +css: function(el, prop) { + if (document.defaultView) { + return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop); + + } else { + if (prop == 'opacity') prop = 'filter'; + var val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b){ return b.toUpperCase(); })]; + if (prop == 'filter') + val = val.replace(/alpha\(opacity=([0-9]+)\)/, + function (a, b) { return b / 100 }); + return val === '' ? 1 : val; + } +}, + +getPageSize : function () { + var d = document, w = window, iebody = d.compatMode && d.compatMode != 'BackCompat' + ? d.documentElement : d.body; + + var width = hs.ie ? iebody.clientWidth : + (d.documentElement.clientWidth || self.innerWidth), + height = hs.ie ? iebody.clientHeight : self.innerHeight; + + hs.page = { + width: width, + height: height, + scrollLeft: hs.ie ? iebody.scrollLeft : pageXOffset, + scrollTop: hs.ie ? iebody.scrollTop : pageYOffset + } +}, + +getPosition : function(el) { + var p = { x: el.offsetLeft, y: el.offsetTop }; + while (el.offsetParent) { + el = el.offsetParent; + p.x += el.offsetLeft; + p.y += el.offsetTop; + if (el != document.body && el != document.documentElement) { + p.x -= el.scrollLeft; + p.y -= el.scrollTop; + } + } + return p; +}, + +expand : function(a, params, custom, type) { + if (!a) a = hs.createElement('a', null, { display: 'none' }, hs.container); + if (typeof a.getParams == 'function') return params; + if (type == 'html') { + for (var i = 0; i < hs.sleeping.length; i++) { + if (hs.sleeping[i] && hs.sleeping[i].a == a) { + hs.sleeping[i].awake(); + hs.sleeping[i] = null; + return false; + } + } + hs.hasHtmlExpanders = true; + } + try { + new hs.Expander(a, params, custom, type); + return false; + } catch (e) { return true; } +}, + +htmlExpand : function(a, params, custom) { + return hs.expand(a, params, custom, 'html'); +}, + +getSelfRendered : function() { + return hs.createElement('div', { + className: 'highslide-html-content', + innerHTML: hs.replaceLang(hs.skin.contentWrapper) + }); +}, +getElementByClass : function (el, tagName, className) { + var els = el.getElementsByTagName(tagName); + for (var i = 0; i < els.length; i++) { + if ((new RegExp(className)).test(els[i].className)) { + return els[i]; + } + } + return null; +}, +replaceLang : function(s) { + s = s.replace(/\s/g, ' '); + var re = /{hs\.lang\.([^}]+)\}/g, + matches = s.match(re), + lang; + if (matches) for (var i = 0; i < matches.length; i++) { + lang = matches[i].replace(re, "$1"); + if (typeof hs.lang[lang] != 'undefined') s = s.replace(matches[i], hs.lang[lang]); + } + return s; +}, + + +getCacheBinding : function (a) { + for (var i = 0; i < hs.cacheBindings.length; i++) { + if (hs.cacheBindings[i][0] == a) { + var c = hs.cacheBindings[i][1]; + hs.cacheBindings[i][1] = c.cloneNode(1); + return c; + } + } + return null; +}, + +preloadAjax : function (e) { + var arr = hs.getAnchors(); + for (var i = 0; i < arr.htmls.length; i++) { + var a = arr.htmls[i]; + if (hs.getParam(a, 'objectType') == 'ajax' && hs.getParam(a, 'cacheAjax')) + hs.push(hs.preloadTheseAjax, a); + } + + hs.preloadAjaxElement(0); +}, + +preloadAjaxElement : function (i) { + if (!hs.preloadTheseAjax[i]) return; + var a = hs.preloadTheseAjax[i]; + var cache = hs.getNode(hs.getParam(a, 'contentId')); + if (!cache) cache = hs.getSelfRendered(); + var ajax = new hs.Ajax(a, cache, 1); + ajax.onError = function () { }; + ajax.onLoad = function () { + hs.push(hs.cacheBindings, [a, cache]); + hs.preloadAjaxElement(i + 1); + }; + ajax.run(); +}, + +focusTopmost : function() { + var topZ = 0, + topmostKey = -1, + expanders = hs.expanders, + exp, + zIndex; + for (var i = 0; i < expanders.length; i++) { + exp = expanders[i]; + if (exp) { + zIndex = exp.wrapper.style.zIndex; + if (zIndex && zIndex > topZ) { + topZ = zIndex; + topmostKey = i; + } + } + } + if (topmostKey == -1) hs.focusKey = -1; + else expanders[topmostKey].focus(); +}, + +getParam : function (a, param) { + a.getParams = a.onclick; + var p = a.getParams ? a.getParams() : null; + a.getParams = null; + + return (p && typeof p[param] != 'undefined') ? p[param] : + (typeof hs[param] != 'undefined' ? hs[param] : null); +}, + +getSrc : function (a) { + var src = hs.getParam(a, 'src'); + if (src) return src; + return a.href; +}, + +getNode : function (id) { + var node = hs.$(id), clone = hs.clones[id], a = {}; + if (!node && !clone) return null; + if (!clone) { + clone = node.cloneNode(true); + clone.id = ''; + hs.clones[id] = clone; + return node; + } else { + return clone.cloneNode(true); + } +}, + +discardElement : function(d) { + if (d) hs.garbageBin.appendChild(d); + hs.garbageBin.innerHTML = ''; +}, +transit : function (adj, exp) { + var last = exp = exp || hs.getExpander(); + if (hs.upcoming) return false; + else hs.last = last; + try { + hs.upcoming = adj; + adj.onclick(); + } catch (e){ + hs.last = hs.upcoming = null; + } + try { + exp.close(); + } catch (e) {} + return false; +}, + +previousOrNext : function (el, op) { + var exp = hs.getExpander(el); + if (exp) return hs.transit(exp.getAdjacentAnchor(op), exp); + else return false; +}, + +previous : function (el) { + return hs.previousOrNext(el, -1); +}, + +next : function (el) { + return hs.previousOrNext(el, 1); +}, + +keyHandler : function(e) { + if (!e) e = window.event; + if (!e.target) e.target = e.srcElement; // ie + if (typeof e.target.form != 'undefined') return true; // form element has focus + var exp = hs.getExpander(); + + var op = null; + switch (e.keyCode) { + case 70: // f + if (exp) exp.doFullExpand(); + return true; + case 32: // Space + case 34: // Page Down + case 39: // Arrow right + case 40: // Arrow down + op = 1; + break; + case 8: // Backspace + case 33: // Page Up + case 37: // Arrow left + case 38: // Arrow up + op = -1; + break; + case 27: // Escape + case 13: // Enter + op = 0; + } + if (op !== null) {hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + if (!hs.enableKeyListener) return true; + + if (e.preventDefault) e.preventDefault(); + else e.returnValue = false; + if (exp) { + if (op == 0) { + exp.close(); + } else { + hs.previousOrNext(exp.key, op); + } + return false; + } + } + return true; +}, + + +registerOverlay : function (overlay) { + hs.push(hs.overlays, hs.extend(overlay, { hsId: 'hsId'+ hs.idCounter++ } )); +}, + + +getWrapperKey : function (element, expOnly) { + var el, re = /^highslide-wrapper-([0-9]+)$/; + // 1. look in open expanders + el = element; + while (el.parentNode) { + if (el.id && re.test(el.id)) return el.id.replace(re, "$1"); + el = el.parentNode; + } + // 2. look in thumbnail + if (!expOnly) { + el = element; + while (el.parentNode) { + if (el.tagName && hs.isHsAnchor(el)) { + for (var key = 0; key < hs.expanders.length; key++) { + var exp = hs.expanders[key]; + if (exp && exp.a == el) return key; + } + } + el = el.parentNode; + } + } + return null; +}, + +getExpander : function (el, expOnly) { + if (typeof el == 'undefined') return hs.expanders[hs.focusKey] || null; + if (typeof el == 'number') return hs.expanders[el] || null; + if (typeof el == 'string') el = hs.$(el); + return hs.expanders[hs.getWrapperKey(el, expOnly)] || null; +}, + +isHsAnchor : function (a) { + return (a.onclick && a.onclick.toString().replace(/\s/g, ' ').match(/hs.(htmlE|e)xpand/)); +}, + +reOrder : function () { + for (var i = 0; i < hs.expanders.length; i++) + if (hs.expanders[i] && hs.expanders[i].isExpanded) hs.focusTopmost(); +}, + +mouseClickHandler : function(e) +{ + if (!e) e = window.event; + if (e.button > 1) return true; + if (!e.target) e.target = e.srcElement; + + var el = e.target; + while (el.parentNode + && !(/highslide-(image|move|html|resize)/.test(el.className))) + { + el = el.parentNode; + } + var exp = hs.getExpander(el); + if (exp && (exp.isClosing || !exp.isExpanded)) return true; + + if (exp && e.type == 'mousedown') { + if (e.target.form) return true; + var match = el.className.match(/highslide-(image|move|resize)/); + if (match) { + hs.dragArgs = { + exp: exp , + type: match[1], + left: exp.x.pos, + width: exp.x.size, + top: exp.y.pos, + height: exp.y.size, + clickX: e.clientX, + clickY: e.clientY + }; + + + hs.addEventListener(document, 'mousemove', hs.dragHandler); + if (e.preventDefault) e.preventDefault(); // FF + + if (/highslide-(image|html)-blur/.test(exp.content.className)) { + exp.focus(); + hs.hasFocused = true; + } + return false; + } + else if (/highslide-html/.test(el.className) && hs.focusKey != exp.key) { + exp.focus(); + exp.doShowHide('hidden'); + } + } else if (e.type == 'mouseup') { + + hs.removeEventListener(document, 'mousemove', hs.dragHandler); + + if (hs.dragArgs) { + if (hs.styleRestoreCursor && hs.dragArgs.type == 'image') + hs.dragArgs.exp.content.style.cursor = hs.styleRestoreCursor; + var hasDragged = hs.dragArgs.hasDragged; + + if (!hasDragged &&!hs.hasFocused && !/(move|resize)/.test(hs.dragArgs.type)) { + exp.close(); + } + else if (hasDragged || (!hasDragged && hs.hasHtmlExpanders)) { + hs.dragArgs.exp.doShowHide('hidden'); + } + + if (hs.dragArgs.exp.releaseMask) + hs.dragArgs.exp.releaseMask.style.display = 'none'; + hs.hasFocused = false; + hs.dragArgs = null; + + } else if (/highslide-image-blur/.test(el.className)) { + el.style.cursor = hs.styleRestoreCursor; + } + } + return false; +}, + +dragHandler : function(e) +{ + if (!hs.dragArgs) return true; + if (!e) e = window.event; + var a = hs.dragArgs, exp = a.exp; + if (exp.iframe) { + if (!exp.releaseMask) exp.releaseMask = hs.createElement('div', null, + { position: 'absolute', width: exp.x.size+'px', height: exp.y.size+'px', + left: exp.x.cb+'px', top: exp.y.cb+'px', zIndex: 4, background: (hs.ie ? 'white' : 'none'), + opacity: .01 }, + exp.wrapper, true); + if (exp.releaseMask.style.display == 'none') + exp.releaseMask.style.display = ''; + } + + a.dX = e.clientX - a.clickX; + a.dY = e.clientY - a.clickY; + + var distance = Math.sqrt(Math.pow(a.dX, 2) + Math.pow(a.dY, 2)); + if (!a.hasDragged) a.hasDragged = (a.type != 'image' && distance > 0) + || (distance > (hs.dragSensitivity || 5)); + + if (a.hasDragged && e.clientX > 5 && e.clientY > 5) { + + if (a.type == 'resize') exp.resize(a); + else { + exp.moveTo(a.left + a.dX, a.top + a.dY); + if (a.type == 'image') exp.content.style.cursor = 'move'; + } + } + return false; +}, + +wrapperMouseHandler : function (e) { + try { + if (!e) e = window.event; + var over = /mouseover/i.test(e.type); + if (!e.target) e.target = e.srcElement; // ie + if (hs.ie) e.relatedTarget = + over ? e.fromElement : e.toElement; // ie + var exp = hs.getExpander(e.target); + if (!exp.isExpanded) return; + if (!exp || !e.relatedTarget || hs.getExpander(e.relatedTarget, true) == exp + || hs.dragArgs) return; + for (var i = 0; i < exp.overlays.length; i++) (function() { + var o = hs.$('hsId'+ exp.overlays[i]); + if (o && o.hideOnMouseOut) { + if (over) hs.setStyles(o, { visibility: 'visible', display: '' }); + hs.animate(o, { opacity: over ? o.opacity : 0 }, o.dur); + } + })(); + } catch (e) {} +}, +addEventListener : function (el, event, func) { + if (el == document && event == 'ready') hs.push(hs.onReady, func); + try { + el.addEventListener(event, func, false); + } catch (e) { + try { + el.detachEvent('on'+ event, func); + el.attachEvent('on'+ event, func); + } catch (e) { + el['on'+ event] = func; + } + } +}, + +removeEventListener : function (el, event, func) { + try { + el.removeEventListener(event, func, false); + } catch (e) { + try { + el.detachEvent('on'+ event, func); + } catch (e) { + el['on'+ event] = null; + } + } +}, + +preloadFullImage : function (i) { + if (hs.continuePreloading && hs.preloadTheseImages[i] && hs.preloadTheseImages[i] != 'undefined') { + var img = document.createElement('img'); + img.onload = function() { + img = null; + hs.preloadFullImage(i + 1); + }; + img.src = hs.preloadTheseImages[i]; + } +}, +preloadImages : function (number) { + if (number && typeof number != 'object') hs.numberOfImagesToPreload = number; + + var arr = hs.getAnchors(); + for (var i = 0; i < arr.images.length && i < hs.numberOfImagesToPreload; i++) { + hs.push(hs.preloadTheseImages, hs.getSrc(arr.images[i])); + } + + // preload outlines + if (hs.outlineType) new hs.Outline(hs.outlineType, function () { hs.preloadFullImage(0)} ); + else + + hs.preloadFullImage(0); + + // preload cursor + if (hs.restoreCursor) var cur = hs.createElement('img', { src: hs.graphicsDir + hs.restoreCursor }); +}, + + +init : function () { + if (!hs.container) { + + hs.getPageSize(); + hs.ieLt7 = hs.ie && hs.uaVersion < 7; + hs.ie6SSL = hs.ieLt7 && location.protocol == 'https:'; + for (var x in hs.langDefaults) { + if (typeof hs[x] != 'undefined') hs.lang[x] = hs[x]; + else if (typeof hs.lang[x] == 'undefined' && typeof hs.langDefaults[x] != 'undefined') + hs.lang[x] = hs.langDefaults[x]; + } + + hs.container = hs.createElement('div', { + className: 'highslide-container' + }, { + position: 'absolute', + left: 0, + top: 0, + width: '100%', + zIndex: hs.zIndexCounter, + direction: 'ltr' + }, + document.body, + true + ); + hs.loading = hs.createElement('a', { + className: 'highslide-loading', + title: hs.lang.loadingTitle, + innerHTML: hs.lang.loadingText, + href: 'javascript:;' + }, { + position: 'absolute', + top: '-9999px', + opacity: hs.loadingOpacity, + zIndex: 1 + }, hs.container + ); + hs.garbageBin = hs.createElement('div', null, { display: 'none' }, hs.container); + hs.clearing = hs.createElement('div', null, + { clear: 'both', paddingTop: '1px' }, null, true); + + // http://www.robertpenner.com/easing/ + Math.linearTween = function (t, b, c, d) { + return c*t/d + b; + }; + Math.easeInQuad = function (t, b, c, d) { + return c*(t/=d)*t + b; + }; + + hs.hideSelects = hs.ieLt7; + hs.hideIframes = ((window.opera && hs.uaVersion < 9) || navigator.vendor == 'KDE' + || (hs.ie && hs.uaVersion < 5.5)); + } +}, +ready : function() { + if (hs.isReady) return; + hs.isReady = true; + + for (var i = 0; i < hs.onReady.length; i++) hs.onReady[i](); +}, + +updateAnchors : function() { + var el, els, all = [], images = [], htmls = [],groups = {}, re; + + for (var i = 0; i < hs.openerTagNames.length; i++) { + els = document.getElementsByTagName(hs.openerTagNames[i]); + for (var j = 0; j < els.length; j++) { + el = els[j]; + re = hs.isHsAnchor(el); + if (re) { + hs.push(all, el); + if (re[0] == 'hs.expand') hs.push(images, el); + else if (re[0] == 'hs.htmlExpand') hs.push(htmls, el); + var g = hs.getParam(el, 'slideshowGroup') || 'none'; + if (!groups[g]) groups[g] = []; + hs.push(groups[g], el); + } + } + } + hs.anchors = { all: all, groups: groups, images: images, htmls: htmls }; + return hs.anchors; + +}, + +getAnchors : function() { + return hs.anchors || hs.updateAnchors(); +}, + + +close : function(el) { + var exp = hs.getExpander(el); + if (exp) exp.close(); + return false; +} +}; // end hs object +hs.fx = function( elem, options, prop ){ + this.options = options; + this.elem = elem; + this.prop = prop; + + if (!options.orig) options.orig = {}; +}; +hs.fx.prototype = { + update: function(){ + (hs.fx.step[this.prop] || hs.fx.step._default)(this); + + if (this.options.step) + this.options.step.call(this.elem, this.now, this); + + }, + custom: function(from, to, unit){ + this.startTime = (new Date()).getTime(); + this.start = from; + this.end = to; + this.unit = unit;// || this.unit || "px"; + this.now = this.start; + this.pos = this.state = 0; + + var self = this; + function t(gotoEnd){ + return self.step(gotoEnd); + } + + t.elem = this.elem; + + if ( t() && hs.timers.push(t) == 1 ) { + hs.timerId = setInterval(function(){ + var timers = hs.timers; + + for ( var i = 0; i < timers.length; i++ ) + if ( !timers[i]() ) + timers.splice(i--, 1); + + if ( !timers.length ) { + clearInterval(hs.timerId); + } + }, 13); + } + }, + step: function(gotoEnd){ + var t = (new Date()).getTime(); + if ( gotoEnd || t >= this.options.duration + this.startTime ) { + this.now = this.end; + this.pos = this.state = 1; + this.update(); + + this.options.curAnim[ this.prop ] = true; + + var done = true; + for ( var i in this.options.curAnim ) + if ( this.options.curAnim[i] !== true ) + done = false; + + if ( done ) { + if (this.options.complete) this.options.complete.call(this.elem); + } + return false; + } else { + var n = t - this.startTime; + this.state = n / this.options.duration; + this.pos = this.options.easing(n, 0, 1, this.options.duration); + this.now = this.start + ((this.end - this.start) * this.pos); + this.update(); + } + return true; + } + +}; + +hs.extend( hs.fx, { + step: { + + opacity: function(fx){ + hs.setStyles(fx.elem, { opacity: fx.now }); + }, + + _default: function(fx){ + try { + if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) + fx.elem.style[ fx.prop ] = fx.now + fx.unit; + else + fx.elem[ fx.prop ] = fx.now; + } catch (e) {} + } + } +}); + +hs.Outline = function (outlineType, onLoad) { + this.onLoad = onLoad; + this.outlineType = outlineType; + var v = hs.uaVersion, tr; + + this.hasAlphaImageLoader = hs.ie && v >= 5.5 && v < 7; + if (!outlineType) { + if (onLoad) onLoad(); + return; + } + + hs.init(); + this.table = hs.createElement( + 'table', { + cellSpacing: 0 + }, { + visibility: 'hidden', + position: 'absolute', + borderCollapse: 'collapse', + width: 0 + }, + hs.container, + true + ); + var tbody = hs.createElement('tbody', null, null, this.table, 1); + + this.td = []; + for (var i = 0; i <= 8; i++) { + if (i % 3 == 0) tr = hs.createElement('tr', null, { height: 'auto' }, tbody, true); + this.td[i] = hs.createElement('td', null, null, tr, true); + var style = i != 4 ? { lineHeight: 0, fontSize: 0} : { position : 'relative' }; + hs.setStyles(this.td[i], style); + } + this.td[4].className = outlineType +' highslide-outline'; + + this.preloadGraphic(); +}; + +hs.Outline.prototype = { +preloadGraphic : function () { + var src = hs.graphicsDir + (hs.outlinesDir || "outlines/")+ this.outlineType +".png"; + + var appendTo = hs.safari ? hs.container : null; + this.graphic = hs.createElement('img', null, { position: 'absolute', + top: '-9999px' }, appendTo, true); // for onload trigger + + var pThis = this; + this.graphic.onload = function() { pThis.onGraphicLoad(); }; + + this.graphic.src = src; +}, + +onGraphicLoad : function () { + var o = this.offset = this.graphic.width / 4, + pos = [[0,0],[0,-4],[-2,0],[0,-8],0,[-2,-8],[0,-2],[0,-6],[-2,-2]], + dim = { height: (2*o) +'px', width: (2*o) +'px' }; + for (var i = 0; i <= 8; i++) { + if (pos[i]) { + if (this.hasAlphaImageLoader) { + var w = (i == 1 || i == 7) ? '100%' : this.graphic.width +'px'; + var div = hs.createElement('div', null, { width: '100%', height: '100%', position: 'relative', overflow: 'hidden'}, this.td[i], true); + hs.createElement ('div', null, { + filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale, src='"+ this.graphic.src + "')", + position: 'absolute', + width: w, + height: this.graphic.height +'px', + left: (pos[i][0]*o)+'px', + top: (pos[i][1]*o)+'px' + }, + div, + true); + } else { + hs.setStyles(this.td[i], { background: 'url('+ this.graphic.src +') '+ (pos[i][0]*o)+'px '+(pos[i][1]*o)+'px'}); + } + + if (window.opera && (i == 3 || i ==5)) + hs.createElement('div', null, dim, this.td[i], true); + + hs.setStyles (this.td[i], dim); + } + } + this.graphic = null; + if (hs.pendingOutlines[this.outlineType]) hs.pendingOutlines[this.outlineType].destroy(); + hs.pendingOutlines[this.outlineType] = this; + if (this.onLoad) this.onLoad(); +}, + +setPosition : function (pos, offset, vis, dur, easing) { + var exp = this.exp, + stl = exp.wrapper.style, + offset = offset || 0, + pos = pos || { + x: exp.x.pos + offset, + y: exp.y.pos + offset, + w: exp.x.get('wsize') - 2 * offset, + h: exp.y.get('wsize') - 2 * offset + }; + if (vis) this.table.style.visibility = (pos.h >= 4 * this.offset) + ? 'visible' : 'hidden'; + hs.setStyles(this.table, { + left: (pos.x - this.offset) +'px', + top: (pos.y - this.offset) +'px', + width: (pos.w + 2 * this.offset) +'px' + }); + + pos.w -= 2 * this.offset; + pos.h -= 2 * this.offset; + hs.setStyles (this.td[4], { + width: pos.w >= 0 ? pos.w +'px' : 0, + height: pos.h >= 0 ? pos.h +'px' : 0 + }); + if (this.hasAlphaImageLoader) this.td[3].style.height + = this.td[5].style.height = this.td[4].style.height; + +}, + +destroy : function(hide) { + if (hide) this.table.style.visibility = 'hidden'; + else hs.discardElement(this.table); +} +}; + +hs.Dimension = function(exp, dim) { + this.exp = exp; + this.dim = dim; + this.ucwh = dim == 'x' ? 'Width' : 'Height'; + this.wh = this.ucwh.toLowerCase(); + this.uclt = dim == 'x' ? 'Left' : 'Top'; + this.lt = this.uclt.toLowerCase(); + this.ucrb = dim == 'x' ? 'Right' : 'Bottom'; + this.rb = this.ucrb.toLowerCase(); + this.p1 = this.p2 = 0; +}; +hs.Dimension.prototype = { +get : function(key) { + switch (key) { + case 'loadingPos': + return this.tpos + this.tb + (this.t - hs.loading['offset'+ this.ucwh]) / 2; + case 'wsize': + return this.size + 2 * this.cb + this.p1 + this.p2; + case 'fitsize': + return this.clientSize - this.marginMin - this.marginMax; + case 'maxsize': + return this.get('fitsize') - 2 * this.cb - this.p1 - this.p2 ; + case 'opos': + return this.pos - (this.exp.outline ? this.exp.outline.offset : 0); + case 'osize': + return this.get('wsize') + (this.exp.outline ? 2*this.exp.outline.offset : 0); + case 'imgPad': + return this.imgSize ? Math.round((this.size - this.imgSize) / 2) : 0; + + } +}, +calcBorders: function() { + // correct for borders + this.cb = (this.exp.content['offset'+ this.ucwh] - this.t) / 2; + + this.marginMax = hs['margin'+ this.ucrb]; +}, +calcThumb: function() { + this.t = this.exp.el[this.wh] ? parseInt(this.exp.el[this.wh]) : + this.exp.el['offset'+ this.ucwh]; + this.tpos = this.exp.tpos[this.dim]; + this.tb = (this.exp.el['offset'+ this.ucwh] - this.t) / 2; + if (this.tpos == 0 || this.tpos == -1) { + this.tpos = (hs.page[this.wh] / 2) + hs.page['scroll'+ this.uclt]; + }; +}, +calcExpanded: function() { + var exp = this.exp; + this.justify = 'auto'; + + + // size and position + this.pos = this.tpos - this.cb + this.tb; + + if (this.maxHeight && this.dim == 'x') + exp.maxWidth = Math.min(exp.maxWidth || this.full, exp.maxHeight * this.full / exp.y.full); + + this.size = Math.min(this.full, exp['max'+ this.ucwh] || this.full); + this.minSize = exp.allowSizeReduction ? + Math.min(exp['min'+ this.ucwh], this.full) :this.full; + if (exp.isImage && exp.useBox) { + this.size = exp[this.wh]; + this.imgSize = this.full; + } + if (this.dim == 'x' && hs.padToMinWidth) this.minSize = exp.minWidth; + this.marginMin = hs['margin'+ this.uclt]; + this.scroll = hs.page['scroll'+ this.uclt]; + this.clientSize = hs.page[this.wh]; +}, +setSize: function(i) { + var exp = this.exp; + if (exp.isImage && (exp.useBox || hs.padToMinWidth)) { + this.imgSize = i; + this.size = Math.max(this.size, this.imgSize); + exp.content.style[this.lt] = this.get('imgPad')+'px'; + } else + this.size = i; + + exp.content.style[this.wh] = i +'px'; + exp.wrapper.style[this.wh] = this.get('wsize') +'px'; + if (exp.outline) exp.outline.setPosition(); + if (exp.releaseMask) exp.releaseMask.style[this.wh] = i +'px'; + if (this.dim == 'y' && exp.iDoc && exp.body.style.height != 'auto') try { + exp.iDoc.body.style.overflow = 'auto'; + } catch (e) {} + if (exp.isHtml) { + var d = exp.scrollerDiv; + if (this.sizeDiff === undefined) + this.sizeDiff = exp.innerContent['offset'+ this.ucwh] - d['offset'+ this.ucwh]; + d.style[this.wh] = (this.size - this.sizeDiff) +'px'; + + if (this.dim == 'x') exp.mediumContent.style.width = 'auto'; + if (exp.body) exp.body.style[this.wh] = 'auto'; + } + if (this.dim == 'x' && exp.overlayBox) exp.sizeOverlayBox(true); +}, +setPos: function(i) { + this.pos = i; + this.exp.wrapper.style[this.lt] = i +'px'; + + if (this.exp.outline) this.exp.outline.setPosition(); + +} +}; + +hs.Expander = function(a, params, custom, contentType) { + if (document.readyState && hs.ie && !hs.isReady) { + hs.addEventListener(document, 'ready', function() { + new hs.Expander(a, params, custom, contentType); + }); + return; + } + this.a = a; + this.custom = custom; + this.contentType = contentType || 'image'; + this.isHtml = (contentType == 'html'); + this.isImage = !this.isHtml; + + hs.continuePreloading = false; + this.overlays = []; + hs.init(); + var key = this.key = hs.expanders.length; + // override inline parameters + for (var i = 0; i < hs.overrides.length; i++) { + var name = hs.overrides[i]; + this[name] = params && typeof params[name] != 'undefined' ? + params[name] : hs[name]; + } + if (!this.src) this.src = a.href; + + // get thumb + var el = (params && params.thumbnailId) ? hs.$(params.thumbnailId) : a; + el = this.thumb = el.getElementsByTagName('img')[0] || el; + this.thumbsUserSetId = el.id || a.id; + + // check if already open + for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && hs.expanders[i].a == a) { + hs.expanders[i].focus(); + return false; + } + } + + // cancel other + if (!hs.allowSimultaneousLoading) for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && hs.expanders[i].thumb != el && !hs.expanders[i].onLoadStarted) { + hs.expanders[i].cancelLoading(); + } + } + hs.expanders[key] = this; + if (!hs.allowMultipleInstances && !hs.upcoming) { + if (hs.expanders[key-1]) hs.expanders[key-1].close(); + if (typeof hs.focusKey != 'undefined' && hs.expanders[hs.focusKey]) + hs.expanders[hs.focusKey].close(); + } + + // initiate metrics + this.el = el; + this.tpos = hs.getPosition(el); + hs.getPageSize(); + var x = this.x = new hs.Dimension(this, 'x'); + x.calcThumb(); + var y = this.y = new hs.Dimension(this, 'y'); + y.calcThumb(); + this.wrapper = hs.createElement( + 'div', { + id: 'highslide-wrapper-'+ this.key, + className: 'highslide-wrapper '+ this.wrapperClassName + }, { + visibility: 'hidden', + position: 'absolute', + zIndex: hs.zIndexCounter += 2 + }, null, true ); + + this.wrapper.onmouseover = this.wrapper.onmouseout = hs.wrapperMouseHandler; + if (this.contentType == 'image' && this.outlineWhileAnimating == 2) + this.outlineWhileAnimating = 0; + + // get the outline + if (!this.outlineType) { + this[this.contentType +'Create'](); + + } else if (hs.pendingOutlines[this.outlineType]) { + this.connectOutline(); + this[this.contentType +'Create'](); + + } else { + this.showLoading(); + var exp = this; + new hs.Outline(this.outlineType, + function () { + exp.connectOutline(); + exp[exp.contentType +'Create'](); + } + ); + } + return true; +}; + +hs.Expander.prototype = { +error : function(e) { + // alert ('Line '+ e.lineNumber +': '+ e.message); + window.location.href = this.src; +}, + +connectOutline : function() { + var outline = this.outline = hs.pendingOutlines[this.outlineType]; + outline.exp = this; + outline.table.style.zIndex = this.wrapper.style.zIndex - 1; + hs.pendingOutlines[this.outlineType] = null; +}, + +showLoading : function() { + if (this.onLoadStarted || this.loading) return; + + this.loading = hs.loading; + var exp = this; + this.loading.onclick = function() { + exp.cancelLoading(); + }; + var exp = this, + l = this.x.get('loadingPos') +'px', + t = this.y.get('loadingPos') +'px'; + setTimeout(function () { + if (exp.loading) hs.setStyles(exp.loading, { left: l, top: t, zIndex: hs.zIndexCounter++ })} + , 100); +}, + +imageCreate : function() { + var exp = this; + + var img = document.createElement('img'); + this.content = img; + img.onload = function () { + if (hs.expanders[exp.key]) exp.contentLoaded(); + }; + if (hs.blockRightClick) img.oncontextmenu = function() { return false; }; + img.className = 'highslide-image'; + hs.setStyles(img, { + visibility: 'hidden', + display: 'block', + position: 'absolute', + maxWidth: '9999px', + zIndex: 3 + }); + img.title = hs.lang.restoreTitle; + if (hs.safari) hs.container.appendChild(img); + if (hs.ie && hs.flushImgSize) img.src = null; + img.src = this.src; + + this.showLoading(); +}, + +htmlCreate : function () { + + this.content = hs.getCacheBinding(this.a); + if (!this.content) + this.content = hs.getNode(this.contentId); + if (!this.content) + this.content = hs.getSelfRendered(); + this.getInline(['maincontent']); + if (this.maincontent) { + var body = hs.getElementByClass(this.content, 'div', 'highslide-body'); + if (body) body.appendChild(this.maincontent); + this.maincontent.style.display = 'block'; + } + + var innerContent = this.innerContent = this.content; + + if (/(swf|iframe)/.test(this.objectType)) this.setObjContainerSize(innerContent); + + // the content tree + hs.container.appendChild(this.wrapper); + hs.setStyles( this.wrapper, { + position: 'static', + padding: '0 '+ hs.marginRight +'px 0 '+ hs.marginLeft +'px' + }); + this.content = hs.createElement( + 'div', { + className: 'highslide-html' + }, { + position: 'relative', + zIndex: 3, + overflow: 'hidden' + }, + this.wrapper + ); + this.mediumContent = hs.createElement('div', null, null, this.content, 1); + this.mediumContent.appendChild(innerContent); + + hs.setStyles (innerContent, { + position: 'relative', + display: 'block', + direction: hs.lang.cssDirection || '' + }); + if (this.width) innerContent.style.width = this.width +'px'; + if (this.height) hs.setStyles(innerContent, { + height: this.height +'px', + overflow: 'hidden' + }); + if (innerContent.offsetWidth < this.minWidth) + innerContent.style.width = this.minWidth +'px'; + + + + if (this.objectType == 'ajax' && !hs.getCacheBinding(this.a)) { + this.showLoading(); + var exp = this; + var ajax = new hs.Ajax(this.a, innerContent); + ajax.src = this.src; + ajax.onLoad = function () { if (hs.expanders[exp.key]) exp.contentLoaded(); }; + ajax.onError = function () { location.href = exp.src; }; + ajax.run(); + } + else + + if (this.objectType == 'iframe' && this.objectLoadTime == 'before') { + this.writeExtendedContent(); + } + else + this.contentLoaded(); +}, + +contentLoaded : function() { + try { + if (!this.content) return; + this.content.onload = null; + if (this.onLoadStarted) return; + else this.onLoadStarted = true; + + var x = this.x, y = this.y; + + if (this.loading) { + hs.setStyles(this.loading, { top: '-9999px' }); + this.loading = null; + } + if (this.isImage) { + x.full = this.content.width; + y.full = this.content.height; + + hs.setStyles(this.content, { + width: x.t +'px', + height: y.t +'px' + }); + this.wrapper.appendChild(this.content); + hs.container.appendChild(this.wrapper); + } else if (this.htmlGetSize) this.htmlGetSize(); + + x.calcBorders(); + y.calcBorders(); + + hs.setStyles (this.wrapper, { + left: (x.tpos + x.tb - x.cb) +'px', + top: (y.tpos + x.tb - y.cb) +'px' + }); + this.getOverlays(); + + var ratio = x.full / y.full; + x.calcExpanded(); + this.justify(x); + + y.calcExpanded(); + this.justify(y); + if (this.isHtml) this.htmlSizeOperations(); + if (this.overlayBox) this.sizeOverlayBox(0, 1); + + + if (this.allowSizeReduction) { + if (this.isImage) + this.correctRatio(ratio); + else this.fitOverlayBox(); + if (this.isImage && this.x.full > (this.x.imgSize || this.x.size)) { + this.createFullExpand(); + if (this.overlays.length == 1) this.sizeOverlayBox(); + } + } + this.show(); + + } catch (e) { + this.error(e); + } +}, + + +setObjContainerSize : function(parent, auto) { + var c = hs.getElementByClass(parent, 'DIV', 'highslide-body'); + if (/(iframe|swf)/.test(this.objectType)) { + if (this.objectWidth) c.style.width = this.objectWidth +'px'; + if (this.objectHeight) c.style.height = this.objectHeight +'px'; + } +}, + +writeExtendedContent : function () { + if (this.hasExtendedContent) return; + var exp = this; + this.body = hs.getElementByClass(this.innerContent, 'DIV', 'highslide-body'); + if (this.objectType == 'iframe') { + this.showLoading(); + var ruler = hs.clearing.cloneNode(1); + this.body.appendChild(ruler); + this.newWidth = this.innerContent.offsetWidth; + if (!this.objectWidth) this.objectWidth = ruler.offsetWidth; + var hDiff = this.innerContent.offsetHeight - this.body.offsetHeight, + h = this.objectHeight || hs.page.height - hDiff - hs.marginTop - hs.marginBottom, + onload = this.objectLoadTime == 'before' ? + ' onload="if (hs.expanders['+ this.key +']) hs.expanders['+ this.key +'].contentLoaded()" ' : ''; + this.body.innerHTML += ''; + this.ruler = this.body.getElementsByTagName('div')[0]; + this.iframe = this.body.getElementsByTagName('iframe')[0]; + + if (this.objectLoadTime == 'after') this.correctIframeSize(); + + } + if (this.objectType == 'swf') { + this.body.id = this.body.id || 'hs-flash-id-' + this.key; + var a = this.swfOptions; + if (!a.params) a.params = {}; + if (typeof a.params.wmode == 'undefined') a.params.wmode = 'transparent'; + if (swfobject) swfobject.embedSWF(this.src, this.body.id, this.objectWidth, this.objectHeight, + a.version || '7', a.expressInstallSwfurl, a.flashvars, a.params, a.attributes); + } + this.hasExtendedContent = true; +}, +htmlGetSize : function() { + if (this.iframe && !this.objectHeight) { // loadtime before + this.iframe.style.height = this.body.style.height = this.getIframePageHeight() +'px'; + } + this.innerContent.appendChild(hs.clearing); + if (!this.x.full) this.x.full = this.innerContent.offsetWidth; + this.y.full = this.innerContent.offsetHeight; + this.innerContent.removeChild(hs.clearing); + if (hs.ie && this.newHeight > parseInt(this.innerContent.currentStyle.height)) { // ie css bug + this.newHeight = parseInt(this.innerContent.currentStyle.height); + } + hs.setStyles( this.wrapper, { position: 'absolute', padding: '0'}); + hs.setStyles( this.content, { width: this.x.t +'px', height: this.y.t +'px'}); + +}, + +getIframePageHeight : function() { + var h; + try { + var doc = this.iDoc = this.iframe.contentDocument || this.iframe.contentWindow.document; + var clearing = doc.createElement('div'); + clearing.style.clear = 'both'; + doc.body.appendChild(clearing); + h = clearing.offsetTop; + if (hs.ie) h += parseInt(doc.body.currentStyle.marginTop) + + parseInt(doc.body.currentStyle.marginBottom) - 1; + } catch (e) { // other domain + h = 300; + } + return h; +}, +correctIframeSize : function () { + var wDiff = this.innerContent.offsetWidth - this.ruler.offsetWidth; + hs.discardElement(this.ruler); + if (wDiff < 0) wDiff = 0; + + var hDiff = this.innerContent.offsetHeight - this.iframe.offsetHeight; + if (this.iDoc && !this.objectHeight && !this.height && this.y.size == this.y.full) try { + this.iDoc.body.style.overflow = 'hidden'; + } catch (e) {} + hs.setStyles(this.iframe, { + width: Math.abs(this.x.size - wDiff) +'px', + height: Math.abs(this.y.size - hDiff) +'px' + }); + hs.setStyles(this.body, { + width: this.iframe.style.width, + height: this.iframe.style.height + }); + + this.scrollingContent = this.iframe; + this.scrollerDiv = this.scrollingContent; + +}, +htmlSizeOperations : function () { + + this.setObjContainerSize(this.innerContent); + + + if (this.objectType == 'swf' && this.objectLoadTime == 'before') this.writeExtendedContent(); + + // handle minimum size + if (this.x.size < this.x.full && !this.allowWidthReduction) this.x.size = this.x.full; + if (this.y.size < this.y.full && !this.allowHeightReduction) this.y.size = this.y.full; + this.scrollerDiv = this.innerContent; + hs.setStyles(this.mediumContent, { + position: 'relative', + width: this.x.size +'px' + }); + hs.setStyles(this.innerContent, { + border: 'none', + width: 'auto', + height: 'auto' + }); + var node = hs.getElementByClass(this.innerContent, 'DIV', 'highslide-body'); + if (node && !/(iframe|swf)/.test(this.objectType)) { + var cNode = node; // wrap to get true size + node = hs.createElement(cNode.nodeName, null, {overflow: 'hidden'}, null, true); + cNode.parentNode.insertBefore(node, cNode); + node.appendChild(hs.clearing); // IE6 + node.appendChild(cNode); + + var wDiff = this.innerContent.offsetWidth - node.offsetWidth; + var hDiff = this.innerContent.offsetHeight - node.offsetHeight; + node.removeChild(hs.clearing); + + var kdeBugCorr = hs.safari || navigator.vendor == 'KDE' ? 1 : 0; // KDE repainting bug + hs.setStyles(node, { + width: (this.x.size - wDiff - kdeBugCorr) +'px', + height: (this.y.size - hDiff) +'px', + overflow: 'auto', + position: 'relative' + } + ); + if (kdeBugCorr && cNode.offsetHeight > node.offsetHeight) { + node.style.width = (parseInt(node.style.width) + kdeBugCorr) + 'px'; + } + this.scrollingContent = node; + this.scrollerDiv = this.scrollingContent; + } + if (this.iframe && this.objectLoadTime == 'before') this.correctIframeSize(); + if (!this.scrollingContent && this.y.size < this.mediumContent.offsetHeight) this.scrollerDiv = this.content; + + if (this.scrollerDiv == this.content && !this.allowWidthReduction && !/(iframe|swf)/.test(this.objectType)) { + this.x.size += 17; // room for scrollbars + } + if (this.scrollerDiv && this.scrollerDiv.offsetHeight > this.scrollerDiv.parentNode.offsetHeight) { + setTimeout("try { hs.expanders["+ this.key +"].scrollerDiv.style.overflow = 'auto'; } catch(e) {}", + hs.expandDuration); + } +}, + +justify : function (p, moveOnly) { + var tgtArr, tgt = p.target, dim = p == this.x ? 'x' : 'y'; + + var hasMovedMin = false; + + var allowReduce = p.exp.allowSizeReduction; + p.pos = Math.round(p.pos - ((p.get('wsize') - p.t) / 2)); + if (p.pos < p.scroll + p.marginMin) { + p.pos = p.scroll + p.marginMin; + hasMovedMin = true; + } + if (!moveOnly && p.size < p.minSize) { + p.size = p.minSize; + allowReduce = false; + } + if (p.pos + p.get('wsize') > p.scroll + p.clientSize - p.marginMax) { + if (!moveOnly && hasMovedMin && allowReduce) { + p.size = Math.min(p.size, p.get(dim == 'y' ? 'fitsize' : 'maxsize')); + } else if (p.get('wsize') < p.get('fitsize')) { + p.pos = p.scroll + p.clientSize - p.marginMax - p.get('wsize'); + } else { // image larger than viewport + p.pos = p.scroll + p.marginMin; + if (!moveOnly && allowReduce) p.size = p.get(dim == 'y' ? 'fitsize' : 'maxsize'); + } + } + + if (!moveOnly && p.size < p.minSize) { + p.size = p.minSize; + allowReduce = false; + } + + + + if (p.pos < p.marginMin) { + var tmpMin = p.pos; + p.pos = p.marginMin; + + if (allowReduce && !moveOnly) p.size = p.size - (p.pos - tmpMin); + + } +}, + +correctRatio : function(ratio) { + var x = this.x, + y = this.y, + changed = false, + xSize = Math.min(x.full, x.size), + ySize = Math.min(y.full, y.size), + useBox = (this.useBox || hs.padToMinWidth); + + if (xSize / ySize > ratio) { // width greater + xSize = ySize * ratio; + if (xSize < x.minSize) { // below minWidth + xSize = x.minSize; + ySize = xSize / ratio; + } + changed = true; + + } else if (xSize / ySize < ratio) { // height greater + ySize = xSize / ratio; + changed = true; + } + + if (hs.padToMinWidth && x.full < x.minSize) { + x.imgSize = x.full; + y.size = y.imgSize = y.full; + } else if (this.useBox) { + x.imgSize = xSize; + y.imgSize = ySize; + } else { + x.size = xSize; + y.size = ySize; + } + changed = this.fitOverlayBox(useBox ? null : ratio, changed); + if (useBox && y.size < y.imgSize) { + y.imgSize = y.size; + x.imgSize = y.size * ratio; + } + if (changed || useBox) { + x.pos = x.tpos - x.cb + x.tb; + x.minSize = x.size; + this.justify(x, true); + + y.pos = y.tpos - y.cb + y.tb; + y.minSize = y.size; + this.justify(y, true); + if (this.overlayBox) this.sizeOverlayBox(); + } +}, +fitOverlayBox : function(ratio, changed) { + var x = this.x, y = this.y; + if (this.overlayBox && (this.isImage || this.allowHeightReduction)) { + while (y.size > this.minHeight && x.size > this.minWidth + && y.get('wsize') > y.get('fitsize')) { + y.size -= 10; + if (ratio) x.size = y.size * ratio; + this.sizeOverlayBox(0, 1); + changed = true; + } + } + return changed; +}, + +show : function () { + var x = this.x, y = this.y; + this.doShowHide('hidden'); + + // Apply size change + this.changeSize( + 1, { + wrapper: { + width : x.get('wsize'), + height : y.get('wsize'), + left: x.pos, + top: y.pos + }, + content: { + left: x.p1 + x.get('imgPad'), + top: y.p1 + y.get('imgPad'), + width:x.imgSize ||x.size, + height:y.imgSize ||y.size + } + }, + hs.expandDuration + ); +}, + +changeSize : function(up, to, dur) { + + if (this.outline && !this.outlineWhileAnimating) { + if (up) this.outline.setPosition(); + else this.outline.destroy( + (this.isHtml && this.preserveContent)); + } + + + if (!up) this.destroyOverlays(); + + var exp = this, + x = exp.x, + y = exp.y, + easing = this.easing; + if (!up) easing = this.easingClose || easing; + var after = up ? + function() { + + if (exp.outline) exp.outline.table.style.visibility = "visible"; + setTimeout(function() { + exp.afterExpand(); + }, 50); + } : + function() { + exp.afterClose(); + }; + if (up) hs.setStyles( this.wrapper, { + width: x.t +'px', + height: y.t +'px' + }); + if (up && this.isHtml) { + hs.setStyles(this.wrapper, { + left: (x.tpos - x.cb + x.tb) +'px', + top: (y.tpos - y.cb + y.tb) +'px' + }); + } + if (this.fadeInOut) { + hs.setStyles(this.wrapper, { opacity: up ? 0 : 1 }); + hs.extend(to.wrapper, { opacity: up }); + } + hs.animate( this.wrapper, to.wrapper, { + duration: dur, + easing: easing, + step: function(val, args) { + if (exp.outline && exp.outlineWhileAnimating && args.prop == 'top') { + var fac = up ? args.pos : 1 - args.pos; + var pos = { + w: x.t + (x.get('wsize') - x.t) * fac, + h: y.t + (y.get('wsize') - y.t) * fac, + x: x.tpos + (x.pos - x.tpos) * fac, + y: y.tpos + (y.pos - y.tpos) * fac + }; + exp.outline.setPosition(pos, 0, 1); + } + if (exp.isHtml) { + if (args.prop == 'left') + exp.mediumContent.style.left = (x.pos - val) +'px'; + if (args.prop == 'top') + exp.mediumContent.style.top = (y.pos - val) +'px'; + } + } + }); + hs.animate( this.content, to.content, dur, easing, after); + if (up) { + this.wrapper.style.visibility = 'visible'; + this.content.style.visibility = 'visible'; + if (this.isHtml) this.innerContent.style.visibility = 'visible'; + this.a.className += ' highslide-active-anchor'; + } +}, + + + + +afterExpand : function() { + this.isExpanded = true; + this.focus(); + + if (this.isHtml && this.objectLoadTime == 'after') this.writeExtendedContent(); + if (this.iframe) { + try { + var exp = this, + doc = this.iframe.contentDocument || this.iframe.contentWindow.document; + hs.addEventListener(doc, 'mousedown', function () { + if (hs.focusKey != exp.key) exp.focus(); + }); + } catch(e) {} + if (hs.ie && typeof this.isClosing != 'boolean') // first open + this.iframe.style.width = (this.objectWidth - 1) +'px'; // hasLayout + } + if (hs.upcoming && hs.upcoming == this.a) hs.upcoming = null; + this.prepareNextOutline(); + var p = hs.page, mX = hs.mouse.x + p.scrollLeft, mY = hs.mouse.y + p.scrollTop; + this.mouseIsOver = this.x.pos < mX && mX < this.x.pos + this.x.get('wsize') + && this.y.pos < mY && mY < this.y.pos + this.y.get('wsize'); + if (this.overlayBox) this.showOverlays(); + +}, + + +prepareNextOutline : function() { + var key = this.key; + var outlineType = this.outlineType; + new hs.Outline(outlineType, + function () { try { hs.expanders[key].preloadNext(); } catch (e) {} }); +}, + + +preloadNext : function() { + var next = this.getAdjacentAnchor(1); + if (next && next.onclick.toString().match(/hs\.expand/)) + var img = hs.createElement('img', { src: hs.getSrc(next) }); +}, + + +getAdjacentAnchor : function(op) { + var current = this.getAnchorIndex(), as = hs.anchors.groups[this.slideshowGroup || 'none']; + + /*< ? if ($cfg->slideshow) : ?>s*/ + if (!as[current + op] && this.slideshow && this.slideshow.repeat) { + if (op == 1) return as[0]; + else if (op == -1) return as[as.length-1]; + } + /*< ? endif ?>s*/ + return as[current + op] || null; +}, + +getAnchorIndex : function() { + var arr = hs.getAnchors().groups[this.slideshowGroup || 'none']; + if (arr) for (var i = 0; i < arr.length; i++) { + if (arr[i] == this.a) return i; + } + return null; +}, + + +cancelLoading : function() { + hs.discardElement (this.wrapper); + hs.expanders[this.key] = null; + if (this.loading) hs.loading.style.left = '-9999px'; +}, + +writeCredits : function () { + this.credits = hs.createElement('a', { + href: hs.creditsHref, + target: hs.creditsTarget, + className: 'highslide-credits', + innerHTML: hs.lang.creditsText, + title: hs.lang.creditsTitle + }); + this.createOverlay({ + overlayId: this.credits, + position: this.creditsPosition || 'top left' + }); +}, + +getInline : function(types, addOverlay) { + for (var i = 0; i < types.length; i++) { + var type = types[i], s = null; + if (!this[type +'Id'] && this.thumbsUserSetId) + this[type +'Id'] = type +'-for-'+ this.thumbsUserSetId; + if (this[type +'Id']) this[type] = hs.getNode(this[type +'Id']); + if (!this[type] && !this[type +'Text'] && this[type +'Eval']) try { + s = eval(this[type +'Eval']); + } catch (e) {} + if (!this[type] && this[type +'Text']) { + s = this[type +'Text']; + } + if (!this[type] && !s) { + this[type] = hs.getNode(this.a['_'+ type + 'Id']); + if (!this[type]) { + var next = this.a.nextSibling; + while (next && !hs.isHsAnchor(next)) { + if ((new RegExp('highslide-'+ type)).test(next.className || null)) { + if (!next.id) this.a['_'+ type + 'Id'] = next.id = 'hsId'+ hs.idCounter++; + this[type] = hs.getNode(next.id); + break; + } + next = next.nextSibling; + } + } + } + + if (!this[type] && s) this[type] = hs.createElement('div', + { className: 'highslide-'+ type, innerHTML: s } ); + + if (addOverlay && this[type]) { + var o = { position: (type == 'heading') ? 'above' : 'below' }; + for (var x in this[type+'Overlay']) o[x] = this[type+'Overlay'][x]; + o.overlayId = this[type]; + this.createOverlay(o); + } + } +}, + + +// on end move and resize +doShowHide : function(visibility) { + if (hs.hideSelects) this.showHideElements('SELECT', visibility); + if (hs.hideIframes) this.showHideElements('IFRAME', visibility); + if (hs.geckoMac) this.showHideElements('*', visibility); +}, +showHideElements : function (tagName, visibility) { + var els = document.getElementsByTagName(tagName); + var prop = tagName == '*' ? 'overflow' : 'visibility'; + for (var i = 0; i < els.length; i++) { + if (prop == 'visibility' || (document.defaultView.getComputedStyle( + els[i], "").getPropertyValue('overflow') == 'auto' + || els[i].getAttribute('hidden-by') != null)) { + var hiddenBy = els[i].getAttribute('hidden-by'); + if (visibility == 'visible' && hiddenBy) { + hiddenBy = hiddenBy.replace('['+ this.key +']', ''); + els[i].setAttribute('hidden-by', hiddenBy); + if (!hiddenBy) els[i].style[prop] = els[i].origProp; + } else if (visibility == 'hidden') { // hide if behind + var elPos = hs.getPosition(els[i]); + elPos.w = els[i].offsetWidth; + elPos.h = els[i].offsetHeight; + + + var clearsX = (elPos.x + elPos.w < this.x.get('opos') + || elPos.x > this.x.get('opos') + this.x.get('osize')); + var clearsY = (elPos.y + elPos.h < this.y.get('opos') + || elPos.y > this.y.get('opos') + this.y.get('osize')); + var wrapperKey = hs.getWrapperKey(els[i]); + if (!clearsX && !clearsY && wrapperKey != this.key) { // element falls behind image + if (!hiddenBy) { + els[i].setAttribute('hidden-by', '['+ this.key +']'); + els[i].origProp = els[i].style[prop]; + els[i].style[prop] = 'hidden'; + + } else if (hiddenBy.indexOf('['+ this.key +']') == -1) { + els[i].setAttribute('hidden-by', hiddenBy + '['+ this.key +']'); + } + } else if ((hiddenBy == '['+ this.key +']' || hs.focusKey == wrapperKey) + && wrapperKey != this.key) { // on move + els[i].setAttribute('hidden-by', ''); + els[i].style[prop] = els[i].origProp || ''; + } else if (hiddenBy && hiddenBy.indexOf('['+ this.key +']') > -1) { + els[i].setAttribute('hidden-by', hiddenBy.replace('['+ this.key +']', '')); + } + + } + } + } +}, + +focus : function() { + this.wrapper.style.zIndex = hs.zIndexCounter += 2; + // blur others + for (var i = 0; i < hs.expanders.length; i++) { + if (hs.expanders[i] && i == hs.focusKey) { + var blurExp = hs.expanders[i]; + blurExp.content.className += ' highslide-'+ blurExp.contentType +'-blur'; + if (blurExp.isImage) { + blurExp.content.style.cursor = hs.ie ? 'hand' : 'pointer'; + blurExp.content.title = hs.lang.focusTitle; + } + } + } + + // focus this + if (this.outline) this.outline.table.style.zIndex + = this.wrapper.style.zIndex - 1; + this.content.className = 'highslide-'+ this.contentType; + if (this.isImage) { + this.content.title = hs.lang.restoreTitle; + + if (hs.restoreCursor) { + hs.styleRestoreCursor = window.opera ? 'pointer' : 'url('+ hs.graphicsDir + hs.restoreCursor +'), pointer'; + if (hs.ie && hs.uaVersion < 6) hs.styleRestoreCursor = 'hand'; + this.content.style.cursor = hs.styleRestoreCursor; + } + } + hs.focusKey = this.key; + hs.addEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); +}, +moveTo: function(x, y) { + this.x.setPos(x); + this.y.setPos(y); +}, +resize : function (e) { + var w, h, r = e.width / e.height; + w = Math.max(e.width + e.dX, Math.min(this.minWidth, this.x.full)); + if (this.isImage && Math.abs(w - this.x.full) < 12) w = this.x.full; + h = this.isHtml ? e.height + e.dY : w / r; + if (h < Math.min(this.minHeight, this.y.full)) { + h = Math.min(this.minHeight, this.y.full); + if (this.isImage) w = h * r; + } + this.resizeTo(w, h); +}, +resizeTo: function(w, h) { + this.y.setSize(h); + this.x.setSize(w); + this.wrapper.style.height = this.y.get('wsize') +'px'; +}, + +close : function() { + if (this.isClosing || !this.isExpanded) return; + this.isClosing = true; + + hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler); + + try { + if (this.isHtml) this.htmlPrepareClose(); + this.content.style.cursor = 'default'; + this.changeSize( + 0, { + wrapper: { + width : this.x.t, + height : this.y.t, + left: this.x.tpos - this.x.cb + this.x.tb, + top: this.y.tpos - this.y.cb + this.y.tb + }, + content: { + left: 0, + top: 0, + width: this.x.t, + height: this.y.t + } + }, hs.restoreDuration + ); + } catch (e) { this.afterClose(); } +}, + +htmlPrepareClose : function() { + if (hs.geckoMac) { // bad redraws + if (!hs.mask) hs.mask = hs.createElement('div', null, + { position: 'absolute' }, hs.container); + hs.setStyles(hs.mask, { width: this.x.size +'px', height: this.y.size +'px', + left: this.x.pos +'px', top: this.y.pos +'px', display: 'block' }); + } + if (this.objectType == 'swf') try { hs.$(this.body.id).StopPlay(); } catch (e) {} + + if (this.objectLoadTime == 'after' && !this.preserveContent) this.destroyObject(); + if (this.scrollerDiv && this.scrollerDiv != this.scrollingContent) + this.scrollerDiv.style.overflow = 'hidden'; +}, + +destroyObject : function () { + if (hs.ie && this.iframe) + try { this.iframe.contentWindow.document.body.innerHTML = ''; } catch (e) {} + if (this.objectType == 'swf') swfobject.removeSWF(this.body.id); + this.body.innerHTML = ''; +}, + +sleep : function() { + if (this.outline) this.outline.table.style.display = 'none'; + this.releaseMask = null; + this.wrapper.style.display = 'none'; + hs.push(hs.sleeping, this); +}, + +awake : function() {try { + + hs.expanders[this.key] = this; + + if (!hs.allowMultipleInstances &&hs.focusKey != this.key) { + try { hs.expanders[hs.focusKey].close(); } catch (e){} + } + + var z = hs.zIndexCounter++, stl = { display: '', zIndex: z }; + hs.setStyles (this.wrapper, stl); + this.isClosing = false; + + var o = this.outline || 0; + if (o) { + if (!this.outlineWhileAnimating) stl.visibility = 'hidden'; + hs.setStyles (o.table, stl); + } + + this.show(); +} catch (e) {} + + +}, + +createOverlay : function (o) { + var el = o.overlayId; + if (typeof el == 'string') el = hs.getNode(el); + if (o.html) el = hs.createElement('div', { innerHTML: o.html }); + if (!el || typeof el == 'string') return; + el.style.display = 'block'; + this.genOverlayBox(); + var width = o.width && /^[0-9]+(px|%)$/.test(o.width) ? o.width : 'auto'; + if (/^(left|right)panel$/.test(o.position) && !/^[0-9]+px$/.test(o.width)) width = '200px'; + var overlay = hs.createElement( + 'div', { + id: 'hsId'+ hs.idCounter++, + hsId: o.hsId + }, { + position: 'absolute', + visibility: 'hidden', + width: width, + direction: hs.lang.cssDirection || '', + opacity: 0 + },this.overlayBox, + true + ); + + overlay.appendChild(el); + hs.extend(overlay, { + opacity: 1, + offsetX: 0, + offsetY: 0, + dur: (o.fade === 0 || o.fade === false || (o.fade == 2 && hs.ie)) ? 0 : 250 + }); + hs.extend(overlay, o); + + if (this.gotOverlays) { + this.positionOverlay(overlay); + if (!overlay.hideOnMouseOut || this.mouseIsOver) + hs.animate(overlay, { opacity: overlay.opacity }, overlay.dur); + } + hs.push(this.overlays, hs.idCounter - 1); +}, +positionOverlay : function(overlay) { + var p = overlay.position || 'middle center', + offX = overlay.offsetX, + offY = overlay.offsetY; + if (overlay.parentNode != this.overlayBox) this.overlayBox.appendChild(overlay); + if (/left$/.test(p)) overlay.style.left = offX +'px'; + + if (/center$/.test(p)) hs.setStyles (overlay, { + left: '50%', + marginLeft: (offX - Math.round(overlay.offsetWidth / 2)) +'px' + }); + + if (/right$/.test(p)) overlay.style.right = - offX +'px'; + + if (/^leftpanel$/.test(p)) { + hs.setStyles(overlay, { + right: '100%', + marginRight: this.x.cb +'px', + top: - this.y.cb +'px', + bottom: - this.y.cb +'px', + overflow: 'auto' + }); + this.x.p1 = overlay.offsetWidth; + + } else if (/^rightpanel$/.test(p)) { + hs.setStyles(overlay, { + left: '100%', + marginLeft: this.x.cb +'px', + top: - this.y.cb +'px', + bottom: - this.y.cb +'px', + overflow: 'auto' + }); + this.x.p2 = overlay.offsetWidth; + } + + if (/^top/.test(p)) overlay.style.top = offY +'px'; + if (/^middle/.test(p)) hs.setStyles (overlay, { + top: '50%', + marginTop: (offY - Math.round(overlay.offsetHeight / 2)) +'px' + }); + if (/^bottom/.test(p)) overlay.style.bottom = - offY +'px'; + if (/^above$/.test(p)) { + hs.setStyles(overlay, { + left: (- this.x.p1 - this.x.cb) +'px', + right: (- this.x.p2 - this.x.cb) +'px', + bottom: '100%', + marginBottom: this.y.cb +'px', + width: 'auto' + }); + this.y.p1 = overlay.offsetHeight; + + } else if (/^below$/.test(p)) { + hs.setStyles(overlay, { + position: 'relative', + left: (- this.x.p1 - this.x.cb) +'px', + right: (- this.x.p2 - this.x.cb) +'px', + top: '100%', + marginTop: this.y.cb +'px', + width: 'auto' + }); + this.y.p2 = overlay.offsetHeight; + overlay.style.position = 'absolute'; + } +}, + +getOverlays : function() { + this.getInline(['heading', 'caption'], true); + if (this.heading && this.dragByHeading) this.heading.className += ' highslide-move'; + if (hs.showCredits) this.writeCredits(); + for (var i = 0; i < hs.overlays.length; i++) { + var o = hs.overlays[i], tId = o.thumbnailId, sg = o.slideshowGroup; + if ((!tId && !sg) || (tId && tId == this.thumbsUserSetId) + || (sg && sg === this.slideshowGroup)) { + if (this.isImage || (this.isHtml && o.useOnHtml)) + this.createOverlay(o); + } + } + var os = []; + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + if (/panel$/.test(o.position)) this.positionOverlay(o); + else hs.push(os, o); + } + for (var i = 0; i < os.length; i++) this.positionOverlay(os[i]); + this.gotOverlays = true; +}, +genOverlayBox : function() { + if (!this.overlayBox) this.overlayBox = hs.createElement ( + 'div', { + className: this.wrapperClassName + }, { + position : 'absolute', + width: (this.x.size || (this.useBox ? this.width : null) + || this.x.full) +'px', + height: (this.y.size || this.y.full) +'px', + visibility : 'hidden', + overflow : 'hidden', + zIndex : hs.ie ? 4 : 'auto' + }, + hs.container, + true + ); +}, +sizeOverlayBox : function(doWrapper, doPanels) { + var overlayBox = this.overlayBox, + x = this.x, + y = this.y; + hs.setStyles( overlayBox, { + width: x.size +'px', + height: y.size +'px' + }); + if (doWrapper || doPanels) { + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + var ie6 = (hs.ieLt7 || document.compatMode == 'BackCompat'); + if (o && /^(above|below)$/.test(o.position)) { + if (ie6) { + o.style.width = (overlayBox.offsetWidth + 2 * x.cb + + x.p1 + x.p2) +'px'; + } + y[o.position == 'above' ? 'p1' : 'p2'] = o.offsetHeight; + } + if (o && ie6 && /^(left|right)panel$/.test(o.position)) { + o.style.height = (overlayBox.offsetHeight + 2* y.cb) +'px'; + } + } + } + if (doWrapper) { + hs.setStyles(this.content, { + top: y.p1 +'px' + }); + hs.setStyles(overlayBox, { + top: (y.p1 + y.cb) +'px' + }); + } +}, + +showOverlays : function() { + var b = this.overlayBox; + b.className = ''; + hs.setStyles(b, { + top: (this.y.p1 + this.y.cb) +'px', + left: (this.x.p1 + this.x.cb) +'px', + overflow : 'visible' + }); + if (hs.safari) b.style.visibility = 'visible'; + this.wrapper.appendChild (b); + for (var i = 0; i < this.overlays.length; i++) { + var o = hs.$('hsId'+ this.overlays[i]); + o.style.zIndex = 4; + if (!o.hideOnMouseOut || this.mouseIsOver) { + o.style.visibility = 'visible'; + hs.setStyles(o, { visibility: 'visible', display: '' }); + hs.animate(o, { opacity: o.opacity }, o.dur); + } + } +}, + +destroyOverlays : function() { + if (!this.overlays.length) return; + if (this.isHtml && this.preserveContent) { + this.overlayBox.style.top = '-9999px'; + hs.container.appendChild(this.overlayBox); + } else + hs.discardElement(this.overlayBox); +}, + + + +createFullExpand : function () { + this.fullExpandLabel = hs.createElement( + 'a', { + href: 'javascript:hs.expanders['+ this.key +'].doFullExpand();', + title: hs.lang.fullExpandTitle, + className: 'highslide-full-expand' + } + ); + + this.createOverlay({ + overlayId: this.fullExpandLabel, + position: hs.fullExpandPosition, + hideOnMouseOut: true, + opacity: hs.fullExpandOpacity + }); +}, + +doFullExpand : function () { + try { + if (this.fullExpandLabel) hs.discardElement(this.fullExpandLabel); + + this.focus(); + var xSize = this.x.size; + this.resizeTo(this.x.full, this.y.full); + + var xpos = this.x.pos - (this.x.size - xSize) / 2; + if (xpos < hs.marginLeft) xpos = hs.marginLeft; + + this.moveTo(xpos, this.y.pos); + this.doShowHide('hidden'); + + } catch (e) { + this.error(e); + } +}, + + +afterClose : function () { + this.a.className = this.a.className.replace('highslide-active-anchor', ''); + + this.doShowHide('visible'); + + if (this.isHtml && this.preserveContent) { + this.sleep(); + } else { + if (this.outline && this.outlineWhileAnimating) this.outline.destroy(); + + hs.discardElement(this.wrapper); + } + if (hs.mask) hs.mask.style.display = 'none'; + + hs.expanders[this.key] = null; + hs.reOrder(); +} + +}; + + +// hs.Ajax object prototype +hs.Ajax = function (a, content, pre) { + this.a = a; + this.content = content; + this.pre = pre; +}; + +hs.Ajax.prototype = { +run : function () { + var xhr; + if (!this.src) this.src = hs.getSrc(this.a); + if (this.src.match('#')) { + var arr = this.src.split('#'); + this.src = arr[0]; + this.id = arr[1]; + } + if (hs.cachedGets[this.src]) { + this.cachedGet = hs.cachedGets[this.src]; + if (this.id) this.getElementContent(); + else this.loadHTML(); + return; + } + try { xhr = new XMLHttpRequest(); } + catch (e) { + try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); } + catch (e) { + try { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } + catch (e) { this.onError(); } + } + } + var pThis = this; + xhr.onreadystatechange = function() { + if(pThis.xhr.readyState == 4) { + if (pThis.id) pThis.getElementContent(); + else pThis.loadHTML(); + } + }; + var src = this.src; + this.xhr = xhr; + if (hs.forceAjaxReload) + src = src.replace(/$/, (/\?/.test(src) ? '&' : '?') +'dummy='+ (new Date()).getTime()); + xhr.open('GET', src, true); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.send(null); +}, + +getElementContent : function() { + hs.init(); + var attribs = window.opera || hs.ie6SSL ? { src: 'about:blank' } : null; + + this.iframe = hs.createElement('iframe', attribs, + { position: 'absolute', top: '-9999px' }, hs.container); + + this.loadHTML(); +}, + +loadHTML : function() { + var s = this.cachedGet || this.xhr.responseText, + regBody; + if (this.pre) hs.cachedGets[this.src] = s; + if (!hs.ie || hs.uaVersion >= 5.5) { + s = s.replace(new RegExp(']*>', 'gi'), '') + .replace(new RegExp(']*>.*?', 'gi'), ''); + if (this.iframe) { + var doc = this.iframe.contentDocument; + if (!doc && this.iframe.contentWindow) doc = this.iframe.contentWindow.document; + if (!doc) { // Opera + var pThis = this; + setTimeout(function() { pThis.loadHTML(); }, 25); + return; + } + doc.open(); + doc.write(s); + doc.close(); + try { s = doc.getElementById(this.id).innerHTML; } catch (e) { + try { s = this.iframe.document.getElementById(this.id).innerHTML; } catch (e) {} // opera + } + hs.discardElement(this.iframe); + } else { + regBody = /(]*>|<\/body>)/ig; + + if (regBody.test(s)) s = s.split(regBody)[hs.ie ? 1 : 2]; + } + } + hs.getElementByClass(this.content, 'DIV', 'highslide-body').innerHTML = s; + this.onLoad(); + for (var x in this) this[x] = null; +} +}; +hs.langDefaults = hs.lang; +// history +var HsExpander = hs.Expander; +if (hs.ie) { + (function () { + try { + document.documentElement.doScroll('left'); + } catch (e) { + setTimeout(arguments.callee, 50); + return; + } + hs.ready(); + })(); +} +hs.addEventListener(document, 'DOMContentLoaded', hs.ready); +hs.addEventListener(window, 'load', hs.ready); + +// set handlers +hs.addEventListener(document, 'ready', function() { + if (hs.expandCursor) { + var style = hs.createElement('style', { type: 'text/css' }, null, + document.getElementsByTagName('HEAD')[0]); + + function addRule(sel, dec) { + if (!hs.ie) { + style.appendChild(document.createTextNode(sel + " {" + dec + "}")); + } else { + var last = document.styleSheets[document.styleSheets.length - 1]; + if (typeof(last.addRule) == "object") last.addRule(sel, dec); + } + } + function fix(prop) { + return 'expression( ( ( ignoreMe = document.documentElement.'+ prop + + ' ? document.documentElement.'+ prop +' : document.body.'+ prop +' ) ) + \'px\' );'; + } + if (hs.expandCursor) addRule ('.highslide img', + 'cursor: url('+ hs.graphicsDir + hs.expandCursor +'), pointer !important;'); + } +}); +hs.addEventListener(window, 'resize', function() { + hs.getPageSize(); +}); +hs.addEventListener(document, 'mousemove', function(e) { + hs.mouse = { x: e.clientX, y: e.clientY }; +}); +hs.addEventListener(document, 'mousedown', hs.mouseClickHandler); +hs.addEventListener(document, 'mouseup', hs.mouseClickHandler); + +hs.addEventListener(document, 'ready', hs.getAnchors); +hs.addEventListener(window, 'load', hs.preloadImages); +hs.addEventListener(window, 'load', hs.preloadAjax); +} \ No newline at end of file diff --git a/js/highslide/highslide.css b/js/highslide/highslide.css new file mode 100644 index 0000000..74d7891 --- /dev/null +++ b/js/highslide/highslide.css @@ -0,0 +1,1047 @@ +/** +* @file: highslide.css +* @version: 4.1.8 +*/ +.highslide-container div { + font-family: Verdana, Helvetica; + font-size: 10pt; +} + +.highslide-container table { + background: none; +} + +.highslide { + outline: none; + text-decoration: none; +} + +.highslide img { + border: 0px solid silver; +} + +.highslide:hover img { + border-color: gray; +} + +.highslide-active-anchor img { + visibility: hidden; +} + +.highslide-gallery .highslide-active-anchor img { + border-color: black; + visibility: visible; + cursor: default; +} + +.highslide-image { + border-width: 2px; + border-style: solid; + border-color: white; + background: gray; +} + +.highslide-wrapper,.highslide-outline { + background: white; +} + +.glossy-dark { + background: #111; +} + +.highslide-image-blur { + +} + +.highslide-number { + font-weight: bold; + color: gray; + font-size: .9em; +} + +.highslide-caption { + display: none; + font-size: 1em; + padding: 5px; + /*background: white;*/ +} + +.highslide-heading { + display: none; + font-weight: bold; + margin: 0.4em; +} + +.highslide-dimming { + position: absolute; + background: black; +} + +a.highslide-full-expand { + background: url(graphics/fullexpand.gif) no-repeat; + display: block; + margin: 0 10px 10px 0; + width: 34px; + height: 34px; +} + +.highslide-loading { + display: block; + color: black; + font-size: 9px; + font-weight: bold; + text-transform: uppercase; + text-decoration: none; + padding: 3px; + border: 1px solid white; + background-color: white; + padding-left: 22px; + background-image: url(graphics/loader.white.gif); + background-repeat: no-repeat; + background-position: 3px 1px; +} + +a.highslide-credits,a.highslide-credits i { + padding: 2px; + color: silver; + text-decoration: none; + font-size: 10px; +} + +a.highslide-credits:hover,a.highslide-credits:hover i { + color: white; + background-color: gray; +} + +.highslide-move,.highslide-move * { + cursor: move; +} + +.highslide-viewport { + display: none; + position: fixed; + width: 100%; + height: 100%; + z-index: 1; + background: none; + left: 0; + top: 0; +} + +.highslide-overlay { + display: none; +} + +.hidden-container { + display: none; +} + +/* Example of a semitransparent, offset closebutton */ +.closebutton { + position: relative; + top: -15px; + left: 15px; + width: 30px; + height: 30px; + cursor: pointer; + background: url(graphics/close.png); + /* NOTE! For IE6, you also need to update the highslide-ie6.css file. */ +} + +/*****************************************************************************/ + /* Thumbnail boxes for the galleries. */ + /* Remove these if you are not using a gallery. */ + /*****************************************************************************/ +.highslide-gallery ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +.highslide-gallery ul li { + display: block; + position: relative; + float: left; + width: 106px; + height: 106px; + border: 1px solid silver; + background: #ededed; + margin: 2px; + line-height: 0; + overflow: hidden; +} + +.highslide-gallery ul a { + position: absolute; + top: 50%; + left: 50%; +} + +.highslide-gallery ul img { + position: relative; + top: -50%; + left: -50%; +} + +html> /**/ body .highslide-gallery ul li { + display: table; + text-align: center; +} + +html> /**/ body .highslide-gallery ul li { + text-align: center; +} + +html> /**/ body .highslide-gallery ul a { + position: static; + display: table-cell; + vertical-align: middle; +} + +html> /**/ body .highslide-gallery ul img { + position: static; +} + +/*****************************************************************************/ + /* Controls for the galleries. */ + /* Remove these if you are not using a gallery */ + /*****************************************************************************/ +.highslide-controls { + width: 195px; + height: 40px; + background: url(graphics/controlbar-white.gif) 0 -90px no-repeat; + margin: 20px 15px 10px 0; +} + +.highslide-controls ul { + position: relative; + left: 15px; + height: 40px; + list-style: none; + margin: 0; + padding: 0; + background: url(graphics/controlbar-white.gif) right -90px no-repeat; +} + +.highslide-controls li { + float: left; + padding: 5px 0; + margin: 0; + list-style: none; +} + +.highslide-controls a { + background-image: url(graphics/controlbar-white.gif); + display: block; + float: left; + height: 30px; + width: 30px; + outline: none; +} + +.highslide-controls a.disabled { + cursor: default; +} + +.highslide-controls a.disabled span { + cursor: default; +} + +.highslide-controls a span { + /* hide the text for these graphic buttons */ + display: none; + cursor: pointer; +} + +/* The CSS sprites for the controlbar - see http://www.google.com/search?q=css+sprites */ +.highslide-controls .highslide-previous a { + background-position: 0 0; +} + +.highslide-controls .highslide-previous a:hover { + background-position: 0 -30px; +} + +.highslide-controls .highslide-previous a.disabled { + background-position: 0 -60px !important; +} + +.highslide-controls .highslide-play a { + background-position: -30px 0; +} + +.highslide-controls .highslide-play a:hover { + background-position: -30px -30px; +} + +.highslide-controls .highslide-play a.disabled { + background-position: -30px -60px !important; +} + +.highslide-controls .highslide-pause a { + background-position: -60px 0; +} + +.highslide-controls .highslide-pause a:hover { + background-position: -60px -30px; +} + +.highslide-controls .highslide-next a { + background-position: -90px 0; +} + +.highslide-controls .highslide-next a:hover { + background-position: -90px -30px; +} + +.highslide-controls .highslide-next a.disabled { + background-position: -90px -60px !important; +} + +.highslide-controls .highslide-move a { + background-position: -120px 0; +} + +.highslide-controls .highslide-move a:hover { + background-position: -120px -30px; +} + +.highslide-controls .highslide-full-expand a { + background-position: -150px 0; +} + +.highslide-controls .highslide-full-expand a:hover { + background-position: -150px -30px; +} + +.highslide-controls .highslide-full-expand a.disabled { + background-position: -150px -60px !important; +} + +.highslide-controls .highslide-close a { + background-position: -180px 0; +} + +.highslide-controls .highslide-close a:hover { + background-position: -180px -30px; +} + +/*****************************************************************************/ + /* Styles for the HTML popups */ + /* Remove these if you are not using Highslide HTML */ + /*****************************************************************************/ +.highslide-maincontent { + display: none; +} + +.highslide-html { + background-color: white; +} + +.highslide-html-content { + display: none; + width: 400px; + padding: 0 5px 5px 5px; +} + +.highslide-header { + padding-bottom: 5px; +} + +.highslide-header ul { + margin: 0; + padding: 0; + text-align: right; +} + +.highslide-header ul li { + display: inline; + padding-left: 1em; +} + +.highslide-header ul li.highslide-previous,.highslide-header ul li.highslide-next + { + display: none; +} + +.highslide-header a { + font-weight: bold; + color: gray; + text-transform: uppercase; + text-decoration: none; +} + +.highslide-header a:hover { + color: black; +} + +.highslide-header .highslide-move a { + cursor: move; +} + +.highslide-footer { + height: 16px; +} + +.highslide-footer .highslide-resize { + display: block; + float: right; + margin-top: 5px; + height: 11px; + width: 11px; + background: url(graphics/resize.gif) no-repeat; +} + +.highslide-footer .highslide-resize span { + display: none; +} + +.highslide-body { + +} + +.highslide-resize { + cursor: nw-resize; +} + +/*****************************************************************************/ + /* Styles for the Individual wrapper class names. */ + /* See www.highslide.com/ref/hs.wrapperClassName */ + /* You can safely remove the class name themes you don't use */ + /*****************************************************************************/ + /* hs.wrapperClassName = 'draggable-header' */ +.draggable-header .highslide-header { + height: 18px; + border-bottom: 1px solid #dddddd; +} + +.draggable-header .highslide-heading { + position: absolute; + margin: 2px 0.4em; +} + +.draggable-header .highslide-header .highslide-move { + cursor: move; + display: block; + height: 16px; + position: absolute; + right: 24px; + top: 0; + width: 100%; + z-index: 1; +} + +.draggable-header .highslide-header .highslide-move * { + display: none; +} + +.draggable-header .highslide-header .highslide-close { + position: absolute; + right: 2px; + top: 2px; + z-index: 5; + padding: 0; +} + +.draggable-header .highslide-header .highslide-close a { + display: block; + height: 16px; + width: 16px; + background-image: url(graphics/closeX.png); +} + +.draggable-header .highslide-header .highslide-close a:hover { + background-position: 0 16px; +} + +.draggable-header .highslide-header .highslide-close span { + display: none; +} + +.draggable-header .highslide-maincontent { + padding-top: 1em; +} + +/* hs.wrapperClassName = 'titlebar' */ +.titlebar .highslide-header { + height: 18px; + border-bottom: 1px solid #dddddd; +} + +.titlebar .highslide-heading { + position: absolute; + width: 90%; + margin: 1px 0 1px 5px; + color: #666666; +} + +.titlebar .highslide-header .highslide-move { + cursor: move; + display: block; + height: 16px; + position: absolute; + right: 24px; + top: 0; + width: 100%; + z-index: 1; +} + +.titlebar .highslide-header .highslide-move * { + display: none; +} + +.titlebar .highslide-header li { + position: relative; + top: 3px; + z-index: 2; + padding: 0 0 0 1em; +} + +.titlebar .highslide-maincontent { + padding-top: 1em; +} + +/* hs.wrapperClassName = 'no-footer' */ +.no-footer .highslide-footer { + display: none; +} + +/* hs.wrapperClassName = 'wide-border' */ +.wide-border { + background: white; +} + +.wide-border .highslide-image { + border-width: 10px; +} + +.wide-border .highslide-caption { + padding: 0 10px 10px 10px; +} + +/* hs.wrapperClassName = 'borderless' */ +.borderless .highslide-image { + border: none; +} + +.borderless .highslide-caption { + border-bottom: 1px solid white; + border-top: 1px solid white; + background: silver; +} + +/* hs.wrapperClassName = 'outer-glow' */ +.outer-glow { + background: #444; +} + +.outer-glow .highslide-image { + border: 5px solid #444444; +} + +.outer-glow .highslide-caption { + border: 5px solid #444444; + border-top: none; + padding: 5px; + background-color: gray; +} + +/* hs.wrapperClassName = 'colored-border' */ +.colored-border { + background: white; +} + +.colored-border .highslide-image { + border: 2px solid green; +} + +.colored-border .highslide-caption { + border: 2px solid green; + border-top: none; +} + +/* hs.wrapperClassName = 'dark' */ +.dark { + background: #111; +} + +.dark .highslide-image { + border-color: black black #202020 black; + background: gray; +} + +.dark .highslide-caption { + color: white; + background: #111; +} + +.dark .highslide-controls,.dark .highslide-controls ul,.dark .highslide-controls a + { + background-image: url(graphics/controlbar-black-border.gif); +} + +/* hs.wrapperClassName = 'floating-caption' */ +.floating-caption .highslide-caption { + position: absolute; + padding: 1em 0 0 0; + background: none; + color: white; + border: none; + font-weight: bold; +} + +/* hs.wrapperClassName = 'controls-in-heading' */ +.controls-in-heading .highslide-heading { + color: gray; + font-weight: bold; + height: 20px; + overflow: hidden; + cursor: default; + padding: 0 0 0 22px; + margin: 0; + background: url(graphics/icon.gif) no-repeat 0 1px; +} + +.controls-in-heading .highslide-controls { + width: 105px; + height: 20px; + position: relative; + margin: 0; + top: -23px; + left: 7px; + background: none; +} + +.controls-in-heading .highslide-controls ul { + position: static; + height: 20px; + background: none; +} + +.controls-in-heading .highslide-controls li { + padding: 0; +} + +.controls-in-heading .highslide-controls a { + background-image: url(graphics/controlbar-white-small.gif); + height: 20px; + width: 20px; +} + +.controls-in-heading .highslide-controls .highslide-move { + display: none; +} + +.controls-in-heading .highslide-controls .highslide-previous a { + background-position: 0 0; +} + +.controls-in-heading .highslide-controls .highslide-previous a:hover { + background-position: 0 -20px; +} + +.controls-in-heading .highslide-controls .highslide-previous a.disabled + { + background-position: 0 -40px !important; +} + +.controls-in-heading .highslide-controls .highslide-play a { + background-position: -20px 0; +} + +.controls-in-heading .highslide-controls .highslide-play a:hover { + background-position: -20px -20px; +} + +.controls-in-heading .highslide-controls .highslide-play a.disabled { + background-position: -20px -40px !important; +} + +.controls-in-heading .highslide-controls .highslide-pause a { + background-position: -40px 0; +} + +.controls-in-heading .highslide-controls .highslide-pause a:hover { + background-position: -40px -20px; +} + +.controls-in-heading .highslide-controls .highslide-next a { + background-position: -60px 0; +} + +.controls-in-heading .highslide-controls .highslide-next a:hover { + background-position: -60px -20px; +} + +.controls-in-heading .highslide-controls .highslide-next a.disabled { + background-position: -60px -40px !important; +} + +.controls-in-heading .highslide-controls .highslide-full-expand a { + background-position: -100px 0; +} + +.controls-in-heading .highslide-controls .highslide-full-expand a:hover + { + background-position: -100px -20px; +} + +.controls-in-heading .highslide-controls .highslide-full-expand a.disabled + { + background-position: -100px -40px !important; +} + +.controls-in-heading .highslide-controls .highslide-close a { + background-position: -120px 0; +} + +.controls-in-heading .highslide-controls .highslide-close a:hover { + background-position: -120px -20px; +} + +/*****************************************************************************/ + /* Styles for text based controls. */ + /* You can safely remove this if you don't use text based controls */ + /*****************************************************************************/ +.text-controls .highslide-controls { + width: auto; + height: auto; + margin: 0; + text-align: center; + background: none; +} + +.text-controls ul { + position: static; + background: none; + height: auto; + left: 0; +} + +.text-controls .highslide-move { + display: none; +} + +.text-controls li { + background-image: url(graphics/controlbar-text-buttons.png); + background-position: right top !important; + padding: 0; + margin-left: 15px; + display: block; + width: auto; +} + +.text-controls a { + background: url(graphics/controlbar-text-buttons.png) no-repeat; + background-position: left top !important; + position: relative; + left: -10px; + display: block; + width: auto; + height: auto; + text-decoration: none !important; +} + +.text-controls a span { + background: url(graphics/controlbar-text-buttons.png) no-repeat; + margin: 1px 2px 1px 10px; + display: block; + min-width: 4em; + height: 18px; + line-height: 18px; + padding: 1px 0 1px 18px; + color: #333; + font-family: "Trebuchet MS", Arial, sans-serif; + font-size: 12px; + font-weight: bold; + white-space: nowrap; +} + +.text-controls .highslide-next { + margin-right: 1em; +} + +.text-controls .highslide-full-expand a span { + min-width: 0; + margin: 1px 0; + padding: 1px 0 1px 10px; +} + +.text-controls .highslide-close a span { + min-width: 0; +} + +.text-controls a:hover span { + color: black; +} + +.text-controls a.disabled span { + color: #999; +} + +.text-controls .highslide-previous span { + background-position: 0 -40px; +} + +.text-controls .highslide-previous a.disabled { + background-position: left top !important; +} + +.text-controls .highslide-previous a.disabled span { + background-position: 0 -140px; +} + +.text-controls .highslide-play span { + background-position: 0 -60px; +} + +.text-controls .highslide-play a.disabled { + background-position: left top !important; +} + +.text-controls .highslide-play a.disabled span { + background-position: 0 -160px; +} + +.text-controls .highslide-pause span { + background-position: 0 -80px; +} + +.text-controls .highslide-next span { + background-position: 0 -100px; +} + +.text-controls .highslide-next a.disabled { + background-position: left top !important; +} + +.text-controls .highslide-next a.disabled span { + background-position: 0 -200px; +} + +.text-controls .highslide-full-expand span { + background: none; +} + +.text-controls .highslide-full-expand a.disabled { + background-position: left top !important; +} + +.text-controls .highslide-close span { + background-position: 0 -120px; +} + +/*****************************************************************************/ + /* Styles for the thumbstrip. */ + /* See www.highslide.com/ref/hs.addSlideshow */ + /* You can safely remove this if you don't use a thumbstrip */ + /*****************************************************************************/ +.highslide-thumbstrip { + height: 100%; +} + +.highslide-thumbstrip div { + overflow: hidden; +} + +.highslide-thumbstrip table { + position: relative; + padding: 0; + border-collapse: collapse; +} + +.highslide-thumbstrip td { + padding: 1px; + /*text-align: center;*/ +} + +.highslide-thumbstrip a { + outline: none; +} + +.highslide-thumbstrip img { + display: block; + border: 1px solid gray; + margin: 0 auto; +} + +.highslide-thumbstrip .highslide-active-anchor img { + visibility: visible; +} + +.highslide-thumbstrip .highslide-marker { + position: absolute; + width: 0; + height: 0; + border-width: 0; + border-style: solid; + border-color: transparent; + /* change this to actual background color in highslide-ie6.css */ +} + +.highslide-thumbstrip-horizontal div { + width: auto; + /* width: 100% breaks in small strips in IE */ +} + +.highslide-thumbstrip-horizontal .highslide-scroll-up { + display: none; + position: absolute; + top: 3px; + left: 3px; + width: 25px; + height: 42px; +} + +.highslide-thumbstrip-horizontal .highslide-scroll-up div { + margin-bottom: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) left center no-repeat; + height: 42px; +} + +.highslide-thumbstrip-horizontal .highslide-scroll-down { + display: none; + position: absolute; + top: 3px; + right: 3px; + width: 25px; + height: 42px; +} + +.highslide-thumbstrip-horizontal .highslide-scroll-down div { + margin-bottom: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) center right no-repeat; + height: 42px; +} + +.highslide-thumbstrip-horizontal table { + margin: 2px 0 10px 0; +} + +.highslide-viewport .highslide-thumbstrip-horizontal table { + margin-left: 10px; +} + +.highslide-thumbstrip-horizontal img { + width: auto; + height: 40px; +} + +.highslide-thumbstrip-horizontal .highslide-marker { + top: 47px; + border-left-width: 6px; + border-right-width: 6px; + border-bottom: 6px solid gray; +} + +.highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker { + margin-left: 10px; +} + +.dark .highslide-thumbstrip-horizontal .highslide-marker,.highslide-viewport .highslide-thumbstrip-horizontal .highslide-marker + { + border-bottom-color: white !important; +} + +.highslide-thumbstrip-vertical-overlay { + overflow: hidden !important; +} + +.highslide-thumbstrip-vertical div { + height: 100%; +} + +.highslide-thumbstrip-vertical a { + display: block; +} + +.highslide-thumbstrip-vertical .highslide-scroll-up { + display: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 25px; +} + +.highslide-thumbstrip-vertical .highslide-scroll-up div { + margin-left: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) top center no-repeat; + height: 25px; +} + +.highslide-thumbstrip-vertical .highslide-scroll-down { + display: none; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 25px; +} + +.highslide-thumbstrip-vertical .highslide-scroll-down div { + margin-left: 10px; + cursor: pointer; + background: url(graphics/scrollarrows.png) bottom center no-repeat; + height: 25px; +} + +.highslide-thumbstrip-vertical table { + margin: 10px 0 0 10px; +} + +.highslide-thumbstrip-vertical img { + max-width: 60px; +} + +.highslide-thumbstrip-vertical .highslide-marker { + left: 0; + margin-top: 8px; + border-top-width: 6px; + border-bottom-width: 6px; + border-left: 6px solid gray; +} + +.dark .highslide-thumbstrip-vertical .highslide-marker,.highslide-viewport .highslide-thumbstrip-vertical .highslide-marker + { + border-left-color: white; +} + +.highslide-viewport .highslide-thumbstrip-float { + overflow: auto; +} + +.highslide-thumbstrip-float ul { + margin: 2px 0; + padding: 0; +} + +.highslide-thumbstrip-float li { + display: block; + height: 60px; + margin: 0 2px; + list-style: none; + float: left; +} + +.highslide-thumbstrip-float img { + display: inline; + border-color: silver; + max-height: 56px; +} + +.highslide-thumbstrip-float .highslide-active-anchor img { + border-color: black; +} + +.highslide-thumbstrip-float .highslide-scroll-up div,.highslide-thumbstrip-float .highslide-scroll-down div + { + display: none; +} + +.highslide-thumbstrip-float .highslide-marker { + display: none; +} \ No newline at end of file diff --git a/js/jquery/jquery.min.js b/js/jquery/jquery.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/js/jquery/jquery.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(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(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/js/prototype/prototype.js b/js/prototype/prototype.js new file mode 100644 index 0000000..9fe6e12 --- /dev/null +++ b/js/prototype/prototype.js @@ -0,0 +1,4874 @@ +/* Prototype JavaScript framework, version 1.6.1 + * (c) 2005-2009 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.1', + + Browser: (function(){ + var ua = navigator.userAgent; + var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; + return { + IE: !!window.attachEvent && !isOpera, + Opera: isOpera, + WebKit: ua.indexOf('AppleWebKit/') > -1, + Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, + MobileSafari: /Apple.*Mobile.*Safari/.test(ua) + } + })(), + + BrowserFeatures: { + XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, + ElementExtensions: (function() { + var constructor = window.Element || window.HTMLElement; + return !!(constructor && constructor.prototype); + })(), + SpecificElementExtensions: (function() { + if (typeof window.HTMLDivElement !== 'undefined') + return true; + + var div = document.createElement('div'); + var form = document.createElement('form'); + var isSupported = false; + + if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { + isSupported = true; + } + + div = form = null; + + return isSupported; + })() + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +var Abstract = { }; + + +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; + } +}; + +/* Based on Alex Arnell's inheritance implementation. */ + +var Class = (function() { + function subclass() {}; + function create() { + 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) { + 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; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("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; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); +(function() { + + var _toString = Object.prototype.toString; + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (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; + } + } + + function toJSON(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 (isElement(object)) return; + + var results = []; + for (var property in object) { + var value = toJSON(object[property]); + if (!isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + var results = []; + for (var property in object) + results.push(property); + return results; + } + + function values(object) { + var results = []; + for (var property in object) + results.push(object[property]); + return results; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) == "[object Array]"; + } + + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return typeof object === "function"; + } + + function isString(object) { + return _toString.call(object) == "[object String]"; + } + + function isNumber(object) { + return _toString.call(object) == "[object Number]"; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toJSON: toJSON, + toQueryString: toQueryString, + toHTML: toHTML, + keys: keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isUndefined: isUndefined + }); +})(); +Object.extend(Function.prototype, (function() { + var slice = Array.prototype.slice; + + function update(array, args) { + var arrayLength = array.length, length = args.length; + while (length--) array[arrayLength + length] = args[length]; + return array; + } + + function merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + function argumentNames() { + var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] + .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') + .replace(/\s+/g, '').split(','); + return names.length == 1 && !names[0] ? [] : names; + } + + function bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = slice.call(arguments, 1); + return function() { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + + function bindAsEventListener(context) { + var __method = this, args = slice.call(arguments, 1); + return function(event) { + var a = update([event || window.event], args); + return __method.apply(context, a); + } + } + + function curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000 + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + return { + argumentNames: argumentNames, + bind: bind, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + } +})()); + + +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"'; +}; + + +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(); + this.currentlyExecuting = false; + } catch(e) { + this.currentlyExecuting = false; + throw e; + } + } + } +}); +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, (function() { + + function prepareReplacement(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; + } + + function gsub(pattern, replacement) { + var result = '', source = this, match; + replacement = prepareReplacement(replacement); + + if (Object.isString(pattern)) + pattern = RegExp.escape(pattern); + + if (!(pattern.length || pattern.source)) { + replacement = replacement(''); + return replacement + source.split('').join(replacement) + 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; + } + + function sub(pattern, replacement, count) { + replacement = prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + } + + function scan(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + } + + function truncate(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + } + + function strip() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + function stripTags() { + return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); + } + + function stripScripts() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + } + + function extractScripts() { + 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]; + }); + } + + function evalScripts() { + return this.extractScripts().map(function(script) { return eval(script) }); + } + + function escapeHTML() { + return this.replace(/&/g,'&').replace(//g,'>'); + } + + function unescapeHTML() { + return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); + } + + + function toQueryParams(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; + }); + } + + function toArray() { + return this.split(''); + } + + function succ() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + } + + function times(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + } + + function camelize() { + 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; + } + + function capitalize() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + } + + function underscore() { + return this.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/-/g, '_') + .toLowerCase(); + } + + function dasherize() { + return this.replace(/_/g, '-'); + } + + function inspect(useDoubleQuotes) { + var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { + if (character in String.specialChar) { + return String.specialChar[character]; + } + return '\\u00' + character.charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + } + + function toJSON() { + return this.inspect(true); + } + + function unfilterJSON(filter) { + return this.replace(filter || Prototype.JSONFilter, '$1'); + } + + function isJSON() { + 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); + } + + function evalJSON(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + } + + function include(pattern) { + return this.indexOf(pattern) > -1; + } + + function startsWith(pattern) { + return this.indexOf(pattern) === 0; + } + + function endsWith(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + } + + function empty() { + return this == ''; + } + + function blank() { + return /^\s*$/.test(this); + } + + function interpolate(object, pattern) { + return new Template(this, pattern).evaluate(object); + } + + return { + gsub: gsub, + sub: sub, + scan: scan, + truncate: truncate, + strip: String.prototype.trim ? String.prototype.trim : strip, + stripTags: stripTags, + stripScripts: stripScripts, + extractScripts: extractScripts, + evalScripts: evalScripts, + escapeHTML: escapeHTML, + unescapeHTML: unescapeHTML, + toQueryParams: toQueryParams, + parseQuery: toQueryParams, + toArray: toArray, + succ: succ, + times: times, + camelize: camelize, + capitalize: capitalize, + underscore: underscore, + dasherize: dasherize, + inspect: inspect, + toJSON: toJSON, + unfilterJSON: unfilterJSON, + isJSON: isJSON, + evalJSON: evalJSON, + include: include, + startsWith: startsWith, + endsWith: endsWith, + empty: empty, + blank: blank, + interpolate: interpolate + }; +})()); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (object && Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return (match[1] + ''); + + 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].replace(/\\\\]/g, ']') : 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 = (function() { + function each(iterator, context) { + var index = 0; + try { + this._each(function(value) { + iterator.call(context, value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + } + + function eachSlice(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); + } + + function all(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; + } + + function any(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; + } + + function collect(iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function detect(iterator, context) { + var result; + this.each(function(value, index) { + if (iterator.call(context, value, index)) { + result = value; + throw $break; + } + }); + return result; + } + + function findAll(iterator, context) { + var results = []; + this.each(function(value, index) { + if (iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function grep(filter, iterator, context) { + iterator = iterator || Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(RegExp.escape(filter)); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator.call(context, value, index)); + }); + return results; + } + + function include(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; + } + + function inGroupsOf(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + } + + function inject(memo, iterator, context) { + this.each(function(value, index) { + memo = iterator.call(context, memo, value, index); + }); + return memo; + } + + function invoke(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + } + + function max(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; + } + + function min(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; + } + + function partition(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]; + } + + function pluck(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + } + + function reject(iterator, context) { + var results = []; + this.each(function(value, index) { + if (!iterator.call(context, value, index)) + results.push(value); + }); + return results; + } + + function sortBy(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'); + } + + function toArray() { + return this.map(); + } + + function zip() { + 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)); + }); + } + + function size() { + return this.toArray().length; + } + + function inspect() { + return '#'; + } + + + + + + + + + + return { + each: each, + eachSlice: eachSlice, + all: all, + every: all, + any: any, + some: any, + collect: collect, + map: collect, + detect: detect, + findAll: findAll, + select: findAll, + filter: findAll, + grep: grep, + include: include, + member: include, + inGroupsOf: inGroupsOf, + inject: inject, + invoke: invoke, + max: max, + min: min, + partition: partition, + pluck: pluck, + reject: reject, + sortBy: sortBy, + toArray: toArray, + entries: toArray, + zip: zip, + size: size, + inspect: inspect, + find: detect + }; +})(); +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +Array.from = $A; + + +(function() { + var arrayProto = Array.prototype, + slice = arrayProto.slice, + _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available + + function each(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + } + if (!_each) _each = each; + + function clear() { + this.length = 0; + return this; + } + + function first() { + return this[0]; + } + + function last() { + return this[this.length - 1]; + } + + function compact() { + return this.select(function(value) { + return value != null; + }); + } + + function flatten() { + return this.inject([], function(array, value) { + if (Object.isArray(value)) + return array.concat(value.flatten()); + array.push(value); + return array; + }); + } + + function without() { + var values = slice.call(arguments, 0); + return this.select(function(value) { + return !values.include(value); + }); + } + + function reverse(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + } + + function uniq(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + } + + function intersect(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + } + + + function clone() { + return slice.call(this, 0); + } + + function size() { + return this.length; + } + + function inspect() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + } + + function toJSON() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } + + function indexOf(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; + } + + function lastIndexOf(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; + } + + function concat() { + var array = slice.call(this, 0), item; + for (var i = 0, length = arguments.length; i < length; i++) { + item = arguments[i]; + if (Object.isArray(item) && !('callee' in item)) { + for (var j = 0, arrayLength = item.length; j < arrayLength; j++) + array.push(item[j]); + } else { + array.push(item); + } + } + return array; + } + + Object.extend(arrayProto, Enumerable); + + if (!arrayProto._reverse) + arrayProto._reverse = arrayProto.reverse; + + Object.extend(arrayProto, { + _each: _each, + clear: clear, + first: first, + last: last, + compact: compact, + flatten: flatten, + without: without, + reverse: reverse, + uniq: uniq, + intersect: intersect, + clone: clone, + toArray: clone, + size: size, + inspect: inspect, + toJSON: toJSON + }); + + var CONCAT_ARGUMENTS_BUGGY = (function() { + return [].concat(arguments)[0][0] !== 1; + })(1,2) + + if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; + + if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; + if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; +})(); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + function initialize(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + } + + function _each(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + } + + function set(key, value) { + return this._object[key] = value; + } + + function get(key) { + if (this._object[key] !== Object.prototype[key]) + return this._object[key]; + } + + function unset(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + } + + function toObject() { + return Object.clone(this._object); + } + + function keys() { + return this.pluck('key'); + } + + function values() { + return this.pluck('value'); + } + + function index(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + } + + function merge(object) { + return this.clone().update(object); + } + + function update(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + } + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + function toQueryString() { + 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('&'); + } + + function inspect() { + return '#'; + } + + function toJSON() { + return Object.toJSON(this.toObject()); + } + + function clone() { + return new Hash(this); + } + + return { + initialize: initialize, + _each: _each, + set: set, + get: get, + unset: unset, + toObject: toObject, + toTemplateReplacements: toObject, + keys: keys, + values: values, + index: index, + merge: merge, + update: update, + toQueryString: toQueryString, + inspect: inspect, + toJSON: toJSON, + clone: clone + }; +})()); + +Hash.from = $H; +Object.extend(Number.prototype, (function() { + function toColorPart() { + return this.toPaddedString(2, 16); + } + + function succ() { + return this + 1; + } + + function times(iterator, context) { + $R(0, this, true).each(iterator, context); + return this; + } + + function toPaddedString(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + } + + function toJSON() { + return isFinite(this) ? this.toString() : 'null'; + } + + function abs() { + return Math.abs(this); + } + + function round() { + return Math.round(this); + } + + function ceil() { + return Math.ceil(this); + } + + function floor() { + return Math.floor(this); + } + + return { + toColorPart: toColorPart, + succ: succ, + times: times, + toPaddedString: toPaddedString, + toJSON: toJSON, + abs: abs, + round: round, + ceil: ceil, + floor: floor + }; +})()); + +function $R(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var ObjectRange = Class.create(Enumerable, (function() { + function initialize(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + } + + function _each(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + } + + function include(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } + + return { + initialize: initialize, + _each: _each, + include: include + }; +})()); + + + +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)) { + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + 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'; + } + + 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') { + 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) { + 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(global) { + + var SETATTRIBUTE_IGNORES_NAME = (function(){ + var elForm = document.createElement("form"); + var elInput = document.createElement("input"); + var root = document.documentElement; + elInput.setAttribute("name", "test"); + elForm.appendChild(elInput); + root.appendChild(elForm); + var isBuggy = elForm.elements + ? (typeof elForm.elements.test == "undefined") + : null; + root.removeChild(elForm); + elForm = elInput = null; + return isBuggy; + })(); + + var element = global.Element; + global.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (SETATTRIBUTE_IGNORES_NAME && 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(global.Element, element || { }); + if (element) global.Element.prototype = element.prototype; +})(this); + +Element.cache = { }; +Element.idCounter = 1; + +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(){ + + var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ + var el = document.createElement("select"), + isBuggy = true; + el.innerHTML = ""; + if (el.options && el.options[0]) { + isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; + } + el = null; + return isBuggy; + })(); + + var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ + try { + var el = document.createElement("table"); + if (el && el.tBodies) { + el.innerHTML = "test"; + var isBuggy = typeof el.tBodies[0] == "undefined"; + el = null; + return isBuggy; + } + } catch (e) { + return true; + } + })(); + + var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { + var s = document.createElement("script"), + isBuggy = false; + try { + s.appendChild(document.createTextNode("")); + isBuggy = !s.firstChild || + s.firstChild && s.firstChild.nodeType !== 3; + } catch (e) { + isBuggy = true; + } + s = null; + return isBuggy; + })(); + + function update(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 === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { + element.text = content; + return element; + } + + if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { + if (tagName in Element._insertionTranslations.tags) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { + element.appendChild(node) + }); + } + else { + element.innerHTML = content.stripScripts(); + } + } + else { + element.innerHTML = content.stripScripts(); + } + + content.evalScripts.bind(content).defer(); + return element; + } + + return update; + })(), + + 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(element, 'parentNode'); + }, + + descendants: function(element) { + return Element.select(element, "*"); + }, + + 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(element, 'previousSibling'); + }, + + nextSiblings: function(element) { + return Element.recursivelyCollect(element, 'nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return Element.previousSiblings(element).reverse() + .concat(Element.nextSiblings(element)); + }, + + 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(element); + 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(element); + return Object.isNumber(expression) ? Element.descendants(element)[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(element); + 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(element); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + + select: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element, args); + }, + + adjacent: function(element) { + var args = Array.prototype.slice.call(arguments, 1); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = Element.readAttribute(element, 'id'); + if (id) return id; + do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id)); + Element.writeAttribute(element, '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(element).height; + }, + + getWidth: function(element) { + return Element.getDimensions(element).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(element, 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(element, className) ? + 'removeClassName' : 'addClassName'](element, className); + }, + + 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(element); + 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(element, 'display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari + 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'; + 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(element, 'position') == 'absolute') return element; + + var offsets = Element.positionedOffset(element); + 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(element, 'position') == 'relative') return element; + + 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; + + 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] || { }); + + source = $(source); + var p = Element.viewportOffset(source); + + element = $(element); + var delta = [0, 0]; + var parent = null; + if (Element.getStyle(element, 'position') == 'absolute') { + parent = Element.getOffsetParent(element); + delta = Element.viewportOffset(parent); + } + + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + 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; + } +}; + +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': + if (!Element.visible(element)) return null; + + 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) { + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + 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); + 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 = (function(){ + + var classProp = 'className'; + var forProp = 'for'; + + var el = document.createElement('div'); + + el.setAttribute(classProp, 'x'); + + if (el.className !== 'x') { + el.setAttribute('class', 'x'); + if (el.className === 'x') { + classProp = 'class'; + } + } + el = null; + + el = document.createElement('label'); + el.setAttribute(forProp, 'x'); + if (el.htmlFor !== 'x') { + el.setAttribute('htmlFor', 'x'); + if (el.htmlFor === 'x') { + forProp = 'htmlFor'; + } + } + el = null; + + return { + read: { + names: { + 'class': classProp, + 'className': classProp, + 'for': forProp, + 'htmlFor': forProp + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute); + }, + _getAttr2: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: (function(){ + + var el = document.createElement('div'); + el.onclick = Prototype.emptyFunction; + var value = el.getAttribute('onclick'); + var f; + + if (String(value).indexOf('{') > -1) { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + attribute = attribute.toString(); + attribute = attribute.split('{')[1]; + attribute = attribute.split('}')[0]; + return attribute.strip(); + }; + } + else if (value === '') { + f = function(element, attribute) { + attribute = element.getAttribute(attribute); + if (!attribute) return null; + return attribute.strip(); + }; + } + el = null; + return f; + })(), + _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._getAttr2, + src: v._getAttr2, + 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); + + if (Prototype.BrowserFeatures.ElementExtensions) { + (function() { + function _descendants(element) { + var nodes = element.getElementsByTagName('*'), results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName !== "!") // Filter out comment nodes. + results.push(node); + return results; + } + + Element.Methods.down = function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? _descendants(element)[expression] : + Element.select(element, expression)[index || 0]; + } + })(); + } + +} + +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; + }; + + 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 ('outerHTML' in document.documentElement) { + 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: ['', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + SELECT: ['', 1] + } +}; + +(function() { + var tags = Element._insertionTranslations.tags; + Object.extend(tags, { + THEAD: tags.TBODY, + TFOOT: tags.TBODY, + TH: tags.TD + }); +})(); + +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); + +(function(div) { + + if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) { + window.HTMLElement = { }; + window.HTMLElement.prototype = div['__proto__']; + Prototype.BrowserFeatures.ElementExtensions = true; + } + + div = null; + +})(document.createElement('div')) + +Element.extend = (function() { + + function checkDeficiency(tagName) { + if (typeof window.Element != 'undefined') { + var proto = window.Element.prototype; + if (proto) { + var id = '_' + (Math.random()+'').slice(2); + var el = document.createElement(tagName); + proto[id] = 'x'; + var isBuggy = (el[id] !== 'x'); + delete proto[id]; + el = null; + return isBuggy; + } + } + return false; + } + + function extendElementWith(element, methods) { + for (var property in methods) { + var value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + } + + var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object'); + + if (Prototype.BrowserFeatures.SpecificElementExtensions) { + if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) { + return function(element) { + if (element && typeof element._extendedByPrototype == 'undefined') { + var t = element.tagName; + if (t && (/^(?:object|applet|embed)$/i.test(t))) { + extendElementWith(element, Element.Methods); + extendElementWith(element, Element.Methods.Simulated); + extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); + } + } + return element; + } + } + return Prototype.K; + } + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || typeof element._extendedByPrototype != 'undefined' || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName.toUpperCase(); + + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + extendElementWith(element, methods); + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + 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]; + + var element = document.createElement(tagName); + var proto = element['__proto__'] || element.constructor.prototype; + element = null; + return proto; + } + + var elementPrototype = window.HTMLElement ? HTMLElement.prototype : + Element.prototype; + + if (F.ElementExtensions) { + copy(Element.Methods, elementPrototype); + copy(Element.Methods.Simulated, elementPrototype, 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() { + return { width: this.getWidth(), height: this.getHeight() }; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; + +(function(viewport) { + var B = Prototype.Browser, doc = document, element, property = {}; + + function getRootElement() { + if (B.WebKit && !doc.evaluate) + return document; + + if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) + return document.body; + + return document.documentElement; + } + + function define(D) { + if (!element) element = getRootElement(); + + property[D] = 'client' + D; + + viewport['get' + D] = function() { return element[property[D]] }; + return viewport['get' + D](); + } + + viewport.getWidth = define.curry('Width'); + + viewport.getHeight = define.curry('Height'); +})(document.viewport); + + +Element.Storage = { + UID: 1 +}; + +Element.addMethods({ + getStorage: function(element) { + if (!(element = $(element))) return; + + var uid; + if (element === window) { + uid = 0; + } else { + if (typeof element._prototypeUID === "undefined") + element._prototypeUID = [Element.Storage.UID++]; + uid = element._prototypeUID[0]; + } + + if (!Element.Storage[uid]) + Element.Storage[uid] = $H(); + + return Element.Storage[uid]; + }, + + store: function(element, key, value) { + if (!(element = $(element))) return; + + if (arguments.length === 2) { + Element.getStorage(element).update(key); + } else { + Element.getStorage(element).set(key, value); + } + + return element; + }, + + retrieve: function(element, key, defaultValue) { + if (!(element = $(element))) return; + var hash = Element.getStorage(element), value = hash.get(key); + + if (Object.isUndefined(value)) { + hash.set(key, defaultValue); + value = defaultValue; + } + + return value; + }, + + clone: function(element, deep) { + if (!(element = $(element))) return; + var clone = element.cloneNode(deep); + clone._prototypeUID = void 0; + if (deep) { + var descendants = Element.select(clone, '*'), + i = descendants.length; + while (i--) { + descendants[i]._prototypeUID = void 0; + } + } + return Element.extend(clone); + } +}); +/* 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() { + + var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ + var isBuggy = false; + if (document.evaluate && window.XPathResult) { + var el = document.createElement('div'); + el.innerHTML = '
'; + + var xpath = ".//*[local-name()='ul' or local-name()='UL']" + + "//*[local-name()='li' or local-name()='LI']"; + + var result = document.evaluate(xpath, el, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + isBuggy = (result.snapshotLength !== 2); + el = null; + } + return isBuggy; + })(); + + return function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + if ((/(\[[\w-]*?:|:checked)/).test(e)) + return false; + + if (IS_DESCENDANT_SELECTOR_BUGGY) return false; + + return true; + } + + })(), + + shouldUseSelectorsAPI: function() { + if (!Prototype.BrowserFeatures.SelectorsAPI) return false; + + if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; + + if (!Selector._div) Selector._div = new Element('div'); + + 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, len = ps.length, name; + + 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 = 0; i"; + } +}); + +if (Prototype.BrowserFeatures.SelectorsAPI && + document.compatMode === 'BackCompat') { + Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ + var div = document.createElement('div'), + span = document.createElement('span'); + + div.id = "prototype_test_id"; + span.className = 'Test'; + div.appendChild(span); + var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); + div = span = null; + return isIgnored; + })(); +} + +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, len = p.length, name; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i = 0; i= 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: [ + { name: 'laterSibling', re: /^\s*~\s*/ }, + { name: 'child', re: /^\s*>\s*/ }, + { name: 'adjacent', re: /^\s*\+\s*/ }, + { name: 'descendant', re: /^\s/ }, + + { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, + { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, + { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, + { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, + { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, + { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } + ], + + 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: { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: (function(){ + + var PROPERTIES_ATTRIBUTES_MAP = (function(){ + var el = document.createElement('div'), + isBuggy = false, + propName = '_countedByPrototype', + value = 'x' + el[propName] = value; + isBuggy = (el.getAttribute(propName) === value); + el = null; + return isBuggy; + })(); + + return PROPERTIES_ATTRIBUTES_MAP ? + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } : + function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = void 0; + return nodes; + } + })(), + + 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++; + } + }, + + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + 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; + }, + + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + 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 (root == document) { + if (!targetNode) return []; + if (!nodes) return [targetNode]; + } else { + if (!root.sourceIndex || root.sourceIndex < 1) { + var nodes = root.getElementsByTagName('*'); + for (var j = 0, node; node = nodes[j]; j++) { + if (node.id === id) return [node]; + } + } + } + + 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); + }, + + 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; + }); + }, + + 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++) { + 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 + ' ').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, { + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} + +var Form = { + reset: function(form) { + form = $(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) { + 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) { + var elements = $(form).getElementsByTagName('*'), + element, + arr = [ ], + serializers = Form.Element.Serializers; + for (var i = 0; element = elements[i]; i++) { + arr.push(element); + } + return arr.inject([], function(elements, child) { + if (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)$/i.test(element.tagName); + }); + }, + + 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)$/i.test(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) { + 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); + } +}); +(function() { + + var 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: {} + }; + + var docEl = document.documentElement; + var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl + && 'onmouseleave' in docEl; + + 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); + }; + } + + function isLeftClick(event) { return _isButton(event, 0) } + + function isMiddleClick(event) { return _isButton(event, 1) } + + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { + event = Event.extend(event); + + var node = event.target, type = event.type, + currentTarget = event.currentTarget; + + if (currentTarget && currentTarget.tagName) { + 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); + } + + function findElement(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + } + + function pointer(event) { + return { x: pointerX(event), y: pointerY(event) }; + } + + function pointerX(event) { + var docElement = document.documentElement, + body = document.body || { scrollLeft: 0 }; + + return event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)); + } + + function pointerY(event) { + var docElement = document.documentElement, + body = document.body || { scrollTop: 0 }; + + return event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)); + } + + + function stop(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + + event.stopped = true; + } + + Event.Methods = { + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop + }; + + + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + function _relatedTarget(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); + } + + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } + }); + + Event.extend = function(event, element) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + + Object.extend(event, { + target: event.srcElement || element, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + + return Object.extend(event, methods); + }; + } else { + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); + Event.extend = Prototype.K; + } + + function _createResponder(element, eventName, handler) { + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) { + CACHE.push(element); + registry = Element.retrieve(element, 'prototype_event_registry', $H()); + } + + var respondersForEvent = registry.get(eventName); + if (Object.isUndefined(respondersForEvent)) { + respondersForEvent = []; + registry.set(eventName, respondersForEvent); + } + + if (respondersForEvent.pluck('handler').include(handler)) return false; + + var responder; + if (eventName.include(":")) { + responder = function(event) { + if (Object.isUndefined(event.eventName)) + return false; + + if (event.eventName !== eventName) + return false; + + Event.extend(event, element); + handler.call(element, event); + }; + } else { + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && + (eventName === "mouseenter" || eventName === "mouseleave")) { + if (eventName === "mouseenter" || eventName === "mouseleave") { + responder = function(event) { + Event.extend(event, element); + + var parent = event.relatedTarget; + while (parent && parent !== element) { + try { parent = parent.parentNode; } + catch(e) { parent = element; } + } + + if (parent === element) return; + + handler.call(element, event); + }; + } + } else { + responder = function(event) { + Event.extend(event, element); + handler.call(element, event); + }; + } + } + + responder.handler = handler; + respondersForEvent.push(responder); + return responder; + } + + function _destroyCache() { + for (var i = 0, length = CACHE.length; i < length; i++) { + Event.stopObserving(CACHE[i]); + CACHE[i] = null; + } + } + + var CACHE = []; + + if (Prototype.Browser.IE) + window.attachEvent('onunload', _destroyCache); + + if (Prototype.Browser.WebKit) + window.addEventListener('unload', Prototype.emptyFunction, false); + + + var _getDOMEventName = Prototype.K; + + if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { + _getDOMEventName = function(eventName) { + var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; + return eventName in translations ? translations[eventName] : eventName; + }; + } + + function observe(element, eventName, handler) { + element = $(element); + + var responder = _createResponder(element, eventName, handler); + + if (!responder) return element; + + if (eventName.include(':')) { + if (element.addEventListener) + element.addEventListener("dataavailable", responder, false); + else { + element.attachEvent("ondataavailable", responder); + element.attachEvent("onfilterchange", responder); + } + } else { + var actualEventName = _getDOMEventName(eventName); + + if (element.addEventListener) + element.addEventListener(actualEventName, responder, false); + else + element.attachEvent("on" + actualEventName, responder); + } + + return element; + } + + function stopObserving(element, eventName, handler) { + element = $(element); + + var registry = Element.retrieve(element, 'prototype_event_registry'); + + if (Object.isUndefined(registry)) return element; + + if (eventName && !handler) { + var responders = registry.get(eventName); + + if (Object.isUndefined(responders)) return element; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + return element; + } else if (!eventName) { + registry.each( function(pair) { + var eventName = pair.key, responders = pair.value; + + responders.each( function(r) { + Element.stopObserving(element, eventName, r.handler); + }); + }); + return element; + } + + var responders = registry.get(eventName); + + if (!responders) return; + + var responder = responders.find( function(r) { return r.handler === handler; }); + if (!responder) return element; + + var actualEventName = _getDOMEventName(eventName); + + if (eventName.include(':')) { + if (element.removeEventListener) + element.removeEventListener("dataavailable", responder, false); + else { + element.detachEvent("ondataavailable", responder); + element.detachEvent("onfilterchange", responder); + } + } else { + if (element.removeEventListener) + element.removeEventListener(actualEventName, responder, false); + else + element.detachEvent('on' + actualEventName, responder); + } + + registry.set(eventName, responders.without(responder)); + + return element; + } + + function fire(element, eventName, memo, bubble) { + element = $(element); + + if (Object.isUndefined(bubble)) + bubble = true; + + 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 = bubble ? 'ondataavailable' : 'onfilterchange'; + } + + 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); + + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving + }); + + Element.addMethods({ + fire: fire, + + observe: observe, + + stopObserving: stopObserving + }); + + Object.extend(document, { + fire: fire.methodize(), + + observe: observe.methodize(), + + stopObserving: stopObserving.methodize(), + + loaded: false + }); + + if (window.Event) Object.extend(window.Event, Event); + else window.Event = Event; +})(); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearTimeout(timer); + document.loaded = true; + document.fire('dom:loaded'); + } + + function checkReadyState() { + if (document.readyState === 'complete') { + document.stopObserving('readystatechange', checkReadyState); + fireContentLoadedEvent(); + } + } + + function pollDoScroll() { + try { document.documentElement.doScroll('left'); } + catch(e) { + timer = pollDoScroll.defer(); + return; + } + fireContentLoadedEvent(); + } + + if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); + } else { + document.observe('readystatechange', checkReadyState); + if (window == top) + timer = pollDoScroll.defer(); + } + + Event.observe(window, 'load', fireContentLoadedEvent); +})(); + +Element.addMethods(); + +/*------------------------------- 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'); + +var Position = { + includeScrollOffsets: false, + + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + 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); + }, + + 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; + }, + + + 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); + +/*--------------------------------------------------------------------------*/ diff --git a/js/script.js b/js/script.js new file mode 100644 index 0000000..6dc9fcf --- /dev/null +++ b/js/script.js @@ -0,0 +1,323 @@ +// enable one object +function obj_enable(objid) { + if ($(objid)) + $(objid).disabled = false; +} +// disable one object +function obj_disable(objid) { + if ($(objid)) + $(objid).disabled = true; +} +// enable array of objects +function objs_enable(objids) { + for (i = 0; i < objids.length; i++) { + obj_enable(objids[i]); + } +} +// disable array of objects +function objs_disable(objids) { + for (i = 0; i < objids.length; i++) { + obj_disable(objids[i]); + } +} +// enable/disable object-array depending on current state of one checkbox or +// radio button +function obj_toggle(obj, objects_to_toggle) { + var disable = true; + if ((obj.type == "checkbox") || (obj.type == "radio")) { + if (obj.checked) { + disable = false; + } + } + for (i = 0; i < objects_to_toggle.length; i++) { + document.getElementById(objects_to_toggle[i]).disabled = disable; + } +} +// slide down DIV +function mySlideDown(objid) { + if (document.getElementById(objid).style.display == 'none') { + new Effect.SlideDown(objid, { + duration : .5 + }); + } +} +// slide up DIV +function mySlideUp(objid) { + if (document.getElementById(objid).style.display != 'none') { + new Effect.SlideUp(objid, { + duration : .5 + }); + } +} +// slide DIV up or down depending on current state +function mySlide(objid) { + if (document.getElementById(objid).style.display != 'none') { + mySlideUp(objid); + } else { + mySlideDown(objid); + } +} + +// jQuery Slide Togle +function slide(element) { + jQuery(element).slideToggle(); +} + +// get list of selected file/s +function GetSelectedFilename() { + var a = ""; + var obj = document.getElementsByName("file[]"); + var anz = 0; + if (!obj.length) { + if (obj.checked) { + a += obj.value; + } + } else { + for (i = 0; i < obj.length; i++) { + if (obj[i].checked) { + a += "\n" + obj[i].value; + anz++; + } + } + } + return a; +} +// build message with selected file/s +function SetSelectedFile(i, k) { + var anz = 0; + var s = ""; + var smp; + var ids = document.getElementsByName("file[]"); + var mp = document.getElementsByName("multipart[]"); + for ( var j = 0; j < ids.length; j++) { + if (ids[j].checked) { + s = ids[j].value; + smp = (mp[j].value == 0) ? "" : " (Multipart: " + mp[j].value + ")"; + anz++; + if (k == 0) + break; + } + } + if (anz == 0) { + WP("", "gd"); + } else if (anz == 1) { + WP(s + smp, "gd"); + } else { + WP("> 1", "gd"); + } +} +function SelectMD(v, anz) { + for (i = 0; i < anz; i++) { + n = "db_multidump_" + i; + obj = document.getElementsByName(n)[0]; + if (obj) { + obj.checked = v; + } + } +} +function checkAllCheckboxes(formName, check) { + var form = $(formName); + var i = form.getElements('checkbox'); + if (check) { + i.each(function(item) { + item.checked = true; + }); + } else { + i.each(function(item) { + item.checked = false; + }); + } +} +function setVal(id, value) { + $(id).value = value; +} +// Check if a checkbox of the given form is checked - returns true or false +function tablesChecked(formName) { + var form = $(formName); + var i = form.getElements('checkbox'); + var checked = false; + i.each(function(item) { + if (item.checked == true) + checked = true; + }); + return checked; +} + +function WP(s, obj) { + document.getElementById(obj).innerHTML = s; +} +function resizeSQL(i) { + var obj = $('sqltextarea'); + var h = 0; + if (i == 0) { + s = '4'; + } else { + if (i == 1) + h = -30; + if (i == 2) + h = 30; + var oh = obj.style.height; + var s = Number(oh.substring(0, oh.length - 2)) + h; + if (s < 24) + s = 24; + } + obj.morph('height:' + s + 'px', { + duration : 0.5 + }); +} +function InsertLib(i) { + var obj = document.getElementsByName('sqllib')[0]; + if (obj.selectedIndex > 0) { + document.getElementById('sqlstring' + i).value = obj.options[obj.selectedIndex].value; + document.getElementById('sqlname' + i).value = obj.options[obj.selectedIndex].text; + } +} +function DisplayExport(s) { + document.getElementById("export_working").InnerHTML = s; +} +function SelectedTableCount() { + var obj = document.getElementsByName('f_export_tables[]')[0]; + var anz = 0; + for ( var i = 0; i < obj.options.length; i++) { + if (obj.options[i].selected) { + anz++; + } + } + return anz; +} +function SelectTableList(s) { + var obj = document.getElementsByName('f_export_tables[]')[0]; + for ( var i = 0; i < obj.options.length; i++) { + obj.options[i].selected = s; + } +} +function hide_csvdivs(i) { + document.getElementById("csv0").style.display = 'none'; + if (i == 0) { + document.getElementById("csv1").style.display = 'none'; + document.getElementById("csv4").style.display = 'none'; + document.getElementById("csv5").style.display = 'none'; + } +} +function check_csvdivs(i) { + hide_csvdivs(i); + if (document.getElementById("radio_csv0").checked) { + document.getElementById("csv0").style.display = 'block'; + } + if (i == 0) { + if (document.getElementById("radio_csv1").checked) { + document.getElementById("csv1").style.display = 'block'; + } else if (document.getElementById("radio_csv2").checked) { + document.getElementById("csv1").style.display = 'block'; + } else if (document.getElementById("radio_csv4").checked) { + document.getElementById("csv4").style.display = 'block'; + } else if (document.getElementById("radio_csv5").checked) { + document.getElementById("csv5").style.display = 'block'; + } + } +} + +/* target="_blank" for links */ +/* + * addEvent function from + * http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html + */ +function addEvent(obj, type, fn) { + if (obj.addEventListener) + obj.addEventListener(type, fn, false); + else if (obj.attachEvent) { + obj["e" + type + fn] = fn; + obj[type + fn] = function() { + obj["e" + type + fn](window.event); + } + obj.attachEvent("on" + type, obj[type + fn]); + } +} + +function removeEvent(obj, type, fn) { + if (obj.removeEventListener) + obj.removeEventListener(type, fn, false); + else if (obj.detachEvent) { + obj.detachEvent("on" + type, obj[type + fn]); + obj[type + fn] = null; + obj["e" + type + fn] = null; + } +} + +/* Create the new window */ +function openInNewWindow(e) { + var event; + if (!e) + event = window.event; + else + event = e; + // Abort if a modifier key is pressed + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) { + return true; + } else { + // Change "_blank" to something like "newWindow" to load all links in + // the same new window + var newWindow = window.open(this.getAttribute('href'), '_blank'); + if (newWindow) { + if (newWindow.focus) { + newWindow.focus(); + } + return false; + } + return true; + } +} + +/* + * Add the openInNewWindow function to the onclick event of links with a class + * name of "new-window" + */ +function getNewWindowLinks() { + // Check that the browser is DOM compliant + if (document.getElementById && document.createElement + && document.appendChild) { + // Change this to the text you want to use to alert the user that a new + // window will be opened + var strNewWindowAlert = ""; + // Find all links + var links = document.getElementsByTagName('a'); + var objWarningText; + var link; + for ( var i = 0; i < links.length; i++) { + link = links[i]; + // Find all links with a class name of "non-html" + if (/\bnew-window\b/.test(link.className)) { + // Create an em element containing the new window warning text + // and insert it after the link text + objWarningText = document.createElement("span"); + objWarningText.appendChild(document + .createTextNode(strNewWindowAlert)); + link.appendChild(objWarningText); + link.onclick = openInNewWindow; + } + } + objWarningText = null; + } +} + +addEvent(window, 'load', getNewWindowLinks); + +// get log by ajax request +function get_log(details) { + var ret = jQuery.ajax({ + url: 'ajax/show_log_entry.php?'+details, + beforeSend: function() { + jQuery('.ajax-reload').show(); + }, + success: function(data) { + jQuery('#ilog').html(data); + jQuery('.ajax-reload').hide(); + }, + error: function() { + var g = new Growler({location:'right', width:"650px"}); + g.growl("There was an error while log request. Please reload this page.", {header:"Error<\/strong>:", className:"message",life: 4, speedin: 1.2 }); + } + }).responseText; + + return false; +} \ No newline at end of file diff --git a/js/scriptaculous/MIT-LICENSE b/js/scriptaculous/MIT-LICENSE new file mode 100644 index 0000000..3889631 --- /dev/null +++ b/js/scriptaculous/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/js/scriptaculous/lib/builder.js b/js/scriptaculous/lib/builder.js new file mode 100644 index 0000000..dba8bec --- /dev/null +++ b/js/scriptaculous/lib/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 + ">"; + } 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 + ">"; + } 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(/"/,'"') + '"'); + 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/js/scriptaculous/lib/controls.js b/js/scriptaculous/lib/controls.js new file mode 100644 index 0000000..c56ccb7 --- /dev/null +++ b/js/scriptaculous/lib/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, + ''); + 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("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + 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("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + 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 "
      " + ret.join('') + "
    "; + } + }, options || { }); + } +}); + +// AJAX in-place editor and collection editor +// Full rewrite by Christophe Porteneuve (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/js/scriptaculous/lib/dragdrop.js b/js/scriptaculous/lib/dragdrop.js new file mode 100644 index 0000000..07c98e2 --- /dev/null +++ b/js/scriptaculous/lib/dragdrop.js @@ -0,0 +1,975 @@ +// script.aculo.us dragdrop.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 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) +// +// 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/ + +if(Object.isUndefined(Effect)) + throw("dragdrop.js requires including script.aculo.us' effects.js library"); + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==$(element) }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null, + tree: false + }, arguments[1] || { }); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if(Object.isArray(containment)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + if(options.accept) options.accept = [options.accept].flatten(); + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + findDeepestChild: function(drops) { + deepest = drops[0]; + + for (i = 1; i < drops.length; ++i) + if (Element.isParent(drops[i].element, deepest.element)) + deepest = drops[i]; + + return deepest; + }, + + isContained: function(element, drop) { + var containmentNode; + if(drop.tree) { + containmentNode = element.treeNode; + } else { + containmentNode = element.parentNode; + } + return drop._containers.detect(function(c) { return containmentNode == c }); + }, + + isAffected: function(point, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.classNames(element).detect( + function(v) { return drop.accept.include(v) } ) )) && + Position.within(drop.element, point[0], point[1]) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.removeClassName(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(drop.hoverclass) + Element.addClassName(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(point, element) { + if(!this.drops.length) return; + var drop, affected = []; + + this.drops.each( function(drop) { + if(Droppables.isAffected(point, element, drop)) + affected.push(drop); + }); + + if(affected.length>0) + drop = Droppables.findDeepestChild(affected); + + if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); + if (drop) { + Position.within(drop.element, point[0], point[1]); + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + + if (drop != this.last_active) Droppables.activate(drop); + } + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) + if (this.last_active.onDrop) { + this.last_active.onDrop(element, this.last_active.element, event); + return true; + } + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +}; + +var Draggables = { + drags: [], + observers: [], + + register: function(draggable) { + if(this.drags.length == 0) { + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.updateDrag.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + } + this.drags.push(draggable); + }, + + unregister: function(draggable) { + this.drags = this.drags.reject(function(d) { return d==draggable }); + if(this.drags.length == 0) { + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + } + }, + + activate: function(draggable) { + if(draggable.options.delay) { + this._timeout = setTimeout(function() { + Draggables._timeout = null; + window.focus(); + Draggables.activeDraggable = draggable; + }.bind(this), draggable.options.delay); + } else { + window.focus(); // allows keypress events if window isn't currently focused, fails for Safari + this.activeDraggable = draggable; + } + }, + + deactivate: function() { + this.activeDraggable = null; + }, + + updateDrag: function(event) { + if(!this.activeDraggable) return; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + // Mozilla-based browsers fire successive mousemove events with + // the same coordinates, prevent needless redrawing (moz bug?) + if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return; + this._lastPointer = pointer; + + this.activeDraggable.updateDrag(event, pointer); + }, + + endDrag: function(event) { + if(this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; + } + if(!this.activeDraggable) return; + this._lastPointer = null; + this.activeDraggable.endDrag(event); + this.activeDraggable = null; + }, + + keyPress: function(event) { + if(this.activeDraggable) + this.activeDraggable.keyPress(event); + }, + + addObserver: function(observer) { + this.observers.push(observer); + this._cacheObserverCallbacks(); + }, + + removeObserver: function(element) { // element instead of observer fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + this._cacheObserverCallbacks(); + }, + + notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag' + if(this[eventName+'Count'] > 0) + this.observers.each( function(o) { + if(o[eventName]) o[eventName](eventName, draggable, event); + }); + if(draggable.options[eventName]) draggable.options[eventName](draggable, event); + }, + + _cacheObserverCallbacks: function() { + ['onStart','onEnd','onDrag'].each( function(eventName) { + Draggables[eventName+'Count'] = Draggables.observers.select( + function(o) { return o[eventName]; } + ).length; + }); + } +}; + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create({ + initialize: function(element) { + var defaults = { + handle: false, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur, + queue: {scope:'_draggable', position:'end'} + }); + }, + endeffect: function(element) { + var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; + new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, + queue: {scope:'_draggable', position:'end'}, + afterFinish: function(){ + Draggable._dragging[element] = false + } + }); + }, + zindex: 1000, + revert: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] } + delay: 0 + }; + + if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)) + Object.extend(defaults, { + starteffect: function(element) { + element._opacity = Element.getOpacity(element); + Draggable._dragging[element] = true; + new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); + } + }); + + var options = Object.extend(defaults, arguments[1] || { }); + + this.element = $(element); + + if(options.handle && Object.isString(options.handle)) + this.handle = this.element.down('.'+options.handle, 0); + + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) { + options.scroll = $(options.scroll); + this._isScrollChild = Element.childOf(this.element, options.scroll); + } + + Element.makePositioned(this.element); // fix IE + + this.options = options; + this.dragging = false; + + this.eventMouseDown = this.initDrag.bindAsEventListener(this); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + + Draggables.register(this); + }, + + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Draggables.unregister(this); + }, + + currentDelta: function() { + return([ + parseInt(Element.getStyle(this.element,'left') || '0'), + parseInt(Element.getStyle(this.element,'top') || '0')]); + }, + + initDrag: function(event) { + if(!Object.isUndefined(Draggable._dragging[this.element]) && + Draggable._dragging[this.element]) return; + if(Event.isLeftClick(event)) { + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if((tag_name = src.tagName.toUpperCase()) && ( + tag_name=='INPUT' || + tag_name=='SELECT' || + tag_name=='OPTION' || + tag_name=='BUTTON' || + tag_name=='TEXTAREA')) return; + + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var pos = Position.cumulativeOffset(this.element); + this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); + + Draggables.activate(this); + Event.stop(event); + } + }, + + startDrag: function(event) { + this.dragging = true; + if(!this.delta) + this.delta = this.currentDelta(); + + if(this.options.zindex) { + this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + this.element.style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + this._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); + if (!this._originallyAbsolute) + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + if(this.options.scroll) { + if (this.options.scroll == window) { + var where = this._getWindowScroll(this.options.scroll); + this.originalScrollLeft = where.left; + this.originalScrollTop = where.top; + } else { + this.originalScrollLeft = this.options.scroll.scrollLeft; + this.originalScrollTop = this.options.scroll.scrollTop; + } + } + + Draggables.notify('onStart', this, event); + + if(this.options.starteffect) this.options.starteffect(this.element); + }, + + updateDrag: function(event, pointer) { + if(!this.dragging) this.startDrag(event); + + if(!this.options.quiet){ + Position.prepare(); + Droppables.show(pointer, this.element); + } + + Draggables.notify('onDrag', this, event); + + this.draw(pointer); + if(this.options.change) this.options.change(this); + + if(this.options.scroll) { + this.stopScrolling(); + + var p; + if (this.options.scroll == window) { + with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; } + } else { + p = Position.page(this.options.scroll); + p[0] += this.options.scroll.scrollLeft + Position.deltaX; + p[1] += this.options.scroll.scrollTop + Position.deltaY; + p.push(p[0]+this.options.scroll.offsetWidth); + p.push(p[1]+this.options.scroll.offsetHeight); + } + var speed = [0,0]; + if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity); + if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity); + if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity); + if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity); + this.startScrolling(speed); + } + + // fix AppleWebKit rendering + if(Prototype.Browser.WebKit) window.scrollBy(0,0); + + Event.stop(event); + }, + + finishDrag: function(event, success) { + this.dragging = false; + + if(this.options.quiet){ + Position.prepare(); + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + Droppables.show(pointer, this.element); + } + + if(this.options.ghosting) { + if (!this._originallyAbsolute) + Position.relativize(this.element); + delete this._originallyAbsolute; + Element.remove(this._clone); + this._clone = null; + } + + var dropped = false; + if(success) { + dropped = Droppables.fire(event, this.element); + if (!dropped) dropped = false; + } + if(dropped && this.options.onDropped) this.options.onDropped(this.element); + Draggables.notify('onEnd', this, event); + + var revert = this.options.revert; + if(revert && Object.isFunction(revert)) revert = revert(this.element); + + var d = this.currentDelta(); + if(revert && this.options.reverteffect) { + if (dropped == 0 || revert != 'failure') + this.options.reverteffect(this.element, + d[1]-this.delta[1], d[0]-this.delta[0]); + } else { + this.delta = d; + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + Draggables.deactivate(this); + Droppables.reset(); + }, + + keyPress: function(event) { + if(event.keyCode!=Event.KEY_ESC) return; + this.finishDrag(event, false); + Event.stop(event); + }, + + endDrag: function(event) { + if(!this.dragging) return; + this.stopScrolling(); + this.finishDrag(event, true); + Event.stop(event); + }, + + draw: function(point) { + var pos = Position.cumulativeOffset(this.element); + if(this.options.ghosting) { + var r = Position.realOffset(this.element); + pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY; + } + + var d = this.currentDelta(); + pos[0] -= d[0]; pos[1] -= d[1]; + + if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) { + pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft; + pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop; + } + + var p = [0,1].map(function(i){ + return (point[i]-pos[i]-this.offset[i]) + }.bind(this)); + + if(this.options.snap) { + if(Object.isFunction(this.options.snap)) { + p = this.options.snap(p[0],p[1],this); + } else { + if(Object.isArray(this.options.snap)) { + p = p.map( function(v, i) { + return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)); + } else { + p = p.map( function(v) { + return (v/this.options.snap).round()*this.options.snap }.bind(this)); + } + }} + + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = p[0] + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = p[1] + "px"; + + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + + stopScrolling: function() { + if(this.scrollInterval) { + clearInterval(this.scrollInterval); + this.scrollInterval = null; + Draggables._lastScrollPointer = null; + } + }, + + startScrolling: function(speed) { + if(!(speed[0] || speed[1])) return; + this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed]; + this.lastScrolled = new Date(); + this.scrollInterval = setInterval(this.scroll.bind(this), 10); + }, + + scroll: function() { + var current = new Date(); + var delta = current - this.lastScrolled; + this.lastScrolled = current; + if(this.options.scroll == window) { + with (this._getWindowScroll(this.options.scroll)) { + if (this.scrollSpeed[0] || this.scrollSpeed[1]) { + var d = delta / 1000; + this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] ); + } + } + } else { + this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000; + this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000; + } + + Position.prepare(); + Droppables.show(Draggables._lastPointer, this.element); + Draggables.notify('onDrag', this); + if (this._isScrollChild) { + Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer); + Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000; + Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000; + if (Draggables._lastScrollPointer[0] < 0) + Draggables._lastScrollPointer[0] = 0; + if (Draggables._lastScrollPointer[1] < 0) + Draggables._lastScrollPointer[1] = 0; + this.draw(Draggables._lastScrollPointer); + } + + if(this.options.change) this.options.change(this); + }, + + _getWindowScroll: function(w) { + var T, L, W, H; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight; + } + } + return { top: T, left: L, width: W, height: H }; + } +}); + +Draggable._dragging = { }; + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create({ + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +}); + +var Sortable = { + SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, + + sortables: { }, + + _findRootElement: function(element) { + while (element.tagName.toUpperCase() != "BODY") { + if(element.id && Sortable.sortables[element.id]) return element; + element = element.parentNode; + } + }, + + options: function(element) { + element = Sortable._findRootElement($(element)); + if(!element) return; + return Sortable.sortables[element.id]; + }, + + destroy: function(element){ + element = $(element); + var s = Sortable.sortables[element.id]; + + if(s) { + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + + delete Sortable.sortables[s.element.id]; + } + }, + + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, + treeTag: 'ul', + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + delay: 0, + hoverclass: null, + ghosting: false, + quiet: false, + scroll: false, + scrollSensitivity: 20, + scrollSpeed: 15, + format: this.SERIALIZE_RULE, + + // these take arrays of elements or ids and can be + // used for better initialization performance + elements: false, + handles: false, + + onChange: Prototype.emptyFunction, + onUpdate: Prototype.emptyFunction + }, arguments[1] || { }); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + quiet: options.quiet, + scroll: options.scroll, + scrollSpeed: options.scrollSpeed, + scrollSensitivity: options.scrollSensitivity, + delay: options.delay, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + tree: options.tree, + hoverclass: options.hoverclass, + onHover: Sortable.onHover + }; + + var options_for_tree = { + onHover: Sortable.onEmptyHover, + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass + }; + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // drop on empty handling + if(options.dropOnEmpty || options.tree) { + Droppables.add(element, options_for_tree); + options.droppables.push(element); + } + + (options.elements || this.findElements(element, options) || []).each( function(e,i) { + var handle = options.handles ? $(options.handles[i]) : + (options.handle ? $(e).select('.' + options.handle)[0] : e); + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + if(options.tree) e.treeNode = element; + options.droppables.push(e); + }); + + if(options.tree) { + (Sortable.findTreeElements(element, options) || []).each( function(e) { + Droppables.add(e, options_for_tree); + e.treeNode = element; + options.droppables.push(e); + }); + } + + // keep reference + this.sortables[element.id] = options; + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.tag); + }, + + findTreeElements: function(element, options) { + return Element.findChildren( + element, options.only, options.tree ? true : false, options.treeTag); + }, + + onHover: function(element, dropon, overlap) { + if(Element.isParent(dropon, element)) return; + + if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) { + return; + } else if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon, overlap) { + var oldParentNode = element.parentNode; + var droponOptions = Sortable.options(dropon); + + if(!Element.isParent(dropon, element)) { + var index; + + var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only}); + var child = null; + + if(children) { + var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap); + + for (index = 0; index < children.length; index += 1) { + if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) { + offset -= Element.offsetSize (children[index], droponOptions.overlap); + } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) { + child = index + 1 < children.length ? children[index + 1] : null; + break; + } else { + child = children[index]; + break; + } + } + } + + dropon.insertBefore(element, child); + + Sortable.options(oldParentNode).onChange(element); + droponOptions.onChange(element); + } + }, + + unmark: function() { + if(Sortable._marker) Sortable._marker.hide(); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = + ($('dropmarker') || Element.extend(document.createElement('DIV'))). + hide().addClassName('dropmarker').setStyle({position:'absolute'}); + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = Position.cumulativeOffset(dropon); + Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'}); + + if(position=='after') + if(sortable.overlap == 'horizontal') + Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'}); + else + Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'}); + + Sortable._marker.show(); + }, + + _tree: function(element, options, parent) { + var children = Sortable.findElements(element, options) || []; + + for (var i = 0; i < children.length; ++i) { + var match = children[i].id.match(options.format); + + if (!match) continue; + + var child = { + id: encodeURIComponent(match ? match[1] : null), + element: element, + parent: parent, + children: [], + position: parent.children.length, + container: $(children[i]).down(options.treeTag) + }; + + /* Get the element containing the children and recurse over it */ + if (child.container) + this._tree(child.container, options, child); + + parent.children.push (child); + } + + return parent; + }, + + tree: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + treeTag: sortableOptions.treeTag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format + }, arguments[1] || { }); + + var root = { + id: null, + parent: null, + children: [], + container: element, + position: 0 + }; + + return Sortable._tree(element, options, root); + }, + + /* Construct a [i] index for a particular node */ + _constructIndex: function(node) { + var index = ''; + do { + if (node.id) index = '[' + node.position + ']' + index; + } while ((node = node.parent) != null); + return index; + }, + + sequence: function(element) { + element = $(element); + var options = Object.extend(this.options(element), arguments[1] || { }); + + return $(this.findElements(element, options) || []).map( function(item) { + return item.id.match(options.format) ? item.id.match(options.format)[1] : ''; + }); + }, + + setSequence: function(element, new_sequence) { + element = $(element); + var options = Object.extend(this.options(element), arguments[2] || { }); + + var nodeMap = { }; + this.findElements(element, options).each( function(n) { + if (n.id.match(options.format)) + nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode]; + n.parentNode.removeChild(n); + }); + + new_sequence.each(function(ident) { + var n = nodeMap[ident]; + if (n) { + n[1].appendChild(n[0]); + delete nodeMap[ident]; + } + }); + }, + + serialize: function(element) { + element = $(element); + var options = Object.extend(Sortable.options(element), arguments[1] || { }); + var name = encodeURIComponent( + (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); + + if (options.tree) { + return Sortable.tree(element, arguments[1]).children.map( function (item) { + return [name + Sortable._constructIndex(item) + "[id]=" + + encodeURIComponent(item.id)].concat(item.children.map(arguments.callee)); + }).flatten().join('&'); + } else { + return Sortable.sequence(element, arguments[1]).map( function(item) { + return name + "[]=" + encodeURIComponent(item); + }).join('&'); + } + } +}; + +// Returns true if child is contained within element +Element.isParent = function(child, element) { + if (!child.parentNode || child == element) return false; + if (child.parentNode == element) return true; + return Element.isParent(child.parentNode, element); +}; + +Element.findChildren = function(element, only, recursive, tagName) { + if(!element.hasChildNodes()) return null; + tagName = tagName.toUpperCase(); + if(only) only = [only].flatten(); + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName.toUpperCase()==tagName && + (!only || (Element.classNames(e).detect(function(v) { return only.include(v) })))) + elements.push(e); + if(recursive) { + var grandchildren = Element.findChildren(e, only, recursive, tagName); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : []); +}; + +Element.offsetSize = function (element, type) { + return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')]; +}; \ No newline at end of file diff --git a/js/scriptaculous/lib/effects.js b/js/scriptaculous/lib/effects.js new file mode 100644 index 0000000..f31a81a --- /dev/null +++ b/js/scriptaculous/lib/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= 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.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 = '
    '; + 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/js/scriptaculous/lib/scriptaculous.js b/js/scriptaculous/lib/scriptaculous.js new file mode 100644 index 0000000..3e5543b --- /dev/null +++ b/js/scriptaculous/lib/scriptaculous.js @@ -0,0 +1,60 @@ +// script.aculo.us scriptaculous.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) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.8.2', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write(' + +
    +

    {L_CONFIG_HEADLINE}: {CONFIGURATION_NAME} ({L_MSD_MODE}: {MSD_MODE})

    +
    + + + +
    diff --git a/tpl/configuration/cronscript.tpl b/tpl/configuration/cronscript.tpl new file mode 100644 index 0000000..c58604a --- /dev/null +++ b/tpl/configuration/cronscript.tpl @@ -0,0 +1,43 @@ + + \ No newline at end of file diff --git a/tpl/configuration/databases.tpl b/tpl/configuration/databases.tpl new file mode 100644 index 0000000..044c1e9 --- /dev/null +++ b/tpl/configuration/databases.tpl @@ -0,0 +1,94 @@ + diff --git a/tpl/configuration/email.tpl b/tpl/configuration/email.tpl new file mode 100644 index 0000000..bb40d88 --- /dev/null +++ b/tpl/configuration/email.tpl @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/tpl/configuration/footer.tpl b/tpl/configuration/footer.tpl new file mode 100644 index 0000000..69deb58 --- /dev/null +++ b/tpl/configuration/footer.tpl @@ -0,0 +1,20 @@ +
    +
    +



    + +
    + + + diff --git a/tpl/configuration/ftp.tpl b/tpl/configuration/ftp.tpl new file mode 100644 index 0000000..ca37342 --- /dev/null +++ b/tpl/configuration/ftp.tpl @@ -0,0 +1,102 @@ + + \ No newline at end of file diff --git a/tpl/configuration/general.tpl b/tpl/configuration/general.tpl new file mode 100644 index 0000000..729600f --- /dev/null +++ b/tpl/configuration/general.tpl @@ -0,0 +1,103 @@ + \ No newline at end of file diff --git a/tpl/configuration/interface.tpl b/tpl/configuration/interface.tpl new file mode 100644 index 0000000..c9ebc08 --- /dev/null +++ b/tpl/configuration/interface.tpl @@ -0,0 +1,72 @@ + diff --git a/tpl/credits/credits.tpl b/tpl/credits/credits.tpl new file mode 100644 index 0000000..85d60a3 --- /dev/null +++ b/tpl/credits/credits.tpl @@ -0,0 +1,14 @@ +
    +

    {L_CREDITS}

    +{CONTENT} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tpl/dump/dump.tpl b/tpl/dump/dump.tpl new file mode 100644 index 0000000..c35d04b --- /dev/null +++ b/tpl/dump/dump.tpl @@ -0,0 +1,266 @@ + +
    +

    {L_DUMP_HEADLINE}

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_CONFIG}:
    {L_COMMENT}:
    {L_CHARSET}:
    {L_GZIP_COMPRESSION}:{GZIP}
    {L_DUMP_FILENAME}:{DUMP_FILENAME}
    {L_FILESIZE}:{DUMP_FILESIZE}
    {L_MULTI_PART}: + + + + + + + + + +
    {L_MULTIPART_ACTUAL_PART}:
    {L_MULTIPART_SIZE}:{MULTIPART.SIZE}
    +
    {L_DBS}:
    {L_INFO_ACTDB}:
    {L_SAVING_TABLE}: +  {L_OF} {TABLE_COUNT} +
    {L_PREFIX}:
    + +
    {L_ERROR}: + +
     
    {L_PROGRESS_TABLE}:
      + + + + + + + + + +
    0 % + +
      + {L_ENTRY}  {L_UPTO} +  {L_OF} +
    +
     
    {L_RECORDS_PER_PAGECALL}: + + + + + + + + + + +
    + + + +
     
    +
     
    {L_PROGRESS_OVER_ALL}: + + + + + +
    % +
    + {L_RECORDS} +  {L_OF}
    +
    +
    {L_PAGE_REFRESHS}:0
    {L_DURATION}:
    {L_ESTIMATED_END}:
    +

    +
    + +

    {L_LOG}:

    +
    +
    +

    +

    +

    \ No newline at end of file diff --git a/tpl/dump/dump_finished.tpl b/tpl/dump/dump_finished.tpl new file mode 100644 index 0000000..2728d0e --- /dev/null +++ b/tpl/dump/dump_finished.tpl @@ -0,0 +1,80 @@ +
    +

    {L_DUMP}

    +

    + + + +

    +

    {L_DONE}

    +

    {TIME_ELAPSED}, {PAGE_REFRESHS} {L_PAGE_REFRESHS}
    +{MSG}

    + + {MULTIDUMP.MSG}.
    + +
    + +

    {L_FILES}:

    +
    + + + + + + + + + + + + + + + +
    #{L_FILE}{L_FILESIZE}
    {FILE.NR}.{FILE.FILENAME}{ICON_OPEN_FILE} + {FILE.FILESIZE}
    +
    +
    + +

    {L_ERROR}:

    +
    + + + + + + + + + + + + +
    {L_TIMESTAMP}{L_ERROR}
    {ERROR.ERRORMSG.TIMESTAMP}{ERROR.ERRORMSG.MSG}
    +
    +
    + +

    {L_LOG}:

    +
    + + + + + + + + + + + + + + +
    #{L_TIMESTAMP}{L_ACTION}
    {ACTION.NR}.{ACTION.TIMESTAMP}{ACTION.ACTION}
    +
    +
    + + + +
    \ No newline at end of file diff --git a/tpl/dump/dump_prepare.tpl b/tpl/dump/dump_prepare.tpl new file mode 100644 index 0000000..23aba4e --- /dev/null +++ b/tpl/dump/dump_prepare.tpl @@ -0,0 +1,221 @@ + + +
    +

    {L_DUMP}

    + + + +
    +

    {L_FM_DUMPSETTINGS} ({L_CONFIG_HEADLINE}: {CONFIG_FILE})

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_DBS}: + {NR_OF_DBS}
    + {DBS_TO_BACKUP}
    {L_TABLES}:{TABLES_TOTAL}
    {L_RECORDS}:{RECORDS_TOTAL}
    {L_DATASIZE}:{DATASIZE_TOTAL}
    +
    +   ({L_DATASIZE_INFO}.) +
    {L_GZIP}: + + {L_YES} + + + + {L_NO} + +
    {L_MULTI_PART}:{L_NO}
    {L_MULTI_PART}:{L_YES}
      {L_MULTIPART_SIZE}:{MULTIPART.SIZE}
    {L_SEND_MAIL_FORM}: + + {L_NO} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_ATTACH_BACKUP}
    {L_MAX_UPLOAD_SIZE}:{SEND_MAIL.ATTACH_BACKUP.SIZE}
    {L_DONT_ATTACH_BACKUP}
    {L_EMAIL_RECIPIENT}:{SEND_MAIL.RECIPIENT}
    {L_EMAIL_CC}:{SEND_MAIL.CC.EMAIL_ADRESS}
    + +
    {L_FTP_TRANSFER} {FTP.NR}: + + + + + + + + + + + +
    {L_FTP_SERVER}, {L_FTP_PORT}:{FTP.CONNECTION.SERVER}:{FTP.CONNECTION.PORT}
    {L_FTP_DIR}:{FTP.CONNECTION.DIR}
    +
    +
    + + + + + + +
    diff --git a/tpl/dump/selectTables.tpl b/tpl/dump/selectTables.tpl new file mode 100644 index 0000000..205a83c --- /dev/null +++ b/tpl/dump/selectTables.tpl @@ -0,0 +1,51 @@ +
    +

    {PAGETITLE}

    +

    {L_DB}: {DATABASE}

    +
    +

    + + + +

    + + + + + + + + + + + + + + + + + + + + + + + +
    #{L_NAME}{L_DUMP}{L_INFO_RECORDS}{L_INFO_SIZE}{L_INFO_LASTUPDATE}{L_TABLE_TYPE}
    {ROW.NR}. {ROW.RECORDS}{ROW.SIZE}{ROW.LAST_UPDATE}{ROW.TABLETYPE}
    +

    + + + +

    +
    +
    diff --git a/tpl/filemanagement/converter.tpl b/tpl/filemanagement/converter.tpl new file mode 100644 index 0000000..a369735 --- /dev/null +++ b/tpl/filemanagement/converter.tpl @@ -0,0 +1,50 @@ +
    +

    {L_CONVERTER}

    +
    + + + + + + + + + + + + + + + + + + +
    {L_CONVERT_TITLE}
    {L_CONVERT_FILE}: + +
    + : + + +
    + + + +
      + +
    +
    +
    + + + + \ No newline at end of file diff --git a/tpl/filemanagement/files.tpl b/tpl/filemanagement/files.tpl new file mode 100644 index 0000000..59a65f3 --- /dev/null +++ b/tpl/filemanagement/files.tpl @@ -0,0 +1,191 @@ +
    +

    {L_FILE_MANAGE}

    +
    +
    + + + + + + + +
    + +

    + {L_AUTODELETE}: {AUTODELETE_ENABLED} + + {AUTODELETE.MSG}
    + +
    + {L_SELECTED_FILE}:   +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + {L_FM_FILES1} {L_OF} `{DB_ACTUAL_OUTPUT}`: +
    {L_ACTION}{L_DB}{L_FM_FILEDATE}Multipart{L_COMMENT}{L_FM_TABLES}{L_FM_RECORDS}{L_FM_FILESIZE}{L_ENCODING}gzScript{L_MYSQL_VERSION}
    + + {ICON_VIEW} + {ICON_DOWNLOAD} + + + + + + + + + + + + + + + + + + {ICON_VIEW} {FILE.NR_OF_MULTIPARTS} {FILE.IS_MULTIPART.FILES} + + + + {L_NO} + + {FILE.COMMENT}{FILE.NR_OF_TABLES}{FILE.NR_OF_RECORDS}{FILE.FILESIZE}{FILE.FILE_CHARSET}{FILE.ICON_COMPRESSED}{FILE.SCRIPT_VERSION}{FILE.MYSQL_VERSION}
    + +
    {DB_ACTUAL_OUTPUT}: {L_FM_NOFILESFOUND}
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_FM_ALL_BU}:
    {L_FM_DBNAME}{L_FM_ANZ_BU}{L_FM_LAST_BU}{L_FM_TOTALSIZE}
    {ICON_VIEW} {DB.DB_NAME}{DB.NR_OF_BACKUPS}{DB.LATEST_BACKUP}{DB.SUM_SIZE}
    {L_FM_TOTALSIZE}:{SUM_SIZE}
    +
    +
    +
    + + + + + + + + + + + + +
    {L_FM_FILEUPLOAD}:
    {L_MAX_UPLOAD_SIZE}: {UPLOAD_MAX_SIZE}
    {L_MAX_UPLOAD_SIZE_INFO}
    +
    + +
    +

    {L_TOOLS}

    +
    + +


    +
    + + +
    + + + diff --git a/tpl/globalHeader.tpl b/tpl/globalHeader.tpl new file mode 100644 index 0000000..60ae988 --- /dev/null +++ b/tpl/globalHeader.tpl @@ -0,0 +1,41 @@ + + + + + + + + + + + + + +MySQLDumper {L_VERSION} {MSD_VERSION} + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/tpl/home/home.tpl b/tpl/home/home.tpl new file mode 100644 index 0000000..35059a1 --- /dev/null +++ b/tpl/home/home.tpl @@ -0,0 +1,62 @@ +
    +

    {L_HOME}

    + + {DIRECTORY_WARNINGS.MSD} + + + {ICON_EDIT} {L_HTACC_EDIT} + {ICON_DELETE} {L_DELETE_HTACCESS} + + + + {L_HTACC_PROPOSED}: + {ICON_EDIT} {L_HTACC_CREATE} + + {ICON_SEARCH} PHP-Info +
    +

    {L_VERSIONSINFORMATIONEN}

    +love your data +

    + {L_MSD_VERSION}: {MSD_VERSION} +
    +{L_OS}: {OS} ({OS_EXT}) +
    +{L_MYSQL_VERSION}: {MYSQL_VERSION}
    +{L_MYSQL_CLIENT_VERSION}: {MYSQL_CLIENT_VERSION}
    +{L_PHP_VERSION}: {PHP_VERSION}  {L_MEMORY}: {MEMORY} +
    +{L_MAX_EXECUTION_TIME}: {MAX_EXECUTION_TIME} {L_SECONDS} ({MAX_EXEC_USED_BY_MSD} {L_SECONDS}) +
    + + {L_PHPBUG} +
    + + + + {L_NOFTPPOSSIBLE} +
    + + + + {L_NOGZPOSSIBLE}
    + + +{L_PHP_EXTENSIONS}: {PHP_EXTENSIONS} +
    + +
    + {L_DISABLEDFUNCTIONS}: {DISABLED_FUNCTIONS.PHP_DISABLED_FUNCTIONS} + +

    +

    {L_MSD_INFO}

    +

    {L_INFO_LOCATION} "{SERVER_NAME}" ({MSD_PATH})
    +{L_INFO_ACTDB}: {DB}
    +{L_BACKUPFILESANZAHL} {NR_OF_BACKUP_FILES} +{L_BACKUPS} ({SIZE_BACKUPS})
    +{L_FM_FREESPACE}: {FREE_DISKSPACE}
    + + {L_LASTBACKUP} {L_VOM} {LAST_BACKUP.INFO}: {LAST_BACKUP.NAME} ({LAST_BACKUP.SIZE}) + +

    + +
    \ No newline at end of file diff --git a/tpl/home/protection_create.tpl b/tpl/home/protection_create.tpl new file mode 100644 index 0000000..3ad41f5 --- /dev/null +++ b/tpl/home/protection_create.tpl @@ -0,0 +1,162 @@ + +
    +

    {L_HTACC_CREATE}

    + + {MSG.TEXT}

    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_USERNAME}:
    {L_PASSWORD}: + +
    {L_PASSWORD_REPEAT}: + +
    {L_PASSWORD_STRENGTH}: +
    +
    {L_ENCRYPTION_TYPE}: + + + + + + + + + + + + + + + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    +
    +
    + +

    +
    +
    + + + + {L_HTACC_CONTENT} .htaccess:

    + {CREATE_SUCCESS.HTACCESS} + +
    {L_HTACC_CONTENT} .htpasswd:

    + {CREATE_SUCCESS.HTPASSWD} +

    + {L_HOME} + + + +

    {L_HTACC_CREATE_ERROR}:

    +

    + {L_HTACC_CONTENT} ".htaccess":

    + + +
    {L_HTACC_CONTENT} ".htpasswd":

    + + +

    + {L_HOME} +

    + +
    diff --git a/tpl/home/protection_edit.tpl b/tpl/home/protection_edit.tpl new file mode 100644 index 0000000..f6840f6 --- /dev/null +++ b/tpl/home/protection_edit.tpl @@ -0,0 +1,76 @@ + + diff --git a/tpl/install/check_directories.tpl b/tpl/install/check_directories.tpl new file mode 100644 index 0000000..bb0dda1 --- /dev/null +++ b/tpl/install/check_directories.tpl @@ -0,0 +1,45 @@ + + + + + + +
    {L_STEP} 1: {L_SELECT_LANGUAGE} ({LANGUAGE}) {ICON_OK}{L_STEP} 2: {L_CHECK_DIRS}{L_STEP} 3: {L_DBPARAMETER} + + {ICON_OK} + +
    +
    +

    {L_STEP} 2: {L_CREATEDIRS}

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    {L_SAFEMODEDESC}

    {L_DIR}{L_RECHTE}{L_EXISTS}{L_IS_WRITABLE}
    {DIR.NAME}{DIR.CHMOD}{DIR.ICON_EXISTS}{DIR.ICON_IS_WRITABLE}
    +
    + +

    +
    +
    diff --git a/tpl/install/db_parameter.tpl b/tpl/install/db_parameter.tpl new file mode 100644 index 0000000..838e2cf --- /dev/null +++ b/tpl/install/db_parameter.tpl @@ -0,0 +1,108 @@ + + + + + + +
    {L_STEP} 1: {L_SELECT_LANGUAGE} ({LANGUAGE}) {ICON_OK}{L_STEP} 2: {L_CHECK_DIRS} {ICON_OK}{L_STEP} 3: {L_DBPARAMETER} + + {ICON_OK} + +
    + +

    {L_STEP} 3: {L_DBPARAMETER}

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + {ICON_SAVE} {CONTINUE.SAVE_AND_CONTINUE} +

    +
    {L_DB_HOST}: + +
    {L_DB_USER}:
    {L_DB_PASS}:
    * {L_DB}:

    ({L_ENTER_DB_INFO})

    {L_PORT}: +
    {L_INSTALL_HELP_PORT} +
    {L_SOCKET}: +
    {L_INSTALL_HELP_SOCKET} +
    +
    {L_TESTCONNECTION}:
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    {CONNECTION_ERROR.MSG}
    +
    {ICON_OK} {L_DBCONNECTION}
    {CONNECTION_OK.RESULT}
    {ICON_OK} {L_NO_DB_FOUND_INFO}
    +
    + +
    +
    +
    + + {ICON_SAVE} {CONTINUE.SAVE_AND_CONTINUE} +

    +
    +
    + \ No newline at end of file diff --git a/tpl/install/header.tpl b/tpl/install/header.tpl new file mode 100644 index 0000000..b4039eb --- /dev/null +++ b/tpl/install/header.tpl @@ -0,0 +1,37 @@ + + + + + + + + + + + + + +MySQLDumper {L_VERSION} {MSD_VERSION} - Installation + + + + + + + + + + + + + + +
    + + + +
    diff --git a/tpl/install/select_language.tpl b/tpl/install/select_language.tpl new file mode 100644 index 0000000..7f3de1a --- /dev/null +++ b/tpl/install/select_language.tpl @@ -0,0 +1,148 @@ + + + + + + + +
    {L_STEP} 1: {L_SELECT_LANGUAGE} ({LANGUAGE}) {ICON_OK}{L_STEP} 2: {L_CHECK_DIRS}{L_STEP} 3: {L_DBPARAMETER}
    + +
    +

    {L_STEP} 1: {L_SELECT_LANGUAGE} ({LANGUAGE})

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +

    {L_MESSAGE}: +
    {L_INFO_FSOCKOPEN_DISABLED}

    +
    + +
    #{L_LANGUAGE}{L_STATUS}{L_ACTION}
    {LANG.NR}. +
    + + +
    +
    {ICON_OK}{LANG.INSTALLED.LANG_INSTALLED}{ICON_NOT_OK}Not installed + +
    + +
    +
    + + + + diff --git a/tpl/log/log.tpl b/tpl/log/log.tpl new file mode 100644 index 0000000..e376d1f --- /dev/null +++ b/tpl/log/log.tpl @@ -0,0 +1,72 @@ +
    +

    {L_LOG}

    + + + + +
    + +
    + + + + +
    + + + + + + +
    + {ICON_OPEN_FILE} {PHPLOG}
    + + + {ICON_OPEN_FILE} {ERRORLOG.ERRORLOG}
    + + + + {ICON_OPEN_FILE} {PERLLOG.FILE_NAME}
    + + + + {ICON_OPEN_FILE} {PERLCOMPLETELOG.FILE_NAME}
    + + {L_INFO_SUM}: +
    + {PHPLOG_SIZE}
    + + + {ERRORLOG.SIZE}
    + + + + {PERLLOG.SIZE}
    + + + + {PERLCOMPLETELOG.SIZE}
    + + + {LOGSIZE_TOTAL} +
    +
    +
    + +
    + {ICON_DELETE} {L_LOG_DELETE} + {ICON_ARROW_UP} {L_NOREVERSE} + {ICON_ARROW_DOWN} {L_REVERSE} +
    +

    {L_LOG} "{LOG}":

    + + +
    + +
    +
    +
    diff --git a/tpl/log/log_ajax.tpl b/tpl/log/log_ajax.tpl new file mode 100644 index 0000000..cbe5869 --- /dev/null +++ b/tpl/log/log_ajax.tpl @@ -0,0 +1,20 @@ + {ICON_SORT} + << + >> + {PAGINATION_ENTRIES} +
    + + + + + + + + + + + + + + +
    #{L_TIMESTAMP}{L_MESSAGE}
    {LINE.NR}.{LINE.TIMESTAMP}{LINE.MSG}
    \ No newline at end of file diff --git a/tpl/menu/menu.tpl b/tpl/menu/menu.tpl new file mode 100644 index 0000000..7faca37 --- /dev/null +++ b/tpl/menu/menu.tpl @@ -0,0 +1,83 @@ + + + + + diff --git a/tpl/restore/file_select_encoding.tpl b/tpl/restore/file_select_encoding.tpl new file mode 100644 index 0000000..85e8f3b --- /dev/null +++ b/tpl/restore/file_select_encoding.tpl @@ -0,0 +1,24 @@ +
    +

    {PAGETITLE}

    +

    {L_DB}: {DATABASE}

    +

    {L_CHOOSE_CHARSET}

    +
    + + + + + + + + +
    {L_FM_CHOOSE_ENCODING}: + +
    +
    + + +
    +
    +
    diff --git a/tpl/restore/restore.tpl b/tpl/restore/restore.tpl new file mode 100644 index 0000000..11eae54 --- /dev/null +++ b/tpl/restore/restore.tpl @@ -0,0 +1,218 @@ + + + + +
    +
    +

    {L_RESTORE}

    +
    + +

    {DB_ON_SERVER}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_FILE}:{FILENAME}
    {L_MULTIPART_ACTUAL_PART}:{MULTIPART.PART}
    {L_CHARSET}:{CHARSET}
    {TABLES_TO_CREATE}
    {L_ERROR}: + 0 +
    {L_NOTICES}: + 0 +
     
    {L_PROGRESS_FILE}:
      + + + + + +
    0 % + +
    +
     
    {L_RECORDS_PER_PAGECALL}: + + + + + + + + + + +
    + + + +
     
    +
     
    {L_PROGRESS_OVER_ALL}: + + + + + +
    0 % + +
    +
    {L_PAGE_REFRESHS}:0
    {L_DURATION}:
    {L_ESTIMATED_END}:
    + +

    {L_LOG}

    + +{L_LOG}{ICON_PLUS} + +
    + +
    +
    +
    + +

    +
    diff --git a/tpl/restore/restorePrepare.tpl b/tpl/restore/restorePrepare.tpl new file mode 100644 index 0000000..cb7ff47 --- /dev/null +++ b/tpl/restore/restorePrepare.tpl @@ -0,0 +1,106 @@ +
    +

    {PAGETITLE}

    +
    +

    + + + +
    {L_SELECTED_FILE}:   +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_FM_FILES1} {L_OF} `{DB_ACTIVE}`:
    {L_DB}{L_FM_FILEDATE}{L_MULTI_PART}{L_COMMENT}{L_FM_TABLES}{L_FM_RECORDS}{L_FM_FILESIZE}{L_ENCODING}gzScript
    + + + + + + + + + + + + + + {ICON_VIEW} {FILE.NR_OF_MULTIPARTS} {FILE.IS_MULTIPART.FILES} + + + + {L_NO} + + {FILE.COMMENT}{FILE.NR_OF_TABLES}{FILE.NR_OF_RECORDS}{FILE.FILESIZE}{FILE.FILE_CHARSET}{FILE.ICON_COMPRESSED}{FILE.SCRIPT_VERSION}
    {DB_ACTUAL}: {L_FM_NOFILESFOUND}
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_FM_ALL_BU}:
    {L_FM_DBNAME}{L_FM_ANZ_BU}{L_FM_LAST_BU}{L_FM_TOTALSIZE}
    {ICON_VIEW} {DB.DB_NAME}{DB.NR_OF_BACKUPS}{DB.LATEST_BACKUP}{DB.SUM_SIZE}
    {L_FM_TOTALSIZE}:{SUM_SIZE}
    +
    +


    +
    + + + diff --git a/tpl/restore/restore_finished.tpl b/tpl/restore/restore_finished.tpl new file mode 100644 index 0000000..db80057 --- /dev/null +++ b/tpl/restore/restore_finished.tpl @@ -0,0 +1,87 @@ +
    +

    {L_RESTORE}

    + + + +
    + +

    {TIME_ELAPSED}, {PAGE_REFRESHS} {L_PAGE_REFRESHS}

    + +
    +

    {L_DONE}

    + +{TABLES_CREATED}
    +{RECORDS_INSERTED}

    + + +

    {L_ERROR}:

    +
    + + + + + + + + + + + + + + +
    #{L_TIMESTAMP}{L_ERROR}
    {ERRORS.ERROR.NR}.{ERRORS.ERROR.TIMESTAMP}{ERRORS.ERROR.MSG}
    +
    +
    + + +

    {L_LOG}:

    +
    + + + + + + + + + + + + + + +
    #{L_TIMESTAMP}{L_ACTION}
    {ACTION.NR}.{ACTION.TIMESTAMP}{ACTION.ACTION}
    +
    + + +
    +

    {L_NOTICE}:

    +
    + + + + + + + + + + + + + + +
    #{L_TIMESTAMP}{L_NOTICE}
    {NOTICES.NOTICE.NR}.{NOTICES.NOTICE.TIMESTAMP} + {NOTICES.NOTICE.NOTICE} +
    +
    +
    + + +
    + + + +
    +
    \ No newline at end of file diff --git a/tpl/restore/selectTables.tpl b/tpl/restore/selectTables.tpl new file mode 100644 index 0000000..a825667 --- /dev/null +++ b/tpl/restore/selectTables.tpl @@ -0,0 +1,56 @@ +
    +

    {PAGETITLE}

    +

    {L_DB}: {DATABASE}

    + +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    #{L_NAME}{L_RESTORE}{L_INFO_RECORDS}{L_INFO_SIZE}{L_INFO_LASTUPDATE}{L_TABLE_TYPE}
    {L_NO_MSD_BACKUP}
    {ROW.NR}. + + + + + + {ROW.RECORDS} + {ROW.SIZE}{ROW.LAST_UPDATE}{ROW.TABLETYPE}
    +

    + + + + +

    +
    +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/db/list_databases.tpl b/tpl/sqlbrowser/db/list_databases.tpl new file mode 100644 index 0000000..5d9807c --- /dev/null +++ b/tpl/sqlbrowser/db/list_databases.tpl @@ -0,0 +1,111 @@ + + + + +
    + +

    {L_INFO_DATABASES}:

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
    + + +
    +
    {L_ACTION} + {ICON_PLUS} + {ICON_MINUS} + # + + + {L_DBS}{L_TABLES}
    {DB_NOT_FOUND.NR}.{DB_NOT_FOUND.DB_NAME}{L_INFO_NODB}
    + {ICON_VIEW} + {ICON_TRUNCATE} + {ICON_DELETE} + + + checked="checked" + + /> + + + + + +
      + {ICON_PLUS} + {ICON_MINUS} +  
      +
    + + +
    +
    +
    +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/db/operation.tpl b/tpl/sqlbrowser/db/operation.tpl new file mode 100644 index 0000000..b585717 --- /dev/null +++ b/tpl/sqlbrowser/db/operation.tpl @@ -0,0 +1,32 @@ +

    {ACTION}:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    #{L_DB}{L_ACTION}{L_SQL_OUTPUT}
    {ROW.NR}.{ROW.DBNAME}{ROW.ACTION}{ICON_OK}{ROW.QUERY}
    {ERROR.NR}.{ERROR.DBNAME}{ERROR.ACTION}{ERROR.QUERY}{ICON_NOTOK}{L_ERROR}: {ERROR.ERROR}
    diff --git a/tpl/sqlbrowser/general/footer.tpl b/tpl/sqlbrowser/general/footer.tpl new file mode 100644 index 0000000..7f5eaa3 --- /dev/null +++ b/tpl/sqlbrowser/general/footer.tpl @@ -0,0 +1 @@ +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/general/mysqlVariables.tpl b/tpl/sqlbrowser/general/mysqlVariables.tpl new file mode 100644 index 0000000..6826089 --- /dev/null +++ b/tpl/sqlbrowser/general/mysqlVariables.tpl @@ -0,0 +1,28 @@ +

    {L_MYSQLVARS}:

    +
    +

    + {L_FILTER_BY}:  +

    +
    +
    + + + + + + + + + + + + + + + + + + + +
    #{L_NAME}{L_VALUE}
    {ROW.NR}.{ROW.VAR_NAME}{ROW.VAR_VALUE}
    {L_INFO_NOVARS}
    +


    \ No newline at end of file diff --git a/tpl/sqlbrowser/general/process.tpl b/tpl/sqlbrowser/general/process.tpl new file mode 100644 index 0000000..07bd828 --- /dev/null +++ b/tpl/sqlbrowser/general/process.tpl @@ -0,0 +1,59 @@ +

    {L_PROZESSE}:

    + +{L_REFRESHTIME}: {REFRESHTIME} {L_SECONDS} + +

    {L_PROCESSKILL1} {KILL_STARTED.KILL_ID} {L_PROCESSKILL2}

    + + + +

    {L_ERRORPROCESSKILL3} {KILL_WAIT.WAITTIME} {L_PROCESSKILL4} {KILLWAIT.KILL_ID} {L_PROCESSKILL2}

    + + + +

    {L_ERROR} {KILL_ERROR.MESSAGE}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {L_ACTION}#{L_PROCESS_ID}{L_DB_USER}{L_DB_HOST}{L_DB}{L_COMMAND}{L_TIME}{L_STATUS}Info
    + + {ICON_DELETE} + +   + {ROW.NR}.{ROW.ID}{ROW.USER}{ROW.HOST}{ROW.DB}{ROW.QUERY}{ROW.TIME}{ROW.STATE}{ROW.INFO}
    {L_INFO_NOPROCESSES}
    + + diff --git a/tpl/sqlbrowser/general/sqlbox.tpl b/tpl/sqlbrowser/general/sqlbox.tpl new file mode 100644 index 0000000..6353295 --- /dev/null +++ b/tpl/sqlbrowser/general/sqlbox.tpl @@ -0,0 +1,52 @@ +
    +

    + {LANG_DB}: + `{DB}` + +  {LANG_TABLE}: `{TABLENAME}` + +

    +
    + +
    + + + + + + + +
    {SQLUPLOAD.LANG_OPENSQLFILE}{SQLUPLOAD.LANG_SQL_MAXSIZE}: {SQLUPLOAD.MAX_FILESIZE}
    +
    + + +
    +
    +
    +

    + {ICON_CLOSE} + {ICON_MINUS} + {ICON_PLUS} + + + {SQLCOMBO.SQL_COMBOBOX} + + {TABLE_COMBOBOX} + + + {ICON_UPLOAD} + {ICON_SEARCH} + {ICON_MYSQL_HELP} +

    +
    +
    +
    + +
    {LANG_SQL_WARNING}
    + + + +
    +
    +
    +
    diff --git a/tpl/sqlbrowser/general/status.tpl b/tpl/sqlbrowser/general/status.tpl new file mode 100644 index 0000000..6dd3c41 --- /dev/null +++ b/tpl/sqlbrowser/general/status.tpl @@ -0,0 +1,31 @@ +

    {L_STATUS}:

    + + +
    +

    + {L_FILTER_BY}:  +

    +
    +
    + + + + + + + + + + + + + + + + + + + + +
    #{L_NAME}{L_VALUE}
    {ROW.NR}.{ROW.VAR_NAME}{ROW.VAR_VALUE}
    {L_INFO_NOSTATUS}
    +


    \ No newline at end of file diff --git a/tpl/sqlbrowser/general/tools.tpl b/tpl/sqlbrowser/general/tools.tpl new file mode 100644 index 0000000..384b794 --- /dev/null +++ b/tpl/sqlbrowser/general/tools.tpl @@ -0,0 +1 @@ +

    {L_TOOLS}:

    diff --git a/tpl/sqlbrowser/mysql_search.tpl b/tpl/sqlbrowser/mysql_search.tpl new file mode 100644 index 0000000..05641c8 --- /dev/null +++ b/tpl/sqlbrowser/mysql_search.tpl @@ -0,0 +1,82 @@ +
    +
    +
    + {LANG_SQLSEARCH} +

    + {LANG_SQL_SEARCHWORDS}: + + + {LANG_SEARCH_EXPLAIN}
    +

    +
    +
    + {LANG_SEARCH_OPTIONS} + + +
    + + +
    + + +
    + {LANG_SEARCH_IN_TABLE}:   + + + {HIDDEN_FIELDS} +
    +
    +
    + + {HITS.LANG_SEARCH_RESULTS}:
    + +

    + + + {HITS.LANG_ACCESS_KEYS} +

    +
    + +
    + + + + + + + + + + + + + + + + + +
     #{HITS.TABLEHEAD.KEY}
    + {HITS.TABLEROW.ICON_EDIT}{HITS.TABLEROW.ICON_DELETE} + {HITS.TABLEROW.NR}. {HITS.TABLEROW.TABLEDATA.VAL}
    + + + + {NO_RESULTS.LANG_SEARCH_NO_RESULTS} + + + + {NO_ENTRIES.LANG_NO_ENTRIES} + +
    + + diff --git a/tpl/sqlbrowser/nav/topnav_general.tpl b/tpl/sqlbrowser/nav/topnav_general.tpl new file mode 100644 index 0000000..0b63546 --- /dev/null +++ b/tpl/sqlbrowser/nav/topnav_general.tpl @@ -0,0 +1,8 @@ +
    +

    {L_SQL_BROWSER}

    + {ICON_DB} {L_DBS} + {ICON_EDIT} {L_SQLBOX} + {ICON_VIEW} {L_MYSQLVARS} + {ICON_VIEW} {L_STATUS} + {ICON_VIEW} {L_PROZESSE} +
    diff --git a/tpl/sqlbrowser/sql_record_insert_inputmask.tpl b/tpl/sqlbrowser/sql_record_insert_inputmask.tpl new file mode 100644 index 0000000..2828079 --- /dev/null +++ b/tpl/sqlbrowser/sql_record_insert_inputmask.tpl @@ -0,0 +1,52 @@ + +
    + + +{HIDDEN_FIELDS} + + + + + + + + + + + + + + + +
    {L_SQL_RECORDNEW}
    {L_NAME}NULL{L_VALUE}
    {ROW.FIELD_NAME} +   + + +   + + + + + + + + + + +
    +
    + + +     +     +

    +
    +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/sql_record_update_inputmask.tpl b/tpl/sqlbrowser/sql_record_update_inputmask.tpl new file mode 100644 index 0000000..6713223 --- /dev/null +++ b/tpl/sqlbrowser/sql_record_update_inputmask.tpl @@ -0,0 +1,55 @@ + +
    + + +{HIDDEN_FIELDS} + + + + + + + + + + + + + + + +
    {L_SQL_RECORDEDIT}
    {L_NAME}NULL{L_VALUE}
    {ROW.FIELD_NAME} +   + + +   + + + + + + + + + + +
    +
    + + +     +     +

    +
    +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/sqlbox/showQueryResults.tpl b/tpl/sqlbrowser/sqlbox/showQueryResults.tpl new file mode 100644 index 0000000..a2b3dbc --- /dev/null +++ b/tpl/sqlbrowser/sqlbox/showQueryResults.tpl @@ -0,0 +1,26 @@ +

    {L_SQL_OUTPUT}

    +
    + {L_SQL_OUT1} {COUNT_DROP} + DROP-, + {COUNT_CREATE} + DELETE-, + {COUNT_DELETE} + CREATE-, + {COUNT_INSERT} + INSERT-, + {COUNT_UPDATE} + UPDATE- + {COUNT_SELECT} + SELECT- + {L_SQL_OUT2}.

    + +
    {SQL_COMMAND.NR}. {SQL_COMMAND.EXEC_TIME}: {SQL_COMMAND.SQL}
    + +
    + + \ No newline at end of file diff --git a/tpl/sqlbrowser/sqlbox/showResults.tpl b/tpl/sqlbrowser/sqlbox/showResults.tpl new file mode 100644 index 0000000..78ed169 --- /dev/null +++ b/tpl/sqlbrowser/sqlbox/showResults.tpl @@ -0,0 +1,57 @@ +
    +
    + + + + + + + + {PAGER.SHOWING_ENTRY_X_OF_Y} + +
    + + + + + + + + + + + + + + + + + + +
    # + + + + {ICON_UP} + + + {ICON_DOWN} + + + {HEADLINE.FIELDS.NAME} + +
    {ROW.NR}.{ROW.FIELD.VAL}
    +
    + + + + \ No newline at end of file diff --git a/tpl/sqlbrowser/sqlbox/sqlbox.tpl b/tpl/sqlbrowser/sqlbox/sqlbox.tpl new file mode 100644 index 0000000..cc62923 --- /dev/null +++ b/tpl/sqlbrowser/sqlbox/sqlbox.tpl @@ -0,0 +1,66 @@ + + +

    {L_DB} `{DB}` + +.`{SHOW_TABLENAME.TABLE}` + +

    + + + +
    + + + + + + + +
    {SQLUPLOAD.LANG_OPENSQLFILE}{SQLUPLOAD.LANG_SQL_MAXSIZE}: {SQLUPLOAD.MAX_FILESIZE}
    +
    + + +
    +
    +
    +

    + {ICON_CLOSE} + {ICON_MINUS} + {ICON_PLUS} + + + {SQLCOMBO.SQL_COMBOBOX} + + + + + + {ICON_MYSQL_HELP} +

    +
    +
    +
    + +
    {LANG_SQL_WARNING}
    + + +
    +
    +
    +
    diff --git a/tpl/sqlbrowser/table/edit_field.tpl b/tpl/sqlbrowser/table/edit_field.tpl new file mode 100644 index 0000000..08ac06b --- /dev/null +++ b/tpl/sqlbrowser/table/edit_field.tpl @@ -0,0 +1,27 @@ +
    + + + + + + + + + + + + + +
    {FIELD_EDIT.KEY}:
    {FIELD_VIEW.KEY}:{FIELD_VIEW.VALUE}
    + + + + + + + + + + + +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/table/edit_table.tpl b/tpl/sqlbrowser/table/edit_table.tpl new file mode 100644 index 0000000..c315437 --- /dev/null +++ b/tpl/sqlbrowser/table/edit_table.tpl @@ -0,0 +1,204 @@ + + + + +

    {L_FIELDS_OF_TABLE} `{DB}` +.`{TABLE}` +

    + +
    +
    + + + +
    + + + style="display:none" + +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + +
    +
    {L_ACTION} + {ICON_PLUS} + {ICON_MINUS} + # + {L_FIELDS} + + Type + + Null + + Key + + Default + + Extra + + {L_COLLATION} +
    + + {ICON_EDIT} + + + + checked="checked" + + /> + + + + {ROW.ENUM_SET.ICON_BROWSE} +
      + {ICON_PLUS} + {ICON_MINUS} +
    + + +
    + +
    +
    +
    \ No newline at end of file diff --git a/tpl/sqlbrowser/table/listTables.tpl b/tpl/sqlbrowser/table/listTables.tpl new file mode 100644 index 0000000..2de6c84 --- /dev/null +++ b/tpl/sqlbrowser/table/listTables.tpl @@ -0,0 +1,252 @@ + + +

    {L_SQL_TABLESOFDB} `{DB_NAME}`

    + +
    +
    + + + + + +{L_INFO_DBEMPTY}
    + + + +1 {L_TABLE} + + + +{TABLE_COUNT} {L_TABLES} + +
    + + style="display:none" + +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + +
    + + + + + + +
    +
    {L_ACTION} + {ICON_PLUS} + {ICON_MINUS} + # + {SORT_NAME} {L_TABLE} + + {SORT_RECORDS} {L_INFO_RECORDS} + + {SORT_DATA_LENGTH} {L_INFO_SIZE} + + {SORT_INDEX_LENGTH} {L_TITLE_INDEX} + + {SORT_AUTO_INCREMENT} + {L_NEXT_AUTO_INCREMENT_SHORT} + + {SORT_DATA_FREE} {L_INFO_OPTIMIZED} + + {SORT_UPDATE_TIME} {L_INFO_LASTUPDATE} + + {SORT_ENGINE} {L_ENGINE} + + {SORT_COLLATION} {L_COLLATION} + + {SORT_COMMENT} {L_COMMENT} +
    + {ICON_VIEW} + {ICON_EDIT} + + + checked="checked" + + /> + + + + {ROW.COMMENT}
      + {ICON_PLUS} + {ICON_MINUS} + {L_INFO_SUM}:{SUM.RECORDS}{SUM.DATA_LENGTH}{SUM.INDEX_LENGTH}{SUM.LAST_UPDATE} 
    + + +
    + + + + + + +
    +
    +
    + + +


    \ No newline at end of file diff --git a/tpl/sqlbrowser/table/operation.tpl b/tpl/sqlbrowser/table/operation.tpl new file mode 100644 index 0000000..7018ab9 --- /dev/null +++ b/tpl/sqlbrowser/table/operation.tpl @@ -0,0 +1,34 @@ +

    {ACTION}:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    #{L_TABLE}{L_ACTION}{L_MESSAGE_TYPE}{L_MESSAGE}
    {ROW.NR}.{ROW.TABLENAME}{ROW.ACTION}{ROW.TYPE}{ROW.MESSAGE}{ICON_OK}
    {ERROR.NR}.{ERROR.TABLENAME}{ERROR.QUERY}{L_ERROR}{ERROR.ERROR}{ICON_NOTOK}
    diff --git a/tpl/sqlbrowser/table/show_tabledata.tpl b/tpl/sqlbrowser/table/show_tabledata.tpl new file mode 100644 index 0000000..a39d8c7 --- /dev/null +++ b/tpl/sqlbrowser/table/show_tabledata.tpl @@ -0,0 +1,165 @@ + + + + + +

    {L_EXECUTED_QUERY}

    +
    + {POSTED_MYSQL_QUERY.QUERY} +

    + {POSTED_MYSQL_QUERY.ROWS_AFFECTED} {L_ROWS_AFFECTED} +

     

    + + + +

    {L_QUERY_FAILED}

    +
    {MYSQL_ERROR.QUERY}
    +
    + {MYSQL_ERROR.ERROR} +
    +

     

    + + + + +

    {L_SQL_DATAOFTABLE} `{DB_NAME}`.`{TABLE_NAME}`

    + +
    +
    +
    + + +
    +
    + + + + + + {L_ENTRIES_PER_PAGE} + {L_STARTING_WITH} +
    + + + + +
    +
    + + + style="display:none" + +> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + {ICON_EDIT} {L_NEW} + + + + + +
    +
    {L_ACTION} + {ICON_PLUS} + {ICON_MINUS} + # + {COL_HEADER.SORT} {COL_HEADER.LABEL} +
    + + {ICON_VIEW} + + + {ICON_EDIT} + + + + checked="checked" + + /> + + +
    +
    + + {ICON_EDIT} {L_NEW} + + + + + +
    +
    + + +


    \ No newline at end of file diff --git a/tpl/sqlbrowser/table/show_tabledata_entry.tpl b/tpl/sqlbrowser/table/show_tabledata_entry.tpl new file mode 100644 index 0000000..da096fe --- /dev/null +++ b/tpl/sqlbrowser/table/show_tabledata_entry.tpl @@ -0,0 +1,30 @@ +
    + + + + + + + + + + + + + +
    {FIELD_EDIT.NAME}:
    {FIELD_VIEW.NAME}:{FIELD_VIEW.VALUE}
    + + + + + + + + + + + + + + +
    \ No newline at end of file