Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/lavabit/magma.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorLadar Levison <ladar@lavabit.com>2016-06-13 21:25:43 +0300
committerLadar Levison <ladar@lavabit.com>2016-06-13 21:25:43 +0300
commit682f1894faf07886ad422990c8a77f97e0f29843 (patch)
tree51eff7cb2d20d34c062f83f5c9ff88c375f0fa2b /res
parent3e50b8dd5273aeec3c3d8d37a9aabe90b40a5dac (diff)
Merging the static web pages from the web-static branch.
Diffstat (limited to 'res')
-rw-r--r--res/pages/about.html53
-rw-r--r--res/pages/contact.html85
-rw-r--r--res/pages/corporate_e_mail.html155
-rw-r--r--res/pages/credits.html101
-rw-r--r--res/pages/css/site.css267
-rw-r--r--res/pages/css_valid.html76
-rw-r--r--res/pages/disposable_e_mail.html93
-rw-r--r--res/pages/features.html100
-rw-r--r--res/pages/help.html57
-rw-r--r--res/pages/history.html58
-rw-r--r--res/pages/images/bg_table_bot_tile.gifbin0 -> 401 bytes
-rw-r--r--res/pages/images/bg_table_head_tile.gifbin0 -> 337 bytes
-rw-r--r--res/pages/images/bg_table_row_tile.gifbin0 -> 37 bytes
-rw-r--r--res/pages/images/bg_table_top_tile.gifbin0 -> 403 bytes
-rw-r--r--res/pages/images/bg_tile.gifbin0 -> 5590 bytes
-rw-r--r--res/pages/images/btn_back.gifbin0 -> 1526 bytes
-rw-r--r--res/pages/images/btn_finish.gifbin0 -> 1581 bytes
-rw-r--r--res/pages/images/btn_next.gifbin0 -> 1515 bytes
-rw-r--r--res/pages/images/btn_sign_up.gifbin0 -> 2399 bytes
-rw-r--r--res/pages/images/btn_submit.gifbin0 -> 1701 bytes
-rw-r--r--res/pages/images/captcha.pngbin0 -> 4378 bytes
-rw-r--r--res/pages/images/check.pngbin0 -> 9814 bytes
-rw-r--r--res/pages/images/chrome-background.pngbin0 -> 1393 bytes
-rw-r--r--res/pages/images/css_valid_grey.gifbin0 -> 146 bytes
-rw-r--r--res/pages/images/css_valid_red.gifbin0 -> 175 bytes
-rw-r--r--res/pages/images/diecut.gifbin0 -> 907 bytes
-rw-r--r--res/pages/images/error.pngbin0 -> 1245 bytes
-rw-r--r--res/pages/images/expander.pngbin0 -> 489 bytes
-rw-r--r--res/pages/images/express_1.jpgbin0 -> 65497 bytes
-rw-r--r--res/pages/images/express_10.jpgbin0 -> 95240 bytes
-rw-r--r--res/pages/images/express_11.jpgbin0 -> 74681 bytes
-rw-r--r--res/pages/images/express_12.jpgbin0 -> 61626 bytes
-rw-r--r--res/pages/images/express_13.jpgbin0 -> 68901 bytes
-rw-r--r--res/pages/images/express_14.jpgbin0 -> 61995 bytes
-rw-r--r--res/pages/images/express_15.jpgbin0 -> 97401 bytes
-rw-r--r--res/pages/images/express_2.jpgbin0 -> 74440 bytes
-rw-r--r--res/pages/images/express_3.jpgbin0 -> 75127 bytes
-rw-r--r--res/pages/images/express_4.jpgbin0 -> 75389 bytes
-rw-r--r--res/pages/images/express_5.jpgbin0 -> 77351 bytes
-rw-r--r--res/pages/images/express_6.jpgbin0 -> 85921 bytes
-rw-r--r--res/pages/images/express_7.jpgbin0 -> 71422 bytes
-rw-r--r--res/pages/images/express_8.jpgbin0 -> 74881 bytes
-rw-r--r--res/pages/images/express_9.jpgbin0 -> 93238 bytes
-rw-r--r--res/pages/images/hosting_logo.jpgbin0 -> 6502 bytes
-rw-r--r--res/pages/images/loading.gifbin0 -> 8787 bytes
-rw-r--r--res/pages/images/loading.pngbin0 -> 878 bytes
-rw-r--r--res/pages/images/loading16x16.gifbin0 -> 1737 bytes
-rw-r--r--res/pages/images/login-sprite.pngbin0 -> 1962 bytes
-rw-r--r--res/pages/images/logo-white.pngbin0 -> 4697 bytes
-rw-r--r--res/pages/images/logo.gifbin0 -> 1950 bytes
-rw-r--r--res/pages/images/logo.pngbin0 -> 1476 bytes
-rw-r--r--res/pages/images/menu-sprite.pngbin0 -> 4106 bytes
-rw-r--r--res/pages/images/nav_about.gifbin0 -> 137 bytes
-rw-r--r--res/pages/images/nav_about_active.gifbin0 -> 137 bytes
-rw-r--r--res/pages/images/nav_about_active_over.gifbin0 -> 143 bytes
-rw-r--r--res/pages/images/nav_about_over.gifbin0 -> 143 bytes
-rw-r--r--res/pages/images/nav_contact.gifbin0 -> 144 bytes
-rw-r--r--res/pages/images/nav_contact_active.gifbin0 -> 144 bytes
-rw-r--r--res/pages/images/nav_contact_active_over.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_contact_over.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_features.gifbin0 -> 149 bytes
-rw-r--r--res/pages/images/nav_features_active.gifbin0 -> 149 bytes
-rw-r--r--res/pages/images/nav_features_active_over.gifbin0 -> 152 bytes
-rw-r--r--res/pages/images/nav_features_over.gifbin0 -> 152 bytes
-rw-r--r--res/pages/images/nav_help.gifbin0 -> 132 bytes
-rw-r--r--res/pages/images/nav_help_active.gifbin0 -> 132 bytes
-rw-r--r--res/pages/images/nav_help_active_over.gifbin0 -> 136 bytes
-rw-r--r--res/pages/images/nav_help_over.gifbin0 -> 136 bytes
-rw-r--r--res/pages/images/nav_home.gifbin0 -> 133 bytes
-rw-r--r--res/pages/images/nav_home_active.gifbin0 -> 133 bytes
-rw-r--r--res/pages/images/nav_home_active_over.gifbin0 -> 138 bytes
-rw-r--r--res/pages/images/nav_home_over.gifbin0 -> 138 bytes
-rw-r--r--res/pages/images/nav_links_over.gifbin0 -> 137 bytes
-rw-r--r--res/pages/images/nav_network.gifbin0 -> 148 bytes
-rw-r--r--res/pages/images/nav_network_active.gifbin0 -> 148 bytes
-rw-r--r--res/pages/images/nav_network_active_over.gifbin0 -> 156 bytes
-rw-r--r--res/pages/images/nav_network_over.gifbin0 -> 156 bytes
-rw-r--r--res/pages/images/nav_news_over.gifbin0 -> 139 bytes
-rw-r--r--res/pages/images/nav_philosophy.gifbin0 -> 159 bytes
-rw-r--r--res/pages/images/nav_philosophy_active.gifbin0 -> 159 bytes
-rw-r--r--res/pages/images/nav_philosophy_active_over.gifbin0 -> 164 bytes
-rw-r--r--res/pages/images/nav_philosophy_over.gifbin0 -> 172 bytes
-rw-r--r--res/pages/images/nav_preferences.gifbin0 -> 164 bytes
-rw-r--r--res/pages/images/nav_preferences_active.gifbin0 -> 164 bytes
-rw-r--r--res/pages/images/nav_preferences_active_over.gifbin0 -> 170 bytes
-rw-r--r--res/pages/images/nav_preferences_over.gifbin0 -> 170 bytes
-rw-r--r--res/pages/images/nav_register.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_register_active.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_register_active_over.gifbin0 -> 157 bytes
-rw-r--r--res/pages/images/nav_register_over.gifbin0 -> 157 bytes
-rw-r--r--res/pages/images/nav_services.gifbin0 -> 148 bytes
-rw-r--r--res/pages/images/nav_services_active.gifbin0 -> 148 bytes
-rw-r--r--res/pages/images/nav_services_active_over.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_services_over.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/nav_signup_over.gifbin0 -> 157 bytes
-rw-r--r--res/pages/images/nav_testimonials.gifbin0 -> 158 bytes
-rw-r--r--res/pages/images/nav_testimonials_active.gifbin0 -> 158 bytes
-rw-r--r--res/pages/images/nav_testimonials_active_over.gifbin0 -> 163 bytes
-rw-r--r--res/pages/images/nav_testimonials_over.gifbin0 -> 171 bytes
-rw-r--r--res/pages/images/nav_webmail.gifbin0 -> 152 bytes
-rw-r--r--res/pages/images/nav_webmail_active.gifbin0 -> 152 bytes
-rw-r--r--res/pages/images/nav_webmail_active_over.gifbin0 -> 159 bytes
-rw-r--r--res/pages/images/nav_webmail_over.gifbin0 -> 151 bytes
-rw-r--r--res/pages/images/outlook_01.pngbin0 -> 99603 bytes
-rw-r--r--res/pages/images/outlook_02.pngbin0 -> 124569 bytes
-rw-r--r--res/pages/images/outlook_03.pngbin0 -> 129224 bytes
-rw-r--r--res/pages/images/outlook_04.pngbin0 -> 135030 bytes
-rw-r--r--res/pages/images/outlook_05.pngbin0 -> 151115 bytes
-rw-r--r--res/pages/images/outlook_06.pngbin0 -> 159783 bytes
-rw-r--r--res/pages/images/outlook_07.pngbin0 -> 167104 bytes
-rw-r--r--res/pages/images/outlook_08.pngbin0 -> 189580 bytes
-rw-r--r--res/pages/images/outlook_09.pngbin0 -> 107128 bytes
-rw-r--r--res/pages/images/pidgin_01.pngbin0 -> 29494 bytes
-rw-r--r--res/pages/images/pidgin_02.pngbin0 -> 31159 bytes
-rw-r--r--res/pages/images/pidgin_03.pngbin0 -> 52091 bytes
-rw-r--r--res/pages/images/pidgin_04.pngbin0 -> 35265 bytes
-rw-r--r--res/pages/images/pidgin_05.pngbin0 -> 23635 bytes
-rw-r--r--res/pages/images/pidgin_06.pngbin0 -> 35567 bytes
-rw-r--r--res/pages/images/pidgin_07.pngbin0 -> 32949 bytes
-rw-r--r--res/pages/images/pidgin_08.pngbin0 -> 22759 bytes
-rw-r--r--res/pages/images/splash_about.gifbin0 -> 16105 bytes
-rw-r--r--res/pages/images/splash_contact.gifbin0 -> 20906 bytes
-rw-r--r--res/pages/images/splash_features.gifbin0 -> 19288 bytes
-rw-r--r--res/pages/images/splash_help.gifbin0 -> 17972 bytes
-rw-r--r--res/pages/images/splash_index.gifbin0 -> 16625 bytes
-rw-r--r--res/pages/images/splash_network.gifbin0 -> 17237 bytes
-rw-r--r--res/pages/images/splash_philosophy.gifbin0 -> 27166 bytes
-rw-r--r--res/pages/images/splash_services.gifbin0 -> 23336 bytes
-rw-r--r--res/pages/images/splash_signup.gifbin0 -> 9796 bytes
-rw-r--r--res/pages/images/splash_testimonials.gifbin0 -> 14620 bytes
-rw-r--r--res/pages/images/sprite.pngbin0 -> 10421 bytes
-rw-r--r--res/pages/images/tab-background.pngbin0 -> 1326 bytes
-rw-r--r--res/pages/images/thunderbird_01.pngbin0 -> 94483 bytes
-rw-r--r--res/pages/images/thunderbird_02.pngbin0 -> 119177 bytes
-rw-r--r--res/pages/images/thunderbird_03.pngbin0 -> 124842 bytes
-rw-r--r--res/pages/images/thunderbird_04.pngbin0 -> 123878 bytes
-rw-r--r--res/pages/images/thunderbird_05.pngbin0 -> 74890 bytes
-rw-r--r--res/pages/images/thunderbird_06.pngbin0 -> 85316 bytes
-rw-r--r--res/pages/images/toolbar-background.pngbin0 -> 1150 bytes
-rw-r--r--res/pages/images/tools-sprite.pngbin0 -> 5396 bytes
-rw-r--r--res/pages/images/trillian_01.pngbin0 -> 28153 bytes
-rw-r--r--res/pages/images/trillian_02.pngbin0 -> 97034 bytes
-rw-r--r--res/pages/images/trillian_03.pngbin0 -> 70563 bytes
-rw-r--r--res/pages/images/trillian_04.pngbin0 -> 28872 bytes
-rw-r--r--res/pages/images/trillian_05.pngbin0 -> 37495 bytes
-rw-r--r--res/pages/images/trillian_06.pngbin0 -> 26555 bytes
-rw-r--r--res/pages/images/trillian_07.pngbin0 -> 27195 bytes
-rw-r--r--res/pages/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--res/pages/images/ui-bg_flat_75_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--res/pages/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--res/pages/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--res/pages/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--res/pages/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--res/pages/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--res/pages/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--res/pages/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--res/pages/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--res/pages/images/ui-icons_454545_256x240.pngbin0 -> 5355 bytes
-rw-r--r--res/pages/images/ui-icons_888888_256x240.pngbin0 -> 4369 bytes
-rw-r--r--res/pages/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--res/pages/images/wai_aaa_grey.gifbin0 -> 139 bytes
-rw-r--r--res/pages/images/wai_aaa_red.gifbin0 -> 166 bytes
-rw-r--r--res/pages/images/windows_live_mail_01.pngbin0 -> 83321 bytes
-rw-r--r--res/pages/images/windows_live_mail_02.pngbin0 -> 161481 bytes
-rw-r--r--res/pages/images/windows_live_mail_03.pngbin0 -> 161948 bytes
-rw-r--r--res/pages/images/windows_live_mail_04.pngbin0 -> 149160 bytes
-rw-r--r--res/pages/images/windows_live_mail_05.pngbin0 -> 145495 bytes
-rw-r--r--res/pages/images/xhtml_strict_grey.gifbin0 -> 155 bytes
-rw-r--r--res/pages/images/xhtml_strict_red.gifbin0 -> 182 bytes
-rw-r--r--res/pages/index.html57
-rw-r--r--res/pages/js/advertising.js49
-rw-r--r--res/pages/js/contact.js80
-rw-r--r--res/pages/js/corporate_e_mail.js11
-rw-r--r--res/pages/js/health.js8
-rw-r--r--res/pages/js/index.js4
-rw-r--r--res/pages/js/jquery.corner.js180
-rw-r--r--res/pages/js/jquery.js8936
-rw-r--r--res/pages/js/overlib.js1491
-rw-r--r--res/pages/js/personal_e_mail.js7
-rw-r--r--res/pages/js/plugins.js19901
-rw-r--r--res/pages/js/register.js105
-rw-r--r--res/pages/js/site.js105
-rw-r--r--res/pages/js/statistics.js8
-rw-r--r--res/pages/json/requests/ad.json7
-rw-r--r--res/pages/json/requests/alert.acknowledge.json7
-rw-r--r--res/pages/json/requests/alert.list.json5
-rw-r--r--res/pages/json/requests/aliases.json5
-rw-r--r--res/pages/json/requests/attachments.add.json8
-rw-r--r--res/pages/json/requests/attachments.progress.json7
-rw-r--r--res/pages/json/requests/attachments.remove.json8
-rw-r--r--res/pages/json/requests/auth.json8
-rw-r--r--res/pages/json/requests/config.edit.json8
-rw-r--r--res/pages/json/requests/config.load.json5
-rw-r--r--res/pages/json/requests/contacts.add.json11
-rw-r--r--res/pages/json/requests/contacts.edit.json11
-rw-r--r--res/pages/json/requests/contacts.list.json7
-rw-r--r--res/pages/json/requests/contacts.load.json8
-rw-r--r--res/pages/json/requests/contacts.move.json9
-rw-r--r--res/pages/json/requests/contacts.remove.json8
-rw-r--r--res/pages/json/requests/display-options.json0
-rw-r--r--res/pages/json/requests/folders.add.json8
-rw-r--r--res/pages/json/requests/folders.list.json7
-rw-r--r--res/pages/json/requests/folders.move.json8
-rw-r--r--res/pages/json/requests/folders.remove.json7
-rw-r--r--res/pages/json/requests/folders.rename.json8
-rw-r--r--res/pages/json/requests/folders.tag.json7
-rw-r--r--res/pages/json/requests/help.page.json0
-rw-r--r--res/pages/json/requests/help.topics.json0
-rw-r--r--res/pages/json/requests/logout.json5
-rw-r--r--res/pages/json/requests/logs.mail.json0
-rw-r--r--res/pages/json/requests/logs.security.json0
-rw-r--r--res/pages/json/requests/logs.statistics.json0
-rw-r--r--res/pages/json/requests/messages.compose.json5
-rw-r--r--res/pages/json/requests/messages.copy.json9
-rw-r--r--res/pages/json/requests/messages.flag.json36
-rw-r--r--res/pages/json/requests/messages.list.json7
-rw-r--r--res/pages/json/requests/messages.load.json9
-rw-r--r--res/pages/json/requests/messages.move.json9
-rw-r--r--res/pages/json/requests/messages.remove.json8
-rw-r--r--res/pages/json/requests/messages.send.json18
-rw-r--r--res/pages/json/requests/messages.tag.json36
-rw-r--r--res/pages/json/requests/messages.tags.json6
-rw-r--r--res/pages/json/requests/meta.json5
-rw-r--r--res/pages/json/requests/scrape.add.json10
-rw-r--r--res/pages/json/requests/scrape.json7
-rw-r--r--res/pages/json/requests/search.json18
-rw-r--r--res/pages/json/requests/settings.identity.json5
-rw-r--r--res/pages/json/responses/ad.json38
-rw-r--r--res/pages/json/responses/alert.acknowledge.json7
-rw-r--r--res/pages/json/responses/alert.list.json17
-rw-r--r--res/pages/json/responses/aliases.json18
-rw-r--r--res/pages/json/responses/attachments.add.json7
-rw-r--r--res/pages/json/responses/attachments.progress.json7
-rw-r--r--res/pages/json/responses/attachments.remove.json5
-rw-r--r--res/pages/json/responses/auth.json16
-rw-r--r--res/pages/json/responses/contacts.add.json7
-rw-r--r--res/pages/json/responses/contacts.edit.json23
-rw-r--r--res/pages/json/responses/contacts.list.json67
-rw-r--r--res/pages/json/responses/contacts.load.json22
-rw-r--r--res/pages/json/responses/contacts.move.json0
-rw-r--r--res/pages/json/responses/contacts.remove.json5
-rw-r--r--res/pages/json/responses/display-options.json11
-rw-r--r--res/pages/json/responses/folders.add.json7
-rw-r--r--res/pages/json/responses/folders.list.json107
-rw-r--r--res/pages/json/responses/folders.move.json5
-rw-r--r--res/pages/json/responses/folders.remove.json5
-rw-r--r--res/pages/json/responses/folders.rename.json5
-rw-r--r--res/pages/json/responses/folders.tag.json9
-rw-r--r--res/pages/json/responses/help.page.json7
-rw-r--r--res/pages/json/responses/help.topics.json16
-rw-r--r--res/pages/json/responses/logout.json7
-rw-r--r--res/pages/json/responses/logs.mail.json59
-rw-r--r--res/pages/json/responses/logs.security.json23
-rw-r--r--res/pages/json/responses/logs.statistics.json42
-rw-r--r--res/pages/json/responses/messages.compose.json5
-rw-r--r--res/pages/json/responses/messages.copy.json11
-rw-r--r--res/pages/json/responses/messages.flag.json14
-rw-r--r--res/pages/json/responses/messages.list.json61
-rw-r--r--res/pages/json/responses/messages.load.json70
-rw-r--r--res/pages/json/responses/messages.move.json5
-rw-r--r--res/pages/json/responses/messages.remove.json5
-rw-r--r--res/pages/json/responses/messages.send.json5
-rw-r--r--res/pages/json/responses/messages.tag.json5
-rw-r--r--res/pages/json/responses/messages.tags.json5
-rw-r--r--res/pages/json/responses/meta.json17
-rw-r--r--res/pages/json/responses/scrape.add.json5
-rw-r--r--res/pages/json/responses/scrape.json25
-rw-r--r--res/pages/json/responses/search.json33
-rw-r--r--res/pages/json/responses/settings.identity.json10
-rw-r--r--res/pages/outlook.html82
-rw-r--r--res/pages/outlook_express.html98
-rw-r--r--res/pages/personal_e_mail.html206
-rw-r--r--res/pages/pidgin.html79
-rw-r--r--res/pages/questions.html95
-rw-r--r--res/pages/report_abuse.html81
-rw-r--r--res/pages/secure.html67
-rw-r--r--res/pages/services.html53
-rw-r--r--res/pages/settings.html130
-rw-r--r--res/pages/site_map.html184
-rw-r--r--res/pages/sitemap.xml261
-rw-r--r--res/pages/thunderbird.html74
-rw-r--r--res/pages/trillian.html73
-rw-r--r--res/pages/troubleshooting.html74
-rw-r--r--res/pages/tutorials.html67
-rw-r--r--res/pages/wai_aaa.html60
-rw-r--r--res/pages/windows_live_mail.html69
-rw-r--r--res/pages/xhtml_valid.html231
-rw-r--r--res/templates/register/message.template16
-rw-r--r--res/templates/register/step1.template34
-rw-r--r--res/templates/register/step2.template16
-rw-r--r--res/templates/register/step3.template22
291 files changed, 35191 insertions, 46 deletions
diff --git a/res/pages/about.html b/res/pages/about.html
new file mode 100644
index 00000000..2082f7b7
--- /dev/null
+++ b/res/pages/about.html
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. About</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page links to content about $Provider and its philosophy." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#about">Skip Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" class="active" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="about" class="content">
+ <h2>About</h2>
+ <h3><a href="history.html">History</a></h3>
+ <p>If you're curious about how long $Provider has been around, why it was founded and how we arrived at our current name, read this brief chronicle of our corporate history.</p>
+ <h3><a href="credits.html">Credits</a></h3>
+ <p>Our services wouldn't be possible without the help of numerous companies and open source software projects. This page contains links to the companies and software projects we use and recommend.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, 'images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, 'images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, 'images/css_valid_red.gif')" onmouseout="ChangeImage(this, 'images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, 'images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, 'images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/contact.html b/res/pages/contact.html
new file mode 100644
index 00000000..c90d381a
--- /dev/null
+++ b/res/pages/contact.html
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Contact</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page lets our users and the community at large contact the $Provider Team." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ <script type="text/javascript" src="/js/jquery.js"></script>
+ <script type="text/javascript" src="/js/contact.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#contact">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" class="active" href="contact">Contact Information</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="contact" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="contact" class="active">Contact $Provider</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Contact $Provider</h2>
+ <p>We want to hear from you. Please use the form below to get in touch—we’re here to help.</p>
+ <form id="contact" method="post" action="contact">
+ <table>
+ <caption>Contact the $Provider team through this form.</caption>
+ <tbody>
+ <tr>
+ <th><label for="department">Department:</label></th>
+ <td><select id="department" name="department"><option>General</option><option>Support</option><option>Sales</option><option>Bugs</option></select></td>
+ </tr>
+ <tr>
+ <th><label for="your_name">Your Name:</label></th>
+ <td><input id="your_name" name="your_name" type="text" value="" /></td>
+ </tr>
+ <tr>
+ <th><label for="your_e_mail">Your E-mail:</label></th>
+ <td><input id="your_e_mail" name="your_e_mail" type="text" value="" /></td>
+ </tr>
+ <tr id="message">
+ <th><label for="your_message">Your Message:</label></th>
+ <td><textarea id="your_message" name="your_message" rows="10" cols="60"></textarea></td>
+ </tr>
+ </tbody>
+ </table>
+ <div>
+ <button id="contact_sub" name="contact_sub" type="submit" title="Use this button to submit the form."><strong>Submit &gt;&gt;</strong></button>
+ </div>
+ </form>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/corporate_e_mail.html b/res/pages/corporate_e_mail.html
new file mode 100644
index 00000000..b7a3bdba
--- /dev/null
+++ b/res/pages/corporate_e_mail.html
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Corporate E-mail</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes the e-mail services we offer companies and domain holders." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ <script type="text/javascript" src="/js/jquery.js"></script>
+ <script type="text/javascript" src="/js/jquery.corner.js"></script>
+ <script type="text/javascript" src="/js/corporate_e_mail.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#corporate_e_mail">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="corporate_e_mail" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="personal_e_mail.html">Personal E-mail</a></li>
+ <li><a href="corporate_e_mail.html" class="active">Corporate E-mail</a></li>
+ <!-- <li><a href="disposable_e_mail.html">Disposable E-mail</a></li> -->
+ <!-- <li><a href="advertising.html">Advertising</a></li> -->
+ </ul>
+ </div>
+ <h2 id="start">Corporate E-mail Services</h2>
+ <p>Have your own domain but want someone else to handle your e-mail? Whether you’re a company of one or 1,000, our e-mail experts can help. Take advantage of our robust and secure e-mail platform so you can focus on your business—and not on your e-mail. With $Provider, you’ll get a better e-mail service with more features—and you’ll pay less than hosting it yourself. And if that’s not enough, you’ll have our e-mail experts watching over your messages to make sure they get delivered.</p>
+ <table id="users" class="standard">
+ <caption>The corporate e-mail prices.</caption>
+ <thead>
+ <tr>
+ <th id="tl">Number of Users</th>
+ <th id="tr">Price Per User</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1 to 100</td>
+ <td>amount year</td>
+ </tr>
+ <tr>
+ <td>101 to 1000</td>
+ <td>amount year</td>
+ </tr>
+ <tr>
+ <td>1001 and up</td>
+ <td>amount year</td>
+ </tr>
+ </tbody>
+ </table>
+ <p id="middleblock">$Provider’s corporate e-mail accounts include the following features and limits. We also offer custom plans—so <a href="contact.html">contact our sales team</a> for a quote that addresses your specific needs.</p>
+ <table id="plan" class="standard">
+ <caption>The corporate account plan.</caption>
+ <tbody>
+ <tr>
+ <th>Storage:</th>
+ <td>8,192 <acronym title="Megabytes">MB</acronym></td>
+ </tr>
+ <tr>
+ <th>Virus Protection:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Personalized Statistical Spam Filter:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Sender Policy Framework (SPF) Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Domainkeys Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Realtime Blacklist (RBL) Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Greylisting Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Filter Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Scatter Back Protection:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Message Forwarding:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Automatic Replies:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Secure Sockets Layer (SSL) Support:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Secure Mail Storage:</th>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Incoming Message Limit:</th>
+ <td>8,192 messages/day</td>
+ </tr>
+ <tr>
+ <th>Outgoing Message Limit:</th>
+ <td>768 messages/day</td>
+ </tr>
+ <tr>
+ <th>Message Size Limit:</th>
+ <td>128 <acronym title="Megabytes">MB</acronym></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/credits.html b/res/pages/credits.html
new file mode 100644
index 00000000..e5c49d53
--- /dev/null
+++ b/res/pages/credits.html
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Credits</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides links to companies and software projects that $Provider uses and endorses." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#credits">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="credits" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="news.html">News</a></li>
+ <li><a href="history.html">History</a></li>
+ <li><a href="credits.html" class="active">Credits</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Credits</h2>
+ <p>$Provider relies on a number of open source software projects and companies. Here is a list, in no particular order, of organizations whose software or products we use and recommend.</p>
+
+ <h4>Operating System</h4>
+ <h5>CentOS <a href="http://www.centos.org">http://www.centos.org</a></h5>
+ <p>CentOS is a free, open-source operating system created from the source code of a large North American Linux vendor. The CentOS Linux distributions are stable and reliable. The security response team is also excellent at providing critical patches in a timely manner.</p>
+ <h4>Database</h4>
+ <h5>MySQL <a href="http://www.mysql.com">http://www.mysql.com</a></h5>
+ <p>Originally engineered for speed, MySQL has been developed into a full-featured database platform. Recent years have seen the creators of MySQL successfully offer commercial support. Today MySQL provides stiff competition for the traditional closed-source commercial database offerings.</p>
+ <h4>Web Browser</h4>
+ <h5>Firefox <a href="http://www.mozilla.com">http://www.mozilla.com</a></h5>
+ <p>Firefox is a fast, secure alternative to Internet Explorer. With extensive cross-platform support and innovative features Firefox has slowly been stealing market share from its blue-logo competitor.</p>
+ <h4>Web Browser</h4>
+ <h5>Opera <a href="http://www.opera.com">http://www.opera.com</a></h5>
+ <p>Opera is another fast and very secure alternative to Internet Explorer. The most recent version of Opera has top-notch support for web standards and includes a number of non-traditional features, such as integrated support for downloading BitTorrent files.</p>
+ <h4>Mail Client</h4>
+ <h5>Thunderbird <a href="http://www.mozilla.com">http://www.mozilla.com</a></h5>
+ <p>Thunderbird is the best <acronym title="Post Office Protocol">POP</acronym>/<acronym title="Internet Message Access Protocol">IMAP</acronym> e-mail client on the market. The fact that Thunderbird is free, open-source software makes recommending it an easy decision. Thunderbird offers support for <acronym title="Post Office Protocol">POP</acronym>, <acronym title="Internet Message Access Protocol">IMAP</acronym>, <acronym title="Secure Sockets Layer">SSL</acronym> and includes filtering software that accurately identifies spam and phishing messages.</p>
+ <h4>SSL Library</h4>
+ <h5>OpenSSL <a href="http://www.openssl.org">http://www.openssl.org</a></h5>
+ <p>OpenSSL is a free, open-source toolkit for transport layer encryption. For something as important as security, we recommend using the proven and secure OpenSSL library.</p>
+ <h4>SPAM Library</h4>
+ <h5>DSPAM <a href="http://dspam.nuclearelephant.com">http://dspam.nuclearelephant.com</a></h5>
+ <p>Accurately identifying spam is a difficult challenge. The DSPAM library is clearly the best filter for the job. After being trained, DSPAM is routinely between 99.5 percent and 99.95 percent accurate.</p>
+ <h4>XML Library</h4>
+ <h5>Libxml2 <a href="http://www.xmlsoft.org">http://www.xmlsoft.org</a></h5>
+ <p>XML files are an efficient and proven method for storing and transferring information. The fast and extensive Libxml2 library is a free, open-source library for handling XML documents.</p>
+ <h4>Antivirus</h4>
+ <h5>ClamAV <a href="http://www.clamav.net">http://www.clamav.net</a></h5>
+ <p>ClamAV is a free, open-source antivirus client that runs atop multiple platforms. ClamAV is widely used for scanning e-mail attachments and often responds to new virus threats faster than commercial alternatives.</p>
+ <h4>Compiler</h4>
+ <h5>GCC <a href="http://gcc.gnu.org">http://gcc.gnu.org</a></h5>
+ <p>GCC is the GNU Compiler Collection and a stalwart for free, open-source software. Without GCC free open-source software wouldn’t exist in form it does today.</p>
+ <h4>Mail Server</h4>
+ <h5>Postfix <a href="http://www.postfix.org">http://www.postfix.org</a></h5>
+ <p>Postfix is an e-mail server that is famous for its speed and security.</p>
+ <h4>Monitoring</h4>
+ <h5>Multi Router Traffic Grapher (MRTG) <a href="http://oss.oetiker.ch/mrtg/">http://oss.oetiker.ch/mrtg/</a></h5>
+ <p><acronym title="Multi Router Traffic Grapher">MRTG</acronym> is the industry-standard free, open-source solution for collecting and graphing <acronym title="Simple Network Management Protocol">SNMP</acronym> information. <acronym title="Multi Router Traffic Grapher">MRTG</acronym> is critically valuable for network administrators attempting to monitor their network.</p>
+ <h4>Monitoring</h4>
+ <h5>Nagios <a href="http://www.nagios.org">http://www.nagios.org</a></h5>
+ <p>With the proper effort, Nagios can be configured to monitor critical network services and notify administrators when it detects an outage. The Golden Rule is that it is always better for an administrator to uncover problems before users start complaining.</p>
+ <h4>Monitoring</h4>
+ <h5>Net-<acronym title="Simple Network Management Protocol">SNMP</acronym> <a href="http://www.net-snmp.org">http://www.net-snmp.org</a></h5>
+ <p>The Net-<acronym title="Simple Network Management Protocol">SNMP</acronym> project provides a high-quality, cross-platform <acronym title="Simple Network Management Protocol">SNMP</acronym> agent. Properly installed, Net-<acronym title="Simple Network Management Protocol">SNMP</acronym> can be used to provide valuable machine statistics to a monitoring program like <acronym title="Multi Router Traffic Grapher">MRTG</acronym>.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/css/site.css b/res/pages/css/site.css
new file mode 100644
index 00000000..74ee047d
--- /dev/null
+++ b/res/pages/css/site.css
@@ -0,0 +1,267 @@
+/* Global section. */
+body { background: url(../images/bg_tile.gif) repeat-x #FFFFFF; color: #000000; text-align: center; }
+div#page { overflow: hidden; text-align: left; font-family: "helvetica", sans-serif; margin: 30px auto auto auto; font-size: 12px; width: 895px; background: url(../images/diecut.gif) no-repeat top right; }
+li.skip { position: absolute; margin-left: -9999px; }
+caption { position: absolute; text-indent: -9999px; }
+div.skip { position: absolute; margin-left: -9999px; }
+acronym { text-decoration: none; border-bottom: none; font-style: normal; }
+input { border: solid 1px #000000; }
+select { border: solid 1px #000000; }
+textarea { border: solid 1px #000000; }
+input.gray { color: #000000; background-color: #F0F0F0; border: solid 1px #000000; }
+input.white { color: #000000; background-color: #FFFFFF; border: solid 1px #000000; }
+input.red { color: #000000; background-color: #FFFFFF; border: solid 1px #FF0000; }
+textarea.gray { color: #000000; background-color: #F0F0F0; border: solid 1px #000000; }
+textarea.white { color: #000000; background-color: #FFFFFF; border: solid 1px #000000; }
+textarea.red { color: #000000; background-color: #FFFFFF; border: solid 1px #FF0000; }
+table { font-size: 12px; }
+table.standard { border-collapse: collapse; width: 605px; font-weight: bold; background-color: #000000; color: #FFFFFF; }
+table.standard thead tr { text-align: center; background: url(../images/bg_table_head_tile.gif) repeat-x top transparent; }
+table.standard tbody tr { background: url(../images/bg_table_row_tile.gif) repeat; border-bottom: 1px solid #333333; }
+table.standard tbody th { padding-left: 10px; text-align: left; }
+table.standard tbody td { text-align: center; }
+table.standard tbody td span { font-size: 10px; }
+
+/* Header section. */
+div#header { overflow: hidden; background: #FFFFFF; color: #000000; margin: 0px 195px 0px 0px; border-top: #000000 solid 39px; border-left: #000000 solid 39px; padding: 39px 173px 0px 38px; }
+h1 { text-indent: -9999px; height: 112px; width: 69px; background: url(../images/logo.gif) no-repeat; margin: 0px; padding: 0px; }
+div#header div { overflow: hidden; float: right; margin-top: -103px; height: 90px; width: 234px; }
+div#header ul#nav_col_1 { margin: 0px 0px 0px 0px; list-style: none; width: 78px; height: 80px; padding: 0px; }
+div#header ul#nav_col_2 { margin: -80px 0px 0px 78px; list-style: none; width: 78px; height: 80px; padding: 0px; }
+div#header ul#nav_col_3 { margin: -80px 0px 0px 156px; list-style: none; width: 78px; height: 80px; padding: 0px; }
+div#header ul li a#nav_home { background: url(../images/nav_home.gif) no-repeat; }
+div#header ul li a#nav_home:hover { background: url(../images/nav_home_over.gif) no-repeat; }
+div#header ul li a#nav_home.active { background: url(../images/nav_home_active.gif) no-repeat; }
+div#header ul li a#nav_home.active:hover { background: url(../images/nav_home_active_over.gif) no-repeat; }
+div#header ul li a#nav_about { background: url(../images/nav_about.gif) no-repeat; }
+div#header ul li a#nav_about:hover { background: url(../images/nav_about_over.gif) no-repeat; }
+div#header ul li a#nav_about.active { background: url(../images/nav_about_active.gif) no-repeat; }
+div#header ul li a#nav_about.active:hover { background: url(../images/nav_about_active_over.gif) no-repeat; }
+div#header ul li a#nav_features { background: url(../images/nav_features.gif) no-repeat; }
+div#header ul li a#nav_features:hover { background: url(../images/nav_features_over.gif) no-repeat; }
+div#header ul li a#nav_features.active { background: url(../images/nav_features_active.gif) no-repeat; }
+div#header ul li a#nav_features.active:hover { background: url(../images/nav_features_active_over.gif) no-repeat; }
+div#header ul li a#nav_services { background: url(../images/nav_services.gif) no-repeat; }
+div#header ul li a#nav_services:hover { background: url(../images/nav_services_over.gif) no-repeat; }
+div#header ul li a#nav_services.active { background: url(../images/nav_services_active.gif) no-repeat; }
+div#header ul li a#nav_services.active:hover { background: url(../images/nav_services_active_over.gif) no-repeat; }
+div#header ul li a#nav_philosophy { background: url(../images/nav_philosophy.gif) no-repeat; }
+div#header ul li a#nav_philosophy:hover { background: url(../images/nav_philosophy_over.gif) no-repeat; }
+div#header ul li a#nav_philosophy.active { background: url(../images/nav_philosophy_active.gif) no-repeat; }
+div#header ul li a#nav_philosophy.active:hover { background: url(../images/nav_philosophy_active_over.gif) no-repeat; }
+div#header ul li a#nav_testimonials { background: url(../images/nav_testimonials.gif) no-repeat; }
+div#header ul li a#nav_testimonials:hover { background: url(../images/nav_testimonials_over.gif) no-repeat; }
+div#header ul li a#nav_testimonials.active { background: url(../images/nav_testimonials_active.gif) no-repeat; }
+div#header ul li a#nav_testimonials.active:hover { background: url(../images/nav_testimonials_active_over.gif) no-repeat; }
+div#header ul li a#nav_network { background: url(../images/nav_network.gif) no-repeat; }
+div#header ul li a#nav_network:hover { background: url(../images/nav_network_over.gif) no-repeat; }
+div#header ul li a#nav_network.active { background: url(../images/nav_network_active.gif) no-repeat; }
+div#header ul li a#nav_network.active:hover { background: url(../images/nav_network_active_over.gif) no-repeat; }
+div#header ul li a#nav_help { background: url(../images/nav_help.gif) no-repeat; }
+div#header ul li a#nav_help:hover { background: url(../images/nav_help_over.gif) no-repeat; }
+div#header ul li a#nav_help.active { background: url(../images/nav_help_active.gif) no-repeat; }
+div#header ul li a#nav_help.active:hover { background: url(../images/nav_help_active_over.gif) no-repeat; }
+div#header ul li a#nav_contact { background: url(../images/nav_contact.gif) no-repeat; }
+div#header ul li a#nav_contact:hover { background: url(../images/nav_contact_over.gif) no-repeat; }
+div#header ul li a#nav_contact.active { background: url(../images/nav_contact_active.gif) no-repeat; }
+div#header ul li a#nav_contact.active:hover { background: url(../images/nav_contact_active_over.gif) no-repeat; }
+div#header ul li a#nav_register { background: url(../images/nav_register.gif) no-repeat; }
+div#header ul li a#nav_register:hover { background: url(../images/nav_register_over.gif) no-repeat; }
+div#header ul li a#nav_register.active { background: url(../images/nav_register_active.gif) no-repeat; }
+div#header ul li a#nav_register.active:hover { background: url(../images/nav_register_active_over.gif) no-repeat; }
+div#header ul li a#nav_preferences { background: url(../images/nav_preferences.gif) no-repeat; }
+div#header ul li a#nav_preferences:hover { background: url(../images/nav_preferences_over.gif) no-repeat; }
+div#header ul li a#nav_preferences.active { background: url(../images/nav_preferences_active.gif) no-repeat; }
+div#header ul li a#nav_preferences.active:hover { background: url(../images/nav_preferences_active_over.gif) no-repeat; }
+div#header ul li a#nav_webmail { background: url(../images/nav_webmail.gif) no-repeat; }
+div#header ul li a#nav_webmail:hover { background: url(../images/nav_webmail_over.gif) no-repeat; }
+div#header ul li a#nav_webmail.active { background: url(../images/nav_webmail_active.gif) no-repeat; }
+div#header ul li a#nav_webmail.active:hover { background: url(../images/nav_webmail_active_over.gif) no-repeat; }
+div#header ul li a { width: 76px; height: 18px; text-indent: -9999px; display: block; margin-bottom: 2px; }
+
+/* Footer section. */
+div#footer { margin: 5px 0px 0px 0px; font-size: 10px; height: 8em; width: 895px; }
+div#footer a { color: #666666; background-color: inherit; text-decoration: underline; }
+div#footer ul a:hover { color: #FFFFFF; background-color: #666666; text-decoration: underline; }
+div#footer a img { border: none; color: #666666; background-color: inherit; }
+div#footer ul { width: 610px; list-style: none; padding: 0px; margin: 0px; }
+div#footer ul li { display: inline; }
+div#footer p#copyright { float: left; color: #666666; background-color: inherit; width: 610px; margin-top: 3px; }
+div#footer p#badges { float: left; width: 285px; text-align: right; margin-top: -1.3em; }
+
+/* The generic content container. */
+div.content { overflow: hidden; background: #FFFFFF; color: #333333; padding: 25px 16px 40px 40px; margin: 0px 0px 0px 0px; border-right: #000000 solid 39px; border-left: #000000 solid 39px; border-bottom: #000000 solid 39px;}
+div.content p { width: 605px; margin: 0px 0px 20px 0px; font-size: 12px; }
+div.content ul { width: 605px; }
+div.content img { width: 605px; }
+div.content h2 { width: 605px; font-size: 14px; }
+div.content h3 { width: 605px; font-size: 14px; margin-bottom: 0px; }
+div.content h4 { width: 605px; font-size: 14px; margin-bottom: 0px; }
+div.content a { color: #FF0000; background-color: inherit; text-decoration: underline; }
+div.content a:hover { color: #FFFFFF; background-color: #FF0000; text-decoration: underline; }
+
+/* The secondary nav column. */
+div.content div#secondary { float: right; width: 127px; overflow: hidden; margin: -6px 0px 0px 0px;}
+div.content div#secondary ul { width: 127px; padding: 0px; margin: 0px; list-style: none; }
+div.content div#secondary ul li a.active { color: #000000; background-color: inherit; }
+div.content div#secondary ul li a.active:hover { color: #FFFFFF; background-color: #000000; }
+div.content div#secondary h3 { font-size: 12px; width: 127px; margin: 0px; }
+
+/* The splash /images. */
+div#index h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_index.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#about h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_about.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#features h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_features.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#services h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_services.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#philosophy h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_philosophy.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#testimonials h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_testimonials.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#network h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_network.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#help h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_help.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+div#contact h2, div#report_abuse h2 { overflow: hidden; text-indent: -9999px; width: 605px; height: 143px; background: url(../images/splash_contact.gif) no-repeat; display: block; margin: 0px 0px 15px 0px; }
+
+/* The home page. */
+div#index div#sign_up_link { float: right; width: 127px; }
+div#index div#sign_up_link a { overflow: hidden; display: block; text-indent: -9999px; height: 25px; width: 109px; background: url(../images/btn_sign_up.gif) no-repeat; padding: 0px; }
+div#index div#news { overflow: hidden; margin: -2px 0px 0px 0px; float: right; width: 127px; padding: 0px; }
+div#index div#news p.news_date { margin-top: 10px; color: #000000; background-color: inherit; }
+div#index div#news p { color: #FF0000; background-color: inherit; margin: 0px; font-size: 12px; width: 127px; }
+div#index div#news h3 { font-size: 14px; width: 127px; margin: 0px; padding: 0px; line-height: 14px; }
+div#index table tbody th { width: 24%; }
+
+/* The full width white papers. */
+div#features p { width: 737px; }
+div#secure p { width: 737px; }
+div#philosophy p { width: 737px; }
+div#testimonials p { width: 737px; margin-bottom: 10px; }
+
+/* The corporate e-mail page. */
+div#corporate_e_mail table#users { width: 300px; }
+div#corporate_e_mail table#plan { width: 400px; }
+div#corporate_e_mail table#plan th { width: 250px; }
+div#corporate_e_mail p#middleblock { margin-top: 20px; }
+
+/* The graphs page. */
+div#graphs img { width: 691px; margin-bottom: 8px; }
+
+/* The health page. */
+div#health td.center, div#health th.center { text-align: center; }
+div#health td.left, div#health th.left { text-align: left; padding-left: 10px; }
+div#health td.green { background: #008800; color: #FFFFFF; }
+div#health td.red { background: #FF0000; color: #FFFFFF; }
+div#health p#time { margin-top: 10px; }
+
+/* The statistics page. */
+div#statistics table { width: 420px; }
+div#statistics table td { text-align: right; padding-right: 10px; }
+div#statistics p#time { margin-top: 10px; }
+
+/* The personal e-mail page. */
+div#personal_e_mail table { width: 737px; }
+div#personal_e_mail table tbody th { width: 245px; }
+div#personal_e_mail p#discount { width: 737px; }
+
+/* The advertising page. */
+div#advertising table#impressions { width: 250px; }
+div#advertising form { width: 605px; margin-top: 25px; }
+div#advertising form table { background: #FFFFFF; color: #000000; margin-bottom: 10px; }
+div#advertising form table tr { background: none; border: none; }
+div#advertising form table th { width: 52%; text-align: right; }
+div#advertising form table td { padding-left: 10px; text-align: left; }
+div#advertising form table td label { display: none; }
+div#advertising form table input { width: 190px; margin-right: 10px; }
+div#advertising form table select { width: 190px; margin-right: 10px; }
+div#advertising form table select#ccexp_mon, div#advertising form table select#ccexp_year { width: 90px; margin-right: 10px; }
+div#advertising form table input#line_one, div#advertising form table input#line_two, div#advertising form table input#link { width: 100%; margin: 0px; }
+div#advertising form div { text-align: right; }
+div#advertising button { overflow: hidden; text-indent: -9999px; border: 0px; margin: 10px 0px 0px 0px; padding: 0px; width: 72px; height: 25px; background: transparent url(../images/btn_submit.gif) no-repeat center top; cursor: pointer; }
+
+/* Tutorial pages. */
+div#outlook p { width: 737px; }
+div#outlook img { width: 730px; }
+div#thunderbird p { width: 737px; }
+div#thunderbird img { width: 730px; }
+div#windows_live_mail p { width: 737px; }
+div#windows_live_mail img { width: 730px; }
+div#pidgin p { width: 737px; }
+div#pidgin img { width: auto; }
+div#trillian p { width: 737px; }
+div#trillian img { width: auto; }
+
+/* The contact and report abuse pages. */
+div#contact form, div#report_abuse form { width: 605px; }
+div#contact th, div#report_abuse th { text-align: right; width: 100px; }
+div#contact td, div#report_abuse td { padding-left: 10px; }
+div#contact select { width: 100px; }
+div#contact form div, div#report_abuse form div { height: 25px; margin: 10px 5px 0px 0px; text-align: right; }
+div#contact tbody input, div#report_abuse tbody input { width: 190px; margin-right: 10px; }
+div#contact textarea, div#report_abuse textarea { width: 480px; height: 190px; }
+div#contact tr#message th, div#report_abuse tr#message th { vertical-align: top; }
+div#contact form table span, div#report_abuse form table span { background-color: #FFFFFF; color: #FF0000; font-weight: bold; }
+div#contact span#your_message_msg, div#report_abuse span#your_message_msg { display: block; text-align: right; width: 480px; background-color: #FFFFFF; color: #FF0000; font-weight: bold; }
+div#contact button#contact_sub, div#report_abuse button#abuse_sub { overflow: hidden; text-indent: -9999px; border: 0px; margin: 0px; padding: 0px; width: 72px; height: 25px; background: transparent url(../images/btn_submit.gif) no-repeat center top; cursor: pointer; }
+
+/* The credits page. */
+div#credits h2 { display: none; }
+div#credits h4 { display: none; }
+div#credits h3 { text-decoration: underline; }
+div#credits h5 { font-size: 14px; width: 737px; line-height: 12px; margin-bottom: 0px; }
+div#credits h5 a { font-size: 12px; float: right; margin-top: -12px; color: #FF0000; background-color: inherit; text-decoration: underline; }
+div#credits h5 a:hover { font-size: 12px; float: right; margin-top: -12px; color: #FF0000; background-color: inherit; text-decoration: underline; }
+div#credits p { width: 545px; }
+
+/* The settings page. */
+div#settings table { margin-bottom: 10px; width: 440px; }
+div#settings table th { display: inline; }
+div#settings table td { display: inline; text-align: left; }
+
+/* Terms and Policies pages */
+p#timestamp { float: right; width: 165px; margin-bottom: -15px; color: #666666; }
+
+/* The sitemap page. */
+div#site_map th { text-align: left; }
+div#site_map table { width: 605px; }
+
+/* The XHTML page. */
+div#xhtml_valid th { text-align: left; }
+div#xhtml_valid table { width: 605px; }
+
+/* The CSS Valid page. */
+div#css_valid th { text-align: left; }
+div#css_valid table { width: 605px; }
+
+/* Registration webapp. */
+div#reg_step1 div#floater { clear: left; }
+div#reg_step1 h2 { overflow: hidden; text-indent: -9999px; width: 214px; height: 143px; background: url(../images/splash_signup.gif) no-repeat; display: block; margin: 0px;}
+div#reg_step1 div#reg_col_1 { float: left; width: 215px; margin: 0px; }
+div#reg_step1 div#reg_col_1 p { width: 215px; margin: 10px 0px 0px 0px; }
+div#reg_step1 p#error { color: #FF0000; background-color: inherit; }
+div#reg_step1 form { float: left; }
+div#reg_step1 table { width: 525px; margin: 0px; }
+div#reg_step1 form p { width: 377px; margin-left: 142px; }
+div#reg_step1 form div#button { width: 377px; margin-left: 142px; text-align: right; }
+div#reg_step1 img { margin: 8px 0px 0px 131px; width: 371px; height: 47px; border: 3px solid #000000; }
+div#reg_step1 table th { text-align: right; vertical-align: top; width: 25%; }
+div#reg_step1 table td { text-align: left; padding-left: 10px; width: 75%; }
+div#reg_step1 table input { width: 190px; margin-right: 10px; }
+div#reg_step1 form table td div { background-color: #FFFFFF; color: #FF0000; font-weight: bold; }
+div#reg_step1 button#next1 { overflow: hidden; text-indent: -9999px; border: 0px; margin: 0px; padding: 0px; width: 55px; height: 25px; background: url(../images/btn_next.gif) no-repeat center top; cursor: pointer; }
+
+div#reg_step2 p, div#reg_step2 table, div#reg_step2 div#buttons { width: 737px; }
+div#reg_step2 p#error { color: #FF0000; background-color: inherit; }
+div#reg_step2 table#payment { margin: 20px 0px 0px 0px; }
+div#reg_step2 table#payment td { padding-left: 10px; text-align: left; }
+div#reg_step2 table#payment td label { display: none; }
+div#reg_step2 table#payment input { width: 190px; margin-right: 10px; }
+div#reg_step2 table#payment select { width: 190px; margin-right: 10px; }
+div#reg_step2 table#payment input#ccsc { width: 50px; }
+div#reg_step2 table#payment th { text-align: right; vertical-align: top; width: 155px; }
+div#reg_step2 div#buttons { margin: 10px 0px 10px 0px; }
+div#reg_step2 table#payment td div { background-color: #FFFFFF; color: #FF0000; font-weight: bold; }
+div#reg_step2 table#payment select#ccexp_mon, div#reg_step2 table#payment select#ccexp_year { width: 90px; margin-right: 10px; }
+div#reg_step2 button#next2 { float: right; overflow: hidden; text-indent: -9999px; border: 0px; margin: 0px; padding: 0px; width: 72px; height: 25px; background: transparent url(../images/btn_finish.gif) no-repeat center top; cursor: pointer; }
+div#reg_step2 button#prev2 { float: left; overflow: hidden; text-indent: -9999px; border: 0px; margin: 0px; padding: 0px; width: 55px; height: 25px; background: transparent url(../images/btn_back.gif) no-repeat center top; cursor: pointer; }
+div#reg_step2 div#plans_msg { background-color: #FFFFFF; color: #FF0000; font-weight: bold; text-align: right; margin: 0px; width: 737px; }
+div#reg_step2 table#plans label { display: none; }
+div#reg_step2 table#plans tbody th { width: 42%; }
+div#reg_step2 #tooltip_ccsc { display: none; line-height: 20px; background-color: #FFFFFF; color: #FF0000; font-weight: bold; }
+
+div#reg_step3 p { width: 737px; }
diff --git a/res/pages/css_valid.html b/res/pages/css_valid.html
new file mode 100644
index 00000000..d28c3d18
--- /dev/null
+++ b/res/pages/css_valid.html
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. CSS Validation</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides links to validate all of the CSS files used by $Provider as valid." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#css_valid">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="portal">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="css_valid" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="xhtml_valid.html">XHTML Valid</a></li>
+ <li><a href="css_valid.html" class="active">CSS Valid</a></li>
+ <li><a href="wai_aaa.html">Web Accessibility</a></li>
+ </ul>
+ </div>
+ <h2 id="start">CSS Validation</h2>
+ <p>$Provider supports web standards. All of the pages across the $Provider website have their presentation controlled using external <acronym title="Cascading Style Sheets">CSS</acronym> files. The <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> is responsible for creating and maintaining the <acronym title="Cascading Style Sheets">CSS</acronym> standard. Along with creating the <acronym title="Cascading Style Sheets">CSS</acronym> specification, the <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> has provided a web-based program that developers can use to verify that their <acronym title="Cascading Style Sheets">CSS</acronym> files precisely meet the <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> standards.</p>
+ <p>Here are links you can uee to validate all of the $Provider <acronym title="Cascading Style Sheets">CSS</acronym> files on our website. $Provider is proud of its support for web standards and encourages developers to use <acronym title="Cascading Style Sheets">CSS</acronym> and ensure that their files validate against the specification.</p>
+ <table>
+ <caption>$Provider <acronym title="Cascading Style Sheets">CSS</acronym> validation links.</caption>
+ <thead>
+ <tr>
+ <th>Description</th>
+ <th>File Name</th>
+ <th>Validation Link</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Web Site CSS File</td>
+ <td>/css/site.css</td>
+ <td><a href="http://jigsaw.w3.org/css-validator/validator?profile=css2&amp;warning=2&amp;uri=http%3A%2F%2F$Provider.com%2Fcss%2Fsite.css">Validate</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; 2014 Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="/images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="/images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="/images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/disposable_e_mail.html b/res/pages/disposable_e_mail.html
new file mode 100644
index 00000000..d5a985ee
--- /dev/null
+++ b/res/pages/disposable_e_mail.html
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Disposable E-mail</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes how to get a disposable e-mail account for temporary use." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#disposable_e_mail">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="portal">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="disposable_e_mail" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="personal_e_mail.html">Personal E-mail</a></li>
+ <li><a href="corporate_e_mail.html">Corporate E-mail</a></li>
+ <li><a href="disposable_e_mail.html" class="active">Disposable E-mail</a></li>
+ <li><a href="advertising.html">Advertising</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Disposable E-mail Services</h2>
+ <p>Need an e-mail address that disappears after a few days? Use our disposable e-mail addresses to temporarily forward e-mail to your account. These accounts are incredibly useful when you need to sign up for something or advertise your address online—but don’t want the mountain of spam that comes with it. Use the form below to sign up and let us know how long you want the address to work.</p>
+ <form id="disposable" method="post" action="register">
+ <table>
+ <caption>Sign up for a disposable e-mail address.</caption>
+ <tbody>
+ <tr>
+ <th><label for="receive_e_mail">Disposable E-Mail Address:</label></th>
+ <td><input id="receive_e_mail" name="receive_e_mail" type="text" value="" /></td>
+ </tr>
+ <tr>
+ <th><label for="forward_e_mail">Forward E-mail To:</label></th>
+ <td><input id="forward_e_mail" name="forward_e_mail" type="text" value="" /></td>
+ </tr>
+ <tr>
+ <th><label for="length">Keep Account Active For:</label></th>
+ <td><select id="length" name="length"><option>1 week</option><option>3 days</option><option>1 day</option><option>8 hours</option><option>4 hours</option><option>1 hour</option></select></td>
+ </tr>
+ <tr>
+ <td colspan="2"><img src="/images/captcha.png" alt="To prevent automated programs from registering accounts we make our new users enter the characters they see in this image. If you have trouble deciphering the image or have a disability, please use our contact form to request a new account." /></td>
+ </tr>
+ <tr>
+ <th><label for="human">Human Verification:</label></th>
+ <td><input id="human" name="human" type="text" value="" /></td>
+ </tr>
+ <tr>
+ <th><label for="tou">I have read and agree to the provisions of the Terms of Use, Privacy Policy and Abuse Policy.</label></th>
+ <td><input id="tou" name="tou" type="checkbox" /></td>
+ </tr>
+ </tbody>
+ </table>
+ <p>
+ <input id="session" name="session" type="hidden" value="random" />
+ <input id="disposable_sub" name="disposable_sub" type="submit" value="Submit &gt;&gt;" title="Use this button to submit the form." />
+ </p>
+ </form>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; 2014 Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="/images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="/images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="/images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/features.html b/res/pages/features.html
new file mode 100644
index 00000000..020c81d0
--- /dev/null
+++ b/res/pages/features.html
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Features</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides detailed descriptions of the features offered by $Provider’s e-mail services." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#features">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" class="active" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="features" class="content">
+ <h2>Features</h2>
+ <p>We’ve built the $Provider e-mail server from the ground up with the most desirable combination of features, performance and flexibility. Read on for detailed descriptions of the key features of the $Provider e-mail platform—one of the most advanced in existence. Our goal is to one day give back to the community by releasing this software to the public.</p>
+ <h3>Antispam</h3>
+ <h4>Spam Filter</h4>
+ <p>$Provider uses the <a href="http://dspam.nuclearelephant.com">DSPAM</a> library to accurately filter spam. <a href="http://dspam.nuclearelephant.com">DSPAM</a> is an adaptive filter that uses complex statistical algorithms to analyze your messages. At $Provider, we use personalized training data that causes DSPAM to learn over time what you consider to be spam. This personalized approach means that many of our users typically see between 99.5% and 99.95% accuracy.</p>
+ <h4>Realtime Time Blacklists (RBL)</h4>
+ <p>These blacklists are maintained by organizations with an interest in combating the spam problem. The $Provider e-mail server is capable of checking these lists as messages arrive and blocking those that appear to come from IP addresses on the list. You can configure the $Provider e-mail server to mark messages that come from blacklisted servers as 'SPAM' or simply accept the default setting, which automatically rejects messages from blacklisted IP addresses.</p>
+ <h4>Scatter Back Blocking</h4>
+ <p>Welcome to the world of e-mail identity theft. Everyday spammers launch new e-mail campaigns using the addresses of unwitting users. The problem arises whenever a spammer’s e-mail campaign tries to send a message to an address that is invalid. A bounce message is generated and sent to the address in the from header. If that address happens to be yours, a virtual avalanche could end up in your inbox. If that ever happens to you while your using $Provider, rest comfortably knowing that you can temporarily block bounce messages using our preferences portal.</p>
+ <h4>Sender Policy Framework (SPF)</h4>
+ <p>SPF is a technique for verifying that a message claiming to originate from a specific domain is actually being received from a server authorized to relay messages for that domain. As an example, SPF can be used to verify that a message claiming to be from 'whitehouse.gov' is actually being transmitted by a server authorized to represent 'whitehouse.gov'. Current estimates indicate that 50 percent of all domain names publish SPF information, while 30 percent check SPF information on inbound e-mail. $Provider users have the option to disable this check, have messages that fail this check marked 'SPAM' or reject these messages during the <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> session.</p>
+ <h3>Antivirus</h3>
+ <h4>ClamAV</h4>
+ <p>The $Provider e-mail server has been tightly integrated with the Clam antivirus engine. This integration allows us to protect our users from a variety of malware threats. Using a sophisticated engine and a worldwide network of e-mail administrators, ClamAV provides the best possible protection against new virus threats.</p>
+ <h3>Privacy</h3>
+ <h4>Transport Layer Encryption</h4>
+ <p>Here at $Provider we take privacy and security seriously. To ensure that no one intercepts your e-mail while it is being downloaded or sent to our servers, we support and encourage the use of Secure Sockets Layer (SSL) encryption. SSL was created specifically to eliminate eavesdropping and ensure that information could be transported securely over an untrusted network.</p>
+ <h4>Secure Mail Storage</h4>
+ <p>The secure mail storage process uses asymmetric encryption to ensure the privacy of messages while being stored on the $Provider servers. Asymmetric encryption is a process that uses public key and private key encryption to make messages unreadable without knowing a user's plaintext password. Presently we use Elliptical Curve Cryptography (ECC) with 512 bits of security to encrypt messages. The private, or decryption, key is then encrypted with a user’s password using the Advanced Encryption Standard (AES) and 256 bits of security. The result is that once a message is stored on our servers in this fashion, it can’t be recovered without knowing a user's password. This provides a priceless level of security, particularly for customers that use e-mail to exchange sensitive information. You can learn more about our asymmetric encryption technology by reading our <a href="secure.html">white paper</a> on the subject.</p>
+ <h3>Basic</h3>
+ <h4>Authentication Support</h4>
+ <p>Our SMTP server supports authentication using the PLAIN and LOGIN methods. By requiring SMTP authentication we guarantee that we only relay e-mail for $Provider users.</p>
+ <h4>Message Forwarding</h4>
+ <p>Our e-mail server supports message forwarding. Because forwarding is controlled from a centralized database, it’s easy for you to set up using our custom preferences portal.</p>
+ <h4>Automatic Replies</h4>
+ <p>Occasionally—but probably not often enough—you go on vacation. When you do, the $Provider e-mail server is ready to let your contacts know using an automatic reply.</p>
+ <h4>Catchall Address</h4>
+ <p>For those of you who choose our services for your personal or corporate domain names, the $Provider e-mail server supports catchall addresses. This feature allows us to redirect all of the e-mail for a domain that isn’t addressed to a valid user into a specific catchall account.</p>
+ <h4>Multiple E-mail Addresses Per Account</h4>
+ <p>If you own multiple domain names, but want them all pointing to a single e-mail account, our server can come to the rescue. Our server supports tying multiple e-mail addresses to a single account.</p>
+ <h4>Server Side Sorting</h4>
+ <p>The $Provider e-mail server allows you to automatically sort your e-mail using a powerful regular expression engine. Imagine having your e-mail pre-sorted on the server into specific folders. For those of you who access your e-mail from multiple computers, this technology means you’ll now see a consistent view of your e-mail.</p>
+ <h4>Archiving</h4>
+ <p>The $Provider e-mail server supports archiving and retention policies. This technology allows our users with special requirements (like Sarbanes-Oxley) to set up rules that guarantee that their e-mail is retained securely.</p>
+ <h3>Server</h3>
+ <h4>Message Limits</h4>
+ <p>The ability to place sending and receiving limits on users is a requirement for a free e-mail service. These limits deter spammers from using our system while protecting your inbox from e-mail bombs.</p>
+ <h4>Storage Quotas</h4>
+ <p>Another important feature of our e-mail server is its ability to internally manage storage quotas. Because all quotas are managed using a database, making updates or changes is very easy.</p>
+ <h4>Message Rollout</h4>
+ <p>A unique feature offered by our service is called message rollout. This feature, if enabled, allows a user to continually store e-mail on our servers. When their quota is reached, the server will automatically delete the oldest messages to make room for incoming e-mail. This feature allows you to retain your e-mail securely on the server without fear that doing so could cause you to lose an incoming message by exceeding your quota.</p>
+ <h4>Compression</h4>
+ <p>Developing a platform to efficiently offer free e-mail services required our programmers to develop innovative solutions that make the most of our hardware resources. One of those solutions is a compression algorithm that allows our servers to efficiently store e-mail on disk. Like many of the technologies we’ve developed at $Provider, compression algorithms are not a novel idea. But the ability of our engineers to take advantage of compression without sacrificing performance gives our software a significant competitive advantage.</p>
+ <h4>Database Driven</h4>
+ <p>Controlling an e-mail service with such a large user base required us to develop a platform that’s centrally controlled, yet flexible. To meet that need we’ve developed our platform around a SQL-based database. This allows our team of engineers to easily administer our services while offering you the ability to customize your e-mail preferences. Gone are the days where a single antispam solution is effective for everyone. Instead, it’s necessary to customize a package of protections that satisfy each user’s willingness to balance spam protection with the risk of losing an important e-mail. Driving our e-mail server with a database gives us that control.</p>
+ <h4>Distributed Caching</h4>
+ <p>Database control isn’t a new concept. A number of people have tried to build large, scalable service-based platforms around databases. The problem is that such architectures are usually hindered by their reliance on a database server that can easily become bottlenecked and expensive to scale. We’ve decided to meet this challenge by using a distributed caching model. Using software based on <a href="http://memcached.org/">memcached</a> we’ve developed a distributed caching model that allows us to offload non-critical tasks from the database. Though difficult to develop, this technology implemented properly enables us to offer incredibly fast performance with incredible efficiency.</p>
+ <h4>Clustering</h4>
+ <p>Keeping our per-user cost structure low enables us to provide more services for less cash. A key aspect of that strategy is engineering systems that can scale to serve millions of users without increasing $Provider’s cost per user. With the $Provider e-mail server we’ve developed proprietary clustering technology that lets us accomplish that goal. As our services continue to grow, we’ll be adding commodity servers to our clusters to meet the demand. Without this technology, growth would require purchasing increasingly powerful servers. Because of the economics in the computer industry, it is far cheaper to buy eight different computers than it is to buy a single computer with eight processors.</p>
+ <h3>Spam Signatures and Advertising</h3>
+ <h4>Intelligent Signatures</h4>
+ <p>The most accurate spam filter is worthless if nobody uses it. As a result we’ve decided to make extra efforts at making things easier to use. Part of that effort involves creating software that allows you to train your personal spam filter whether you’re accessing e-mail using a POP client or webmail. The $Provider e-mail server can dynamically insert spam signatures into the bottom of your messages as they are downloaded. These signatures contain links that can be used to conveniently train your personalized server-based statistical spam filter.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/help.html b/res/pages/help.html
new file mode 100644
index 00000000..dd0afa46
--- /dev/null
+++ b/res/pages/help.html
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Help</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides access to our help portal." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#help">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" class="active" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="help" class="content">
+ <h2>Help</h2>
+ <h3><a href="settings.html">Configuration Settings</a></h3>
+ <p>Advanced users can find information on what settings to use in your e-mail client. We cover all the basics, including domain name, ports and encryption standards.</p>
+ <h3><a href="questions.html">Common Questions</a></h3>
+ <p>Everyone has questions—and we have answers. This page covers the most commonly asked questions about $Provider, our services and the technical <q>plumbing</q> we use. If you don’t see the answer to your question, please use the <a href="contact">contact page</a> to ask our team directly.</p>
+ <h3><a href="troubleshooting.html">Troubleshooting Tips</a></h3>
+ <p>Every person’s computer is different. We do our best to make connecting to our service easy, but sometimes things just won’t work no matter how hard you try! Maybe it’s your ISP. Maybe it’s your company’s firewall. Whatever the problem, odds are that the $Provider Support Team has solved it in the past. This page provides a list of common errors and the tricks for solving them. If these helpful hints aren’t enough, use our <a href="contact.html">contact page</a> to get help directly from the $Provider Support Team.</p>
+ <h3><a href="tutorials.html">Tutorials</a></h3>
+ <p>Sometimes all it takes is a clear example to follow. Here are step-by-step guides on how to configure different e-mail clients for use with $Provider. With the help of these tutorials, you should be accessing your $Provider e-mail in just a few minutes.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/history.html b/res/pages/history.html
new file mode 100644
index 00000000..5ec8abee
--- /dev/null
+++ b/res/pages/history.html
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. History</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a brief description of our corporate history." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#history">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" hindex.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="history" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="news.html">News</a></li>
+ <li><a href="history.html" class="active">History</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ </div>
+ <h2 id="start"> History</h2>
+ <p>Magma has been in development since 2004. It offers support for SMTP, POP, IMAP and HTTP. Support for DMTP and DMAP are currently in development.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/images/bg_table_bot_tile.gif b/res/pages/images/bg_table_bot_tile.gif
new file mode 100644
index 00000000..5925456a
--- /dev/null
+++ b/res/pages/images/bg_table_bot_tile.gif
Binary files differ
diff --git a/res/pages/images/bg_table_head_tile.gif b/res/pages/images/bg_table_head_tile.gif
new file mode 100644
index 00000000..738112c8
--- /dev/null
+++ b/res/pages/images/bg_table_head_tile.gif
Binary files differ
diff --git a/res/pages/images/bg_table_row_tile.gif b/res/pages/images/bg_table_row_tile.gif
new file mode 100644
index 00000000..5f2a41f2
--- /dev/null
+++ b/res/pages/images/bg_table_row_tile.gif
Binary files differ
diff --git a/res/pages/images/bg_table_top_tile.gif b/res/pages/images/bg_table_top_tile.gif
new file mode 100644
index 00000000..fa047342
--- /dev/null
+++ b/res/pages/images/bg_table_top_tile.gif
Binary files differ
diff --git a/res/pages/images/bg_tile.gif b/res/pages/images/bg_tile.gif
new file mode 100644
index 00000000..80ac2f51
--- /dev/null
+++ b/res/pages/images/bg_tile.gif
Binary files differ
diff --git a/res/pages/images/btn_back.gif b/res/pages/images/btn_back.gif
new file mode 100644
index 00000000..61882fdc
--- /dev/null
+++ b/res/pages/images/btn_back.gif
Binary files differ
diff --git a/res/pages/images/btn_finish.gif b/res/pages/images/btn_finish.gif
new file mode 100644
index 00000000..7eb4acae
--- /dev/null
+++ b/res/pages/images/btn_finish.gif
Binary files differ
diff --git a/res/pages/images/btn_next.gif b/res/pages/images/btn_next.gif
new file mode 100644
index 00000000..675d8d6b
--- /dev/null
+++ b/res/pages/images/btn_next.gif
Binary files differ
diff --git a/res/pages/images/btn_sign_up.gif b/res/pages/images/btn_sign_up.gif
new file mode 100644
index 00000000..0c278c52
--- /dev/null
+++ b/res/pages/images/btn_sign_up.gif
Binary files differ
diff --git a/res/pages/images/btn_submit.gif b/res/pages/images/btn_submit.gif
new file mode 100644
index 00000000..6683b003
--- /dev/null
+++ b/res/pages/images/btn_submit.gif
Binary files differ
diff --git a/res/pages/images/captcha.png b/res/pages/images/captcha.png
new file mode 100644
index 00000000..274148e3
--- /dev/null
+++ b/res/pages/images/captcha.png
Binary files differ
diff --git a/res/pages/images/check.png b/res/pages/images/check.png
new file mode 100644
index 00000000..fb09cf2a
--- /dev/null
+++ b/res/pages/images/check.png
Binary files differ
diff --git a/res/pages/images/chrome-background.png b/res/pages/images/chrome-background.png
new file mode 100644
index 00000000..f54dc488
--- /dev/null
+++ b/res/pages/images/chrome-background.png
Binary files differ
diff --git a/res/pages/images/css_valid_grey.gif b/res/pages/images/css_valid_grey.gif
new file mode 100644
index 00000000..f8c3ecda
--- /dev/null
+++ b/res/pages/images/css_valid_grey.gif
Binary files differ
diff --git a/res/pages/images/css_valid_red.gif b/res/pages/images/css_valid_red.gif
new file mode 100644
index 00000000..52f357da
--- /dev/null
+++ b/res/pages/images/css_valid_red.gif
Binary files differ
diff --git a/res/pages/images/diecut.gif b/res/pages/images/diecut.gif
new file mode 100644
index 00000000..07d1b6d1
--- /dev/null
+++ b/res/pages/images/diecut.gif
Binary files differ
diff --git a/res/pages/images/error.png b/res/pages/images/error.png
new file mode 100644
index 00000000..5f189a2f
--- /dev/null
+++ b/res/pages/images/error.png
Binary files differ
diff --git a/res/pages/images/expander.png b/res/pages/images/expander.png
new file mode 100644
index 00000000..500acd38
--- /dev/null
+++ b/res/pages/images/expander.png
Binary files differ
diff --git a/res/pages/images/express_1.jpg b/res/pages/images/express_1.jpg
new file mode 100644
index 00000000..6644fab7
--- /dev/null
+++ b/res/pages/images/express_1.jpg
Binary files differ
diff --git a/res/pages/images/express_10.jpg b/res/pages/images/express_10.jpg
new file mode 100644
index 00000000..0c275dd0
--- /dev/null
+++ b/res/pages/images/express_10.jpg
Binary files differ
diff --git a/res/pages/images/express_11.jpg b/res/pages/images/express_11.jpg
new file mode 100644
index 00000000..6e33f9aa
--- /dev/null
+++ b/res/pages/images/express_11.jpg
Binary files differ
diff --git a/res/pages/images/express_12.jpg b/res/pages/images/express_12.jpg
new file mode 100644
index 00000000..10a94d1d
--- /dev/null
+++ b/res/pages/images/express_12.jpg
Binary files differ
diff --git a/res/pages/images/express_13.jpg b/res/pages/images/express_13.jpg
new file mode 100644
index 00000000..78c64680
--- /dev/null
+++ b/res/pages/images/express_13.jpg
Binary files differ
diff --git a/res/pages/images/express_14.jpg b/res/pages/images/express_14.jpg
new file mode 100644
index 00000000..42f2e230
--- /dev/null
+++ b/res/pages/images/express_14.jpg
Binary files differ
diff --git a/res/pages/images/express_15.jpg b/res/pages/images/express_15.jpg
new file mode 100644
index 00000000..51d85ae0
--- /dev/null
+++ b/res/pages/images/express_15.jpg
Binary files differ
diff --git a/res/pages/images/express_2.jpg b/res/pages/images/express_2.jpg
new file mode 100644
index 00000000..9f006b5f
--- /dev/null
+++ b/res/pages/images/express_2.jpg
Binary files differ
diff --git a/res/pages/images/express_3.jpg b/res/pages/images/express_3.jpg
new file mode 100644
index 00000000..781b2fb4
--- /dev/null
+++ b/res/pages/images/express_3.jpg
Binary files differ
diff --git a/res/pages/images/express_4.jpg b/res/pages/images/express_4.jpg
new file mode 100644
index 00000000..d664e75b
--- /dev/null
+++ b/res/pages/images/express_4.jpg
Binary files differ
diff --git a/res/pages/images/express_5.jpg b/res/pages/images/express_5.jpg
new file mode 100644
index 00000000..db7ea558
--- /dev/null
+++ b/res/pages/images/express_5.jpg
Binary files differ
diff --git a/res/pages/images/express_6.jpg b/res/pages/images/express_6.jpg
new file mode 100644
index 00000000..c25d1f80
--- /dev/null
+++ b/res/pages/images/express_6.jpg
Binary files differ
diff --git a/res/pages/images/express_7.jpg b/res/pages/images/express_7.jpg
new file mode 100644
index 00000000..1b26a5ec
--- /dev/null
+++ b/res/pages/images/express_7.jpg
Binary files differ
diff --git a/res/pages/images/express_8.jpg b/res/pages/images/express_8.jpg
new file mode 100644
index 00000000..9a02b4f0
--- /dev/null
+++ b/res/pages/images/express_8.jpg
Binary files differ
diff --git a/res/pages/images/express_9.jpg b/res/pages/images/express_9.jpg
new file mode 100644
index 00000000..875c5968
--- /dev/null
+++ b/res/pages/images/express_9.jpg
Binary files differ
diff --git a/res/pages/images/hosting_logo.jpg b/res/pages/images/hosting_logo.jpg
new file mode 100644
index 00000000..ee79026f
--- /dev/null
+++ b/res/pages/images/hosting_logo.jpg
Binary files differ
diff --git a/res/pages/images/loading.gif b/res/pages/images/loading.gif
new file mode 100644
index 00000000..cf1d51a2
--- /dev/null
+++ b/res/pages/images/loading.gif
Binary files differ
diff --git a/res/pages/images/loading.png b/res/pages/images/loading.png
new file mode 100644
index 00000000..8e6739c8
--- /dev/null
+++ b/res/pages/images/loading.png
Binary files differ
diff --git a/res/pages/images/loading16x16.gif b/res/pages/images/loading16x16.gif
new file mode 100644
index 00000000..c1cc19ae
--- /dev/null
+++ b/res/pages/images/loading16x16.gif
Binary files differ
diff --git a/res/pages/images/login-sprite.png b/res/pages/images/login-sprite.png
new file mode 100644
index 00000000..ebee30d8
--- /dev/null
+++ b/res/pages/images/login-sprite.png
Binary files differ
diff --git a/res/pages/images/logo-white.png b/res/pages/images/logo-white.png
new file mode 100644
index 00000000..5519bcc4
--- /dev/null
+++ b/res/pages/images/logo-white.png
Binary files differ
diff --git a/res/pages/images/logo.gif b/res/pages/images/logo.gif
new file mode 100644
index 00000000..1a611f63
--- /dev/null
+++ b/res/pages/images/logo.gif
Binary files differ
diff --git a/res/pages/images/logo.png b/res/pages/images/logo.png
new file mode 100644
index 00000000..5953d2de
--- /dev/null
+++ b/res/pages/images/logo.png
Binary files differ
diff --git a/res/pages/images/menu-sprite.png b/res/pages/images/menu-sprite.png
new file mode 100644
index 00000000..bab82758
--- /dev/null
+++ b/res/pages/images/menu-sprite.png
Binary files differ
diff --git a/res/pages/images/nav_about.gif b/res/pages/images/nav_about.gif
new file mode 100644
index 00000000..54060606
--- /dev/null
+++ b/res/pages/images/nav_about.gif
Binary files differ
diff --git a/res/pages/images/nav_about_active.gif b/res/pages/images/nav_about_active.gif
new file mode 100644
index 00000000..503517d6
--- /dev/null
+++ b/res/pages/images/nav_about_active.gif
Binary files differ
diff --git a/res/pages/images/nav_about_active_over.gif b/res/pages/images/nav_about_active_over.gif
new file mode 100644
index 00000000..18cc5cc8
--- /dev/null
+++ b/res/pages/images/nav_about_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_about_over.gif b/res/pages/images/nav_about_over.gif
new file mode 100644
index 00000000..16945860
--- /dev/null
+++ b/res/pages/images/nav_about_over.gif
Binary files differ
diff --git a/res/pages/images/nav_contact.gif b/res/pages/images/nav_contact.gif
new file mode 100644
index 00000000..0e87175d
--- /dev/null
+++ b/res/pages/images/nav_contact.gif
Binary files differ
diff --git a/res/pages/images/nav_contact_active.gif b/res/pages/images/nav_contact_active.gif
new file mode 100644
index 00000000..beae43b4
--- /dev/null
+++ b/res/pages/images/nav_contact_active.gif
Binary files differ
diff --git a/res/pages/images/nav_contact_active_over.gif b/res/pages/images/nav_contact_active_over.gif
new file mode 100644
index 00000000..f7bf69fc
--- /dev/null
+++ b/res/pages/images/nav_contact_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_contact_over.gif b/res/pages/images/nav_contact_over.gif
new file mode 100644
index 00000000..810b3194
--- /dev/null
+++ b/res/pages/images/nav_contact_over.gif
Binary files differ
diff --git a/res/pages/images/nav_features.gif b/res/pages/images/nav_features.gif
new file mode 100644
index 00000000..f22a7ab7
--- /dev/null
+++ b/res/pages/images/nav_features.gif
Binary files differ
diff --git a/res/pages/images/nav_features_active.gif b/res/pages/images/nav_features_active.gif
new file mode 100644
index 00000000..b0c82b69
--- /dev/null
+++ b/res/pages/images/nav_features_active.gif
Binary files differ
diff --git a/res/pages/images/nav_features_active_over.gif b/res/pages/images/nav_features_active_over.gif
new file mode 100644
index 00000000..b861c34c
--- /dev/null
+++ b/res/pages/images/nav_features_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_features_over.gif b/res/pages/images/nav_features_over.gif
new file mode 100644
index 00000000..9b96cfbd
--- /dev/null
+++ b/res/pages/images/nav_features_over.gif
Binary files differ
diff --git a/res/pages/images/nav_help.gif b/res/pages/images/nav_help.gif
new file mode 100644
index 00000000..a94b0115
--- /dev/null
+++ b/res/pages/images/nav_help.gif
Binary files differ
diff --git a/res/pages/images/nav_help_active.gif b/res/pages/images/nav_help_active.gif
new file mode 100644
index 00000000..91396d65
--- /dev/null
+++ b/res/pages/images/nav_help_active.gif
Binary files differ
diff --git a/res/pages/images/nav_help_active_over.gif b/res/pages/images/nav_help_active_over.gif
new file mode 100644
index 00000000..5c7c8ba7
--- /dev/null
+++ b/res/pages/images/nav_help_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_help_over.gif b/res/pages/images/nav_help_over.gif
new file mode 100644
index 00000000..2ce8a9f4
--- /dev/null
+++ b/res/pages/images/nav_help_over.gif
Binary files differ
diff --git a/res/pages/images/nav_home.gif b/res/pages/images/nav_home.gif
new file mode 100644
index 00000000..5bbd6627
--- /dev/null
+++ b/res/pages/images/nav_home.gif
Binary files differ
diff --git a/res/pages/images/nav_home_active.gif b/res/pages/images/nav_home_active.gif
new file mode 100644
index 00000000..ee3568c1
--- /dev/null
+++ b/res/pages/images/nav_home_active.gif
Binary files differ
diff --git a/res/pages/images/nav_home_active_over.gif b/res/pages/images/nav_home_active_over.gif
new file mode 100644
index 00000000..23eaa1ad
--- /dev/null
+++ b/res/pages/images/nav_home_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_home_over.gif b/res/pages/images/nav_home_over.gif
new file mode 100644
index 00000000..14c11ce1
--- /dev/null
+++ b/res/pages/images/nav_home_over.gif
Binary files differ
diff --git a/res/pages/images/nav_links_over.gif b/res/pages/images/nav_links_over.gif
new file mode 100644
index 00000000..e6e01c7b
--- /dev/null
+++ b/res/pages/images/nav_links_over.gif
Binary files differ
diff --git a/res/pages/images/nav_network.gif b/res/pages/images/nav_network.gif
new file mode 100644
index 00000000..5f7ea1a2
--- /dev/null
+++ b/res/pages/images/nav_network.gif
Binary files differ
diff --git a/res/pages/images/nav_network_active.gif b/res/pages/images/nav_network_active.gif
new file mode 100644
index 00000000..7a1a0d52
--- /dev/null
+++ b/res/pages/images/nav_network_active.gif
Binary files differ
diff --git a/res/pages/images/nav_network_active_over.gif b/res/pages/images/nav_network_active_over.gif
new file mode 100644
index 00000000..8010fdc8
--- /dev/null
+++ b/res/pages/images/nav_network_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_network_over.gif b/res/pages/images/nav_network_over.gif
new file mode 100644
index 00000000..257d1465
--- /dev/null
+++ b/res/pages/images/nav_network_over.gif
Binary files differ
diff --git a/res/pages/images/nav_news_over.gif b/res/pages/images/nav_news_over.gif
new file mode 100644
index 00000000..f1440ba9
--- /dev/null
+++ b/res/pages/images/nav_news_over.gif
Binary files differ
diff --git a/res/pages/images/nav_philosophy.gif b/res/pages/images/nav_philosophy.gif
new file mode 100644
index 00000000..3d9d1f42
--- /dev/null
+++ b/res/pages/images/nav_philosophy.gif
Binary files differ
diff --git a/res/pages/images/nav_philosophy_active.gif b/res/pages/images/nav_philosophy_active.gif
new file mode 100644
index 00000000..36b112e6
--- /dev/null
+++ b/res/pages/images/nav_philosophy_active.gif
Binary files differ
diff --git a/res/pages/images/nav_philosophy_active_over.gif b/res/pages/images/nav_philosophy_active_over.gif
new file mode 100644
index 00000000..a0db7211
--- /dev/null
+++ b/res/pages/images/nav_philosophy_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_philosophy_over.gif b/res/pages/images/nav_philosophy_over.gif
new file mode 100644
index 00000000..b09b9a00
--- /dev/null
+++ b/res/pages/images/nav_philosophy_over.gif
Binary files differ
diff --git a/res/pages/images/nav_preferences.gif b/res/pages/images/nav_preferences.gif
new file mode 100644
index 00000000..ccc3bef9
--- /dev/null
+++ b/res/pages/images/nav_preferences.gif
Binary files differ
diff --git a/res/pages/images/nav_preferences_active.gif b/res/pages/images/nav_preferences_active.gif
new file mode 100644
index 00000000..6440bb93
--- /dev/null
+++ b/res/pages/images/nav_preferences_active.gif
Binary files differ
diff --git a/res/pages/images/nav_preferences_active_over.gif b/res/pages/images/nav_preferences_active_over.gif
new file mode 100644
index 00000000..19d3c1d0
--- /dev/null
+++ b/res/pages/images/nav_preferences_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_preferences_over.gif b/res/pages/images/nav_preferences_over.gif
new file mode 100644
index 00000000..1112e81b
--- /dev/null
+++ b/res/pages/images/nav_preferences_over.gif
Binary files differ
diff --git a/res/pages/images/nav_register.gif b/res/pages/images/nav_register.gif
new file mode 100644
index 00000000..06d069d9
--- /dev/null
+++ b/res/pages/images/nav_register.gif
Binary files differ
diff --git a/res/pages/images/nav_register_active.gif b/res/pages/images/nav_register_active.gif
new file mode 100644
index 00000000..e071117c
--- /dev/null
+++ b/res/pages/images/nav_register_active.gif
Binary files differ
diff --git a/res/pages/images/nav_register_active_over.gif b/res/pages/images/nav_register_active_over.gif
new file mode 100644
index 00000000..cf814fd7
--- /dev/null
+++ b/res/pages/images/nav_register_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_register_over.gif b/res/pages/images/nav_register_over.gif
new file mode 100644
index 00000000..4514383d
--- /dev/null
+++ b/res/pages/images/nav_register_over.gif
Binary files differ
diff --git a/res/pages/images/nav_services.gif b/res/pages/images/nav_services.gif
new file mode 100644
index 00000000..b350e38a
--- /dev/null
+++ b/res/pages/images/nav_services.gif
Binary files differ
diff --git a/res/pages/images/nav_services_active.gif b/res/pages/images/nav_services_active.gif
new file mode 100644
index 00000000..692395e3
--- /dev/null
+++ b/res/pages/images/nav_services_active.gif
Binary files differ
diff --git a/res/pages/images/nav_services_active_over.gif b/res/pages/images/nav_services_active_over.gif
new file mode 100644
index 00000000..533f9a7e
--- /dev/null
+++ b/res/pages/images/nav_services_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_services_over.gif b/res/pages/images/nav_services_over.gif
new file mode 100644
index 00000000..77d7ca8f
--- /dev/null
+++ b/res/pages/images/nav_services_over.gif
Binary files differ
diff --git a/res/pages/images/nav_signup_over.gif b/res/pages/images/nav_signup_over.gif
new file mode 100644
index 00000000..3e671786
--- /dev/null
+++ b/res/pages/images/nav_signup_over.gif
Binary files differ
diff --git a/res/pages/images/nav_testimonials.gif b/res/pages/images/nav_testimonials.gif
new file mode 100644
index 00000000..a58f2e4f
--- /dev/null
+++ b/res/pages/images/nav_testimonials.gif
Binary files differ
diff --git a/res/pages/images/nav_testimonials_active.gif b/res/pages/images/nav_testimonials_active.gif
new file mode 100644
index 00000000..a168ce38
--- /dev/null
+++ b/res/pages/images/nav_testimonials_active.gif
Binary files differ
diff --git a/res/pages/images/nav_testimonials_active_over.gif b/res/pages/images/nav_testimonials_active_over.gif
new file mode 100644
index 00000000..60eb9901
--- /dev/null
+++ b/res/pages/images/nav_testimonials_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_testimonials_over.gif b/res/pages/images/nav_testimonials_over.gif
new file mode 100644
index 00000000..50470991
--- /dev/null
+++ b/res/pages/images/nav_testimonials_over.gif
Binary files differ
diff --git a/res/pages/images/nav_webmail.gif b/res/pages/images/nav_webmail.gif
new file mode 100644
index 00000000..4a2b59b7
--- /dev/null
+++ b/res/pages/images/nav_webmail.gif
Binary files differ
diff --git a/res/pages/images/nav_webmail_active.gif b/res/pages/images/nav_webmail_active.gif
new file mode 100644
index 00000000..e8d61fcf
--- /dev/null
+++ b/res/pages/images/nav_webmail_active.gif
Binary files differ
diff --git a/res/pages/images/nav_webmail_active_over.gif b/res/pages/images/nav_webmail_active_over.gif
new file mode 100644
index 00000000..6fbe444c
--- /dev/null
+++ b/res/pages/images/nav_webmail_active_over.gif
Binary files differ
diff --git a/res/pages/images/nav_webmail_over.gif b/res/pages/images/nav_webmail_over.gif
new file mode 100644
index 00000000..2bb01559
--- /dev/null
+++ b/res/pages/images/nav_webmail_over.gif
Binary files differ
diff --git a/res/pages/images/outlook_01.png b/res/pages/images/outlook_01.png
new file mode 100644
index 00000000..bd82a212
--- /dev/null
+++ b/res/pages/images/outlook_01.png
Binary files differ
diff --git a/res/pages/images/outlook_02.png b/res/pages/images/outlook_02.png
new file mode 100644
index 00000000..4c14fb68
--- /dev/null
+++ b/res/pages/images/outlook_02.png
Binary files differ
diff --git a/res/pages/images/outlook_03.png b/res/pages/images/outlook_03.png
new file mode 100644
index 00000000..735dfac5
--- /dev/null
+++ b/res/pages/images/outlook_03.png
Binary files differ
diff --git a/res/pages/images/outlook_04.png b/res/pages/images/outlook_04.png
new file mode 100644
index 00000000..cba705bb
--- /dev/null
+++ b/res/pages/images/outlook_04.png
Binary files differ
diff --git a/res/pages/images/outlook_05.png b/res/pages/images/outlook_05.png
new file mode 100644
index 00000000..9a5532c6
--- /dev/null
+++ b/res/pages/images/outlook_05.png
Binary files differ
diff --git a/res/pages/images/outlook_06.png b/res/pages/images/outlook_06.png
new file mode 100644
index 00000000..30d5090e
--- /dev/null
+++ b/res/pages/images/outlook_06.png
Binary files differ
diff --git a/res/pages/images/outlook_07.png b/res/pages/images/outlook_07.png
new file mode 100644
index 00000000..da6c8bdd
--- /dev/null
+++ b/res/pages/images/outlook_07.png
Binary files differ
diff --git a/res/pages/images/outlook_08.png b/res/pages/images/outlook_08.png
new file mode 100644
index 00000000..34f92254
--- /dev/null
+++ b/res/pages/images/outlook_08.png
Binary files differ
diff --git a/res/pages/images/outlook_09.png b/res/pages/images/outlook_09.png
new file mode 100644
index 00000000..188c597e
--- /dev/null
+++ b/res/pages/images/outlook_09.png
Binary files differ
diff --git a/res/pages/images/pidgin_01.png b/res/pages/images/pidgin_01.png
new file mode 100644
index 00000000..3464994c
--- /dev/null
+++ b/res/pages/images/pidgin_01.png
Binary files differ
diff --git a/res/pages/images/pidgin_02.png b/res/pages/images/pidgin_02.png
new file mode 100644
index 00000000..f4b15657
--- /dev/null
+++ b/res/pages/images/pidgin_02.png
Binary files differ
diff --git a/res/pages/images/pidgin_03.png b/res/pages/images/pidgin_03.png
new file mode 100644
index 00000000..095538f4
--- /dev/null
+++ b/res/pages/images/pidgin_03.png
Binary files differ
diff --git a/res/pages/images/pidgin_04.png b/res/pages/images/pidgin_04.png
new file mode 100644
index 00000000..0986653e
--- /dev/null
+++ b/res/pages/images/pidgin_04.png
Binary files differ
diff --git a/res/pages/images/pidgin_05.png b/res/pages/images/pidgin_05.png
new file mode 100644
index 00000000..bd40c974
--- /dev/null
+++ b/res/pages/images/pidgin_05.png
Binary files differ
diff --git a/res/pages/images/pidgin_06.png b/res/pages/images/pidgin_06.png
new file mode 100644
index 00000000..17c53d57
--- /dev/null
+++ b/res/pages/images/pidgin_06.png
Binary files differ
diff --git a/res/pages/images/pidgin_07.png b/res/pages/images/pidgin_07.png
new file mode 100644
index 00000000..6bd450fd
--- /dev/null
+++ b/res/pages/images/pidgin_07.png
Binary files differ
diff --git a/res/pages/images/pidgin_08.png b/res/pages/images/pidgin_08.png
new file mode 100644
index 00000000..691fc76d
--- /dev/null
+++ b/res/pages/images/pidgin_08.png
Binary files differ
diff --git a/res/pages/images/splash_about.gif b/res/pages/images/splash_about.gif
new file mode 100644
index 00000000..1fff0fd8
--- /dev/null
+++ b/res/pages/images/splash_about.gif
Binary files differ
diff --git a/res/pages/images/splash_contact.gif b/res/pages/images/splash_contact.gif
new file mode 100644
index 00000000..44e06a9e
--- /dev/null
+++ b/res/pages/images/splash_contact.gif
Binary files differ
diff --git a/res/pages/images/splash_features.gif b/res/pages/images/splash_features.gif
new file mode 100644
index 00000000..40c64df2
--- /dev/null
+++ b/res/pages/images/splash_features.gif
Binary files differ
diff --git a/res/pages/images/splash_help.gif b/res/pages/images/splash_help.gif
new file mode 100644
index 00000000..f5af01c1
--- /dev/null
+++ b/res/pages/images/splash_help.gif
Binary files differ
diff --git a/res/pages/images/splash_index.gif b/res/pages/images/splash_index.gif
new file mode 100644
index 00000000..1529df53
--- /dev/null
+++ b/res/pages/images/splash_index.gif
Binary files differ
diff --git a/res/pages/images/splash_network.gif b/res/pages/images/splash_network.gif
new file mode 100644
index 00000000..a9fe7b60
--- /dev/null
+++ b/res/pages/images/splash_network.gif
Binary files differ
diff --git a/res/pages/images/splash_philosophy.gif b/res/pages/images/splash_philosophy.gif
new file mode 100644
index 00000000..43b50f8a
--- /dev/null
+++ b/res/pages/images/splash_philosophy.gif
Binary files differ
diff --git a/res/pages/images/splash_services.gif b/res/pages/images/splash_services.gif
new file mode 100644
index 00000000..72e3dec2
--- /dev/null
+++ b/res/pages/images/splash_services.gif
Binary files differ
diff --git a/res/pages/images/splash_signup.gif b/res/pages/images/splash_signup.gif
new file mode 100644
index 00000000..1cebc982
--- /dev/null
+++ b/res/pages/images/splash_signup.gif
Binary files differ
diff --git a/res/pages/images/splash_testimonials.gif b/res/pages/images/splash_testimonials.gif
new file mode 100644
index 00000000..765c6d96
--- /dev/null
+++ b/res/pages/images/splash_testimonials.gif
Binary files differ
diff --git a/res/pages/images/sprite.png b/res/pages/images/sprite.png
new file mode 100644
index 00000000..b42ee44f
--- /dev/null
+++ b/res/pages/images/sprite.png
Binary files differ
diff --git a/res/pages/images/tab-background.png b/res/pages/images/tab-background.png
new file mode 100644
index 00000000..49b3f891
--- /dev/null
+++ b/res/pages/images/tab-background.png
Binary files differ
diff --git a/res/pages/images/thunderbird_01.png b/res/pages/images/thunderbird_01.png
new file mode 100644
index 00000000..6c52b685
--- /dev/null
+++ b/res/pages/images/thunderbird_01.png
Binary files differ
diff --git a/res/pages/images/thunderbird_02.png b/res/pages/images/thunderbird_02.png
new file mode 100644
index 00000000..5a91c1ee
--- /dev/null
+++ b/res/pages/images/thunderbird_02.png
Binary files differ
diff --git a/res/pages/images/thunderbird_03.png b/res/pages/images/thunderbird_03.png
new file mode 100644
index 00000000..e5100459
--- /dev/null
+++ b/res/pages/images/thunderbird_03.png
Binary files differ
diff --git a/res/pages/images/thunderbird_04.png b/res/pages/images/thunderbird_04.png
new file mode 100644
index 00000000..4a0f3672
--- /dev/null
+++ b/res/pages/images/thunderbird_04.png
Binary files differ
diff --git a/res/pages/images/thunderbird_05.png b/res/pages/images/thunderbird_05.png
new file mode 100644
index 00000000..27bb0b48
--- /dev/null
+++ b/res/pages/images/thunderbird_05.png
Binary files differ
diff --git a/res/pages/images/thunderbird_06.png b/res/pages/images/thunderbird_06.png
new file mode 100644
index 00000000..f491d658
--- /dev/null
+++ b/res/pages/images/thunderbird_06.png
Binary files differ
diff --git a/res/pages/images/toolbar-background.png b/res/pages/images/toolbar-background.png
new file mode 100644
index 00000000..0b65ee2a
--- /dev/null
+++ b/res/pages/images/toolbar-background.png
Binary files differ
diff --git a/res/pages/images/tools-sprite.png b/res/pages/images/tools-sprite.png
new file mode 100644
index 00000000..740a2698
--- /dev/null
+++ b/res/pages/images/tools-sprite.png
Binary files differ
diff --git a/res/pages/images/trillian_01.png b/res/pages/images/trillian_01.png
new file mode 100644
index 00000000..998569d5
--- /dev/null
+++ b/res/pages/images/trillian_01.png
Binary files differ
diff --git a/res/pages/images/trillian_02.png b/res/pages/images/trillian_02.png
new file mode 100644
index 00000000..2d50d989
--- /dev/null
+++ b/res/pages/images/trillian_02.png
Binary files differ
diff --git a/res/pages/images/trillian_03.png b/res/pages/images/trillian_03.png
new file mode 100644
index 00000000..6a9df8d4
--- /dev/null
+++ b/res/pages/images/trillian_03.png
Binary files differ
diff --git a/res/pages/images/trillian_04.png b/res/pages/images/trillian_04.png
new file mode 100644
index 00000000..4ec0aa10
--- /dev/null
+++ b/res/pages/images/trillian_04.png
Binary files differ
diff --git a/res/pages/images/trillian_05.png b/res/pages/images/trillian_05.png
new file mode 100644
index 00000000..85bc2d8d
--- /dev/null
+++ b/res/pages/images/trillian_05.png
Binary files differ
diff --git a/res/pages/images/trillian_06.png b/res/pages/images/trillian_06.png
new file mode 100644
index 00000000..8738bdf6
--- /dev/null
+++ b/res/pages/images/trillian_06.png
Binary files differ
diff --git a/res/pages/images/trillian_07.png b/res/pages/images/trillian_07.png
new file mode 100644
index 00000000..2cff96de
--- /dev/null
+++ b/res/pages/images/trillian_07.png
Binary files differ
diff --git a/res/pages/images/ui-bg_flat_0_aaaaaa_40x100.png b/res/pages/images/ui-bg_flat_0_aaaaaa_40x100.png
new file mode 100644
index 00000000..5b5dab2a
--- /dev/null
+++ b/res/pages/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/res/pages/images/ui-bg_flat_75_ffffff_40x100.png b/res/pages/images/ui-bg_flat_75_ffffff_40x100.png
new file mode 100644
index 00000000..ac8b229a
--- /dev/null
+++ b/res/pages/images/ui-bg_flat_75_ffffff_40x100.png
Binary files differ
diff --git a/res/pages/images/ui-bg_glass_55_fbf9ee_1x400.png b/res/pages/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 00000000..ad3d6346
--- /dev/null
+++ b/res/pages/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/res/pages/images/ui-bg_glass_65_ffffff_1x400.png b/res/pages/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 00000000..42ccba26
--- /dev/null
+++ b/res/pages/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/res/pages/images/ui-bg_glass_75_dadada_1x400.png b/res/pages/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 00000000..5a46b47c
--- /dev/null
+++ b/res/pages/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/res/pages/images/ui-bg_glass_75_e6e6e6_1x400.png b/res/pages/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 00000000..86c2baa6
--- /dev/null
+++ b/res/pages/images/ui-bg_glass_75_e6e6e6_1x400.png
Binary files differ
diff --git a/res/pages/images/ui-bg_glass_95_fef1ec_1x400.png b/res/pages/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 00000000..4443fdc1
--- /dev/null
+++ b/res/pages/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/res/pages/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/res/pages/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 00000000..7c9fa6c6
--- /dev/null
+++ b/res/pages/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/res/pages/images/ui-icons_222222_256x240.png b/res/pages/images/ui-icons_222222_256x240.png
new file mode 100644
index 00000000..b273ff11
--- /dev/null
+++ b/res/pages/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/res/pages/images/ui-icons_2e83ff_256x240.png b/res/pages/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 00000000..09d1cdc8
--- /dev/null
+++ b/res/pages/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/res/pages/images/ui-icons_454545_256x240.png b/res/pages/images/ui-icons_454545_256x240.png
new file mode 100644
index 00000000..b7571021
--- /dev/null
+++ b/res/pages/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/res/pages/images/ui-icons_888888_256x240.png b/res/pages/images/ui-icons_888888_256x240.png
new file mode 100644
index 00000000..6d02426c
--- /dev/null
+++ b/res/pages/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/res/pages/images/ui-icons_cd0a0a_256x240.png b/res/pages/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 00000000..2ab019b7
--- /dev/null
+++ b/res/pages/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/res/pages/images/wai_aaa_grey.gif b/res/pages/images/wai_aaa_grey.gif
new file mode 100644
index 00000000..a2eded6a
--- /dev/null
+++ b/res/pages/images/wai_aaa_grey.gif
Binary files differ
diff --git a/res/pages/images/wai_aaa_red.gif b/res/pages/images/wai_aaa_red.gif
new file mode 100644
index 00000000..77efd116
--- /dev/null
+++ b/res/pages/images/wai_aaa_red.gif
Binary files differ
diff --git a/res/pages/images/windows_live_mail_01.png b/res/pages/images/windows_live_mail_01.png
new file mode 100644
index 00000000..cf496b8b
--- /dev/null
+++ b/res/pages/images/windows_live_mail_01.png
Binary files differ
diff --git a/res/pages/images/windows_live_mail_02.png b/res/pages/images/windows_live_mail_02.png
new file mode 100644
index 00000000..5fb61302
--- /dev/null
+++ b/res/pages/images/windows_live_mail_02.png
Binary files differ
diff --git a/res/pages/images/windows_live_mail_03.png b/res/pages/images/windows_live_mail_03.png
new file mode 100644
index 00000000..15dbeb77
--- /dev/null
+++ b/res/pages/images/windows_live_mail_03.png
Binary files differ
diff --git a/res/pages/images/windows_live_mail_04.png b/res/pages/images/windows_live_mail_04.png
new file mode 100644
index 00000000..b087f787
--- /dev/null
+++ b/res/pages/images/windows_live_mail_04.png
Binary files differ
diff --git a/res/pages/images/windows_live_mail_05.png b/res/pages/images/windows_live_mail_05.png
new file mode 100644
index 00000000..1842f729
--- /dev/null
+++ b/res/pages/images/windows_live_mail_05.png
Binary files differ
diff --git a/res/pages/images/xhtml_strict_grey.gif b/res/pages/images/xhtml_strict_grey.gif
new file mode 100644
index 00000000..5ae2de46
--- /dev/null
+++ b/res/pages/images/xhtml_strict_grey.gif
Binary files differ
diff --git a/res/pages/images/xhtml_strict_red.gif b/res/pages/images/xhtml_strict_red.gif
new file mode 100644
index 00000000..36eede73
--- /dev/null
+++ b/res/pages/images/xhtml_strict_red.gif
Binary files differ
diff --git a/res/pages/index.html b/res/pages/index.html
new file mode 100644
index 00000000..f124ac96
--- /dev/null
+++ b/res/pages/index.html
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Home</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. We provide advanced features and fast reliable service for people who are serious about their e-mail." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="js/jquery.js"></script>
+ <script type="text/javascript" src="js/jquery.corner.js"></script>
+ <script type="text/javascript" src="js/index.js"></script>
+ <script type="text/javascript" src="js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#index">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" class="active" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="index" class="content">
+ <h2>Home</h2>
+ <p>Magma was built for people like you. People who want fast, reliable, private POP3 e-mail services with the most advanced <a href="features.html">features</a>. Our team of programmers answered with a system so <a href="secure.html">secure</a> that even our administrators can’t read your e-mail.</p>
+ <div id="sign_up_link">
+ <a href="register">Sign Up Now</a>
+ </div>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, 'images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, 'images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, 'images/css_valid_red.gif')" onmouseout="ChangeImage(this, 'images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, 'images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, 'images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/res/pages/js/advertising.js b/res/pages/js/advertising.js
new file mode 100644
index 00000000..36924829
--- /dev/null
+++ b/res/pages/js/advertising.js
@@ -0,0 +1,49 @@
+
+
+$(document).ready(function() {
+
+ if ($.browser.mozilla) {
+ $("div#advertising table#impressions").wrap("<div id='impressions_wrapper' style='padding: 0px; margin: 0px; width: 250px;'></div>");
+ $("div#advertising div#impressions_wrapper").corner("round");
+ };
+
+ if ($("#plan").val() == 100) {
+ $("#total").html('$100.00');
+ }
+ else if ($("#plan").val() == 200) {
+ $("#total").html('$198.00');
+ }
+ else if ($("#plan").val() == 500) {
+ $("#total").html('$475.00');
+ }
+ else if ($("#plan").val() == 1000) {
+ $("#total").html('$900.00');
+ }
+
+ $("input").focus(function() {
+ $(this).removeClass("red");
+ $(this).removeClass("white");
+ $(this).addClass("gray");
+ });
+
+ $("input").blur(function() {
+ $(this).removeClass("gray");
+ $(this).addClass("white");
+ });
+
+ $("select#plan").change(function() {
+ if ($("#plan").val() == 100) {
+ $("#total").html('$100.00');
+ }
+ else if ($("#plan").val() == 200) {
+ $("#total").html('$198.00');
+ }
+ else if ($("#plan").val() == 500) {
+ $("#total").html('$475.00');
+ }
+ else if ($("#plan").val() == 1000) {
+ $("#total").html('$900.00');
+ }
+ });
+
+}); \ No newline at end of file
diff --git a/res/pages/js/contact.js b/res/pages/js/contact.js
new file mode 100644
index 00000000..b41941d9
--- /dev/null
+++ b/res/pages/js/contact.js
@@ -0,0 +1,80 @@
+new function() {
+ $.fn.validator = {
+ init: function(o) {
+ if(o.name == 'your_e_mail') { this.your_e_mail(o) };
+ if(o.name == 'your_name') { this.your_name(o) };
+ if(o.name == 'your_message') { this.your_message(o) };
+ },
+ your_e_mail: function(o) {
+ var email = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,10})$/;
+ if (o.value.match(email)) {
+ doSuccess(o);
+ } else {
+ doError(o,'A valid e-mail address is required.');
+ };
+ },
+ your_name: function (o) {
+ var name = /\w+/;
+ if (o.value.match(name)) {
+ doSuccess(o);
+ } else {
+ doError(o,'Please enter your name.');
+ };
+ },
+ your_message: function (o) {
+ var message = /\w+/;
+ if (o.value.match(message)) {
+ doSuccess(o);
+ } else {
+ doError(o,'Please enter a message.');
+ };
+ }
+ };
+
+ function doSuccess(o) {
+ $('#' + o.id).removeClass("red");
+ $('#' + o.id).addClass("white");
+ $('#' + o.id + '_msg').remove();
+ }
+
+ function doError(o,m) {
+ $('#' + o.id).removeClass("white");
+ $('#' + o.id).addClass("red");
+ $('#' + o.id + '_msg').remove();
+ $('#' + o.id).after('<span id="' + o.id + '_msg">' + m + '</span>');
+ }
+};
+
+$(document).ready(function() {
+
+ $("input").focus(function() {
+ $(this).removeClass("red");
+ $(this).removeClass("white");
+ $(this).addClass("gray");
+ });
+
+ $("textarea").focus(function() {
+ $(this).removeClass("red");
+ $(this).removeClass("white");
+ $(this).addClass("gray");
+ });
+
+ $("input").blur(function() {
+ $(this).removeClass("gray");
+ $(this).addClass("white");
+ $(this).validator.init(this);
+ });
+
+ $("textarea").blur(function() {
+ $(this).removeClass("gray");
+ $(this).addClass("white");
+ $(this).validator.init(this);
+ });
+
+ $("form").submit(function() {
+ var output = true;
+ $(this).find("#your_name, #your_e_mail, #your_message").blur();
+ $(this).find(".red").each(function() { output = false; });
+ return output;
+ });
+}); \ No newline at end of file
diff --git a/res/pages/js/corporate_e_mail.js b/res/pages/js/corporate_e_mail.js
new file mode 100644
index 00000000..66b7d1b4
--- /dev/null
+++ b/res/pages/js/corporate_e_mail.js
@@ -0,0 +1,11 @@
+
+$(document).ready(function() {
+
+ if ($.browser.mozilla) {
+ $("div#corporate_e_mail table#users").wrap("<div id='users_wrapper' style='padding: 0px; margin: 0px 0px 0px 0px; width: 300px;'></div>");
+ $("div#corporate_e_mail div#users_wrapper").corner("round");
+
+ $("div#corporate_e_mail table#plan").wrap("<div id='plan_wrapper' style='padding: 0px; margin: 0px; width: 400px;'></div>");
+ $("div#corporate_e_mail div#plan_wrapper").corner("round");
+ }
+}); \ No newline at end of file
diff --git a/res/pages/js/health.js b/res/pages/js/health.js
new file mode 100644
index 00000000..fe4fae2f
--- /dev/null
+++ b/res/pages/js/health.js
@@ -0,0 +1,8 @@
+
+$(document).ready(function() {
+
+ if ($.browser.mozilla) {
+ $("div#health table").wrap("<div id='table_wrapper' style='padding: 0px; margin: 0px; width: 605px;'></div>");
+ $("div#health div#table_wrapper").corner("round");
+ }
+}); \ No newline at end of file
diff --git a/res/pages/js/index.js b/res/pages/js/index.js
new file mode 100644
index 00000000..3fb33e95
--- /dev/null
+++ b/res/pages/js/index.js
@@ -0,0 +1,4 @@
+
+$(document).ready(function() {
+ $("div#index table").wrap("<div id='table_wrapper' style='padding: 0px; margin: 0px; width: 605px;'></div>").parent().corner("round");
+}); \ No newline at end of file
diff --git a/res/pages/js/jquery.corner.js b/res/pages/js/jquery.corner.js
new file mode 100644
index 00000000..fca227e8
--- /dev/null
+++ b/res/pages/js/jquery.corner.js
@@ -0,0 +1,180 @@
+/*!
+ * jQuery corner plugin: simple corner rounding
+ * Examples and documentation at: http://jquery.malsup.com/corner/
+ * version 1.99 (28-JUL-2009)
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+/**
+ * corner() takes a single string argument: $('#myDiv').corner("effect corners width")
+ *
+ * effect: name of the effect to apply, such as round, bevel, notch, bite, etc (default is round).
+ * corners: one or more of: top, bottom, tr, tl, br, or bl.
+ * by default, all four corners are adorned.
+ * width: width of the effect; in the case of rounded corners this is the radius.
+ * specify this value using the px suffix such as 10px (and yes, it must be pixels).
+ *
+ * @name corner
+ * @type jQuery
+ * @param String options Options which control the corner style
+ * @cat Plugins/Corner
+ * @return jQuery
+ * @author Dave Methvin (http://methvin.com/jquery/jq-corner.html)
+ * @author Mike Alsup (http://jquery.malsup.com/corner/)
+ */
+;(function($) {
+
+var expr = (function() {
+ if (! $.browser.msie) return false;
+ var div = document.createElement('div');
+ try { div.style.setExpression('width','0+0'); }
+ catch(e) { return false; }
+ return true;
+})();
+
+function sz(el, p) {
+ return parseInt($.css(el,p))||0;
+};
+function hex2(s) {
+ var s = parseInt(s).toString(16);
+ return ( s.length < 2 ) ? '0'+s : s;
+};
+function gpc(node) {
+ for ( ; node && node.nodeName.toLowerCase() != 'html'; node = node.parentNode ) {
+ var v = $.css(node,'backgroundColor');
+ if (v == 'rgba(0, 0, 0, 0)')
+ continue; // webkit
+ if (v.indexOf('rgb') >= 0) {
+ var rgb = v.match(/\d+/g);
+ return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
+ }
+ if ( v && v != 'transparent' )
+ return v;
+ }
+ return '#ffffff';
+};
+
+function getWidth(fx, i, width) {
+ switch(fx) {
+ case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
+ case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
+ case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width))));
+ case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
+ case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
+ case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
+ case 'curl': return Math.round(width*(Math.atan(i)));
+ case 'tear': return Math.round(width*(Math.cos(i)));
+ case 'wicked': return Math.round(width*(Math.tan(i)));
+ case 'long': return Math.round(width*(Math.sqrt(i)));
+ case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
+ case 'dog': return (i&1) ? (i+1) : width;
+ case 'dog2': return (i&2) ? (i+1) : width;
+ case 'dog3': return (i&3) ? (i+1) : width;
+ case 'fray': return (i%2)*width;
+ case 'notch': return width;
+ case 'bevel': return i+1;
+ }
+};
+
+$.fn.corner = function(o) {
+ // in 1.3+ we can fix mistakes with the ready state
+ if (this.length == 0) {
+ if (!$.isReady && this.selector) {
+ var s = this.selector, c = this.context;
+ $(function() {
+ $(s,c).corner(o);
+ });
+ }
+ return this;
+ }
+
+ o = (o||"").toLowerCase();
+ var keep = /keep/.test(o); // keep borders?
+ var cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]); // corner color
+ var sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]); // strip color
+ var width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10; // corner width
+ var re = /round|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dog/;
+ var fx = ((o.match(re)||['round'])[0]);
+ var edges = { T:0, B:1 };
+ var opts = {
+ TL: /top|tl/.test(o), TR: /top|tr/.test(o),
+ BL: /bottom|bl/.test(o), BR: /bottom|br/.test(o)
+ };
+ if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
+ opts = { TL:1, TR:1, BL:1, BR:1 };
+ var strip = document.createElement('div');
+ strip.style.overflow = 'hidden';
+ strip.style.height = '1px';
+ strip.style.backgroundColor = sc || 'transparent';
+ strip.style.borderStyle = 'solid';
+ return this.each(function(index){
+ var pad = {
+ T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
+ B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
+ };
+
+ if (typeof this.style.zoom != undefined) this.style.zoom = 1; // force 'hasLayout' in IE
+ if (!keep) this.style.border = 'none';
+ strip.style.borderColor = cc || gpc(this.parentNode);
+ var cssHeight = $.curCSS(this, 'height');
+
+ for (var j in edges) {
+ var bot = edges[j];
+ // only add stips if needed
+ if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
+ strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
+ var d = document.createElement('div');
+ $(d).addClass('jquery-corner');
+ var ds = d.style;
+
+ bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
+
+ if (bot && cssHeight != 'auto') {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.bottom = ds.left = ds.padding = ds.margin = '0';
+ if (expr)
+ ds.setExpression('width', 'this.parentNode.offsetWidth');
+ else
+ ds.width = '100%';
+ }
+ else if (!bot && $.browser.msie) {
+ if ($.css(this,'position') == 'static')
+ this.style.position = 'relative';
+ ds.position = 'absolute';
+ ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
+
+ // fix ie6 problem when blocked element has a border width
+ if (expr) {
+ var bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
+ ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"');
+ }
+ else
+ ds.width = '100%';
+ }
+ else {
+ ds.position = 'relative';
+ ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
+ (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
+ }
+
+ for (var i=0; i < width; i++) {
+ var w = Math.max(0,getWidth(fx,i, width));
+ var e = strip.cloneNode(false);
+ e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
+ bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
+ }
+ }
+ }
+ });
+};
+
+$.fn.uncorner = function() {
+ $('div.jquery-corner', this).remove();
+ return this;
+};
+
+})(jQuery);
diff --git a/res/pages/js/jquery.js b/res/pages/js/jquery.js
new file mode 100644
index 00000000..ec5c98a8
--- /dev/null
+++ b/res/pages/js/jquery.js
@@ -0,0 +1,8936 @@
+/*!
+ * jQuery JavaScript Library v1.6.2pre
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Mon May 16 10:38:36 2011 -0400
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // (both of which we optimize for)
+ quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Check for digits
+ rdigit = /\d/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = (context ? context.ownerDocument || context : document);
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return (context || rootjQuery).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if (selector.selector !== undefined) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.6.2pre",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + (this.selector ? " " : "") + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, +i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).unbind( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery._Deferred();
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" && "setInterval" in obj;
+ },
+
+ isNaN: function( obj ) {
+ return obj == null || !rdigit.test( obj ) || isNaN( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw msg;
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return (new Function( "return " + data ))();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ // (xml & tmp used internally)
+ parseXML: function( data , xml , tmp ) {
+
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+
+ tmp = xml.documentElement;
+
+ if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // The extra typeof function check is to prevent crashes
+ // in Safari 2 (See: #3039)
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array ) {
+
+ if ( indexOf ) {
+ return indexOf.call( array, elem );
+ }
+
+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can be optionally by executed if its a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return (new Date()).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+// Expose jQuery to the global object
+return jQuery;
+
+})();
+
+
+var // Promise methods
+ promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ),
+ // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+ // Create a simple deferred (one callbacks list)
+ _Deferred: function() {
+ var // callbacks list
+ callbacks = [],
+ // stored [ context , args ]
+ fired,
+ // to avoid firing when already doing so
+ firing,
+ // flag to know if the deferred has been cancelled
+ cancelled,
+ // the deferred itself
+ deferred = {
+
+ // done( f1, f2, ...)
+ done: function() {
+ if ( !cancelled ) {
+ var args = arguments,
+ i,
+ length,
+ elem,
+ type,
+ _fired;
+ if ( fired ) {
+ _fired = fired;
+ fired = 0;
+ }
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ deferred.done.apply( deferred, elem );
+ } else if ( type === "function" ) {
+ callbacks.push( elem );
+ }
+ }
+ if ( _fired ) {
+ deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
+ }
+ }
+ return this;
+ },
+
+ // resolve with given context and args
+ resolveWith: function( context, args ) {
+ if ( !cancelled && !fired && !firing ) {
+ // make sure args are available (#8421)
+ args = args || [];
+ firing = 1;
+ try {
+ while( callbacks[ 0 ] ) {
+ callbacks.shift().apply( context, args );
+ }
+ }
+ finally {
+ fired = [ context, args ];
+ firing = 0;
+ }
+ }
+ return this;
+ },
+
+ // resolve with this as context and given arguments
+ resolve: function() {
+ deferred.resolveWith( this, arguments );
+ return this;
+ },
+
+ // Has this deferred been resolved?
+ isResolved: function() {
+ return !!( firing || fired );
+ },
+
+ // Cancel
+ cancel: function() {
+ cancelled = 1;
+ callbacks = [];
+ return this;
+ }
+ };
+
+ return deferred;
+ },
+
+ // Full fledged deferred (two callbacks list)
+ Deferred: function( func ) {
+ var deferred = jQuery._Deferred(),
+ failDeferred = jQuery._Deferred(),
+ promise;
+ // Add errorDeferred methods, then and promise
+ jQuery.extend( deferred, {
+ then: function( doneCallbacks, failCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks );
+ return this;
+ },
+ always: function() {
+ return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
+ },
+ fail: failDeferred.done,
+ rejectWith: failDeferred.resolveWith,
+ reject: failDeferred.resolve,
+ isRejected: failDeferred.isResolved,
+ pipe: function( fnDone, fnFail ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject );
+ } else {
+ newDefer[ action ]( returned );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ if ( promise ) {
+ return promise;
+ }
+ promise = obj = {};
+ }
+ var i = promiseMethods.length;
+ while( i-- ) {
+ obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
+ }
+ return obj;
+ }
+ });
+ // Make sure only one callback list will be used
+ deferred.done( failDeferred.cancel ).fail( deferred.cancel );
+ // Unexpose cancel
+ delete deferred.cancel;
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = arguments,
+ i = 0,
+ length = args.length,
+ count = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ // Strange bug in FF4:
+ // Values changed onto the arguments object sometimes end up as undefined values
+ // outside the $.when method. Cloning the object into a fresh array solves the issue
+ deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
+ }
+ };
+ }
+ if ( length > 1 ) {
+ for( ; i < length; i++ ) {
+ if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return deferred.promise();
+ }
+});
+
+
+
+jQuery.support = (function() {
+
+ var div = document.createElement( "div" ),
+ documentElement = document.documentElement,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ marginDiv,
+ support,
+ fragment,
+ body,
+ testElementParent,
+ testElement,
+ testElementStyle,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName( "tbody" ).length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName( "link" ).length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55$/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function click() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ div.detachEvent( "onclick", click );
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains it's value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.firstChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ div.innerHTML = "";
+
+ // Figure out if the W3C box model works as expected
+ div.style.width = div.style.paddingLeft = "1px";
+
+ body = document.getElementsByTagName( "body" )[ 0 ];
+ // We use our own, invisible, body unless the body is already present
+ // in which case we use a div (#9239)
+ testElement = document.createElement( body ? "div" : "body" );
+ testElementStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0,
+ // Set background to avoid IE crashes when removing (#9028)
+ background: "none"
+ };
+ if ( body ) {
+ jQuery.extend( testElementStyle, {
+ position: "absolute",
+ left: -1000,
+ top: -1000
+ });
+ }
+ for ( i in testElementStyle ) {
+ testElement.style[ i ] = testElementStyle[ i ];
+ }
+ testElement.appendChild( div );
+ testElementParent = body || documentElement;
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ support.boxModel = div.offsetWidth === 2;
+
+ if ( "zoom" in div.style ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE < 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+ div.innerHTML = "";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ // Remove the body element we added
+ testElement.innerHTML = "";
+ testElementParent.removeChild( testElement );
+
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ } ) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ return support;
+})();
+
+// Keep track of boxModel
+jQuery.boxModel = jQuery.support.boxModel;
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([a-z])([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ jQuery.expando ] = id = ++jQuery.uuid;
+ } else {
+ id = jQuery.expando;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
+ } else {
+ cache[ id ] = jQuery.extend(cache[ id ], name);
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // Internal jQuery data is stored in a separate object inside the object's data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data
+ if ( pvt ) {
+ if ( !thisCache[ internalKey ] ) {
+ thisCache[ internalKey ] = {};
+ }
+
+ thisCache = thisCache[ internalKey ];
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
+ // not attempt to inspect the internal events object using jQuery.data, as this
+ // internal data object is undocumented and subject to change.
+ if ( name === "events" && !thisCache[name] ) {
+ return thisCache[ internalKey ] && thisCache[ internalKey ].events;
+ }
+
+ return getByName ? thisCache[ jQuery.camelCase( name ) ] : thisCache;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando, isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+ var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
+
+ if ( thisCache ) {
+ delete thisCache[ name ];
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !isEmptyDataObject(thisCache) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( pvt ) {
+ delete cache[ id ][ internalKey ];
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ var internalCache = cache[ id ][ internalKey ];
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ if ( jQuery.support.deleteExpando || cache != window ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the entire user cache at once because it's faster than
+ // iterating through each key, but we need to continue to persist internal
+ // data if it existed
+ if ( internalCache ) {
+ cache[ id ] = {};
+ // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
+ // metadata on plain JS objects when the object is serialized using
+ // JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+
+ cache[ id ][ internalKey ] = internalCache;
+
+ // Otherwise, we need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ } else if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ jQuery.expando ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ } else {
+ elem[ jQuery.expando ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var data = null;
+
+ if ( typeof key === "undefined" ) {
+ if ( this.length ) {
+ data = jQuery.data( this[0] );
+
+ if ( this[0].nodeType === 1 ) {
+ var attr = this[0].attributes, name;
+ for ( var i = 0, l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( this[0], name, data[ name ] );
+ }
+ }
+ }
+ }
+
+ return data;
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ var parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ data = dataAttr( this[0], key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+
+ } else {
+ return this.each(function() {
+ var $this = jQuery( this ),
+ args = [ parts[0], value ];
+
+ $this.triggerHandler( "setData" + parts[1] + "!", args );
+ jQuery.data( this, key, value );
+ $this.triggerHandler( "changeData" + parts[1] + "!", args );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ !jQuery.isNaN( data ) ? parseFloat( data ) :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
+// property to be considered empty objects; this property always exists in
+// order to make sure JSON.stringify does not expose internal metadata
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery.data( elem, deferDataKey, undefined, true );
+ if ( defer &&
+ ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
+ ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery.data( elem, queueDataKey, undefined, true ) &&
+ !jQuery.data( elem, markDataKey, undefined, true ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.resolve();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = (type || "fx") + "mark";
+ jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 );
+ if ( count ) {
+ jQuery.data( elem, key, count, true );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ if ( elem ) {
+ type = (type || "fx") + "queue";
+ var q = jQuery.data( elem, type, undefined, true );
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery.data( elem, type, jQuery.makeArray(data), true );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ defer;
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift("inprogress");
+ }
+
+ fn.call(elem, function() {
+ jQuery.dequeue(elem, type);
+ });
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function() {
+ var elem = this;
+ setTimeout(function() {
+ jQuery.dequeue( elem, type );
+ }, time );
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
+ count++;
+ tmp.done( resolve );
+ }
+ }
+ resolve();
+ return defer.promise();
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ rinvalidChar = /\:|^on/,
+ formHook, boolHook;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.prop );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.addClass( value.call(this, i, self.attr("class") || "") );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className ) {
+ elem.className = value;
+
+ } else {
+ var className = " " + elem.className + " ",
+ setClass = elem.className;
+
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+ setClass += " " + classNames[c];
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.removeClass( value.call(this, i, self.attr("class")) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ var classNames = (value || "").split( rspace );
+
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ var elem = this[i];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ var className = (" " + elem.className + " ").replace(rclass, " ");
+ for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[c] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ";
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ return (elem.value || "").replace(rreturn, "");
+ }
+
+ return undefined;
+ }
+
+ var isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+ var option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attrFix: {
+ // Always normalize to ensure hook usage
+ tabindex: "tabIndex"
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( !("getAttribute" in elem) ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // Normalize the name if needed
+ name = notxml && jQuery.attrFix[ name ] || name;
+
+ hooks = jQuery.attrHooks[ name ];
+
+ if ( !hooks ) {
+ // Use boolHook for boolean attributes
+ if ( rboolean.test( name ) ) {
+
+ hooks = boolHook;
+
+ // Use formHook for forms and if the name contains certain characters
+ } else if ( formHook && name !== "className" &&
+ (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) {
+
+ hooks = formHook;
+ }
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return undefined;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml ) {
+ return hooks.get( elem, name );
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, name ) {
+ var propName;
+ if ( elem.nodeType === 1 ) {
+ name = jQuery.attrFix[ name ] || name;
+
+ if ( jQuery.support.getSetAttribute ) {
+ // Use removeAttribute in browsers that support it
+ elem.removeAttribute( name );
+ } else {
+ jQuery.attr( elem, name, "" );
+ elem.removeAttributeNode( elem.getAttributeNode( name ) );
+ }
+
+ // Set corresponding property to false for boolean attributes
+ if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabIndex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return undefined;
+ }
+
+ var ret, hooks,
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // Try to normalize/fix the name
+ name = notxml && jQuery.propFix[ name ] || name;
+
+ hooks = jQuery.propHooks[ name ];
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return (elem[ name ] = value);
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {}
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ return jQuery.prop( elem, name ) ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// Use the value property for back compat
+// Use the formHook for button elements in IE6/7 (#1954)
+jQuery.attrHooks.value = {
+ get: function( elem, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.get( elem, name );
+ }
+ return elem.value;
+ },
+ set: function( elem, value, name ) {
+ if ( formHook && jQuery.nodeName( elem, "button" ) ) {
+ return formHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !jQuery.support.getSetAttribute ) {
+
+ // propFix is more comprehensive and contains all fixes
+ jQuery.attrFix = jQuery.propFix;
+
+ // Use this for any attribute on a form in IE6/7
+ formHook = jQuery.attrHooks.name = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ // Return undefined if nodeValue is empty string
+ return ret && ret.nodeValue !== "" ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Check form objects in IE (multiple bugs related)
+ // Only use nodeValue if the attribute node exists on the form
+ var ret = elem.getAttributeNode( name );
+ if ( ret ) {
+ ret.nodeValue = value;
+ return value;
+ }
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return (elem.style.cssText = "" + value);
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ }
+ });
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
+ }
+ }
+ });
+});
+
+
+
+
+var hasOwn = Object.prototype.hasOwnProperty,
+ rnamespaces = /\.(.*)$/,
+ rformElems = /^(?:textarea|input|select)$/i,
+ rperiod = /\./g,
+ rspaces = / /g,
+ rescape = /[^\w\s.|`]/g,
+ fcleanup = function( nm ) {
+ return nm.replace(rescape, "\\$&");
+ };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+ // Bind an event to an element
+ // Original by Dean Edwards
+ add: function( elem, types, handler, data ) {
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ } else if ( !handler ) {
+ // Fixes bug #7229. Fix recommended by jdalton
+ return;
+ }
+
+ var handleObjIn, handleObj;
+
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the function being executed has a unique ID
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure
+ var elemData = jQuery._data( elem );
+
+ // If no elemData is found then we must be trying to bind to one of the
+ // banned noData elements
+ if ( !elemData ) {
+ return;
+ }
+
+ var events = elemData.events,
+ eventHandle = elemData.handle;
+
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ }
+
+ // Add elem as a property of the handle function
+ // This is to prevent a memory leak with non-native events in IE.
+ eventHandle.elem = elem;
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ var type, i = 0, namespaces;
+
+ while ( (type = types[ i++ ]) ) {
+ handleObj = handleObjIn ?
+ jQuery.extend({}, handleObjIn) :
+ { handler: handler, data: data };
+
+ // Namespaced event handlers
+ if ( type.indexOf(".") > -1 ) {
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+ } else {
+ namespaces = [];
+ handleObj.namespace = "";
+ }
+
+ handleObj.type = type;
+ if ( !handleObj.guid ) {
+ handleObj.guid = handler.guid;
+ }
+
+ // Get the current list of functions bound to this event
+ var handlers = events[ type ],
+ special = jQuery.event.special[ type ] || {};
+
+ // Init the event handler queue
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+
+ // Check for a special event handler
+ // Only use addEventListener/attachEvent if the special
+ // events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add the function to the element's handler list
+ handlers.push( handleObj );
+
+ // Keep track of which events have been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, pos ) {
+ // don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ if ( handler === false ) {
+ handler = returnFalse;
+ }
+
+ var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ events = elemData && elemData.events;
+
+ if ( !elemData || !events ) {
+ return;
+ }
+
+ // types is actually an event object here
+ if ( types && types.type ) {
+ handler = types.handler;
+ types = types.type;
+ }
+
+ // Unbind all events for the element
+ if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+ types = types || "";
+
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types );
+ }
+
+ return;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).unbind("mouseover mouseout", fn);
+ types = types.split(" ");
+
+ while ( (type = types[ i++ ]) ) {
+ origType = type;
+ handleObj = null;
+ all = type.indexOf(".") < 0;
+ namespaces = [];
+
+ if ( !all ) {
+ // Namespaced event handlers
+ namespaces = type.split(".");
+ type = namespaces.shift();
+
+ namespace = new RegExp("(^|\\.)" +
+ jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ eventType = events[ type ];
+
+ if ( !eventType ) {
+ continue;
+ }
+
+ if ( !handler ) {
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ jQuery.event.remove( elem, origType, handleObj.handler, j );
+ eventType.splice( j--, 1 );
+ }
+ }
+
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+
+ for ( j = pos || 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( handler.guid === handleObj.guid ) {
+ // remove the given handler for the given type
+ if ( all || namespace.test( handleObj.namespace ) ) {
+ if ( pos == null ) {
+ eventType.splice( j--, 1 );
+ }
+
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+
+ if ( pos != null ) {
+ break;
+ }
+ }
+ }
+
+ // remove generic event handler if no more handlers exist
+ if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ ret = null;
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ var handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ delete elemData.events;
+ delete elemData.handle;
+
+ if ( jQuery.isEmptyObject( elemData ) ) {
+ jQuery.removeData( elem, undefined, true );
+ }
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ exclusive;
+
+ if ( type.indexOf("!") >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
+
+ // triggerHandler() and global events don't bubble or run the default action
+ if ( onlyHandlers || !elem ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ // Handle a global trigger
+ if ( !elem ) {
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ jQuery.each( jQuery.cache, function() {
+ // internalKey variable is just used to make it easier to find
+ // and potentially change this stuff later; currently it just
+ // points to jQuery.expando
+ var internalKey = jQuery.expando,
+ internalCache = this[ internalKey ];
+ if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
+ jQuery.event.trigger( event, data, internalCache.handle.elem );
+ }
+ });
+ return;
+ }
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ event.target = elem;
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ var cur = elem,
+ // IE doesn't like method names with a colon (#3533, #8272)
+ ontype = type.indexOf(":") < 0 ? "on" + type : "";
+
+ // Fire event on the current element, then bubble up the DOM tree
+ do {
+ var handle = jQuery._data( cur, "handle" );
+
+ event.currentTarget = cur;
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Trigger an inline bound script
+ if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
+ event.result = false;
+ event.preventDefault();
+ }
+
+ // Bubble up to document, then to window
+ cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
+ } while ( cur && !event.isPropagationStopped() );
+
+ // If nobody prevented the default action, do it now
+ if ( !event.isDefaultPrevented() ) {
+ var old,
+ special = jQuery.event.special[ type ] || {};
+
+ if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction)() check here because IE6/7 fails that test.
+ // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
+ try {
+ if ( ontype && elem[ type ] ) {
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ }
+ } catch ( ieError ) {}
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+
+ jQuery.event.triggered = undefined;
+ }
+ }
+
+ return event.result;
+ },
+
+ handle: function( event ) {
+ event = jQuery.event.fix( event || window.event );
+ // Snapshot the handlers list since a called handler may add/remove events.
+ var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0),
+ run_all = !event.exclusive && !event.namespace,
+ args = Array.prototype.slice.call( arguments, 0 );
+
+ // Use the fix-ed Event rather than the (read-only) native event
+ args[0] = event;
+ event.currentTarget = this;
+
+ for ( var j = 0, l = handlers.length; j < l; j++ ) {
+ var handleObj = handlers[ j ];
+
+ // Triggered event must 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event.
+ if ( run_all || event.namespace_re.test( handleObj.namespace ) ) {
+ // Pass in a reference to the handler function itself
+ // So that we can later remove it
+ event.handler = handleObj.handler;
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ var ret = handleObj.handler.apply( this, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+ return event.result;
+ },
+
+ props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // store a copy of the original event object
+ // and "clone" to set read-only properties
+ var originalEvent = event;
+ event = jQuery.Event( originalEvent );
+
+ for ( var i = this.props.length, prop; i; ) {
+ prop = this.props[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary
+ if ( !event.target ) {
+ // Fixes #1925 where srcElement might not be defined either
+ event.target = event.srcElement || document;
+ }
+
+ // check if target is a textnode (safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && event.fromElement ) {
+ event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && event.clientX != null ) {
+ var eventDocument = event.target.ownerDocument || document,
+ doc = eventDocument.documentElement,
+ body = eventDocument.body;
+
+ event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+ event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add which for key events
+ if ( event.which == null && (event.charCode != null || event.keyCode != null) ) {
+ event.which = event.charCode != null ? event.charCode : event.keyCode;
+ }
+
+ // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+ if ( !event.metaKey && event.ctrlKey ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && event.button !== undefined ) {
+ event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+ }
+
+ return event;
+ },
+
+ // Deprecated, use jQuery.guid instead
+ guid: 1E8,
+
+ // Deprecated, use jQuery.proxy instead
+ proxy: jQuery.proxy,
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady,
+ teardown: jQuery.noop
+ },
+
+ live: {
+ add: function( handleObj ) {
+ jQuery.event.add( this,
+ liveConvert( handleObj.origType, handleObj.selector ),
+ jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
+ },
+
+ remove: function( handleObj ) {
+ jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );
+ }
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ }
+};
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !this.preventDefault ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // timeStamp is buggy for some events on Firefox(#3843)
+ // So we won't rely on the native value
+ this.timeStamp = jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+ // Check if mouse(over|out) are still within the same parent element
+ var parent = event.relatedTarget;
+
+ // set the correct event type
+ event.type = event.data;
+
+ // Firefox sometimes assigns relatedTarget a XUL element
+ // which we cannot access the parentNode property of
+ try {
+
+ // Chrome does something similar, the parentNode property
+ // can be accessed but is null.
+ if ( parent && parent !== document && !parent.parentNode ) {
+ return;
+ }
+
+ // Traverse up the tree
+ while ( parent && parent !== this ) {
+ parent = parent.parentNode;
+ }
+
+ if ( parent !== this ) {
+ // handle event if we actually just moused on to a non sub-element
+ jQuery.event.handle.apply( this, arguments );
+ }
+
+ // assuming we've left the element since we most likely mousedover a xul element
+ } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+ event.type = event.data;
+ jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ setup: function( data ) {
+ jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+ },
+ teardown: function( data ) {
+ jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+ }
+ };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function( data, namespaces ) {
+ if ( !jQuery.nodeName( this, "form" ) ) {
+ jQuery.event.add(this, "click.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+ var elem = e.target,
+ type = elem.type;
+
+ if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+ trigger( "submit", this, arguments );
+ }
+ });
+
+ } else {
+ return false;
+ }
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialSubmit" );
+ }
+ };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+ var changeFilters,
+
+ getVal = function( elem ) {
+ var type = elem.type, val = elem.value;
+
+ if ( type === "radio" || type === "checkbox" ) {
+ val = elem.checked;
+
+ } else if ( type === "select-multiple" ) {
+ val = elem.selectedIndex > -1 ?
+ jQuery.map( elem.options, function( elem ) {
+ return elem.selected;
+ }).join("-") :
+ "";
+
+ } else if ( jQuery.nodeName( elem, "select" ) ) {
+ val = elem.selectedIndex;
+ }
+
+ return val;
+ },
+
+ testChange = function testChange( e ) {
+ var elem = e.target, data, val;
+
+ if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {
+ return;
+ }
+
+ data = jQuery._data( elem, "_change_data" );
+ val = getVal(elem);
+
+ // the current data will be also retrieved by beforeactivate
+ if ( e.type !== "focusout" || elem.type !== "radio" ) {
+ jQuery._data( elem, "_change_data", val );
+ }
+
+ if ( data === undefined || val === data ) {
+ return;
+ }
+
+ if ( data != null || val ) {
+ e.type = "change";
+ e.liveFired = undefined;
+ jQuery.event.trigger( e, arguments[1], elem );
+ }
+ };
+
+ jQuery.event.special.change = {
+ filters: {
+ focusout: testChange,
+
+ beforedeactivate: testChange,
+
+ click: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Change has to be called before submit
+ // Keydown will be called before keypress, which is used in submit-event delegation
+ keydown: function( e ) {
+ var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
+
+ if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
+ (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+ type === "select-multiple" ) {
+ testChange.call( this, e );
+ }
+ },
+
+ // Beforeactivate happens also before the previous element is blurred
+ // with this event you can't trigger a change event, but you can store
+ // information
+ beforeactivate: function( e ) {
+ var elem = e.target;
+ jQuery._data( elem, "_change_data", getVal(elem) );
+ }
+ },
+
+ setup: function( data, namespaces ) {
+ if ( this.type === "file" ) {
+ return false;
+ }
+
+ for ( var type in changeFilters ) {
+ jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+ }
+
+ return rformElems.test( this.nodeName );
+ },
+
+ teardown: function( namespaces ) {
+ jQuery.event.remove( this, ".specialChange" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+
+ changeFilters = jQuery.event.special.change.filters;
+
+ // Handle when the input is .focus()'d
+ changeFilters.focus = changeFilters.beforeactivate;
+}
+
+function trigger( type, elem, args ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ // Don't pass args or remember liveFired; they apply to the donor event.
+ var event = jQuery.extend( {}, args[ 0 ] );
+ event.type = type;
+ event.originalEvent = {};
+ event.liveFired = undefined;
+ jQuery.event.handle.call( elem, event );
+ if ( event.isDefaultPrevented() ) {
+ args[ 0 ].preventDefault();
+ }
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0;
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+
+ function handler( donor ) {
+ // Donor event is always a native one; fix it and switch its type.
+ // Let focusin/out handler cancel the donor focus/blur event.
+ var e = jQuery.event.fix( donor );
+ e.type = fix;
+ e.originalEvent = {};
+ jQuery.event.trigger( e, null, e.target );
+ if ( e.isDefaultPrevented() ) {
+ donor.preventDefault();
+ }
+ }
+ });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+ jQuery.fn[ name ] = function( type, data, fn ) {
+ var handler;
+
+ // Handle object literals
+ if ( typeof type === "object" ) {
+ for ( var key in type ) {
+ this[ name ](key, data, type[key], fn);
+ }
+ return this;
+ }
+
+ if ( arguments.length === 2 || data === false ) {
+ fn = data;
+ data = undefined;
+ }
+
+ if ( name === "one" ) {
+ handler = function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ };
+ handler.guid = fn.guid || jQuery.guid++;
+ } else {
+ handler = fn;
+ }
+
+ if ( type === "unload" && name !== "one" ) {
+ this.one( type, data, fn );
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.add( this[i], type, handler, data );
+ }
+ }
+
+ return this;
+ };
+});
+
+jQuery.fn.extend({
+ unbind: function( type, fn ) {
+ // Handle object literals
+ if ( typeof type === "object" && !type.preventDefault ) {
+ for ( var key in type ) {
+ this.unbind(key, type[key]);
+ }
+
+ } else {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ jQuery.event.remove( this[i], type, fn );
+ }
+ }
+
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.live( types, data, fn, selector );
+ },
+
+ undelegate: function( selector, types, fn ) {
+ if ( arguments.length === 0 ) {
+ return this.unbind( "live" );
+
+ } else {
+ return this.die( types, null, fn, selector );
+ }
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+var liveMap = {
+ focus: "focusin",
+ blur: "focusout",
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+ jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+ var type, i = 0, match, namespaces, preType,
+ selector = origSelector || this.selector,
+ context = origSelector ? this : jQuery( this.context );
+
+ if ( typeof types === "object" && !types.preventDefault ) {
+ for ( var key in types ) {
+ context[ name ]( key, data, types[key], selector );
+ }
+
+ return this;
+ }
+
+ if ( name === "die" && !types &&
+ origSelector && origSelector.charAt(0) === "." ) {
+
+ context.unbind( origSelector );
+
+ return this;
+ }
+
+ if ( data === false || jQuery.isFunction( data ) ) {
+ fn = data || returnFalse;
+ data = undefined;
+ }
+
+ types = (types || "").split(" ");
+
+ while ( (type = types[ i++ ]) != null ) {
+ match = rnamespaces.exec( type );
+ namespaces = "";
+
+ if ( match ) {
+ namespaces = match[0];
+ type = type.replace( rnamespaces, "" );
+ }
+
+ if ( type === "hover" ) {
+ types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+ continue;
+ }
+
+ preType = type;
+
+ if ( liveMap[ type ] ) {
+ types.push( liveMap[ type ] + namespaces );
+ type = type + namespaces;
+
+ } else {
+ type = (liveMap[ type ] || type) + namespaces;
+ }
+
+ if ( name === "live" ) {
+ // bind live handler
+ for ( var j = 0, l = context.length; j < l; j++ ) {
+ jQuery.event.add( context[j], "live." + liveConvert( type, selector ),
+ { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+ }
+
+ } else {
+ // unbind live handler
+ context.unbind( "live." + liveConvert( type, selector ), fn );
+ }
+ }
+
+ return this;
+ };
+});
+
+function liveHandler( event ) {
+ var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
+ elems = [],
+ selectors = [],
+ events = jQuery._data( this, "events" );
+
+ // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)
+ if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
+ return;
+ }
+
+ if ( event.namespace ) {
+ namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
+ }
+
+ event.liveFired = this;
+
+ var live = events.live.slice(0);
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+ selectors.push( handleObj.selector );
+
+ } else {
+ live.splice( j--, 1 );
+ }
+ }
+
+ match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+ for ( i = 0, l = match.length; i < l; i++ ) {
+ close = match[i];
+
+ for ( j = 0; j < live.length; j++ ) {
+ handleObj = live[j];
+
+ if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {
+ elem = close.elem;
+ related = null;
+
+ // Those two events require additional checking
+ if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+ event.type = handleObj.preType;
+ related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+
+ // Make sure not to accidentally match a child element with the same selector
+ if ( related && jQuery.contains( elem, related ) ) {
+ related = elem;
+ }
+ }
+
+ if ( !related || related !== elem ) {
+ elems.push({ elem: elem, handleObj: handleObj, level: close.level });
+ }
+ }
+ }
+ }
+
+ for ( i = 0, l = elems.length; i < l; i++ ) {
+ match = elems[i];
+
+ if ( maxLevel && match.level > maxLevel ) {
+ break;
+ }
+
+ event.currentTarget = match.elem;
+ event.data = match.handleObj.data;
+ event.handleObj = match.handleObj;
+
+ ret = match.handleObj.origHandler.apply( match.elem, arguments );
+
+ if ( ret === false || event.isPropagationStopped() ) {
+ maxLevel = match.level;
+
+ if ( ret === false ) {
+ stop = false;
+ }
+ if ( event.isImmediatePropagationStopped() ) {
+ break;
+ }
+ }
+ }
+
+ return stop;
+}
+
+function liveConvert( type, selector ) {
+ return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.bind( name, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+ var match,
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ var left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( var type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ var found, item,
+ filter = Expr.filter[ type ],
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ var pass = not ^ !!found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return "text" === elem.getAttribute( 'type' );
+ },
+ radio: function( elem ) {
+ return "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return "file" === elem.type;
+ },
+ password: function( elem ) {
+ return "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ return "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ return "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ var first = match[2],
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ var doneName = match[0],
+ parent = elem.parentNode;
+
+ if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+ var count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent.sizcache = doneName;
+ }
+
+ var diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+Sizzle.getText = function( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += Sizzle.getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector,
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ if ( matches ) {
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ return matches.call( node, expr );
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem.sizcache === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem.sizcache = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.POS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && ( typeof selector === "string" ?
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array
+ if ( jQuery.isArray( selectors ) ) {
+ var match, selector,
+ matches = {},
+ level = 1;
+
+ if ( cur && selectors.length ) {
+ for ( i = 0, l = selectors.length; i < l; i++ ) {
+ selector = selectors[i];
+
+ if ( !matches[ selector ] ) {
+ matches[ selector ] = POS.test( selector ) ?
+ jQuery( selector, context || this.context ) :
+ selector;
+ }
+ }
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( selector in matches ) {
+ match = matches[ selector ];
+
+ if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
+ ret.push({ selector: selector, elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+ if ( !elem || typeof elem === "string" ) {
+ return jQuery.inArray( this[0],
+ // If it receives a string, the selector is used
+ // If it receives nothing, the siblings are used
+ elem ? jQuery( elem ) : this.parent().children() );
+ }
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until ),
+ // The variable 'args' was introduced in
+ // https://github.com/jquery/jquery/commit/52a0238
+ // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
+ // http://code.google.com/p/v8/issues/detail?id=1050
+ args = slice.call(arguments);
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, args.join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return (elem === qualifier) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+ });
+}
+
+
+
+
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery( this );
+
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ return this.each(function() {
+ jQuery( this ).wrapAll( html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery(arguments[0]);
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery(arguments[0]).toArray() );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnocache.test( value ) &&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+ value = value.replace(rxhtmlTag, "<$1></$2>");
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery( this );
+
+ self.html( value.call(this, i, self.html()) );
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || (l > 1 && i < lastIndex) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var internalKey = jQuery.expando,
+ oldData = jQuery.data( src ),
+ curData = jQuery.data( dest, oldData );
+
+ // Switch to use the internal data object, if it exists, for the next
+ // stage of data copying
+ if ( (oldData = oldData[ internalKey ]) ) {
+ var events = oldData.events;
+ curData = curData[ internalKey ] = jQuery.extend({}, oldData);
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( var type in events ) {
+ for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." : "" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );
+ }
+ }
+ }
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults, doc;
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
+
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Chrome and Firefox seem to allow this to occur and will throw exception
+ // Fixes #8950
+ if ( !doc.createDocumentFragment ) {
+ doc = document;
+ }
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+ args[0].charAt(0) === "<" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ args[0] ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( "getElementsByTagName" in elem ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( "querySelectorAll" in elem ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( elem.getElementsByTagName ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var clone = elem.cloneNode(true),
+ srcElements,
+ destElements,
+ i;
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName
+ // instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType;
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ var ret = [], j;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ), checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ] && cache[ id ][ internalKey ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ rdashAlpha = /-([a-z])/ig,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+ rrelNum = /^[+\-]=/,
+ rrelNumFilter = /[^+\-\.\de]+/g,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+ curCSS,
+
+ getComputedStyle,
+ currentStyle,
+
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn.css = function( name, value ) {
+ // Setting 'undefined' is a no-op
+ if ( arguments.length === 2 && value === undefined ) {
+ return this;
+ }
+
+ return jQuery.access( this, name, value, true, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ });
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity", "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "zIndex": true,
+ "fontWeight": true,
+ "opacity": true,
+ "zoom": true,
+ "lineHeight": true,
+ "widows": true,
+ "orphans": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( type === "number" && isNaN( value ) || value == null ) {
+ return;
+ }
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && rrelNum.test( value ) ) {
+ value = +value.replace( rrelNumFilter, "" ) + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ },
+
+ camelCase: function( string ) {
+ return string.replace( rdashAlpha, fcamelCase );
+ }
+});
+
+// DEPRECATED, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+jQuery.each(["height", "width"], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ var val;
+
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ val = getWH( elem, name, extra );
+
+ } else {
+ jQuery.swap( elem, cssShow, function() {
+ val = getWH( elem, name, extra );
+ });
+ }
+
+ if ( val <= 0 ) {
+ val = curCSS( elem, name, name );
+
+ if ( val === "0px" && currentStyle ) {
+ val = currentStyle( elem, name, name );
+ }
+
+ if ( val != null ) {
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+ }
+
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+
+ // Should return "auto" instead of 0, use 0 for
+ // temporary backwards-compat
+ return val === "" || val === "auto" ? "0px" : val;
+ }
+
+ return typeof val === "string" ? val : val + "px";
+ }
+ },
+
+ set: function( elem, value ) {
+ if ( rnumpx.test( value ) ) {
+ // ignore negative width and height values #1599
+ value = parseFloat(value);
+
+ if ( value >= 0 ) {
+ return value + "px";
+ }
+
+ } else {
+ return value;
+ }
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle;
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ var opacity = jQuery.isNaN( value ) ?
+ "" :
+ "alpha(opacity=" + value * 100 + ")",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ var ret;
+ jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ ret = curCSS( elem, "margin-right", "marginRight" );
+ } else {
+ ret = elem.style.marginRight;
+ }
+ });
+ return ret;
+ }
+ };
+ }
+});
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( !(defaultView = elem.ownerDocument.defaultView) ) {
+ return undefined;
+ }
+
+ if ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
+ style = elem.style;
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+ // Remember the original values
+ left = style.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : (ret || 0);
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWH( elem, name, extra ) {
+ var which = name === "width" ? cssWidth : cssHeight,
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+ if ( extra === "border" ) {
+ return val;
+ }
+
+ jQuery.each( which, function() {
+ if ( !extra ) {
+ val -= parseFloat(jQuery.css( elem, "padding" + this )) || 0;
+ }
+
+ if ( extra === "margin" ) {
+ val += parseFloat(jQuery.css( elem, "margin" + this )) || 0;
+
+ } else {
+ val -= parseFloat(jQuery.css( elem, "border" + this + "Width" )) || 0;
+ }
+ });
+
+ return val;
+}
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts;
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for(; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for(; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.bind( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function ( target, settings ) {
+ if ( !settings ) {
+ // Only one parameter, we extend ajaxSettings
+ settings = target;
+ target = jQuery.extend( true, jQuery.ajaxSettings, settings );
+ } else {
+ // target was provided, we extend into it
+ jQuery.extend( true, target, jQuery.ajaxSettings, settings );
+ }
+ // Flatten fields we don't want deep extended
+ for( var field in { context: 1, url: 1 } ) {
+ if ( field in settings ) {
+ target[ field ] = settings[ field ];
+ } else if( field in jQuery.ajaxSettings ) {
+ target[ field ] = jQuery.ajaxSettings[ field ];
+ }
+ }
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": "*/*"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery._Deferred(),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, statusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = statusText;
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.done;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefiler, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( status < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ jQuery.error( e );
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj === "object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for( key in s.converters ) {
+ if( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = s.contentType === "application/x-www-form-urlencoded" &&
+ ( typeof s.data === "string" );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+ responses.text = xhr.responseText;
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow,
+ requestAnimationFrame = window.webkitRequestAnimationFrame ||
+ window.mozRequestAnimationFrame ||
+ window.oRequestAnimationFrame;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
+ jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[i];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data(elem, "olddisplay") || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ if ( this[i].style ) {
+ var display = jQuery.css( this[i], "display" );
+
+ if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
+ jQuery._data( this[i], "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed(speed, easing, callback);
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ return this[ optall.queue === false ? "each" : "queue" ](function() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p,
+ display, e,
+ parts, start, end, unit;
+
+ // will store per property easing and be used to determine when an animation is complete
+ opt.animatedProperties = {};
+
+ for ( p in prop ) {
+
+ // property name normalization
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ val = prop[ name ];
+
+ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show" && !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height
+ // animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+ if ( !jQuery.support.inlineBlockNeedsLayout ) {
+ this.style.display = "inline-block";
+
+ } else {
+ display = defaultDisplay( this.nodeName );
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( display === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.display = "inline";
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test(val) ) {
+ e[ val === "toggle" ? hidden ? "show" : "hide" : val ]();
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ((end || 1) / e.cur()) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ });
+ },
+
+ stop: function( clearQueue, gotoEnd ) {
+ if ( clearQueue ) {
+ this.queue([]);
+ }
+
+ this.each(function() {
+ var timers = jQuery.timers,
+ i = timers.length;
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+ while ( i-- ) {
+ if ( timers[i].elem === this ) {
+ if (gotoEnd) {
+ // force the next step to be the last
+ timers[i](true);
+ }
+
+ timers.splice(i, 1);
+ }
+ }
+ });
+
+ // start the next in the queue if the last step wasn't forced
+ if ( !gotoEnd ) {
+ this.dequeue();
+ }
+
+ return this;
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show", 1),
+ slideUp: genFx("hide", 1),
+ slideToggle: genFx("toggle", 1),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
+
+ // Queueing
+ opt.old = opt.complete;
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue !== false ) {
+ jQuery.dequeue( this );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx,
+ raf;
+
+ this.startTime = fxNow || createFxNow();
+ this.start = from;
+ this.end = to;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+ this.now = this.start;
+ this.pos = this.state = 0;
+
+ function t( gotoEnd ) {
+ return self.step(gotoEnd);
+ }
+
+ t.elem = this.elem;
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ // Use requestAnimationFrame instead of setInterval if available
+ if ( requestAnimationFrame ) {
+ timerId = 1;
+ raf = function() {
+ // When timerId gets set to null at any point, this stops
+ if ( timerId ) {
+ requestAnimationFrame( raf );
+ fx.tick();
+ }
+ };
+ requestAnimationFrame( raf );
+ } else {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any
+ // flash of content
+ this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom(this.cur(), 0);
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options,
+ i, n;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( i in options.animatedProperties ) {
+ if ( options.animatedProperties[i] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function (index, value) {
+ elem.style[ "overflow" + value ] = options.overflow[index];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery(elem).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( var p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[p] );
+ }
+ }
+
+ // Execute the complete function
+ options.complete.call( elem );
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ((this.end - this.start) * this.pos);
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ for ( var timers = jQuery.timers, i = 0 ; i < timers.length ; ++i ) {
+ if ( !timers[i]() ) {
+ timers.splice(i--, 1);
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( "body" ),
+ display = elem.css( "display" );
+
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ document.body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake html
+ // document to it, Webkit & Firefox won't allow reusing the iframe document
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( "<!doctype><html><body></body></html>" );
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+
+ document.body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0], box;
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ var doc = elem.ownerDocument,
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow(doc),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ jQuery.offset.initialize();
+
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ doc = elem.ownerDocument,
+ docElem = doc.documentElement,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+ initialize: function() {
+ var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, "marginTop") ) || 0,
+ html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+ jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+ container.innerHTML = html;
+ body.insertBefore( container, body.firstChild );
+ innerDiv = container.firstChild;
+ checkDiv = innerDiv.firstChild;
+ td = innerDiv.nextSibling.firstChild.firstChild;
+
+ this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+ this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+ checkDiv.style.position = "fixed";
+ checkDiv.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+ checkDiv.style.position = checkDiv.style.top = "";
+
+ innerDiv.style.overflow = "hidden";
+ innerDiv.style.position = "relative";
+
+ this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+ this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+ body.removeChild( container );
+ jQuery.offset.initialize = jQuery.noop;
+ },
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ jQuery.offset.initialize();
+
+ if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = (position === "absolute" || position === "fixed") && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if (options.top != null) {
+ props.top = (options.top - curOffset.top) + curTop;
+ }
+ if (options.left != null) {
+ props.left = (options.left - curOffset.left) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function( val ) {
+ var elem, win;
+
+ if ( val === undefined ) {
+ elem = this[ 0 ];
+
+ if ( !elem ) {
+ return null;
+ }
+
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery( win ).scrollLeft(),
+ i ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn["inner" + name] = function() {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, "padding" ) ) :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn["outer" + name] = function( margin ) {
+ return this[0] ?
+ parseFloat( jQuery.css( this[0], type, margin ? "margin" : "border" ) ) :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ if ( jQuery.isWindow( elem ) ) {
+ // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+ var docElemProp = elem.document.documentElement[ "client" + name ];
+ return elem.document.compatMode === "CSS1Compat" && docElemProp ||
+ elem.document.body[ "client" + name ] || docElemProp;
+
+ // Get document width or height
+ } else if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ return Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" + name],
+ elem.body["offset" + name], elem.documentElement["offset" + name]
+ );
+
+ // Get or set width or height on the element
+ } else if ( size === undefined ) {
+ var orig = jQuery.css( elem, type ),
+ ret = parseFloat( orig );
+
+ return jQuery.isNaN( ret ) ? orig : ret;
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ } else {
+ return this.css( type, typeof size === "string" ? size : size + "px" );
+ }
+ };
+
+});
+
+
+window.jQuery = window.$ = jQuery;
+})(window);
diff --git a/res/pages/js/overlib.js b/res/pages/js/overlib.js
new file mode 100644
index 00000000..e900ab4a
--- /dev/null
+++ b/res/pages/js/overlib.js
@@ -0,0 +1,1491 @@
+//\/////
+//\ overLIB 4.21 - You may not remove or change this notice.
+//\ Copyright Erik Bosrup 1998-2004. All rights reserved.
+//\
+//\ Contributors are listed on the homepage.
+//\ This file might be old, always check for the latest version at:
+//\ http://www.bosrup.com/web/overlib/
+//\
+//\ Please read the license agreement (available through the link above)
+//\ before using overLIB. Direct any licensing questions to erik@bosrup.com.
+//\
+//\ Do not sell this as your own work or remove this copyright notice.
+//\ For full details on copying or changing this script please read the
+//\ license agreement at the link above. Please give credit on sites that
+//\ use overLIB and submit changes of the script so other people can use
+//\ them as well.
+// $Revision: 1.119 $ $Date: 2005/07/02 23:41:44 $
+//\/////
+//\mini
+
+////////
+// PRE-INIT
+// Ignore these lines, configuration is below.
+////////
+var olLoaded = 0;var pmStart = 10000000; var pmUpper = 10001000; var pmCount = pmStart+1; var pmt=''; var pms = new Array(); var olInfo = new Info('4.21', 1);
+var FREPLACE = 0; var FBEFORE = 1; var FAFTER = 2; var FALTERNATE = 3; var FCHAIN=4;
+var olHideForm=0; // parameter for hiding SELECT and ActiveX elements in IE5.5+
+var olHautoFlag = 0; // flags for over-riding VAUTO and HAUTO if corresponding
+var olVautoFlag = 0; // positioning commands are used on the command line
+var hookPts = new Array(), postParse = new Array(), cmdLine = new Array(), runTime = new Array();
+// for plugins
+registerCommands('donothing,inarray,caparray,sticky,background,noclose,caption,left,right,center,offsetx,offsety,fgcolor,bgcolor,textcolor,capcolor,closecolor,width,border,cellpad,status,autostatus,autostatuscap,height,closetext,snapx,snapy,fixx,fixy,relx,rely,fgbackground,bgbackground,padx,pady,fullhtml,above,below,capicon,textfont,captionfont,closefont,textsize,captionsize,closesize,timeout,function,delay,hauto,vauto,closeclick,wrap,followmouse,mouseoff,closetitle,cssoff,compatmode,cssclass,fgclass,bgclass,textfontclass,captionfontclass,closefontclass');
+
+////////
+// DEFAULT CONFIGURATION
+// Settings you want everywhere are set here. All of this can also be
+// changed on your html page or through an overLIB call.
+////////
+if (typeof ol_fgcolor=='undefined') var ol_fgcolor="#CCCCFF";
+if (typeof ol_bgcolor=='undefined') var ol_bgcolor="#333399";
+if (typeof ol_textcolor=='undefined') var ol_textcolor="#000000";
+if (typeof ol_capcolor=='undefined') var ol_capcolor="#FFFFFF";
+if (typeof ol_closecolor=='undefined') var ol_closecolor="#9999FF";
+if (typeof ol_textfont=='undefined') var ol_textfont="Verdana,Arial,Helvetica";
+if (typeof ol_captionfont=='undefined') var ol_captionfont="Verdana,Arial,Helvetica";
+if (typeof ol_closefont=='undefined') var ol_closefont="Verdana,Arial,Helvetica";
+if (typeof ol_textsize=='undefined') var ol_textsize="1";
+if (typeof ol_captionsize=='undefined') var ol_captionsize="1";
+if (typeof ol_closesize=='undefined') var ol_closesize="1";
+if (typeof ol_width=='undefined') var ol_width="200";
+if (typeof ol_border=='undefined') var ol_border="1";
+if (typeof ol_cellpad=='undefined') var ol_cellpad=2;
+if (typeof ol_offsetx=='undefined') var ol_offsetx=10;
+if (typeof ol_offsety=='undefined') var ol_offsety=10;
+if (typeof ol_text=='undefined') var ol_text="Default Text";
+if (typeof ol_cap=='undefined') var ol_cap="";
+if (typeof ol_sticky=='undefined') var ol_sticky=0;
+if (typeof ol_background=='undefined') var ol_background="";
+if (typeof ol_close=='undefined') var ol_close="Close";
+if (typeof ol_hpos=='undefined') var ol_hpos=RIGHT;
+if (typeof ol_status=='undefined') var ol_status="";
+if (typeof ol_autostatus=='undefined') var ol_autostatus=0;
+if (typeof ol_height=='undefined') var ol_height=-1;
+if (typeof ol_snapx=='undefined') var ol_snapx=0;
+if (typeof ol_snapy=='undefined') var ol_snapy=0;
+if (typeof ol_fixx=='undefined') var ol_fixx=-1;
+if (typeof ol_fixy=='undefined') var ol_fixy=-1;
+if (typeof ol_relx=='undefined') var ol_relx=null;
+if (typeof ol_rely=='undefined') var ol_rely=null;
+if (typeof ol_fgbackground=='undefined') var ol_fgbackground="";
+if (typeof ol_bgbackground=='undefined') var ol_bgbackground="";
+if (typeof ol_padxl=='undefined') var ol_padxl=1;
+if (typeof ol_padxr=='undefined') var ol_padxr=1;
+if (typeof ol_padyt=='undefined') var ol_padyt=1;
+if (typeof ol_padyb=='undefined') var ol_padyb=1;
+if (typeof ol_fullhtml=='undefined') var ol_fullhtml=0;
+if (typeof ol_vpos=='undefined') var ol_vpos=BELOW;
+if (typeof ol_aboveheight=='undefined') var ol_aboveheight=0;
+if (typeof ol_capicon=='undefined') var ol_capicon="";
+if (typeof ol_frame=='undefined') var ol_frame=self;
+if (typeof ol_timeout=='undefined') var ol_timeout=0;
+if (typeof ol_function=='undefined') var ol_function=null;
+if (typeof ol_delay=='undefined') var ol_delay=0;
+if (typeof ol_hauto=='undefined') var ol_hauto=0;
+if (typeof ol_vauto=='undefined') var ol_vauto=0;
+if (typeof ol_closeclick=='undefined') var ol_closeclick=0;
+if (typeof ol_wrap=='undefined') var ol_wrap=0;
+if (typeof ol_followmouse=='undefined') var ol_followmouse=1;
+if (typeof ol_mouseoff=='undefined') var ol_mouseoff=0;
+if (typeof ol_closetitle=='undefined') var ol_closetitle='Close';
+if (typeof ol_compatmode=='undefined') var ol_compatmode=0;
+if (typeof ol_css=='undefined') var ol_css=CSSOFF;
+if (typeof ol_fgclass=='undefined') var ol_fgclass="";
+if (typeof ol_bgclass=='undefined') var ol_bgclass="";
+if (typeof ol_textfontclass=='undefined') var ol_textfontclass="";
+if (typeof ol_captionfontclass=='undefined') var ol_captionfontclass="";
+if (typeof ol_closefontclass=='undefined') var ol_closefontclass="";
+
+////////
+// ARRAY CONFIGURATION
+////////
+
+// You can use these arrays to store popup text here instead of in the html.
+if (typeof ol_texts=='undefined') var ol_texts = new Array("Text 0", "Text 1");
+if (typeof ol_caps=='undefined') var ol_caps = new Array("Caption 0", "Caption 1");
+
+////////
+// END OF CONFIGURATION
+// Don't change anything below this line, all configuration is above.
+////////
+
+
+
+
+
+////////
+// INIT
+////////
+// Runtime variables init. Don't change for config!
+var o3_text="";
+var o3_cap="";
+var o3_sticky=0;
+var o3_background="";
+var o3_close="Close";
+var o3_hpos=RIGHT;
+var o3_offsetx=2;
+var o3_offsety=2;
+var o3_fgcolor="";
+var o3_bgcolor="";
+var o3_textcolor="";
+var o3_capcolor="";
+var o3_closecolor="";
+var o3_width=100;
+var o3_border=1;
+var o3_cellpad=2;
+var o3_status="";
+var o3_autostatus=0;
+var o3_height=-1;
+var o3_snapx=0;
+var o3_snapy=0;
+var o3_fixx=-1;
+var o3_fixy=-1;
+var o3_relx=null;
+var o3_rely=null;
+var o3_fgbackground="";
+var o3_bgbackground="";
+var o3_padxl=0;
+var o3_padxr=0;
+var o3_padyt=0;
+var o3_padyb=0;
+var o3_fullhtml=0;
+var o3_vpos=BELOW;
+var o3_aboveheight=0;
+var o3_capicon="";
+var o3_textfont="Verdana,Arial,Helvetica";
+var o3_captionfont="Verdana,Arial,Helvetica";
+var o3_closefont="Verdana,Arial,Helvetica";
+var o3_textsize="1";
+var o3_captionsize="1";
+var o3_closesize="1";
+var o3_frame=self;
+var o3_timeout=0;
+var o3_timerid=0;
+var o3_allowmove=0;
+var o3_function=null;
+var o3_delay=0;
+var o3_delayid=0;
+var o3_hauto=0;
+var o3_vauto=0;
+var o3_closeclick=0;
+var o3_wrap=0;
+var o3_followmouse=1;
+var o3_mouseoff=0;
+var o3_closetitle='';
+var o3_compatmode=0;
+var o3_css=CSSOFF;
+var o3_fgclass="";
+var o3_bgclass="";
+var o3_textfontclass="";
+var o3_captionfontclass="";
+var o3_closefontclass="";
+
+// Display state variables
+var o3_x = 0;
+var o3_y = 0;
+var o3_showingsticky = 0;
+var o3_removecounter = 0;
+
+// Our layer
+var over = null;
+var fnRef, hoveringSwitch = false;
+var olHideDelay;
+
+// Decide browser version
+var isMac = (navigator.userAgent.indexOf("Mac") != -1);
+var olOp = (navigator.userAgent.toLowerCase().indexOf('opera') > -1 && document.createTextNode); // Opera 7
+var olNs4 = (navigator.appName=='Netscape' && parseInt(navigator.appVersion) == 4);
+var olNs6 = (document.getElementById) ? true : false;
+var olKq = (olNs6 && /konqueror/i.test(navigator.userAgent));
+var olIe4 = (document.all) ? true : false;
+var olIe5 = false;
+var olIe55 = false; // Added additional variable to identify IE5.5+
+var docRoot = 'document.body';
+
+// Resize fix for NS4.x to keep track of layer
+if (olNs4) {
+ var oW = window.innerWidth;
+ var oH = window.innerHeight;
+ window.onresize = function() { if (oW != window.innerWidth || oH != window.innerHeight) location.reload(); }
+}
+
+// Microsoft Stupidity Check(tm).
+if (olIe4) {
+ var agent = navigator.userAgent;
+ if (/MSIE/.test(agent)) {
+ var versNum = parseFloat(agent.match(/MSIE[ ](\d\.\d+)\.*/i)[1]);
+ if (versNum >= 5){
+ olIe5=true;
+ olIe55=(versNum>=5.5&&!olOp) ? true : false;
+ if (olNs6) olNs6=false;
+ }
+ }
+ if (olNs6) olIe4 = false;
+}
+
+// Check for compatability mode.
+if (document.compatMode && document.compatMode == 'CSS1Compat') {
+ docRoot= ((olIe4 && !olOp) ? 'document.documentElement' : docRoot);
+}
+
+// Add window onload handlers to indicate when all modules have been loaded
+// For Netscape 6+ and Mozilla, uses addEventListener method on the window object
+// For IE it uses the attachEvent method of the window object and for Netscape 4.x
+// it sets the window.onload handler to the OLonload_handler function for Bubbling
+if(window.addEventListener) window.addEventListener("load",OLonLoad_handler,false);
+else if (window.attachEvent) window.attachEvent("onload",OLonLoad_handler);
+
+var capExtent;
+
+////////
+// PUBLIC FUNCTIONS
+////////
+
+// overlib(arg0,...,argN)
+// Loads parameters into global runtime variables.
+function overlib() {
+ if (!olLoaded || isExclusive(overlib.arguments)) return true;
+ if (olCheckMouseCapture) olMouseCapture();
+ if (over) {
+ over = (typeof over.id != 'string') ? o3_frame.document.all['overDiv'] : over;
+ cClick();
+ }
+
+ // Load defaults to runtime.
+ olHideDelay=0;
+ o3_text=ol_text;
+ o3_cap=ol_cap;
+ o3_sticky=ol_sticky;
+ o3_background=ol_background;
+ o3_close=ol_close;
+ o3_hpos=ol_hpos;
+ o3_offsetx=ol_offsetx;
+ o3_offsety=ol_offsety;
+ o3_fgcolor=ol_fgcolor;
+ o3_bgcolor=ol_bgcolor;
+ o3_textcolor=ol_textcolor;
+ o3_capcolor=ol_capcolor;
+ o3_closecolor=ol_closecolor;
+ o3_width=ol_width;
+ o3_border=ol_border;
+ o3_cellpad=ol_cellpad;
+ o3_status=ol_status;
+ o3_autostatus=ol_autostatus;
+ o3_height=ol_height;
+ o3_snapx=ol_snapx;
+ o3_snapy=ol_snapy;
+ o3_fixx=ol_fixx;
+ o3_fixy=ol_fixy;
+ o3_relx=ol_relx;
+ o3_rely=ol_rely;
+ o3_fgbackground=ol_fgbackground;
+ o3_bgbackground=ol_bgbackground;
+ o3_padxl=ol_padxl;
+ o3_padxr=ol_padxr;
+ o3_padyt=ol_padyt;
+ o3_padyb=ol_padyb;
+ o3_fullhtml=ol_fullhtml;
+ o3_vpos=ol_vpos;
+ o3_aboveheight=ol_aboveheight;
+ o3_capicon=ol_capicon;
+ o3_textfont=ol_textfont;
+ o3_captionfont=ol_captionfont;
+ o3_closefont=ol_closefont;
+ o3_textsize=ol_textsize;
+ o3_captionsize=ol_captionsize;
+ o3_closesize=ol_closesize;
+ o3_timeout=ol_timeout;
+ o3_function=ol_function;
+ o3_delay=ol_delay;
+ o3_hauto=ol_hauto;
+ o3_vauto=ol_vauto;
+ o3_closeclick=ol_closeclick;
+ o3_wrap=ol_wrap;
+ o3_followmouse=ol_followmouse;
+ o3_mouseoff=ol_mouseoff;
+ o3_closetitle=ol_closetitle;
+ o3_css=ol_css;
+ o3_compatmode=ol_compatmode;
+ o3_fgclass=ol_fgclass;
+ o3_bgclass=ol_bgclass;
+ o3_textfontclass=ol_textfontclass;
+ o3_captionfontclass=ol_captionfontclass;
+ o3_closefontclass=ol_closefontclass;
+
+ setRunTimeVariables();
+
+ fnRef = '';
+
+ // Special for frame support, over must be reset...
+ o3_frame = ol_frame;
+
+ if(!(over=createDivContainer())) return false;
+
+ parseTokens('o3_', overlib.arguments);
+ if (!postParseChecks()) return false;
+
+ if (o3_delay == 0) {
+ return runHook("olMain", FREPLACE);
+ } else {
+ o3_delayid = setTimeout("runHook('olMain', FREPLACE)", o3_delay);
+ return false;
+ }
+}
+
+// Clears popups if appropriate
+function nd(time) {
+ if (olLoaded && !isExclusive()) {
+ hideDelay(time); // delay popup close if time specified
+
+ if (o3_removecounter >= 1) { o3_showingsticky = 0 };
+
+ if (o3_showingsticky == 0) {
+ o3_allowmove = 0;
+ if (over != null && o3_timerid == 0) runHook("hideObject", FREPLACE, over);
+ } else {
+ o3_removecounter++;
+ }
+ }
+
+ return true;
+}
+
+// The Close onMouseOver function for stickies
+function cClick() {
+ if (olLoaded) {
+ runHook("hideObject", FREPLACE, over);
+ o3_showingsticky = 0;
+ }
+ return false;
+}
+
+// Method for setting page specific defaults.
+function overlib_pagedefaults() {
+ parseTokens('ol_', overlib_pagedefaults.arguments);
+}
+
+
+////////
+// OVERLIB MAIN FUNCTION
+////////
+
+// This function decides what it is we want to display and how we want it done.
+function olMain() {
+ var layerhtml, styleType;
+ runHook("olMain", FBEFORE);
+
+ if (o3_background!="" || o3_fullhtml) {
+ // Use background instead of box.
+ layerhtml = runHook('ol_content_background', FALTERNATE, o3_css, o3_text, o3_background, o3_fullhtml);
+ } else {
+ // They want a popup box.
+ styleType = (pms[o3_css-1-pmStart] == "cssoff" || pms[o3_css-1-pmStart] == "cssclass");
+
+ // Prepare popup background
+ if (o3_fgbackground != "") o3_fgbackground = "background=\""+o3_fgbackground+"\"";
+ if (o3_bgbackground != "") o3_bgbackground = (styleType ? "background=\""+o3_bgbackground+"\"" : o3_bgbackground);
+
+ // Prepare popup colors
+ if (o3_fgcolor != "") o3_fgcolor = (styleType ? "bgcolor=\""+o3_fgcolor+"\"" : o3_fgcolor);
+ if (o3_bgcolor != "") o3_bgcolor = (styleType ? "bgcolor=\""+o3_bgcolor+"\"" : o3_bgcolor);
+
+ // Prepare popup height
+ if (o3_height > 0) o3_height = (styleType ? "height=\""+o3_height+"\"" : o3_height);
+ else o3_height = "";
+
+ // Decide which kinda box.
+ if (o3_cap=="") {
+ // Plain
+ layerhtml = runHook('ol_content_simple', FALTERNATE, o3_css, o3_text);
+ } else {
+ // With caption
+ if (o3_sticky) {
+ // Show close text
+ layerhtml = runHook('ol_content_caption', FALTERNATE, o3_css, o3_text, o3_cap, o3_close);
+ } else {
+ // No close text
+ layerhtml = runHook('ol_content_caption', FALTERNATE, o3_css, o3_text, o3_cap, "");
+ }
+ }
+ }
+
+ // We want it to stick!
+ if (o3_sticky) {
+ if (o3_timerid > 0) {
+ clearTimeout(o3_timerid);
+ o3_timerid = 0;
+ }
+ o3_showingsticky = 1;
+ o3_removecounter = 0;
+ }
+
+ // Created a separate routine to generate the popup to make it easier
+ // to implement a plugin capability
+ if (!runHook("createPopup", FREPLACE, layerhtml)) return false;
+
+ // Prepare status bar
+ if (o3_autostatus > 0) {
+ o3_status = o3_text;
+ if (o3_autostatus > 1) o3_status = o3_cap;
+ }
+
+ // When placing the layer the first time, even stickies may be moved.
+ o3_allowmove = 0;
+
+ // Initiate a timer for timeout
+ if (o3_timeout > 0) {
+ if (o3_timerid > 0) clearTimeout(o3_timerid);
+ o3_timerid = setTimeout("cClick()", o3_timeout);
+ }
+
+ // Show layer
+ runHook("disp", FREPLACE, o3_status);
+ runHook("olMain", FAFTER);
+
+ return (olOp && event && event.type == 'mouseover' && !o3_status) ? '' : (o3_status != '');
+}
+
+////////
+// LAYER GENERATION FUNCTIONS
+////////
+// These functions just handle popup content with tags that should adhere to the W3C standards specification.
+
+// Makes simple table without caption
+function ol_content_simple(text) {
+ var cpIsMultiple = /,/.test(o3_cellpad);
+ var txt = '<table width="'+o3_width+ '" border="0" cellpadding="'+o3_border+'" cellspacing="0" '+(o3_bgclass ? 'class="'+o3_bgclass+'"' : o3_bgcolor+' '+o3_height)+'><tr><td><table width="100%" border="0" '+((olNs4||!cpIsMultiple) ? 'cellpadding="'+o3_cellpad+'" ' : '')+'cellspacing="0" '+(o3_fgclass ? 'class="'+o3_fgclass+'"' : o3_fgcolor+' '+o3_fgbackground+' '+o3_height)+'><tr><td valign="TOP"'+(o3_textfontclass ? ' class="'+o3_textfontclass+'">' : ((!olNs4&&cpIsMultiple) ? ' style="'+setCellPadStr(o3_cellpad)+'">' : '>'))+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize))+'</td></tr></table></td></tr></table>';
+
+ set_background("");
+ return txt;
+}
+
+// Makes table with caption and optional close link
+function ol_content_caption(text,title,close) {
+ var nameId, txt, cpIsMultiple = /,/.test(o3_cellpad);
+ var closing, closeevent;
+
+ closing = "";
+ closeevent = "onmouseover";
+ if (o3_closeclick == 1) closeevent = (o3_closetitle ? "title='" + o3_closetitle +"'" : "") + " onclick";
+ if (o3_capicon != "") {
+ nameId = ' hspace = \"5\"'+' align = \"middle\" alt = \"\"';
+ if (typeof o3_dragimg != 'undefined' && o3_dragimg) nameId =' hspace=\"5\"'+' name=\"'+o3_dragimg+'\" id=\"'+o3_dragimg+'\" align=\"middle\" alt=\"Drag Enabled\" title=\"Drag Enabled\"';
+ o3_capicon = '<img src=\"'+o3_capicon+'\"'+nameId+' />';
+ }
+
+ if (close != "")
+ closing = '<td '+(!o3_compatmode && o3_closefontclass ? 'class="'+o3_closefontclass : 'align="RIGHT')+'"><a href="javascript:return '+fnRef+'cClick();"'+((o3_compatmode && o3_closefontclass) ? ' class="' + o3_closefontclass + '" ' : ' ')+closeevent+'="return '+fnRef+'cClick();">'+(o3_closefontclass ? '' : wrapStr(0,o3_closesize,'close'))+close+(o3_closefontclass ? '' : wrapStr(1,o3_closesize,'close'))+'</a></td>';
+ txt = '<table width="'+o3_width+ '" border="0" cellpadding="'+o3_border+'" cellspacing="0" '+(o3_bgclass ? 'class="'+o3_bgclass+'"' : o3_bgcolor+' '+o3_bgbackground+' '+o3_height)+'><tr><td><table width="100%" border="0" cellpadding="2" cellspacing="0"><tr><td'+(o3_captionfontclass ? ' class="'+o3_captionfontclass+'">' : '>')+(o3_captionfontclass ? '' : '<b>'+wrapStr(0,o3_captionsize,'caption'))+o3_capicon+title+(o3_captionfontclass ? '' : wrapStr(1,o3_captionsize)+'</b>')+'</td>'+closing+'</tr></table><table width="100%" border="0" '+((olNs4||!cpIsMultiple) ? 'cellpadding="'+o3_cellpad+'" ' : '')+'cellspacing="0" '+(o3_fgclass ? 'class="'+o3_fgclass+'"' : o3_fgcolor+' '+o3_fgbackground+' '+o3_height)+'><tr><td valign="TOP"'+(o3_textfontclass ? ' class="'+o3_textfontclass+'">' :((!olNs4&&cpIsMultiple) ? ' style="'+setCellPadStr(o3_cellpad)+'">' : '>'))+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize)) + '</td></tr></table></td></tr></table>';
+
+ set_background("");
+ return txt;
+}
+
+// Sets the background picture,padding and lots more. :)
+function ol_content_background(text,picture,hasfullhtml) {
+ if (hasfullhtml) {
+ txt=text;
+ } else {
+ txt='<table width="'+o3_width+'" border="0" cellpadding="0" cellspacing="0" height="'+o3_height+'"><tr><td colspan="3" height="'+o3_padyt+'"></td></tr><tr><td width="'+o3_padxl+'"></td><td valign="TOP" width="'+(o3_width-o3_padxl-o3_padxr)+(o3_textfontclass ? '" class="'+o3_textfontclass : '')+'">'+(o3_textfontclass ? '' : wrapStr(0,o3_textsize,'text'))+text+(o3_textfontclass ? '' : wrapStr(1,o3_textsize))+'</td><td width="'+o3_padxr+'"></td></tr><tr><td colspan="3" height="'+o3_padyb+'"></td></tr></table>';
+ }
+
+ set_background(picture);
+ return txt;
+}
+
+// Loads a picture into the div.
+function set_background(pic) {
+ if (pic == "") {
+ if (olNs4) {
+ over.background.src = null;
+ } else if (over.style) {
+ over.style.backgroundImage = "none";
+ }
+ } else {
+ if (olNs4) {
+ over.background.src = pic;
+ } else if (over.style) {
+ over.style.width=o3_width + 'px';
+ over.style.backgroundImage = "url("+pic+")";
+ }
+ }
+}
+
+////////
+// HANDLING FUNCTIONS
+////////
+var olShowId=-1;
+
+// Displays the popup
+function disp(statustext) {
+ runHook("disp", FBEFORE);
+
+ if (o3_allowmove == 0) {
+ runHook("placeLayer", FREPLACE);
+ (olNs6&&olShowId<0) ? olShowId=setTimeout("runHook('showObject', FREPLACE, over)", 1) : runHook("showObject", FREPLACE, over);
+ o3_allowmove = (o3_sticky || o3_followmouse==0) ? 0 : 1;
+ }
+
+ runHook("disp", FAFTER);
+
+ if (statustext != "") self.status = statustext;
+}
+
+// Creates the actual popup structure
+function createPopup(lyrContent){
+ runHook("createPopup", FBEFORE);
+
+ if (o3_wrap) {
+ var wd,ww,theObj = (olNs4 ? over : over.style);
+ theObj.top = theObj.left = ((olIe4&&!olOp) ? 0 : -10000) + (!olNs4 ? 'px' : 0);
+ layerWrite(lyrContent);
+ wd = (olNs4 ? over.clip.width : over.offsetWidth);
+ if (wd > (ww=windowWidth())) {
+ lyrContent=lyrContent.replace(/\&nbsp;/g, ' ');
+ o3_width=ww;
+ o3_wrap=0;
+ }
+ }
+
+ layerWrite(lyrContent);
+
+ // Have to set o3_width for placeLayer() routine if o3_wrap is turned on
+ if (o3_wrap) o3_width=(olNs4 ? over.clip.width : over.offsetWidth);
+
+ runHook("createPopup", FAFTER, lyrContent);
+
+ return true;
+}
+
+// Decides where we want the popup.
+function placeLayer() {
+ var placeX, placeY, widthFix = 0;
+
+ // HORIZONTAL PLACEMENT, re-arranged to work in Safari
+ if (o3_frame.innerWidth) widthFix=18;
+ iwidth = windowWidth();
+
+ // Horizontal scroll offset
+ winoffset=(olIe4) ? eval('o3_frame.'+docRoot+'.scrollLeft') : o3_frame.pageXOffset;
+
+ placeX = runHook('horizontalPlacement',FCHAIN,iwidth,winoffset,widthFix);
+
+ // VERTICAL PLACEMENT, re-arranged to work in Safari
+ if (o3_frame.innerHeight) {
+ iheight=o3_frame.innerHeight;
+ } else if (eval('o3_frame.'+docRoot)&&eval("typeof o3_frame."+docRoot+".clientHeight=='number'")&&eval('o3_frame.'+docRoot+'.clientHeight')) {
+ iheight=eval('o3_frame.'+docRoot+'.clientHeight');
+ }
+
+ // Vertical scroll offset
+ scrolloffset=(olIe4) ? eval('o3_frame.'+docRoot+'.scrollTop') : o3_frame.pageYOffset;
+ placeY = runHook('verticalPlacement',FCHAIN,iheight,scrolloffset);
+
+ // Actually move the object.
+ repositionTo(over, placeX, placeY);
+}
+
+// Moves the layer
+function olMouseMove(e) {
+ var e = (e) ? e : event;
+
+ if (e.pageX) {
+ o3_x = e.pageX;
+ o3_y = e.pageY;
+ } else if (e.clientX) {
+ o3_x = eval('e.clientX+o3_frame.'+docRoot+'.scrollLeft');
+ o3_y = eval('e.clientY+o3_frame.'+docRoot+'.scrollTop');
+ }
+
+ if (o3_allowmove == 1) runHook("placeLayer", FREPLACE);
+
+ // MouseOut handler
+ if (hoveringSwitch && !olNs4 && runHook("cursorOff", FREPLACE)) {
+ (olHideDelay ? hideDelay(olHideDelay) : cClick());
+ hoveringSwitch = !hoveringSwitch;
+ }
+}
+
+// Fake function for 3.0 users.
+function no_overlib() { return ver3fix; }
+
+// Capture the mouse and chain other scripts.
+function olMouseCapture() {
+ capExtent = document;
+ var fN, str = '', l, k, f, wMv, sS, mseHandler = olMouseMove;
+ var re = /function[ ]*(\w*)\(/;
+
+ wMv = (!olIe4 && window.onmousemove);
+ if (document.onmousemove || wMv) {
+ if (wMv) capExtent = window;
+ f = capExtent.onmousemove.toString();
+ fN = f.match(re);
+ if (fN == null) {
+ str = f+'(e); ';
+ } else if (fN[1] == 'anonymous' || fN[1] == 'olMouseMove' || (wMv && fN[1] == 'onmousemove')) {
+ if (!olOp && wMv) {
+ l = f.indexOf('{')+1;
+ k = f.lastIndexOf('}');
+ sS = f.substring(l,k);
+ if ((l = sS.indexOf('(')) != -1) {
+ sS = sS.substring(0,l).replace(/^\s+/,'').replace(/\s+$/,'');
+ if (eval("typeof " + sS + " == 'undefined'")) window.onmousemove = null;
+ else str = sS + '(e);';
+ }
+ }
+ if (!str) {
+ olCheckMouseCapture = false;
+ return;
+ }
+ } else {
+ if (fN[1]) str = fN[1]+'(e); ';
+ else {
+ l = f.indexOf('{')+1;
+ k = f.lastIndexOf('}');
+ str = f.substring(l,k) + '\n';
+ }
+ }
+ str += 'olMouseMove(e); ';
+ mseHandler = new Function('e', str);
+ }
+
+ capExtent.onmousemove = mseHandler;
+ if (olNs4) capExtent.captureEvents(Event.MOUSEMOVE);
+}
+
+////////
+// PARSING FUNCTIONS
+////////
+
+// Does the actual command parsing.
+function parseTokens(pf, ar) {
+ // What the next argument is expected to be.
+ var v, i, mode=-1, par = (pf != 'ol_');
+ var fnMark = (par && !ar.length ? 1 : 0);
+
+ for (i = 0; i < ar.length; i++) {
+ if (mode < 0) {
+ // Arg is maintext,unless its a number between pmStart and pmUpper
+ // then its a command.
+ if (typeof ar[i] == 'number' && ar[i] > pmStart && ar[i] < pmUpper) {
+ fnMark = (par ? 1 : 0);
+ i--; // backup one so that the next block can parse it
+ } else {
+ switch(pf) {
+ case 'ol_':
+ ol_text = ar[i].toString();
+ break;
+ default:
+ o3_text=ar[i].toString();
+ }
+ }
+ mode = 0;
+ } else {
+ // Note: NS4 doesn't like switch cases with vars.
+ if (ar[i] >= pmCount || ar[i]==DONOTHING) { continue; }
+ if (ar[i]==INARRAY) { fnMark = 0; eval(pf+'text=ol_texts['+ar[++i]+'].toString()'); continue; }
+ if (ar[i]==CAPARRAY) { eval(pf+'cap=ol_caps['+ar[++i]+'].toString()'); continue; }
+ if (ar[i]==STICKY) { if (pf!='ol_') eval(pf+'sticky=1'); continue; }
+ if (ar[i]==BACKGROUND) { eval(pf+'background="'+ar[++i]+'"'); continue; }
+ if (ar[i]==NOCLOSE) { if (pf!='ol_') opt_NOCLOSE(); continue; }
+ if (ar[i]==CAPTION) { eval(pf+"cap='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==CENTER || ar[i]==LEFT || ar[i]==RIGHT) { eval(pf+'hpos='+ar[i]); if(pf!='ol_') olHautoFlag=1; continue; }
+ if (ar[i]==OFFSETX) { eval(pf+'offsetx='+ar[++i]); continue; }
+ if (ar[i]==OFFSETY) { eval(pf+'offsety='+ar[++i]); continue; }
+ if (ar[i]==FGCOLOR) { eval(pf+'fgcolor="'+ar[++i]+'"'); continue; }
+ if (ar[i]==BGCOLOR) { eval(pf+'bgcolor="'+ar[++i]+'"'); continue; }
+ if (ar[i]==TEXTCOLOR) { eval(pf+'textcolor="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CAPCOLOR) { eval(pf+'capcolor="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CLOSECOLOR) { eval(pf+'closecolor="'+ar[++i]+'"'); continue; }
+ if (ar[i]==WIDTH) { eval(pf+'width='+ar[++i]); continue; }
+ if (ar[i]==BORDER) { eval(pf+'border='+ar[++i]); continue; }
+ if (ar[i]==CELLPAD) { i=opt_MULTIPLEARGS(++i,ar,(pf+'cellpad')); continue; }
+ if (ar[i]==STATUS) { eval(pf+"status='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==AUTOSTATUS) { eval(pf +'autostatus=('+pf+'autostatus == 1) ? 0 : 1'); continue; }
+ if (ar[i]==AUTOSTATUSCAP) { eval(pf +'autostatus=('+pf+'autostatus == 2) ? 0 : 2'); continue; }
+ if (ar[i]==HEIGHT) { eval(pf+'height='+pf+'aboveheight='+ar[++i]); continue; } // Same param again.
+ if (ar[i]==CLOSETEXT) { eval(pf+"close='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==SNAPX) { eval(pf+'snapx='+ar[++i]); continue; }
+ if (ar[i]==SNAPY) { eval(pf+'snapy='+ar[++i]); continue; }
+ if (ar[i]==FIXX) { eval(pf+'fixx='+ar[++i]); continue; }
+ if (ar[i]==FIXY) { eval(pf+'fixy='+ar[++i]); continue; }
+ if (ar[i]==RELX) { eval(pf+'relx='+ar[++i]); continue; }
+ if (ar[i]==RELY) { eval(pf+'rely='+ar[++i]); continue; }
+ if (ar[i]==FGBACKGROUND) { eval(pf+'fgbackground="'+ar[++i]+'"'); continue; }
+ if (ar[i]==BGBACKGROUND) { eval(pf+'bgbackground="'+ar[++i]+'"'); continue; }
+ if (ar[i]==PADX) { eval(pf+'padxl='+ar[++i]); eval(pf+'padxr='+ar[++i]); continue; }
+ if (ar[i]==PADY) { eval(pf+'padyt='+ar[++i]); eval(pf+'padyb='+ar[++i]); continue; }
+ if (ar[i]==FULLHTML) { if (pf!='ol_') eval(pf+'fullhtml=1'); continue; }
+ if (ar[i]==BELOW || ar[i]==ABOVE) { eval(pf+'vpos='+ar[i]); if (pf!='ol_') olVautoFlag=1; continue; }
+ if (ar[i]==CAPICON) { eval(pf+'capicon="'+ar[++i]+'"'); continue; }
+ if (ar[i]==TEXTFONT) { eval(pf+"textfont='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==CAPTIONFONT) { eval(pf+"captionfont='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==CLOSEFONT) { eval(pf+"closefont='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==TEXTSIZE) { eval(pf+'textsize="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CAPTIONSIZE) { eval(pf+'captionsize="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CLOSESIZE) { eval(pf+'closesize="'+ar[++i]+'"'); continue; }
+ if (ar[i]==TIMEOUT) { eval(pf+'timeout='+ar[++i]); continue; }
+ if (ar[i]==FUNCTION) { if (pf=='ol_') { if (typeof ar[i+1]!='number') { v=ar[++i]; ol_function=(typeof v=='function' ? v : null); }} else {fnMark = 0; v = null; if (typeof ar[i+1]!='number') v = ar[++i]; opt_FUNCTION(v); } continue; }
+ if (ar[i]==DELAY) { eval(pf+'delay='+ar[++i]); continue; }
+ if (ar[i]==HAUTO) { eval(pf+'hauto=('+pf+'hauto == 0) ? 1 : 0'); continue; }
+ if (ar[i]==VAUTO) { eval(pf+'vauto=('+pf+'vauto == 0) ? 1 : 0'); continue; }
+ if (ar[i]==CLOSECLICK) { eval(pf +'closeclick=('+pf+'closeclick == 0) ? 1 : 0'); continue; }
+ if (ar[i]==WRAP) { eval(pf +'wrap=('+pf+'wrap == 0) ? 1 : 0'); continue; }
+ if (ar[i]==FOLLOWMOUSE) { eval(pf +'followmouse=('+pf+'followmouse == 1) ? 0 : 1'); continue; }
+ if (ar[i]==MOUSEOFF) { eval(pf +'mouseoff=('+pf+'mouseoff==0) ? 1 : 0'); v=ar[i+1]; if (pf != 'ol_' && eval(pf+'mouseoff') && typeof v == 'number' && (v < pmStart || v > pmUpper)) olHideDelay=ar[++i]; continue; }
+ if (ar[i]==CLOSETITLE) { eval(pf+"closetitle='"+escSglQuote(ar[++i])+"'"); continue; }
+ if (ar[i]==CSSOFF||ar[i]==CSSCLASS) { eval(pf+'css='+ar[i]); continue; }
+ if (ar[i]==COMPATMODE) { eval(pf+'compatmode=('+pf+'compatmode==0) ? 1 : 0'); continue; }
+ if (ar[i]==FGCLASS) { eval(pf+'fgclass="'+ar[++i]+'"'); continue; }
+ if (ar[i]==BGCLASS) { eval(pf+'bgclass="'+ar[++i]+'"'); continue; }
+ if (ar[i]==TEXTFONTCLASS) { eval(pf+'textfontclass="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CAPTIONFONTCLASS) { eval(pf+'captionfontclass="'+ar[++i]+'"'); continue; }
+ if (ar[i]==CLOSEFONTCLASS) { eval(pf+'closefontclass="'+ar[++i]+'"'); continue; }
+ i = parseCmdLine(pf, i, ar);
+ }
+ }
+
+ if (fnMark && o3_function) o3_text = o3_function();
+
+ if ((pf == 'o3_') && o3_wrap) {
+ o3_width = 0;
+
+ var tReg=/<.*\n*>/ig;
+ if (!tReg.test(o3_text)) o3_text = o3_text.replace(/[ ]+/g, '&nbsp;');
+ if (!tReg.test(o3_cap))o3_cap = o3_cap.replace(/[ ]+/g, '&nbsp;');
+ }
+ if ((pf == 'o3_') && o3_sticky) {
+ if (!o3_close && (o3_frame != ol_frame)) o3_close = ol_close;
+ if (o3_mouseoff && (o3_frame == ol_frame)) opt_NOCLOSE(' ');
+ }
+}
+
+
+////////
+// LAYER FUNCTIONS
+////////
+
+// Writes to a layer
+function layerWrite(txt) {
+ txt += "\n";
+ if (olNs4) {
+ var lyr = o3_frame.document.layers['overDiv'].document
+ lyr.write(txt)
+ lyr.close()
+ } else if (typeof over.innerHTML != 'undefined') {
+ if (olIe5 && isMac) over.innerHTML = '';
+ over.innerHTML = txt;
+ } else {
+ range = o3_frame.document.createRange();
+ range.setStartAfter(over);
+ domfrag = range.createContextualFragment(txt);
+
+ while (over.hasChildNodes()) {
+ over.removeChild(over.lastChild);
+ }
+
+ over.appendChild(domfrag);
+ }
+}
+
+// Make an object visible
+function showObject(obj) {
+ runHook("showObject", FBEFORE);
+
+ var theObj=(olNs4 ? obj : obj.style);
+ theObj.visibility = 'visible';
+
+ runHook("showObject", FAFTER);
+}
+
+// Hides an object
+function hideObject(obj) {
+ runHook("hideObject", FBEFORE);
+
+ var theObj=(olNs4 ? obj : obj.style);
+ if (olNs6 && olShowId>0) { clearTimeout(olShowId); olShowId=0; }
+ theObj.visibility = 'hidden';
+ theObj.top = theObj.left = ((olIe4&&!olOp) ? 0 : -10000) + (!olNs4 ? 'px' : 0);
+
+ if (o3_timerid > 0) clearTimeout(o3_timerid);
+ if (o3_delayid > 0) clearTimeout(o3_delayid);
+
+ o3_timerid = 0;
+ o3_delayid = 0;
+ self.status = "";
+
+ if (obj.onmouseout||obj.onmouseover) {
+ if (olNs4) obj.releaseEvents(Event.MOUSEOUT || Event.MOUSEOVER);
+ obj.onmouseout = obj.onmouseover = null;
+ }
+
+ runHook("hideObject", FAFTER);
+}
+
+// Move a layer
+function repositionTo(obj, xL, yL) {
+ var theObj=(olNs4 ? obj : obj.style);
+ theObj.left = xL + (!olNs4 ? 'px' : 0);
+ theObj.top = yL + (!olNs4 ? 'px' : 0);
+}
+
+// Check position of cursor relative to overDiv DIVision; mouseOut function
+function cursorOff() {
+ var left = parseInt(over.style.left);
+ var top = parseInt(over.style.top);
+ var right = left + (over.offsetWidth >= parseInt(o3_width) ? over.offsetWidth : parseInt(o3_width));
+ var bottom = top + (over.offsetHeight >= o3_aboveheight ? over.offsetHeight : o3_aboveheight);
+
+ if (o3_x < left || o3_x > right || o3_y < top || o3_y > bottom) return true;
+
+ return false;
+}
+
+
+////////
+// COMMAND FUNCTIONS
+////////
+
+// Calls callme or the default function.
+function opt_FUNCTION(callme) {
+ o3_text = (callme ? (typeof callme=='string' ? (/.+\(.*\)/.test(callme) ? eval(callme) : callme) : callme()) : (o3_function ? o3_function() : 'No Function'));
+
+ return 0;
+}
+
+// Handle hovering
+function opt_NOCLOSE(unused) {
+ if (!unused) o3_close = "";
+
+ if (olNs4) {
+ over.captureEvents(Event.MOUSEOUT || Event.MOUSEOVER);
+ over.onmouseover = function () { if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid = 0; } }
+ over.onmouseout = function (e) { if (olHideDelay) hideDelay(olHideDelay); else cClick(e); }
+ } else {
+ over.onmouseover = function () {hoveringSwitch = true; if (o3_timerid > 0) { clearTimeout(o3_timerid); o3_timerid =0; } }
+ }
+
+ return 0;
+}
+
+// Function to scan command line arguments for multiples
+function opt_MULTIPLEARGS(i, args, parameter) {
+ var k=i, re, pV, str='';
+
+ for(k=i; k<args.length; k++) {
+ if(typeof args[k] == 'number' && args[k]>pmStart) break;
+ str += args[k] + ',';
+ }
+ if (str) str = str.substring(0,--str.length);
+
+ k--; // reduce by one so the for loop this is in works correctly
+ pV=(olNs4 && /cellpad/i.test(parameter)) ? str.split(',')[0] : str;
+ eval(parameter + '="' + pV + '"');
+
+ return k;
+}
+
+// Remove &nbsp; in texts when done.
+function nbspCleanup() {
+ if (o3_wrap) {
+ o3_text = o3_text.replace(/\&nbsp;/g, ' ');
+ o3_cap = o3_cap.replace(/\&nbsp;/g, ' ');
+ }
+}
+
+// Escape embedded single quotes in text strings
+function escSglQuote(str) {
+ return str.toString().replace(/'/g,"\\'");
+}
+
+// Onload handler for window onload event
+function OLonLoad_handler(e) {
+ var re = /\w+\(.*\)[;\s]+/g, olre = /overlib\(|nd\(|cClick\(/, fn, l, i;
+
+ if(!olLoaded) olLoaded=1;
+
+ // Remove it for Gecko based browsers
+ if(window.removeEventListener && e.eventPhase == 3) window.removeEventListener("load",OLonLoad_handler,false);
+ else if(window.detachEvent) { // and for IE and Opera 4.x but execute calls to overlib, nd, or cClick()
+ window.detachEvent("onload",OLonLoad_handler);
+ var fN = document.body.getAttribute('onload');
+ if (fN) {
+ fN=fN.toString().match(re);
+ if (fN && fN.length) {
+ for (i=0; i<fN.length; i++) {
+ if (/anonymous/.test(fN[i])) continue;
+ while((l=fN[i].search(/\)[;\s]+/)) != -1) {
+ fn=fN[i].substring(0,l+1);
+ fN[i] = fN[i].substring(l+2);
+ if (olre.test(fn)) eval(fn);
+ }
+ }
+ }
+ }
+ }
+}
+
+// Wraps strings in Layer Generation Functions with the correct tags
+// endWrap true(if end tag) or false if start tag
+// fontSizeStr - font size string such as '1' or '10px'
+// whichString is being wrapped -- 'text', 'caption', or 'close'
+function wrapStr(endWrap,fontSizeStr,whichString) {
+ var fontStr, fontColor, isClose=((whichString=='close') ? 1 : 0), hasDims=/[%\-a-z]+$/.test(fontSizeStr);
+ fontSizeStr = (olNs4) ? (!hasDims ? fontSizeStr : '1') : fontSizeStr;
+ if (endWrap) return (hasDims&&!olNs4) ? (isClose ? '</span>' : '</div>') : '</font>';
+ else {
+ fontStr='o3_'+whichString+'font';
+ fontColor='o3_'+((whichString=='caption')? 'cap' : whichString)+'color';
+ return (hasDims&&!olNs4) ? (isClose ? '<span style="font-family: '+quoteMultiNameFonts(eval(fontStr))+'; color: '+eval(fontColor)+'; font-size: '+fontSizeStr+';">' : '<div style="font-family: '+quoteMultiNameFonts(eval(fontStr))+'; color: '+eval(fontColor)+'; font-size: '+fontSizeStr+';">') : '<font face="'+eval(fontStr)+'" color="'+eval(fontColor)+'" size="'+(parseInt(fontSizeStr)>7 ? '7' : fontSizeStr)+'">';
+ }
+}
+
+// Quotes Multi word font names; needed for CSS Standards adherence in font-family
+function quoteMultiNameFonts(theFont) {
+ var v, pM=theFont.split(',');
+ for (var i=0; i<pM.length; i++) {
+ v=pM[i];
+ v=v.replace(/^\s+/,'').replace(/\s+$/,'');
+ if(/\s/.test(v) && !/['"]/.test(v)) {
+ v="\'"+v+"\'";
+ pM[i]=v;
+ }
+ }
+ return pM.join();
+}
+
+// dummy function which will be overridden
+function isExclusive(args) {
+ return false;
+}
+
+// Sets cellpadding style string value
+function setCellPadStr(parameter) {
+ var Str='', j=0, ary = new Array(), top, bottom, left, right;
+
+ Str+='padding: ';
+ ary=parameter.replace(/\s+/g,'').split(',');
+
+ switch(ary.length) {
+ case 2:
+ top=bottom=ary[j];
+ left=right=ary[++j];
+ break;
+ case 3:
+ top=ary[j];
+ left=right=ary[++j];
+ bottom=ary[++j];
+ break;
+ case 4:
+ top=ary[j];
+ right=ary[++j];
+ bottom=ary[++j];
+ left=ary[++j];
+ break;
+ }
+
+ Str+= ((ary.length==1) ? ary[0] + 'px;' : top + 'px ' + right + 'px ' + bottom + 'px ' + left + 'px;');
+
+ return Str;
+}
+
+// function will delay close by time milliseconds
+function hideDelay(time) {
+ if (time&&!o3_delay) {
+ if (o3_timerid > 0) clearTimeout(o3_timerid);
+
+ o3_timerid=setTimeout("cClick()",(o3_timeout=time));
+ }
+}
+
+// Was originally in the placeLayer() routine; separated out for future ease
+function horizontalPlacement(browserWidth, horizontalScrollAmount, widthFix) {
+ var placeX, iwidth=browserWidth, winoffset=horizontalScrollAmount;
+ var parsedWidth = parseInt(o3_width);
+
+ if (o3_fixx > -1 || o3_relx != null) {
+ // Fixed position
+ placeX=(o3_relx != null ? ( o3_relx < 0 ? winoffset +o3_relx+ iwidth - parsedWidth - widthFix : winoffset+o3_relx) : o3_fixx);
+ } else {
+ // If HAUTO, decide what to use.
+ if (o3_hauto == 1) {
+ if ((o3_x - winoffset) > (iwidth / 2)) {
+ o3_hpos = LEFT;
+ } else {
+ o3_hpos = RIGHT;
+ }
+ }
+
+ // From mouse
+ if (o3_hpos == CENTER) { // Center
+ placeX = o3_x+o3_offsetx-(parsedWidth/2);
+
+ if (placeX < winoffset) placeX = winoffset;
+ }
+
+ if (o3_hpos == RIGHT) { // Right
+ placeX = o3_x+o3_offsetx;
+
+ if ((placeX+parsedWidth) > (winoffset+iwidth - widthFix)) {
+ placeX = iwidth+winoffset - parsedWidth - widthFix;
+ if (placeX < 0) placeX = 0;
+ }
+ }
+ if (o3_hpos == LEFT) { // Left
+ placeX = o3_x-o3_offsetx-parsedWidth;
+ if (placeX < winoffset) placeX = winoffset;
+ }
+
+ // Snapping!
+ if (o3_snapx > 1) {
+ var snapping = placeX % o3_snapx;
+
+ if (o3_hpos == LEFT) {
+ placeX = placeX - (o3_snapx+snapping);
+ } else {
+ // CENTER and RIGHT
+ placeX = placeX+(o3_snapx - snapping);
+ }
+
+ if (placeX < winoffset) placeX = winoffset;
+ }
+ }
+
+ return placeX;
+}
+
+// was originally in the placeLayer() routine; separated out for future ease
+function verticalPlacement(browserHeight,verticalScrollAmount) {
+ var placeY, iheight=browserHeight, scrolloffset=verticalScrollAmount;
+ var parsedHeight=(o3_aboveheight ? parseInt(o3_aboveheight) : (olNs4 ? over.clip.height : over.offsetHeight));
+
+ if (o3_fixy > -1 || o3_rely != null) {
+ // Fixed position
+ placeY=(o3_rely != null ? (o3_rely < 0 ? scrolloffset+o3_rely+iheight - parsedHeight : scrolloffset+o3_rely) : o3_fixy);
+ } else {
+ // If VAUTO, decide what to use.
+ if (o3_vauto == 1) {
+ if ((o3_y - scrolloffset) > (iheight / 2) && o3_vpos == BELOW && (o3_y + parsedHeight + o3_offsety - (scrolloffset + iheight) > 0)) {
+ o3_vpos = ABOVE;
+ } else if (o3_vpos == ABOVE && (o3_y - (parsedHeight + o3_offsety) - scrolloffset < 0)) {
+ o3_vpos = BELOW;
+ }
+ }
+
+ // From mouse
+ if (o3_vpos == ABOVE) {
+ if (o3_aboveheight == 0) o3_aboveheight = parsedHeight;
+
+ placeY = o3_y - (o3_aboveheight+o3_offsety);
+ if (placeY < scrolloffset) placeY = scrolloffset;
+ } else {
+ // BELOW
+ placeY = o3_y+o3_offsety;
+ }
+
+ // Snapping!
+ if (o3_snapy > 1) {
+ var snapping = placeY % o3_snapy;
+
+ if (o3_aboveheight > 0 && o3_vpos == ABOVE) {
+ placeY = placeY - (o3_snapy+snapping);
+ } else {
+ placeY = placeY+(o3_snapy - snapping);
+ }
+
+ if (placeY < scrolloffset) placeY = scrolloffset;
+ }
+ }
+
+ return placeY;
+}
+
+// checks positioning flags
+function checkPositionFlags() {
+ if (olHautoFlag) olHautoFlag = o3_hauto=0;
+ if (olVautoFlag) olVautoFlag = o3_vauto=0;
+ return true;
+}
+
+// get Browser window width
+function windowWidth() {
+ var w;
+ if (o3_frame.innerWidth) w=o3_frame.innerWidth;
+ else if (eval('o3_frame.'+docRoot)&&eval("typeof o3_frame."+docRoot+".clientWidth=='number'")&&eval('o3_frame.'+docRoot+'.clientWidth'))
+ w=eval('o3_frame.'+docRoot+'.clientWidth');
+ return w;
+}
+
+// create the div container for popup content if it doesn't exist
+function createDivContainer(id,frm,zValue) {
+ id = (id || 'overDiv'), frm = (frm || o3_frame), zValue = (zValue || 1000);
+ var objRef, divContainer = layerReference(id);
+
+ if (divContainer == null) {
+ if (olNs4) {
+ divContainer = frm.document.layers[id] = new Layer(window.innerWidth, frm);
+ objRef = divContainer;
+ } else {
+ var body = (olIe4 ? frm.document.all.tags('BODY')[0] : frm.document.getElementsByTagName("BODY")[0]);
+ if (olIe4&&!document.getElementById) {
+ body.insertAdjacentHTML("beforeEnd",'<div id="'+id+'"></div>');
+ divContainer=layerReference(id);
+ } else {
+ divContainer = frm.document.createElement("DIV");
+ divContainer.id = id;
+ body.appendChild(divContainer);
+ }
+ objRef = divContainer.style;
+ }
+
+ objRef.position = 'absolute';
+ objRef.visibility = 'hidden';
+ objRef.zIndex = zValue;
+ if (olIe4&&!olOp) objRef.left = objRef.top = '0px';
+ else objRef.left = objRef.top = -10000 + (!olNs4 ? 'px' : 0);
+ }
+
+ return divContainer;
+}
+
+// get reference to a layer with ID=id
+function layerReference(id) {
+ return (olNs4 ? o3_frame.document.layers[id] : (document.all ? o3_frame.document.all[id] : o3_frame.document.getElementById(id)));
+}
+////////
+// UTILITY FUNCTIONS
+////////
+
+// Checks if something is a function.
+function isFunction(fnRef) {
+ var rtn = true;
+
+ if (typeof fnRef == 'object') {
+ for (var i = 0; i < fnRef.length; i++) {
+ if (typeof fnRef[i]=='function') continue;
+ rtn = false;
+ break;
+ }
+ } else if (typeof fnRef != 'function') {
+ rtn = false;
+ }
+
+ return rtn;
+}
+
+// Converts an array into an argument string for use in eval.
+function argToString(array, strtInd, argName) {
+ var jS = strtInd, aS = '', ar = array;
+ argName=(argName ? argName : 'ar');
+
+ if (ar.length > jS) {
+ for (var k = jS; k < ar.length; k++) aS += argName+'['+k+'], ';
+ aS = aS.substring(0, aS.length-2);
+ }
+
+ return aS;
+}
+
+// Places a hook in the correct position in a hook point.
+function reOrder(hookPt, fnRef, order) {
+ var newPt = new Array(), match, i, j;
+
+ if (!order || typeof order == 'undefined' || typeof order == 'number') return hookPt;
+
+ if (typeof order=='function') {
+ if (typeof fnRef=='object') {
+ newPt = newPt.concat(fnRef);
+ } else {
+ newPt[newPt.length++]=fnRef;
+ }
+
+ for (i = 0; i < hookPt.length; i++) {
+ match = false;
+ if (typeof fnRef == 'function' && hookPt[i] == fnRef) {
+ continue;
+ } else {
+ for(j = 0; j < fnRef.length; j++) if (hookPt[i] == fnRef[j]) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) newPt[newPt.length++] = hookPt[i];
+ }
+
+ newPt[newPt.length++] = order;
+
+ } else if (typeof order == 'object') {
+ if (typeof fnRef == 'object') {
+ newPt = newPt.concat(fnRef);
+ } else {
+ newPt[newPt.length++] = fnRef;
+ }
+
+ for (j = 0; j < hookPt.length; j++) {
+ match = false;
+ if (typeof fnRef == 'function' && hookPt[j] == fnRef) {
+ continue;
+ } else {
+ for (i = 0; i < fnRef.length; i++) if (hookPt[j] == fnRef[i]) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) newPt[newPt.length++]=hookPt[j];
+ }
+
+ for (i = 0; i < newPt.length; i++) hookPt[i] = newPt[i];
+ newPt.length = 0;
+
+ for (j = 0; j < hookPt.length; j++) {
+ match = false;
+ for (i = 0; i < order.length; i++) {
+ if (hookPt[j] == order[i]) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) newPt[newPt.length++] = hookPt[j];
+ }
+ newPt = newPt.concat(order);
+ }
+
+ hookPt = newPt;
+
+ return hookPt;
+}
+
+////////
+// PLUGIN ACTIVATION FUNCTIONS
+////////
+
+// Runs plugin functions to set runtime variables.
+function setRunTimeVariables(){
+ if (typeof runTime != 'undefined' && runTime.length) {
+ for (var k = 0; k < runTime.length; k++) {
+ runTime[k]();
+ }
+ }
+}
+
+// Runs plugin functions to parse commands.
+function parseCmdLine(pf, i, args) {
+ if (typeof cmdLine != 'undefined' && cmdLine.length) {
+ for (var k = 0; k < cmdLine.length; k++) {
+ var j = cmdLine[k](pf, i, args);
+ if (j >- 1) {
+ i = j;
+ break;
+ }
+ }
+ }
+
+ return i;
+}
+
+// Runs plugin functions to do things after parse.
+function postParseChecks(pf,args){
+ if (typeof postParse != 'undefined' && postParse.length) {
+ for (var k = 0; k < postParse.length; k++) {
+ if (postParse[k](pf,args)) continue;
+ return false; // end now since have an error
+ }
+ }
+ return true;
+}
+
+
+////////
+// PLUGIN REGISTRATION FUNCTIONS
+////////
+
+// Registers commands and creates constants.
+function registerCommands(cmdStr) {
+ if (typeof cmdStr!='string') return;
+
+ var pM = cmdStr.split(',');
+ pms = pms.concat(pM);
+
+ for (var i = 0; i< pM.length; i++) {
+ eval(pM[i].toUpperCase()+'='+pmCount++);
+ }
+}
+
+// Registers no-parameter commands
+function registerNoParameterCommands(cmdStr) {
+ if (!cmdStr && typeof cmdStr != 'string') return;
+ pmt=(!pmt) ? cmdStr : pmt + ',' + cmdStr;
+}
+
+// Register a function to hook at a certain point.
+function registerHook(fnHookTo, fnRef, hookType, optPm) {
+ var hookPt, last = typeof optPm;
+
+ if (fnHookTo == 'plgIn'||fnHookTo == 'postParse') return;
+ if (typeof hookPts[fnHookTo] == 'undefined') hookPts[fnHookTo] = new FunctionReference();
+
+ hookPt = hookPts[fnHookTo];
+
+ if (hookType != null) {
+ if (hookType == FREPLACE) {
+ hookPt.ovload = fnRef; // replace normal overlib routine
+ if (fnHookTo.indexOf('ol_content_') > -1) hookPt.alt[pms[CSSOFF-1-pmStart]]=fnRef;
+
+ } else if (hookType == FBEFORE || hookType == FAFTER) {
+ var hookPt=(hookType == 1 ? hookPt.before : hookPt.after);
+
+ if (typeof fnRef == 'object') {
+ hookPt = hookPt.concat(fnRef);
+ } else {
+ hookPt[hookPt.length++] = fnRef;
+ }
+
+ if (optPm) hookPt = reOrder(hookPt, fnRef, optPm);
+
+ } else if (hookType == FALTERNATE) {
+ if (last=='number') hookPt.alt[pms[optPm-1-pmStart]] = fnRef;
+ } else if (hookType == FCHAIN) {
+ hookPt = hookPt.chain;
+ if (typeof fnRef=='object') hookPt=hookPt.concat(fnRef); // add other functions
+ else hookPt[hookPt.length++]=fnRef;
+ }
+
+ return;
+ }
+}
+
+// Register a function that will set runtime variables.
+function registerRunTimeFunction(fn) {
+ if (isFunction(fn)) {
+ if (typeof fn == 'object') {
+ runTime = runTime.concat(fn);
+ } else {
+ runTime[runTime.length++] = fn;
+ }
+ }
+}
+
+// Register a function that will handle command parsing.
+function registerCmdLineFunction(fn){
+ if (isFunction(fn)) {
+ if (typeof fn == 'object') {
+ cmdLine = cmdLine.concat(fn);
+ } else {
+ cmdLine[cmdLine.length++] = fn;
+ }
+ }
+}
+
+// Register a function that does things after command parsing.
+function registerPostParseFunction(fn){
+ if (isFunction(fn)) {
+ if (typeof fn == 'object') {
+ postParse = postParse.concat(fn);
+ } else {
+ postParse[postParse.length++] = fn;
+ }
+ }
+}
+
+////////
+// PLUGIN REGISTRATION FUNCTIONS
+////////
+
+// Runs any hooks registered.
+function runHook(fnHookTo, hookType) {
+ var l = hookPts[fnHookTo], k, rtnVal = null, optPm, arS, ar = runHook.arguments;
+
+ if (hookType == FREPLACE) {
+ arS = argToString(ar, 2);
+
+ if (typeof l == 'undefined' || !(l = l.ovload)) rtnVal = eval(fnHookTo+'('+arS+')');
+ else rtnVal = eval('l('+arS+')');
+
+ } else if (hookType == FBEFORE || hookType == FAFTER) {
+ if (typeof l != 'undefined') {
+ l=(hookType == 1 ? l.before : l.after);
+
+ if (l.length) {
+ arS = argToString(ar, 2);
+ for (var k = 0; k < l.length; k++) eval('l[k]('+arS+')');
+ }
+ }
+ } else if (hookType == FALTERNATE) {
+ optPm = ar[2];
+ arS = argToString(ar, 3);
+
+ if (typeof l == 'undefined' || (l = l.alt[pms[optPm-1-pmStart]]) == 'undefined') {
+ rtnVal = eval(fnHookTo+'('+arS+')');
+ } else {
+ rtnVal = eval('l('+arS+')');
+ }
+ } else if (hookType == FCHAIN) {
+ arS=argToString(ar,2);
+ l=l.chain;
+
+ for (k=l.length; k > 0; k--) if((rtnVal=eval('l[k-1]('+arS+')'))!=void(0)) break;
+ }
+
+ return rtnVal;
+}
+
+////////
+// OBJECT CONSTRUCTORS
+////////
+
+// Object for handling hooks.
+function FunctionReference() {
+ this.ovload = null;
+ this.before = new Array();
+ this.after = new Array();
+ this.alt = new Array();
+ this.chain = new Array();
+}
+
+// Object for simple access to the overLIB version used.
+// Examples: simpleversion:351 major:3 minor:5 revision:1
+function Info(version, prerelease) {
+ this.version = version;
+ this.prerelease = prerelease;
+
+ this.simpleversion = Math.round(this.version*100);
+ this.major = parseInt(this.simpleversion / 100);
+ this.minor = parseInt(this.simpleversion / 10) - this.major * 10;
+ this.revision = parseInt(this.simpleversion) - this.major * 100 - this.minor * 10;
+ this.meets = meets;
+}
+
+// checks for Core Version required
+function meets(reqdVersion) {
+ return (!reqdVersion) ? false : this.simpleversion >= Math.round(100*parseFloat(reqdVersion));
+}
+
+
+////////
+// STANDARD REGISTRATIONS
+////////
+registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSOFF);
+registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSOFF);
+registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSOFF);
+registerHook("ol_content_simple", ol_content_simple, FALTERNATE, CSSCLASS);
+registerHook("ol_content_caption", ol_content_caption, FALTERNATE, CSSCLASS);
+registerHook("ol_content_background", ol_content_background, FALTERNATE, CSSCLASS);
+registerPostParseFunction(checkPositionFlags);
+registerHook("hideObject", nbspCleanup, FAFTER);
+registerHook("horizontalPlacement", horizontalPlacement, FCHAIN);
+registerHook("verticalPlacement", verticalPlacement, FCHAIN);
+if (olNs4||(olIe5&&isMac)||olKq) olLoaded=1;
+registerNoParameterCommands('sticky,autostatus,autostatuscap,fullhtml,hauto,vauto,closeclick,wrap,followmouse,mouseoff,compatmode');
+///////
+// ESTABLISH MOUSECAPTURING
+///////
+
+// Capture events, alt. diffuses the overlib function.
+var olCheckMouseCapture=true;
+if ((olNs4 || olNs6 || olIe4)) {
+ olMouseCapture();
+} else {
+ overlib = no_overlib;
+ nd = no_overlib;
+ ver3fix = true;
+}
diff --git a/res/pages/js/personal_e_mail.js b/res/pages/js/personal_e_mail.js
new file mode 100644
index 00000000..3c849c26
--- /dev/null
+++ b/res/pages/js/personal_e_mail.js
@@ -0,0 +1,7 @@
+
+$(document).ready(function() {
+ if ($.browser.mozilla) {
+ $("div#personal_e_mail table").wrap("<div id='wrapper' style='padding: 0px; margin: 0px; width: 737px;'></div>");
+ $("div#personal_e_mail div#wrapper").corner("round");
+ }
+}); \ No newline at end of file
diff --git a/res/pages/js/plugins.js b/res/pages/js/plugins.js
new file mode 100644
index 00000000..63baf6f0
--- /dev/null
+++ b/res/pages/js/plugins.js
@@ -0,0 +1,19901 @@
+/*
+ * File: jquery.dataTables.js
+ * Version: 1.7.6
+ * Description: Paginate, search and sort HTML tables
+ * Author: Allan Jardine (www.sprymedia.co.uk)
+ * Created: 28/3/2008
+ * Language: Javascript
+ * License: GPL v2 or BSD 3 point style
+ * Project: Mtaala
+ * Contact: allan.jardine@sprymedia.co.uk
+ *
+ * Copyright 2008-2010 Allan Jardine, all rights reserved.
+ *
+ * This source file is free software, under either the GPL v2 license or a
+ * BSD style license, as supplied with this software.
+ *
+ * This source file 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 license files for details.
+ *
+ * For details please refer to: http://www.datatables.net
+ */
+
+/*
+ * When considering jsLint, we need to allow eval() as it it is used for reading cookies and
+ * building the dynamic multi-column sort functions.
+ */
+/*jslint evil: true, undef: true, browser: true */
+/*globals $, jQuery,_fnExternApiFunc,_fnInitalise,_fnInitComplete,_fnLanguageProcess,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnGatherData,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxUpdateDraw,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnArrayCmp,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap*/
+
+(function($, window, document) {
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - DataTables variables
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /*
+ * Variable: dataTableSettings
+ * Purpose: Store the settings for each dataTables instance
+ * Scope: jQuery.fn
+ */
+ $.fn.dataTableSettings = [];
+ var _aoSettings = $.fn.dataTableSettings; /* Short reference for fast internal lookup */
+
+ /*
+ * Variable: dataTableExt
+ * Purpose: Container for customisable parts of DataTables
+ * Scope: jQuery.fn
+ */
+ $.fn.dataTableExt = {};
+ var _oExt = $.fn.dataTableExt;
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - DataTables extensible objects
+ *
+ * The _oExt object is used to provide an area where user dfined plugins can be
+ * added to DataTables. The following properties of the object are used:
+ * oApi - Plug-in API functions
+ * aTypes - Auto-detection of types
+ * oSort - Sorting functions used by DataTables (based on the type)
+ * oPagination - Pagination functions for different input styles
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /*
+ * Variable: sVersion
+ * Purpose: Version string for plug-ins to check compatibility
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: Allowed format is a.b.c.d.e where:
+ * a:int, b:int, c:int, d:string(dev|beta), e:int. d and e are optional
+ */
+ _oExt.sVersion = "1.7.6";
+
+ /*
+ * Variable: sErrMode
+ * Purpose: How should DataTables report an error. Can take the value 'alert' or 'throw'
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.sErrMode = "alert";
+
+ /*
+ * Variable: iApiIndex
+ * Purpose: Index for what 'this' index API functions should use
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.iApiIndex = 0;
+
+ /*
+ * Variable: oApi
+ * Purpose: Container for plugin API functions
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.oApi = { };
+
+ /*
+ * Variable: aFiltering
+ * Purpose: Container for plugin filtering functions
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.afnFiltering = [ ];
+
+ /*
+ * Variable: aoFeatures
+ * Purpose: Container for plugin function functions
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: Array of objects with the following parameters:
+ * fnInit: Function for initialisation of Feature. Takes oSettings and returns node
+ * cFeature: Character that will be matched in sDom - case sensitive
+ * sFeature: Feature name - just for completeness :-)
+ */
+ _oExt.aoFeatures = [ ];
+
+ /*
+ * Variable: ofnSearch
+ * Purpose: Container for custom filtering functions
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: This is an object (the name should match the type) for custom filtering function,
+ * which can be used for live DOM checking or formatted text filtering
+ */
+ _oExt.ofnSearch = { };
+
+ /*
+ * Variable: afnSortData
+ * Purpose: Container for custom sorting data source functions
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: Array (associative) of functions which is run prior to a column of this
+ * 'SortDataType' being sorted upon.
+ * Function input parameters:
+ * object:oSettings- DataTables settings object
+ * int:iColumn - Target column number
+ * Return value: Array of data which exactly matched the full data set size for the column to
+ * be sorted upon
+ */
+ _oExt.afnSortData = [ ];
+
+ /*
+ * Variable: oStdClasses
+ * Purpose: Storage for the various classes that DataTables uses
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.oStdClasses = {
+ /* Two buttons buttons */
+ "sPagePrevEnabled": "paginate_enabled_previous",
+ "sPagePrevDisabled": "paginate_disabled_previous",
+ "sPageNextEnabled": "paginate_enabled_next",
+ "sPageNextDisabled": "paginate_disabled_next",
+ "sPageJUINext": "",
+ "sPageJUIPrev": "",
+
+ /* Full numbers paging buttons */
+ "sPageButton": "paginate_button",
+ "sPageButtonActive": "paginate_active",
+ "sPageButtonStaticDisabled": "paginate_button",
+ "sPageFirst": "first",
+ "sPagePrevious": "previous",
+ "sPageNext": "next",
+ "sPageLast": "last",
+
+ /* Stripping classes */
+ "sStripOdd": "odd",
+ "sStripEven": "even",
+
+ /* Empty row */
+ "sRowEmpty": "dataTables_empty",
+
+ /* Features */
+ "sWrapper": "dataTables_wrapper",
+ "sFilter": "dataTables_filter",
+ "sInfo": "dataTables_info",
+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
+ "sLength": "dataTables_length",
+ "sProcessing": "dataTables_processing",
+
+ /* Sorting */
+ "sSortAsc": "sorting_asc",
+ "sSortDesc": "sorting_desc",
+ "sSortable": "sorting", /* Sortable in both directions */
+ "sSortableAsc": "sorting_asc_disabled",
+ "sSortableDesc": "sorting_desc_disabled",
+ "sSortableNone": "sorting_disabled",
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
+ "sSortJUIAsc": "",
+ "sSortJUIDesc": "",
+ "sSortJUI": "",
+ "sSortJUIAscAllowed": "",
+ "sSortJUIDescAllowed": "",
+ "sSortJUIWrapper": "",
+
+ /* Scrolling */
+ "sScrollWrapper": "dataTables_scroll",
+ "sScrollHead": "dataTables_scrollHead",
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
+ "sScrollBody": "dataTables_scrollBody",
+ "sScrollFoot": "dataTables_scrollFoot",
+ "sScrollFootInner": "dataTables_scrollFootInner",
+
+ /* Misc */
+ "sFooterTH": ""
+ };
+
+ /*
+ * Variable: oJUIClasses
+ * Purpose: Storage for the various classes that DataTables uses - jQuery UI suitable
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.oJUIClasses = {
+ /* Two buttons buttons */
+ "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
+ "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
+ "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
+ "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
+ "sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
+ "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
+
+ /* Full numbers paging buttons */
+ "sPageButton": "fg-button ui-button ui-state-default",
+ "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
+ "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
+ "sPageFirst": "first ui-corner-tl ui-corner-bl",
+ "sPagePrevious": "previous",
+ "sPageNext": "next",
+ "sPageLast": "last ui-corner-tr ui-corner-br",
+
+ /* Stripping classes */
+ "sStripOdd": "odd",
+ "sStripEven": "even",
+
+ /* Empty row */
+ "sRowEmpty": "dataTables_empty",
+
+ /* Features */
+ "sWrapper": "dataTables_wrapper",
+ "sFilter": "dataTables_filter",
+ "sInfo": "dataTables_info",
+ "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
+ "ui-buttonset-multi paging_", /* Note that the type is postfixed */
+ "sLength": "dataTables_length",
+ "sProcessing": "dataTables_processing",
+
+ /* Sorting */
+ "sSortAsc": "ui-state-default",
+ "sSortDesc": "ui-state-default",
+ "sSortable": "ui-state-default",
+ "sSortableAsc": "ui-state-default",
+ "sSortableDesc": "ui-state-default",
+ "sSortableNone": "ui-state-default",
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
+ "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
+ "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
+ "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
+ "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
+ "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
+ "sSortJUIWrapper": "DataTables_sort_wrapper",
+
+ /* Scrolling */
+ "sScrollWrapper": "dataTables_scroll",
+ "sScrollHead": "dataTables_scrollHead ui-state-default",
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
+ "sScrollBody": "dataTables_scrollBody",
+ "sScrollFoot": "dataTables_scrollFoot ui-state-default",
+ "sScrollFootInner": "dataTables_scrollFootInner",
+
+ /* Misc */
+ "sFooterTH": "ui-state-default"
+ };
+
+ /*
+ * Variable: oPagination
+ * Purpose: Container for the various type of pagination that dataTables supports
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt.oPagination = {
+ /*
+ * Variable: two_button
+ * Purpose: Standard two button (forward/back) pagination
+ * Scope: jQuery.fn.dataTableExt.oPagination
+ */
+ "two_button": {
+ /*
+ * Function: oPagination.two_button.fnInit
+ * Purpose: Initalise dom elements required for pagination with forward/back buttons only
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * node:nPaging - the DIV which contains this pagination control
+ * function:fnCallbackDraw - draw function which must be called on update
+ */
+ "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
+ {
+ var nPrevious, nNext, nPreviousInner, nNextInner;
+
+ /* Store the next and previous elements in the oSettings object as they can be very
+ * usful for automation - particularly testing
+ */
+ if ( !oSettings.bJUI )
+ {
+ nPrevious = document.createElement( 'div' );
+ nNext = document.createElement( 'div' );
+ }
+ else
+ {
+ nPrevious = document.createElement( 'a' );
+ nNext = document.createElement( 'a' );
+
+ nNextInner = document.createElement('span');
+ nNextInner.className = oSettings.oClasses.sPageJUINext;
+ nNext.appendChild( nNextInner );
+
+ nPreviousInner = document.createElement('span');
+ nPreviousInner.className = oSettings.oClasses.sPageJUIPrev;
+ nPrevious.appendChild( nPreviousInner );
+ }
+
+ nPrevious.className = oSettings.oClasses.sPagePrevDisabled;
+ nNext.className = oSettings.oClasses.sPageNextDisabled;
+
+ nPrevious.title = oSettings.oLanguage.oPaginate.sPrevious;
+ nNext.title = oSettings.oLanguage.oPaginate.sNext;
+
+ nPaging.appendChild( nPrevious );
+ nPaging.appendChild( nNext );
+
+ $(nPrevious).bind( 'click.DT', function() {
+ if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
+ {
+ /* Only draw when the page has actually changed */
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ $(nNext).bind( 'click.DT', function() {
+ if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
+ {
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ /* Take the brutal approach to cancelling text selection */
+ $(nPrevious).bind( 'selectstart.DT', function () { return false; } );
+ $(nNext).bind( 'selectstart.DT', function () { return false; } );
+
+ /* ID the first elements only */
+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
+ {
+ nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
+ nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
+ nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
+ }
+ },
+
+ /*
+ * Function: oPagination.two_button.fnUpdate
+ * Purpose: Update the two button pagination at the end of the draw
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * function:fnCallbackDraw - draw function to call on page change
+ */
+ "fnUpdate": function ( oSettings, fnCallbackDraw )
+ {
+ if ( !oSettings.aanFeatures.p )
+ {
+ return;
+ }
+
+ /* Loop over each instance of the pager */
+ var an = oSettings.aanFeatures.p;
+ for ( var i=0, iLen=an.length ; i<iLen ; i++ )
+ {
+ if ( an[i].childNodes.length !== 0 )
+ {
+ an[i].childNodes[0].className =
+ ( oSettings._iDisplayStart === 0 ) ?
+ oSettings.oClasses.sPagePrevDisabled : oSettings.oClasses.sPagePrevEnabled;
+
+ an[i].childNodes[1].className =
+ ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
+ oSettings.oClasses.sPageNextDisabled : oSettings.oClasses.sPageNextEnabled;
+ }
+ }
+ }
+ },
+
+
+ /*
+ * Variable: iFullNumbersShowPages
+ * Purpose: Change the number of pages which can be seen
+ * Scope: jQuery.fn.dataTableExt.oPagination
+ */
+ "iFullNumbersShowPages": 5,
+
+ /*
+ * Variable: full_numbers
+ * Purpose: Full numbers pagination
+ * Scope: jQuery.fn.dataTableExt.oPagination
+ */
+ "full_numbers": {
+ /*
+ * Function: oPagination.full_numbers.fnInit
+ * Purpose: Initalise dom elements required for pagination with a list of the pages
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * node:nPaging - the DIV which contains this pagination control
+ * function:fnCallbackDraw - draw function which must be called on update
+ */
+ "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
+ {
+ var nFirst = document.createElement( 'span' );
+ var nPrevious = document.createElement( 'span' );
+ var nList = document.createElement( 'span' );
+ var nNext = document.createElement( 'span' );
+ var nLast = document.createElement( 'span' );
+
+ nFirst.innerHTML = oSettings.oLanguage.oPaginate.sFirst;
+ nPrevious.innerHTML = oSettings.oLanguage.oPaginate.sPrevious;
+ nNext.innerHTML = oSettings.oLanguage.oPaginate.sNext;
+ nLast.innerHTML = oSettings.oLanguage.oPaginate.sLast;
+
+ var oClasses = oSettings.oClasses;
+ nFirst.className = oClasses.sPageButton+" "+oClasses.sPageFirst;
+ nPrevious.className = oClasses.sPageButton+" "+oClasses.sPagePrevious;
+ nNext.className= oClasses.sPageButton+" "+oClasses.sPageNext;
+ nLast.className = oClasses.sPageButton+" "+oClasses.sPageLast;
+
+ nPaging.appendChild( nFirst );
+ nPaging.appendChild( nPrevious );
+ nPaging.appendChild( nList );
+ nPaging.appendChild( nNext );
+ nPaging.appendChild( nLast );
+
+ $(nFirst).bind( 'click.DT', function () {
+ if ( oSettings.oApi._fnPageChange( oSettings, "first" ) )
+ {
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ $(nPrevious).bind( 'click.DT', function() {
+ if ( oSettings.oApi._fnPageChange( oSettings, "previous" ) )
+ {
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ $(nNext).bind( 'click.DT', function() {
+ if ( oSettings.oApi._fnPageChange( oSettings, "next" ) )
+ {
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ $(nLast).bind( 'click.DT', function() {
+ if ( oSettings.oApi._fnPageChange( oSettings, "last" ) )
+ {
+ fnCallbackDraw( oSettings );
+ }
+ } );
+
+ /* Take the brutal approach to cancelling text selection */
+ $('span', nPaging)
+ .bind( 'mousedown.DT', function () { return false; } )
+ .bind( 'selectstart.DT', function () { return false; } );
+
+ /* ID the first elements only */
+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.p == "undefined" )
+ {
+ nPaging.setAttribute( 'id', oSettings.sTableId+'_paginate' );
+ nFirst.setAttribute( 'id', oSettings.sTableId+'_first' );
+ nPrevious.setAttribute( 'id', oSettings.sTableId+'_previous' );
+ nNext.setAttribute( 'id', oSettings.sTableId+'_next' );
+ nLast.setAttribute( 'id', oSettings.sTableId+'_last' );
+ }
+ },
+
+ /*
+ * Function: oPagination.full_numbers.fnUpdate
+ * Purpose: Update the list of page buttons shows
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * function:fnCallbackDraw - draw function to call on page change
+ */
+ "fnUpdate": function ( oSettings, fnCallbackDraw )
+ {
+ if ( !oSettings.aanFeatures.p )
+ {
+ return;
+ }
+
+ var iPageCount = _oExt.oPagination.iFullNumbersShowPages;
+ var iPageCountHalf = Math.floor(iPageCount / 2);
+ var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
+ var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
+ var sList = "";
+ var iStartButton, iEndButton, i, iLen;
+ var oClasses = oSettings.oClasses;
+
+ /* Pages calculation */
+ if (iPages < iPageCount)
+ {
+ iStartButton = 1;
+ iEndButton = iPages;
+ }
+ else
+ {
+ if (iCurrentPage <= iPageCountHalf)
+ {
+ iStartButton = 1;
+ iEndButton = iPageCount;
+ }
+ else
+ {
+ if (iCurrentPage >= (iPages - iPageCountHalf))
+ {
+ iStartButton = iPages - iPageCount + 1;
+ iEndButton = iPages;
+ }
+ else
+ {
+ iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
+ iEndButton = iStartButton + iPageCount - 1;
+ }
+ }
+ }
+
+ /* Build the dynamic list */
+ for ( i=iStartButton ; i<=iEndButton ; i++ )
+ {
+ if ( iCurrentPage != i )
+ {
+ sList += '<span class="'+oClasses.sPageButton+'">'+i+'</span>';
+ }
+ else
+ {
+ sList += '<span class="'+oClasses.sPageButtonActive+'">'+i+'</span>';
+ }
+ }
+
+ /* Loop over each instance of the pager */
+ var an = oSettings.aanFeatures.p;
+ var anButtons, anStatic, nPaginateList;
+ var fnClick = function() {
+ /* Use the information in the element to jump to the required page */
+ var iTarget = (this.innerHTML * 1) - 1;
+ oSettings._iDisplayStart = iTarget * oSettings._iDisplayLength;
+ fnCallbackDraw( oSettings );
+ return false;
+ };
+ var fnFalse = function () { return false; };
+
+ for ( i=0, iLen=an.length ; i<iLen ; i++ )
+ {
+ if ( an[i].childNodes.length === 0 )
+ {
+ continue;
+ }
+
+ /* Build up the dynamic list forst - html and listeners */
+ var qjPaginateList = $('span:eq(2)', an[i]);
+ qjPaginateList.html( sList );
+ $('span', qjPaginateList).bind( 'click.DT', fnClick ).bind( 'mousedown.DT', fnFalse )
+ .bind( 'selectstart.DT', fnFalse );
+
+ /* Update the 'premanent botton's classes */
+ anButtons = an[i].getElementsByTagName('span');
+ anStatic = [
+ anButtons[0], anButtons[1],
+ anButtons[anButtons.length-2], anButtons[anButtons.length-1]
+ ];
+ $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
+ if ( iCurrentPage == 1 )
+ {
+ anStatic[0].className += " "+oClasses.sPageButtonStaticDisabled;
+ anStatic[1].className += " "+oClasses.sPageButtonStaticDisabled;
+ }
+ else
+ {
+ anStatic[0].className += " "+oClasses.sPageButton;
+ anStatic[1].className += " "+oClasses.sPageButton;
+ }
+
+ if ( iPages === 0 || iCurrentPage == iPages || oSettings._iDisplayLength == -1 )
+ {
+ anStatic[2].className += " "+oClasses.sPageButtonStaticDisabled;
+ anStatic[3].className += " "+oClasses.sPageButtonStaticDisabled;
+ }
+ else
+ {
+ anStatic[2].className += " "+oClasses.sPageButton;
+ anStatic[3].className += " "+oClasses.sPageButton;
+ }
+ }
+ }
+ }
+ };
+
+ /*
+ * Variable: oSort
+ * Purpose: Wrapper for the sorting functions that can be used in DataTables
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: The functions provided in this object are basically standard javascript sort
+ * functions - they expect two inputs which they then compare and then return a priority
+ * result. For each sort method added, two functions need to be defined, an ascending sort and
+ * a descending sort.
+ */
+ _oExt.oSort = {
+ /*
+ * text sorting
+ */
+ "string-asc": function ( a, b )
+ {
+ var x = a.toLowerCase();
+ var y = b.toLowerCase();
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ },
+
+ "string-desc": function ( a, b )
+ {
+ var x = a.toLowerCase();
+ var y = b.toLowerCase();
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ },
+
+
+ /*
+ * html sorting (ignore html tags)
+ */
+ "html-asc": function ( a, b )
+ {
+ var x = a.replace( /<.*?>/g, "" ).toLowerCase();
+ var y = b.replace( /<.*?>/g, "" ).toLowerCase();
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ },
+
+ "html-desc": function ( a, b )
+ {
+ var x = a.replace( /<.*?>/g, "" ).toLowerCase();
+ var y = b.replace( /<.*?>/g, "" ).toLowerCase();
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ },
+
+
+ /*
+ * date sorting
+ */
+ "date-asc": function ( a, b )
+ {
+ var x = Date.parse( a );
+ var y = Date.parse( b );
+
+ if ( isNaN(x) || x==="" )
+ {
+ x = Date.parse( "01/01/1970 00:00:00" );
+ }
+ if ( isNaN(y) || y==="" )
+ {
+ y = Date.parse( "01/01/1970 00:00:00" );
+ }
+
+ return x - y;
+ },
+
+ "date-desc": function ( a, b )
+ {
+ var x = Date.parse( a );
+ var y = Date.parse( b );
+
+ if ( isNaN(x) || x==="" )
+ {
+ x = Date.parse( "01/01/1970 00:00:00" );
+ }
+ if ( isNaN(y) || y==="" )
+ {
+ y = Date.parse( "01/01/1970 00:00:00" );
+ }
+
+ return y - x;
+ },
+
+
+ /*
+ * numerical sorting
+ */
+ "numeric-asc": function ( a, b )
+ {
+ var x = (a=="-" || a==="") ? 0 : a*1;
+ var y = (b=="-" || b==="") ? 0 : b*1;
+ return x - y;
+ },
+
+ "numeric-desc": function ( a, b )
+ {
+ var x = (a=="-" || a==="") ? 0 : a*1;
+ var y = (b=="-" || b==="") ? 0 : b*1;
+ return y - x;
+ }
+ };
+
+
+ /*
+ * Variable: aTypes
+ * Purpose: Container for the various type of type detection that dataTables supports
+ * Scope: jQuery.fn.dataTableExt
+ * Notes: The functions in this array are expected to parse a string to see if it is a data
+ * type that it recognises. If so then the function should return the name of the type (a
+ * corresponding sort function should be defined!), if the type is not recognised then the
+ * function should return null such that the parser and move on to check the next type.
+ * Note that ordering is important in this array - the functions are processed linearly,
+ * starting at index 0.
+ * Note that the input for these functions is always a string! It cannot be any other data
+ * type
+ */
+ _oExt.aTypes = [
+ /*
+ * Function: -
+ * Purpose: Check to see if a string is numeric
+ * Returns: string:'numeric' or null
+ * Inputs: string:sText - string to check
+ */
+ function ( sData )
+ {
+ /* Allow zero length strings as a number */
+ if ( sData.length === 0 )
+ {
+ return 'numeric';
+ }
+
+ var sValidFirstChars = "0123456789-";
+ var sValidChars = "0123456789.";
+ var Char;
+ var bDecimal = false;
+
+ /* Check for a valid first char (no period and allow negatives) */
+ Char = sData.charAt(0);
+ if (sValidFirstChars.indexOf(Char) == -1)
+ {
+ return null;
+ }
+
+ /* Check all the other characters are valid */
+ for ( var i=1 ; i<sData.length ; i++ )
+ {
+ Char = sData.charAt(i);
+ if (sValidChars.indexOf(Char) == -1)
+ {
+ return null;
+ }
+
+ /* Only allowed one decimal place... */
+ if ( Char == "." )
+ {
+ if ( bDecimal )
+ {
+ return null;
+ }
+ bDecimal = true;
+ }
+ }
+
+ return 'numeric';
+ },
+
+ /*
+ * Function: -
+ * Purpose: Check to see if a string is actually a formatted date
+ * Returns: string:'date' or null
+ * Inputs: string:sText - string to check
+ */
+ function ( sData )
+ {
+ var iParse = Date.parse(sData);
+ if ( (iParse !== null && !isNaN(iParse)) || sData.length === 0 )
+ {
+ return 'date';
+ }
+ return null;
+ },
+
+ /*
+ * Function: -
+ * Purpose: Check to see if a string should be treated as an HTML string
+ * Returns: string:'html' or null
+ * Inputs: string:sText - string to check
+ */
+ function ( sData )
+ {
+ if ( sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
+ {
+ return 'html';
+ }
+ return null;
+ }
+ ];
+
+ /*
+ * Function: fnVersionCheck
+ * Purpose: Check a version string against this version of DataTables. Useful for plug-ins
+ * Returns: bool:true -this version of DataTables is greater or equal to the required version
+ * false -this version of DataTales is not suitable
+ * Inputs: string:sVersion - the version to check against. May be in the following formats:
+ * "a", "a.b" or "a.b.c"
+ * Notes: This function will only check the first three parts of a version string. It is
+ * assumed that beta and dev versions will meet the requirements. This might change in future
+ */
+ _oExt.fnVersionCheck = function( sVersion )
+ {
+ /* This is cheap, but very effective */
+ var fnZPad = function (Zpad, count)
+ {
+ while(Zpad.length < count) {
+ Zpad += '0';
+ }
+ return Zpad;
+ };
+ var aThis = _oExt.sVersion.split('.');
+ var aThat = sVersion.split('.');
+ var sThis = '', sThat = '';
+
+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
+ {
+ sThis += fnZPad( aThis[i], 3 );
+ sThat += fnZPad( aThat[i], 3 );
+ }
+
+ return parseInt(sThis, 10) >= parseInt(sThat, 10);
+ };
+
+ /*
+ * Variable: _oExternConfig
+ * Purpose: Store information for DataTables to access globally about other instances
+ * Scope: jQuery.fn.dataTableExt
+ */
+ _oExt._oExternConfig = {
+ /* int:iNextUnique - next unique number for an instance */
+ "iNextUnique": 0
+ };
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - DataTables prototype
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /*
+ * Function: dataTable
+ * Purpose: DataTables information
+ * Returns: -
+ * Inputs: object:oInit - initalisation options for the table
+ */
+ $.fn.dataTable = function( oInit )
+ {
+ /*
+ * Function: classSettings
+ * Purpose: Settings container function for all 'class' properties which are required
+ * by dataTables
+ * Returns: -
+ * Inputs: -
+ */
+ function classSettings ()
+ {
+ this.fnRecordsTotal = function ()
+ {
+ if ( this.oFeatures.bServerSide ) {
+ return parseInt(this._iRecordsTotal, 10);
+ } else {
+ return this.aiDisplayMaster.length;
+ }
+ };
+
+ this.fnRecordsDisplay = function ()
+ {
+ if ( this.oFeatures.bServerSide ) {
+ return parseInt(this._iRecordsDisplay, 10);
+ } else {
+ return this.aiDisplay.length;
+ }
+ };
+
+ this.fnDisplayEnd = function ()
+ {
+ if ( this.oFeatures.bServerSide ) {
+ if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
+ return this._iDisplayStart+this.aiDisplay.length;
+ } else {
+ return Math.min( this._iDisplayStart+this._iDisplayLength,
+ this._iRecordsDisplay );
+ }
+ } else {
+ return this._iDisplayEnd;
+ }
+ };
+
+ /*
+ * Variable: oInstance
+ * Purpose: The DataTables object for this table
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oInstance = null;
+
+ /*
+ * Variable: sInstance
+ * Purpose: Unique idendifier for each instance of the DataTables object
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sInstance = null;
+
+ /*
+ * Variable: oFeatures
+ * Purpose: Indicate the enablement of key dataTable features
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oFeatures = {
+ "bPaginate": true,
+ "bLengthChange": true,
+ "bFilter": true,
+ "bSort": true,
+ "bInfo": true,
+ "bAutoWidth": true,
+ "bProcessing": false,
+ "bSortClasses": true,
+ "bStateSave": false,
+ "bServerSide": false
+ };
+
+ /*
+ * Variable: oScroll
+ * Purpose: Container for scrolling options
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oScroll = {
+ "sX": "",
+ "sXInner": "",
+ "sY": "",
+ "bCollapse": false,
+ "bInfinite": false,
+ "iLoadGap": 100,
+ "iBarWidth": 0,
+ "bAutoCss": true
+ };
+
+ /*
+ * Variable: aanFeatures
+ * Purpose: Array referencing the nodes which are used for the features
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: The parameters of this object match what is allowed by sDom - i.e.
+ * 'l' - Length changing
+ * 'f' - Filtering input
+ * 't' - The table!
+ * 'i' - Information
+ * 'p' - Pagination
+ * 'r' - pRocessing
+ */
+ this.aanFeatures = [];
+
+ /*
+ * Variable: oLanguage
+ * Purpose: Store the language strings used by dataTables
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: The words in the format _VAR_ are variables which are dynamically replaced
+ * by javascript
+ */
+ this.oLanguage = {
+ "sProcessing": "Processing...",
+ "sLengthMenu": "Show _MENU_ entries",
+ "sZeroRecords": "No matching records found",
+ "sEmptyTable": "No data available in table",
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
+ "sInfoPostFix": "",
+ "sSearch": "Search:",
+ "sUrl": "",
+ "oPaginate": {
+ "sFirst": "First",
+ "sPrevious": "Previous",
+ "sNext": "Next",
+ "sLast": "Last"
+ },
+ "fnInfoCallback": null
+ };
+
+ /*
+ * Variable: aoData
+ * Purpose: Store data information
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: This is an array of objects with the following parameters:
+ * int: _iId - internal id for tracking
+ * array: _aData - internal data - used for sorting / filtering etc
+ * node: nTr - display node
+ * array node: _anHidden - hidden TD nodes
+ * string: _sRowStripe
+ */
+ this.aoData = [];
+
+ /*
+ * Variable: aiDisplay
+ * Purpose: Array of indexes which are in the current display (after filtering etc)
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.aiDisplay = [];
+
+ /*
+ * Variable: aiDisplayMaster
+ * Purpose: Array of indexes for display - no filtering
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.aiDisplayMaster = [];
+
+ /*
+ * Variable: aoColumns
+ * Purpose: Store information about each column that is in use
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.aoColumns = [];
+
+ /*
+ * Variable: iNextId
+ * Purpose: Store the next unique id to be used for a new row
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.iNextId = 0;
+
+ /*
+ * Variable: asDataSearch
+ * Purpose: Search data array for regular expression searching
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.asDataSearch = [];
+
+ /*
+ * Variable: oPreviousSearch
+ * Purpose: Store the previous search incase we want to force a re-search
+ * or compare the old search to a new one
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oPreviousSearch = {
+ "sSearch": "",
+ "bRegex": false,
+ "bSmart": true
+ };
+
+ /*
+ * Variable: aoPreSearchCols
+ * Purpose: Store the previous search for each column
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.aoPreSearchCols = [];
+
+ /*
+ * Variable: aaSorting
+ * Purpose: Sorting information
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: Index 0 - column number
+ * Index 1 - current sorting direction
+ * Index 2 - index of asSorting for this column
+ */
+ this.aaSorting = [ [0, 'asc', 0] ];
+
+ /*
+ * Variable: aaSortingFixed
+ * Purpose: Sorting information that is always applied
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.aaSortingFixed = null;
+
+ /*
+ * Variable: asStripClasses
+ * Purpose: Classes to use for the striping of a table
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.asStripClasses = [];
+
+ /*
+ * Variable: asDestoryStrips
+ * Purpose: If restoring a table - we should restore it's striping classes as well
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.asDestoryStrips = [];
+
+ /*
+ * Variable: sDestroyWidth
+ * Purpose: If restoring a table - we should restore it's width
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sDestroyWidth = 0;
+
+ /*
+ * Variable: fnRowCallback
+ * Purpose: Call this function every time a row is inserted (draw)
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnRowCallback = null;
+
+ /*
+ * Variable: fnHeaderCallback
+ * Purpose: Callback function for the header on each draw
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnHeaderCallback = null;
+
+ /*
+ * Variable: fnFooterCallback
+ * Purpose: Callback function for the footer on each draw
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnFooterCallback = null;
+
+ /*
+ * Variable: aoDrawCallback
+ * Purpose: Array of callback functions for draw callback functions
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: Each array element is an object with the following parameters:
+ * function:fn - function to call
+ * string:sName - name callback (feature). useful for arranging array
+ */
+ this.aoDrawCallback = [];
+
+ /*
+ * Variable: fnInitComplete
+ * Purpose: Callback function for when the table has been initalised
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnInitComplete = null;
+
+ /*
+ * Variable: sTableId
+ * Purpose: Cache the table ID for quick access
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sTableId = "";
+
+ /*
+ * Variable: nTable
+ * Purpose: Cache the table node for quick access
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.nTable = null;
+
+ /*
+ * Variable: nTHead
+ * Purpose: Permanent ref to the thead element
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.nTHead = null;
+
+ /*
+ * Variable: nTFoot
+ * Purpose: Permanent ref to the tfoot element - if it exists
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.nTFoot = null;
+
+ /*
+ * Variable: nTBody
+ * Purpose: Permanent ref to the tbody element
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.nTBody = null;
+
+ /*
+ * Variable: nTableWrapper
+ * Purpose: Cache the wrapper node (contains all DataTables controlled elements)
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.nTableWrapper = null;
+
+ /*
+ * Variable: bInitialised
+ * Purpose: Indicate if all required information has been read in
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.bInitialised = false;
+
+ /*
+ * Variable: aoOpenRows
+ * Purpose: Information about open rows
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: Has the parameters 'nTr' and 'nParent'
+ */
+ this.aoOpenRows = [];
+
+ /*
+ * Variable: sDom
+ * Purpose: Dictate the positioning that the created elements will take
+ * Scope: jQuery.dataTable.classSettings
+ * Notes:
+ * The following options are allowed:
+ * 'l' - Length changing
+ * 'f' - Filtering input
+ * 't' - The table!
+ * 'i' - Information
+ * 'p' - Pagination
+ * 'r' - pRocessing
+ * The following constants are allowed:
+ * 'H' - jQueryUI theme "header" classes
+ * 'F' - jQueryUI theme "footer" classes
+ * The following syntax is expected:
+ * '<' and '>' - div elements
+ * '<"class" and '>' - div with a class
+ * Examples:
+ * '<"wrapper"flipt>', '<lf<t>ip>'
+ */
+ this.sDom = 'lfrtip';
+
+ /*
+ * Variable: sPaginationType
+ * Purpose: Note which type of sorting should be used
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sPaginationType = "two_button";
+
+ /*
+ * Variable: iCookieDuration
+ * Purpose: The cookie duration (for bStateSave) in seconds - default 2 hours
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.iCookieDuration = 60 * 60 * 2;
+
+ /*
+ * Variable: sCookiePrefix
+ * Purpose: The cookie name prefix
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sCookiePrefix = "SpryMedia_DataTables_";
+
+ /*
+ * Variable: fnCookieCallback
+ * Purpose: Callback function for cookie creation
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnCookieCallback = null;
+
+ /*
+ * Variable: aoStateSave
+ * Purpose: Array of callback functions for state saving
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: Each array element is an object with the following parameters:
+ * function:fn - function to call. Takes two parameters, oSettings and the JSON string to
+ * save that has been thus far created. Returns a JSON string to be inserted into a
+ * json object (i.e. '"param": [ 0, 1, 2]')
+ * string:sName - name of callback
+ */
+ this.aoStateSave = [];
+
+ /*
+ * Variable: aoStateLoad
+ * Purpose: Array of callback functions for state loading
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: Each array element is an object with the following parameters:
+ * function:fn - function to call. Takes two parameters, oSettings and the object stored.
+ * May return false to cancel state loading.
+ * string:sName - name of callback
+ */
+ this.aoStateLoad = [];
+
+ /*
+ * Variable: oLoadedState
+ * Purpose: State that was loaded from the cookie. Useful for back reference
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oLoadedState = null;
+
+ /*
+ * Variable: sAjaxSource
+ * Purpose: Source url for AJAX data for the table
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.sAjaxSource = null;
+
+ /*
+ * Variable: bAjaxDataGet
+ * Purpose: Note if draw should be blocked while getting data
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.bAjaxDataGet = true;
+
+ /*
+ * Variable: fnServerData
+ * Purpose: Function to get the server-side data - can be overruled by the developer
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnServerData = function ( url, data, callback ) {
+ $.ajax( {
+ "url": url,
+ "data": data,
+ "success": callback,
+ "dataType": "json",
+ "cache": false,
+ "error": function (xhr, error, thrown) {
+ if ( error == "parsererror" ) {
+ alert( "DataTables warning: JSON data from server could not be parsed. "+
+ "This is caused by a JSON formatting error." );
+ }
+ }
+ } );
+ };
+
+ /*
+ * Variable: fnFormatNumber
+ * Purpose: Format numbers for display
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.fnFormatNumber = function ( iIn )
+ {
+ if ( iIn < 1000 )
+ {
+ /* A small optimisation for what is likely to be the vast majority of use cases */
+ return iIn;
+ }
+ else
+ {
+ var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
+
+ for ( var i=0 ; i<iLen ; i++ )
+ {
+ if ( i%3 === 0 && i !== 0 )
+ {
+ out = ','+out;
+ }
+ out = a[iLen-i-1]+out;
+ }
+ }
+ return out;
+ };
+
+ /*
+ * Variable: aLengthMenu
+ * Purpose: List of options that can be used for the user selectable length menu
+ * Scope: jQuery.dataTable.classSettings
+ * Note: This varaible can take for form of a 1D array, in which case the value and the
+ * displayed value in the menu are the same, or a 2D array in which case the value comes
+ * from the first array, and the displayed value to the end user comes from the second
+ * array. 2D example: [ [ 10, 25, 50, 100, -1 ], [ 10, 25, 50, 100, 'All' ] ];
+ */
+ this.aLengthMenu = [ 10, 25, 50, 100 ];
+
+ /*
+ * Variable: iDraw
+ * Purpose: Counter for the draws that the table does. Also used as a tracker for
+ * server-side processing
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.iDraw = 0;
+
+ /*
+ * Variable: bDrawing
+ * Purpose: Indicate if a redraw is being done - useful for Ajax
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.bDrawing = 0;
+
+ /*
+ * Variable: iDrawError
+ * Purpose: Last draw error
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.iDrawError = -1;
+
+ /*
+ * Variable: _iDisplayLength, _iDisplayStart, _iDisplayEnd
+ * Purpose: Display length variables
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: These variable must NOT be used externally to get the data length. Rather, use
+ * the fnRecordsTotal() (etc) functions.
+ */
+ this._iDisplayLength = 10;
+ this._iDisplayStart = 0;
+ this._iDisplayEnd = 10;
+
+ /*
+ * Variable: _iRecordsTotal, _iRecordsDisplay
+ * Purpose: Display length variables used for server side processing
+ * Scope: jQuery.dataTable.classSettings
+ * Notes: These variable must NOT be used externally to get the data length. Rather, use
+ * the fnRecordsTotal() (etc) functions.
+ */
+ this._iRecordsTotal = 0;
+ this._iRecordsDisplay = 0;
+
+ /*
+ * Variable: bJUI
+ * Purpose: Should we add the markup needed for jQuery UI theming?
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.bJUI = false;
+
+ /*
+ * Variable: bJUI
+ * Purpose: Should we add the markup needed for jQuery UI theming?
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oClasses = _oExt.oStdClasses;
+
+ /*
+ * Variable: bFiltered and bSorted
+ * Purpose: Flags to allow callback functions to see what actions have been performed
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.bFiltered = false;
+ this.bSorted = false;
+
+ /*
+ * Variable: oInit
+ * Purpose: Initialisation object that is used for the table
+ * Scope: jQuery.dataTable.classSettings
+ */
+ this.oInit = null;
+ }
+
+ /*
+ * Variable: oApi
+ * Purpose: Container for publicly exposed 'private' functions
+ * Scope: jQuery.dataTable
+ */
+ this.oApi = {};
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - API functions
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /*
+ * Function: fnDraw
+ * Purpose: Redraw the table
+ * Returns: -
+ * Inputs: bool:bComplete - Refilter and resort (if enabled) the table before the draw.
+ * Optional: default - true
+ */
+ this.fnDraw = function( bComplete )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ if ( typeof bComplete != 'undefined' && bComplete === false )
+ {
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+ else
+ {
+ _fnReDraw( oSettings );
+ }
+ };
+
+ /*
+ * Function: fnFilter
+ * Purpose: Filter the input based on data
+ * Returns: -
+ * Inputs: string:sInput - string to filter the table on
+ * int:iColumn - optional - column to limit filtering to
+ * bool:bRegex - optional - treat as regular expression or not - default false
+ * bool:bSmart - optional - perform smart filtering or not - default true
+ * bool:bShowGlobal - optional - show the input global filter in it's input box(es)
+ * - default true
+ */
+ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ if ( !oSettings.oFeatures.bFilter )
+ {
+ return;
+ }
+
+ if ( typeof bRegex == 'undefined' )
+ {
+ bRegex = false;
+ }
+
+ if ( typeof bSmart == 'undefined' )
+ {
+ bSmart = true;
+ }
+
+ if ( typeof bShowGlobal == 'undefined' )
+ {
+ bShowGlobal = true;
+ }
+
+ if ( typeof iColumn == "undefined" || iColumn === null )
+ {
+ /* Global filter */
+ _fnFilterComplete( oSettings, {
+ "sSearch":sInput,
+ "bRegex": bRegex,
+ "bSmart": bSmart
+ }, 1 );
+
+ if ( bShowGlobal && typeof oSettings.aanFeatures.f != 'undefined' )
+ {
+ var n = oSettings.aanFeatures.f;
+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+ {
+ $('input', n[i]).val( sInput );
+ }
+ }
+ }
+ else
+ {
+ /* Single column filter */
+ oSettings.aoPreSearchCols[ iColumn ].sSearch = sInput;
+ oSettings.aoPreSearchCols[ iColumn ].bRegex = bRegex;
+ oSettings.aoPreSearchCols[ iColumn ].bSmart = bSmart;
+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+ }
+ };
+
+ /*
+ * Function: fnSettings
+ * Purpose: Get the settings for a particular table for extern. manipulation
+ * Returns: -
+ * Inputs: -
+ */
+ this.fnSettings = function( nNode )
+ {
+ return _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ };
+
+ /*
+ * Function: fnVersionCheck
+ * Notes: The function is the same as the 'static' function provided in the ext variable
+ */
+ this.fnVersionCheck = _oExt.fnVersionCheck;
+
+ /*
+ * Function: fnSort
+ * Purpose: Sort the table by a particular row
+ * Returns: -
+ * Inputs: int:iCol - the data index to sort on. Note that this will
+ * not match the 'display index' if you have hidden data entries
+ */
+ this.fnSort = function( aaSort )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ oSettings.aaSorting = aaSort;
+ _fnSort( oSettings );
+ };
+
+ /*
+ * Function: fnSortListener
+ * Purpose: Attach a sort listener to an element for a given column
+ * Returns: -
+ * Inputs: node:nNode - the element to attach the sort listener to
+ * int:iColumn - the column that a click on this node will sort on
+ * function:fnCallback - callback function when sort is run - optional
+ */
+ this.fnSortListener = function( nNode, iColumn, fnCallback )
+ {
+ _fnSortAttachListener( _fnSettingsFromNode( this[_oExt.iApiIndex] ), nNode, iColumn,
+ fnCallback );
+ };
+
+ /*
+ * Function: fnAddData
+ * Purpose: Add new row(s) into the table
+ * Returns: array int: array of indexes (aoData) which have been added (zero length on error)
+ * Inputs: array:mData - the data to be added. The length must match
+ * the original data from the DOM
+ * or
+ * array array:mData - 2D array of data to be added
+ * bool:bRedraw - redraw the table or not - default true
+ * Notes: Warning - the refilter here will cause the table to redraw
+ * starting at zero
+ * Notes: Thanks to Yekimov Denis for contributing the basis for this function!
+ */
+ this.fnAddData = function( mData, bRedraw )
+ {
+ if ( mData.length === 0 )
+ {
+ return [];
+ }
+
+ var aiReturn = [];
+ var iTest;
+
+ /* Find settings from table node */
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ /* Check if we want to add multiple rows or not */
+ if ( typeof mData[0] == "object" )
+ {
+ for ( var i=0 ; i<mData.length ; i++ )
+ {
+ iTest = _fnAddData( oSettings, mData[i] );
+ if ( iTest == -1 )
+ {
+ return aiReturn;
+ }
+ aiReturn.push( iTest );
+ }
+ }
+ else
+ {
+ iTest = _fnAddData( oSettings, mData );
+ if ( iTest == -1 )
+ {
+ return aiReturn;
+ }
+ aiReturn.push( iTest );
+ }
+
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnReDraw( oSettings );
+ }
+ return aiReturn;
+ };
+
+ /*
+ * Function: fnDeleteRow
+ * Purpose: Remove a row for the table
+ * Returns: array:aReturn - the row that was deleted
+ * Inputs: mixed:mTarget -
+ * int: - index of aoData to be deleted, or
+ * node(TR): - TR element you want to delete
+ * function:fnCallBack - callback function - default null
+ * bool:bRedraw - redraw the table or not - default true
+ */
+ this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
+ {
+ /* Find settings from table node */
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ var i, iAODataIndex;
+
+ iAODataIndex = (typeof mTarget == 'object') ?
+ _fnNodeToDataIndex(oSettings, mTarget) : mTarget;
+
+ /* Return the data array from this row */
+ var oData = oSettings.aoData.splice( iAODataIndex, 1 );
+
+ /* Remove the target row from the search array */
+ var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
+ oSettings.asDataSearch.splice( iDisplayIndex, 1 );
+
+ /* Delete from the display arrays */
+ _fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
+ _fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
+
+ /* If there is a user callback function - call it */
+ if ( typeof fnCallBack == "function" )
+ {
+ fnCallBack.call( this, oSettings, oData );
+ }
+
+ /* Check for an 'overflow' they case for dislaying the table */
+ if ( oSettings._iDisplayStart >= oSettings.aiDisplay.length )
+ {
+ oSettings._iDisplayStart -= oSettings._iDisplayLength;
+ if ( oSettings._iDisplayStart < 0 )
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ }
+
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+
+ return oData;
+ };
+
+ /*
+ * Function: fnClearTable
+ * Purpose: Quickly and simply clear a table
+ * Returns: -
+ * Inputs: bool:bRedraw - redraw the table or not - default true
+ * Notes: Thanks to Yekimov Denis for contributing the basis for this function!
+ */
+ this.fnClearTable = function( bRedraw )
+ {
+ /* Find settings from table node */
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ _fnClearTable( oSettings );
+
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnDraw( oSettings );
+ }
+ };
+
+ /*
+ * Function: fnOpen
+ * Purpose: Open a display row (append a row after the row in question)
+ * Returns: node:nNewRow - the row opened
+ * Inputs: node:nTr - the table row to 'open'
+ * string:sHtml - the HTML to put into the row
+ * string:sClass - class to give the new TD cell
+ */
+ this.fnOpen = function( nTr, sHtml, sClass )
+ {
+ /* Find settings from table node */
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ /* the old open one if there is one */
+ this.fnClose( nTr );
+
+ var nNewRow = document.createElement("tr");
+ var nNewCell = document.createElement("td");
+ nNewRow.appendChild( nNewCell );
+ nNewCell.className = sClass;
+ nNewCell.colSpan = _fnVisbleColumns( oSettings );
+ nNewCell.innerHTML = sHtml;
+
+ /* If the nTr isn't on the page at the moment - then we don't insert at the moment */
+ var nTrs = $('tr', oSettings.nTBody);
+ if ( $.inArray(nTr, nTrs) != -1 )
+ {
+ $(nNewRow).insertAfter(nTr);
+ }
+
+ oSettings.aoOpenRows.push( {
+ "nTr": nNewRow,
+ "nParent": nTr
+ } );
+
+ return nNewRow;
+ };
+
+ /*
+ * Function: fnClose
+ * Purpose: Close a display row
+ * Returns: int: 0 (success) or 1 (failed)
+ * Inputs: node:nTr - the table row to 'close'
+ */
+ this.fnClose = function( nTr )
+ {
+ /* Find settings from table node */
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
+ {
+ if ( oSettings.aoOpenRows[i].nParent == nTr )
+ {
+ var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
+ if ( nTrParent )
+ {
+ /* Remove it if it is currently on display */
+ nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
+ }
+ oSettings.aoOpenRows.splice( i, 1 );
+ return 0;
+ }
+ }
+ return 1;
+ };
+
+ /*
+ * Function: fnGetData
+ * Purpose: Return an array with the data which is used to make up the table
+ * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array
+ * or
+ * array string (if iRow specified)
+ * Inputs: mixed:mRow - optional - if not present, then the full 2D array for the table
+ * if given then:
+ * int: - return 1D array for aoData entry of this index
+ * node(TR): - return 1D array for this TR element
+ * Inputs: int:iRow - optional - if present then the array returned will be the data for
+ * the row with the index 'iRow'
+ */
+ this.fnGetData = function( mRow )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ if ( typeof mRow != 'undefined' )
+ {
+ var iRow = (typeof mRow == 'object') ?
+ _fnNodeToDataIndex(oSettings, mRow) : mRow;
+ return ( (aRowData = oSettings.aoData[iRow]) ? aRowData._aData : null);
+ }
+ return _fnGetDataMaster( oSettings );
+ };
+
+ /*
+ * Function: fnGetNodes
+ * Purpose: Return an array with the TR nodes used for drawing the table
+ * Returns: array node: TR elements
+ * or
+ * node (if iRow specified)
+ * Inputs: int:iRow - optional - if present then the array returned will be the node for
+ * the row with the index 'iRow'
+ */
+ this.fnGetNodes = function( iRow )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+
+ if ( typeof iRow != 'undefined' )
+ {
+ return ( (aRowData = oSettings.aoData[iRow]) ? aRowData.nTr : null );
+ }
+ return _fnGetTrNodes( oSettings );
+ };
+
+ /*
+ * Function: fnGetPosition
+ * Purpose: Get the array indexes of a particular cell from it's DOM element
+ * Returns: int: - row index, or array[ int, int, int ]: - row index, column index (visible)
+ * and column index including hidden columns
+ * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is
+ * dependent on this input
+ */
+ this.fnGetPosition = function( nNode )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ var i;
+
+ if ( nNode.nodeName.toUpperCase() == "TR" )
+ {
+ return _fnNodeToDataIndex(oSettings, nNode);
+ }
+ else if ( nNode.nodeName.toUpperCase() == "TD" )
+ {
+ var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode);
+ var iCorrector = 0;
+ for ( var j=0 ; j<oSettings.aoColumns.length ; j++ )
+ {
+ if ( oSettings.aoColumns[j].bVisible )
+ {
+ if ( oSettings.aoData[iDataIndex].nTr.getElementsByTagName('td')[j-iCorrector] == nNode )
+ {
+ return [ iDataIndex, j-iCorrector, j ];
+ }
+ }
+ else
+ {
+ iCorrector++;
+ }
+ }
+ }
+ return null;
+ };
+
+ /*
+ * Function: fnUpdate
+ * Purpose: Update a table cell or row
+ * Returns: int: 0 okay, 1 error
+ * Inputs: array string 'or' string:mData - data to update the cell/row with
+ * mixed:mRow -
+ * int: - index of aoData to be updated, or
+ * node(TR): - TR element you want to update
+ * int:iColumn - the column to update - optional (not used of mData is 2D)
+ * bool:bRedraw - redraw the table or not - default true
+ * bool:bAction - perform predraw actions or not (you will want this as 'true' if
+ * you have bRedraw as true) - default true
+ */
+ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ var iVisibleColumn;
+ var sDisplay;
+ var iRow = (typeof mRow == 'object') ?
+ _fnNodeToDataIndex(oSettings, mRow) : mRow;
+
+ if ( typeof mData != 'object' )
+ {
+ sDisplay = mData;
+ oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
+
+ if ( oSettings.aoColumns[iColumn].fnRender !== null )
+ {
+ sDisplay = oSettings.aoColumns[iColumn].fnRender( {
+ "iDataRow": iRow,
+ "iDataColumn": iColumn,
+ "aData": oSettings.aoData[iRow]._aData,
+ "oSettings": oSettings
+ } );
+
+ if ( oSettings.aoColumns[iColumn].bUseRendered )
+ {
+ oSettings.aoData[iRow]._aData[iColumn] = sDisplay;
+ }
+ }
+
+ iVisibleColumn = _fnColumnIndexToVisible( oSettings, iColumn );
+ if ( iVisibleColumn !== null )
+ {
+ oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
+ sDisplay;
+ }
+ else
+ {
+ oSettings.aoData[iRow]._anHidden[iColumn].innerHTML = sDisplay;
+ }
+ }
+ else
+ {
+ if ( mData.length != oSettings.aoColumns.length )
+ {
+ _fnLog( oSettings, 0, 'An array passed to fnUpdate must have the same number of '+
+ 'columns as the table in question - in this case '+oSettings.aoColumns.length );
+ return 1;
+ }
+
+ for ( var i=0 ; i<mData.length ; i++ )
+ {
+ sDisplay = mData[i];
+ oSettings.aoData[iRow]._aData[i] = sDisplay;
+
+ if ( oSettings.aoColumns[i].fnRender !== null )
+ {
+ sDisplay = oSettings.aoColumns[i].fnRender( {
+ "iDataRow": iRow,
+ "iDataColumn": i,
+ "aData": oSettings.aoData[iRow]._aData,
+ "oSettings": oSettings
+ } );
+
+ if ( oSettings.aoColumns[i].bUseRendered )
+ {
+ oSettings.aoData[iRow]._aData[i] = sDisplay;
+ }
+ }
+
+ iVisibleColumn = _fnColumnIndexToVisible( oSettings, i );
+ if ( iVisibleColumn !== null )
+ {
+ oSettings.aoData[iRow].nTr.getElementsByTagName('td')[iVisibleColumn].innerHTML =
+ sDisplay;
+ }
+ else
+ {
+ oSettings.aoData[iRow]._anHidden[i].innerHTML = sDisplay;
+ }
+ }
+ }
+
+ /* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
+ * will rebuild the search array - however, the redraw might be disabled by the user)
+ */
+ var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
+ oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow( oSettings,
+ oSettings.aoData[iRow]._aData );
+
+ /* Perform pre-draw actions */
+ if ( typeof bAction == 'undefined' || bAction )
+ {
+ _fnAjustColumnSizing( oSettings );
+ }
+
+ /* Redraw the table */
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnReDraw( oSettings );
+ }
+ return 0;
+ };
+
+
+ /*
+ * Function: fnShowColoumn
+ * Purpose: Show a particular column
+ * Returns: -
+ * Inputs: int:iCol - the column whose display should be changed
+ * bool:bShow - show (true) or hide (false) the column
+ * bool:bRedraw - redraw the table or not - default true
+ */
+ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ var i, iLen;
+ var iColumns = oSettings.aoColumns.length;
+ var nTd, anTds, nCell, anTrs, jqChildren;
+
+ /* No point in doing anything if we are requesting what is already true */
+ if ( oSettings.aoColumns[iCol].bVisible == bShow )
+ {
+ return;
+ }
+
+ var nTrHead = $('>tr', oSettings.nTHead)[0];
+ var nTrFoot = $('>tr', oSettings.nTFoot)[0];
+ var anTheadTh = [];
+ var anTfootTh = [];
+ for ( i=0 ; i<iColumns ; i++ )
+ {
+ anTheadTh.push( oSettings.aoColumns[i].nTh );
+ anTfootTh.push( oSettings.aoColumns[i].nTf );
+ }
+
+ /* Show the column */
+ if ( bShow )
+ {
+ var iInsert = 0;
+ for ( i=0 ; i<iCol ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ iInsert++;
+ }
+ }
+
+ /* Need to decide if we should use appendChild or insertBefore */
+ if ( iInsert >= _fnVisbleColumns( oSettings ) )
+ {
+ nTrHead.appendChild( anTheadTh[iCol] );
+ anTrs = $('>tr', oSettings.nTHead);
+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
+ {
+ anTrs[i].appendChild( oSettings.aoColumns[iCol].anThExtra[i-1] );
+ }
+
+ if ( nTrFoot )
+ {
+ nTrFoot.appendChild( anTfootTh[iCol] );
+ anTrs = $('>tr', oSettings.nTFoot);
+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
+ {
+ anTrs[i].appendChild( oSettings.aoColumns[iCol].anTfExtra[i-1] );
+ }
+ }
+
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+ {
+ nTd = oSettings.aoData[i]._anHidden[iCol];
+ oSettings.aoData[i].nTr.appendChild( nTd );
+ }
+ }
+ else
+ {
+ /* Which coloumn should we be inserting before? */
+ var iBefore;
+ for ( i=iCol ; i<iColumns ; i++ )
+ {
+ iBefore = _fnColumnIndexToVisible( oSettings, i );
+ if ( iBefore !== null )
+ {
+ break;
+ }
+ }
+
+ nTrHead.insertBefore( anTheadTh[iCol], nTrHead.getElementsByTagName('th')[iBefore] );
+ anTrs = $('>tr', oSettings.nTHead);
+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
+ {
+ jqChildren = $(anTrs[i]).children();
+ anTrs[i].insertBefore( oSettings.aoColumns[iCol].anThExtra[i-1], jqChildren[iBefore] );
+ }
+
+ if ( nTrFoot )
+ {
+ nTrFoot.insertBefore( anTfootTh[iCol], nTrFoot.getElementsByTagName('th')[iBefore] );
+ anTrs = $('>tr', oSettings.nTFoot);
+ for ( i=1, iLen=anTrs.length ; i<iLen ; i++ )
+ {
+ jqChildren = $(anTrs[i]).children();
+ anTrs[i].insertBefore( oSettings.aoColumns[iCol].anTfExtra[i-1], jqChildren[iBefore] );
+ }
+ }
+
+ anTds = _fnGetTdNodes( oSettings );
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+ {
+ nTd = oSettings.aoData[i]._anHidden[iCol];
+ oSettings.aoData[i].nTr.insertBefore( nTd, $('>td:eq('+iBefore+')',
+ oSettings.aoData[i].nTr)[0] );
+ }
+ }
+
+ oSettings.aoColumns[iCol].bVisible = true;
+ }
+ else
+ {
+ /* Remove a column from display */
+ nTrHead.removeChild( anTheadTh[iCol] );
+ for ( i=0, iLen=oSettings.aoColumns[iCol].anThExtra.length ; i<iLen ; i++ )
+ {
+ nCell = oSettings.aoColumns[iCol].anThExtra[i];
+ nCell.parentNode.removeChild( nCell );
+ }
+
+ if ( nTrFoot )
+ {
+ nTrFoot.removeChild( anTfootTh[iCol] );
+ for ( i=0, iLen=oSettings.aoColumns[iCol].anTfExtra.length ; i<iLen ; i++ )
+ {
+ nCell = oSettings.aoColumns[iCol].anTfExtra[i];
+ nCell.parentNode.removeChild( nCell );
+ }
+ }
+
+ anTds = _fnGetTdNodes( oSettings );
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+ {
+ nTd = anTds[ ( i*oSettings.aoColumns.length) + (iCol*1) ];
+ oSettings.aoData[i]._anHidden[iCol] = nTd;
+ nTd.parentNode.removeChild( nTd );
+ }
+
+ oSettings.aoColumns[iCol].bVisible = false;
+ }
+
+ /* If there are any 'open' rows, then we need to alter the colspan for this col change */
+ for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
+ {
+ oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
+ }
+
+ /* Do a redraw incase anything depending on the table columns needs it
+ * (built-in: scrolling)
+ */
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnAjustColumnSizing( oSettings );
+ _fnDraw( oSettings );
+ }
+
+ _fnSaveState( oSettings );
+ };
+
+ /*
+ * Function: fnPageChange
+ * Purpose: Change the pagination
+ * Returns: -
+ * Inputs: string:sAction - paging action to take: "first", "previous", "next" or "last"
+ * bool:bRedraw - redraw the table or not - optional - default true
+ */
+ this.fnPageChange = function ( sAction, bRedraw )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ _fnPageChange( oSettings, sAction );
+ _fnCalculateEnd( oSettings );
+
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ _fnDraw( oSettings );
+ }
+ };
+
+ /*
+ * Function: fnDestroy
+ * Purpose: Destructor for the DataTable
+ * Returns: -
+ * Inputs: -
+ */
+ this.fnDestroy = function ( )
+ {
+ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] );
+ var nOrig = oSettings.nTableWrapper.parentNode;
+ var nBody = oSettings.nTBody;
+ var i, iLen;
+
+ /* Flag to note that the table is currently being destoryed - no action should be taken */
+ oSettings.bDestroying = true;
+
+ /* Blitz all DT events */
+ $(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
+
+ /* Restore hidden columns */
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible === false )
+ {
+ this.fnSetColumnVis( i, true );
+ }
+ }
+
+ /* If there is an 'empty' indicator row, remove it */
+ $('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
+
+ /* When scrolling we had to break the table up - restore it */
+ if ( oSettings.nTable != oSettings.nTHead.parentNode )
+ {
+ $('>thead', oSettings.nTable).remove();
+ oSettings.nTable.appendChild( oSettings.nTHead );
+ }
+
+ if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
+ {
+ $('>tfoot', oSettings.nTable).remove();
+ oSettings.nTable.appendChild( oSettings.nTFoot );
+ }
+
+ /* Remove the DataTables generated nodes, events and classes */
+ oSettings.nTable.parentNode.removeChild( oSettings.nTable );
+ $(oSettings.nTableWrapper).remove();
+
+ oSettings.aaSorting = [];
+ oSettings.aaSortingFixed = [];
+ _fnSortingClasses( oSettings );
+
+ $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripClasses.join(' ') );
+
+ if ( !oSettings.bJUI )
+ {
+ $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
+ _oExt.oStdClasses.sSortableAsc,
+ _oExt.oStdClasses.sSortableDesc,
+ _oExt.oStdClasses.sSortableNone ].join(' ')
+ );
+ }
+ else
+ {
+ $('th', oSettings.nTHead).removeClass( [ _oExt.oStdClasses.sSortable,
+ _oExt.oJUIClasses.sSortableAsc,
+ _oExt.oJUIClasses.sSortableDesc,
+ _oExt.oJUIClasses.sSortableNone ].join(' ')
+ );
+ $('th span', oSettings.nTHead).remove();
+ }
+
+ /* Add the TR elements back into the table in their original order */
+ nOrig.appendChild( oSettings.nTable );
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+ {
+ nBody.appendChild( oSettings.aoData[i].nTr );
+ }
+
+ /* Restore the width of the original table */
+ oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
+
+ /* If the were originally odd/even type classes - then we add them back here. Note
+ * this is not fool proof (for example if not all rows as odd/even classes - but
+ * it's a good effort without getting carried away
+ */
+ $('>tr:even', nBody).addClass( oSettings.asDestoryStrips[0] );
+ $('>tr:odd', nBody).addClass( oSettings.asDestoryStrips[1] );
+
+ /* Remove the settings object from the settings array */
+ for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
+ {
+ if ( _aoSettings[i] == oSettings )
+ {
+ _aoSettings.splice( i, 1 );
+ }
+ }
+
+ /* End it all */
+ oSettings = null;
+ };
+
+ /*
+ * Function: fnAjustColumnSizing
+ * Purpose: Update tale sizing based on content. This would most likely be used for scrolling
+ * and will typically need a redraw after it.
+ * Returns: -
+ * Inputs: bool:bRedraw - redraw the table or not, you will typically want to - default true
+ */
+ this.fnAdjustColumnSizing = function ( bRedraw )
+ {
+ var oSettings = _fnSettingsFromNode(this[_oExt.iApiIndex]);
+ _fnAjustColumnSizing( oSettings );
+
+ if ( typeof bRedraw == 'undefined' || bRedraw )
+ {
+ this.fnDraw( false );
+ }
+ else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
+ {
+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
+ this.oApi._fnScrollDraw(oSettings);
+ }
+ };
+
+ /*
+ * Plugin API functions
+ *
+ * This call will add the functions which are defined in _oExt.oApi to the
+ * DataTables object, providing a rather nice way to allow plug-in API functions. Note that
+ * this is done here, so that API function can actually override the built in API functions if
+ * required for a particular purpose.
+ */
+
+ /*
+ * Function: _fnExternApiFunc
+ * Purpose: Create a wrapper function for exporting an internal func to an external API func
+ * Returns: function: - wrapped function
+ * Inputs: string:sFunc - API function name
+ */
+ function _fnExternApiFunc (sFunc)
+ {
+ return function() {
+ var aArgs = [_fnSettingsFromNode(this[_oExt.iApiIndex])].concat(
+ Array.prototype.slice.call(arguments) );
+ return _oExt.oApi[sFunc].apply( this, aArgs );
+ };
+ }
+
+ for ( var sFunc in _oExt.oApi )
+ {
+ if ( sFunc )
+ {
+ /*
+ * Function: anon
+ * Purpose: Wrap the plug-in API functions in order to provide the settings as 1st arg
+ * and execute in this scope
+ * Returns: -
+ * Inputs: -
+ */
+ this[sFunc] = _fnExternApiFunc(sFunc);
+ }
+ }
+
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Local functions
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Initalisation
+ */
+
+ /*
+ * Function: _fnInitalise
+ * Purpose: Draw the table for the first time, adding all required features
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnInitalise ( oSettings )
+ {
+ var i, iLen;
+
+ /* Ensure that the table data is fully initialised */
+ if ( oSettings.bInitialised === false )
+ {
+ setTimeout( function(){ _fnInitalise( oSettings ); }, 200 );
+ return;
+ }
+
+ /* Show the display HTML options */
+ _fnAddOptionsHtml( oSettings );
+
+ /* Draw the headers for the table */
+ _fnDrawHead( oSettings );
+
+ /* Okay to show that something is going on now */
+ _fnProcessingDisplay( oSettings, true );
+
+ /* Calculate sizes for columns */
+ if ( oSettings.oFeatures.bAutoWidth )
+ {
+ _fnCalculateColumnWidths( oSettings );
+ }
+
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ if ( oSettings.aoColumns[i].sWidth !== null )
+ {
+ oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
+ }
+ }
+
+ /* If there is default sorting required - let's do it. The sort function will do the
+ * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
+ * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
+ */
+ if ( oSettings.oFeatures.bSort )
+ {
+ _fnSort( oSettings );
+ }
+ else
+ {
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+
+ /* if there is an ajax source load the data */
+ if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
+ {
+ oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, [], function(json) {
+ /* Got the data - add it to the table */
+ for ( i=0 ; i<json.aaData.length ; i++ )
+ {
+ _fnAddData( oSettings, json.aaData[i] );
+ }
+
+ /* Reset the init display for cookie saving. We've already done a filter, and
+ * therefore cleared it before. So we need to make it appear 'fresh'
+ */
+ oSettings.iInitDisplayStart = oSettings._iDisplayStart;
+
+ if ( oSettings.oFeatures.bSort )
+ {
+ _fnSort( oSettings );
+ }
+ else
+ {
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+
+ _fnProcessingDisplay( oSettings, false );
+ _fnInitComplete( oSettings, json );
+ } );
+ return;
+ }
+
+ /* Server-side processing initialisation complete is done at the end of _fnDraw */
+ if ( !oSettings.oFeatures.bServerSide )
+ {
+ _fnProcessingDisplay( oSettings, false );
+ _fnInitComplete( oSettings );
+ }
+ }
+
+ /*
+ * Function: _fnInitalise
+ * Purpose: Draw the table for the first time, adding all required features
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnInitComplete ( oSettings, json )
+ {
+ oSettings._bInitComplete = true;
+ if ( typeof oSettings.fnInitComplete == 'function' )
+ {
+ if ( typeof json != 'undefined' )
+ {
+ oSettings.fnInitComplete.call( oSettings.oInstance, oSettings, json );
+ }
+ else
+ {
+ oSettings.fnInitComplete.call( oSettings.oInstance, oSettings );
+ }
+ }
+ }
+
+ /*
+ * Function: _fnLanguageProcess
+ * Purpose: Copy language variables from remote object to a local one
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * object:oLanguage - Language information
+ * bool:bInit - init once complete
+ */
+ function _fnLanguageProcess( oSettings, oLanguage, bInit )
+ {
+ _fnMap( oSettings.oLanguage, oLanguage, 'sProcessing' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sLengthMenu' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sEmptyTable' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfo' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoEmpty' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoFiltered' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sInfoPostFix' );
+ _fnMap( oSettings.oLanguage, oLanguage, 'sSearch' );
+
+ if ( typeof oLanguage.oPaginate != 'undefined' )
+ {
+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sFirst' );
+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sPrevious' );
+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sNext' );
+ _fnMap( oSettings.oLanguage.oPaginate, oLanguage.oPaginate, 'sLast' );
+ }
+
+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
+ * sZeroRecords - assuming that is given.
+ */
+ if ( typeof oLanguage.sEmptyTable == 'undefined' &&
+ typeof oLanguage.sZeroRecords != 'undefined' )
+ {
+ _fnMap( oSettings.oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
+ }
+
+ if ( bInit )
+ {
+ _fnInitalise( oSettings );
+ }
+ }
+
+ /*
+ * Function: _fnAddColumn
+ * Purpose: Add a column to the list used for the table with default values
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * node:nTh - the th element for this column
+ */
+ function _fnAddColumn( oSettings, nTh )
+ {
+ oSettings.aoColumns[ oSettings.aoColumns.length++ ] = {
+ "sType": null,
+ "_bAutoType": true,
+ "bVisible": true,
+ "bSearchable": true,
+ "bSortable": true,
+ "asSorting": [ 'asc', 'desc' ],
+ "sSortingClass": oSettings.oClasses.sSortable,
+ "sSortingClassJUI": oSettings.oClasses.sSortJUI,
+ "sTitle": nTh ? nTh.innerHTML : '',
+ "sName": '',
+ "sWidth": null,
+ "sWidthOrig": null,
+ "sClass": null,
+ "fnRender": null,
+ "bUseRendered": true,
+ "iDataSort": oSettings.aoColumns.length-1,
+ "sSortDataType": 'std',
+ "nTh": nTh ? nTh : document.createElement('th'),
+ "nTf": null,
+ "anThExtra": [],
+ "anTfExtra": []
+ };
+
+ var iCol = oSettings.aoColumns.length-1;
+ var oCol = oSettings.aoColumns[ iCol ];
+
+ /* Add a column specific filter */
+ if ( typeof oSettings.aoPreSearchCols[ iCol ] == 'undefined' ||
+ oSettings.aoPreSearchCols[ iCol ] === null )
+ {
+ oSettings.aoPreSearchCols[ iCol ] = {
+ "sSearch": "",
+ "bRegex": false,
+ "bSmart": true
+ };
+ }
+ else
+ {
+ /* Don't require that the user must specify bRegex and / or bSmart */
+ if ( typeof oSettings.aoPreSearchCols[ iCol ].bRegex == 'undefined' )
+ {
+ oSettings.aoPreSearchCols[ iCol ].bRegex = true;
+ }
+
+ if ( typeof oSettings.aoPreSearchCols[ iCol ].bSmart == 'undefined' )
+ {
+ oSettings.aoPreSearchCols[ iCol ].bSmart = true;
+ }
+ }
+
+ /* Use the column options function to initialise classes etc */
+ _fnColumnOptions( oSettings, iCol, null );
+ }
+
+ /*
+ * Function: _fnColumnOptions
+ * Purpose: Apply options for a column
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * int:iCol - column index to consider
+ * object:oOptions - object with sType, bVisible and bSearchable
+ */
+ function _fnColumnOptions( oSettings, iCol, oOptions )
+ {
+ var oCol = oSettings.aoColumns[ iCol ];
+
+ /* User specified column options */
+ if ( typeof oOptions != 'undefined' && oOptions !== null )
+ {
+ if ( typeof oOptions.sType != 'undefined' )
+ {
+ oCol.sType = oOptions.sType;
+ oCol._bAutoType = false;
+ }
+
+ _fnMap( oCol, oOptions, "bVisible" );
+ _fnMap( oCol, oOptions, "bSearchable" );
+ _fnMap( oCol, oOptions, "bSortable" );
+ _fnMap( oCol, oOptions, "sTitle" );
+ _fnMap( oCol, oOptions, "sName" );
+ _fnMap( oCol, oOptions, "sWidth" );
+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
+ _fnMap( oCol, oOptions, "sClass" );
+ _fnMap( oCol, oOptions, "fnRender" );
+ _fnMap( oCol, oOptions, "bUseRendered" );
+ _fnMap( oCol, oOptions, "iDataSort" );
+ _fnMap( oCol, oOptions, "asSorting" );
+ _fnMap( oCol, oOptions, "sSortDataType" );
+ }
+
+ /* Feature sorting overrides column specific when off */
+ if ( !oSettings.oFeatures.bSort )
+ {
+ oCol.bSortable = false;
+ }
+
+ /* Check that the class assignment is correct for sorting */
+ if ( !oCol.bSortable ||
+ ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
+ {
+ oCol.sSortingClass = oSettings.oClasses.sSortableNone;
+ oCol.sSortingClassJUI = "";
+ }
+ else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
+ {
+ oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
+ }
+ else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
+ {
+ oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
+ }
+ }
+
+ /*
+ * Function: _fnAddData
+ * Purpose: Add a data array to the table, creating DOM node etc
+ * Returns: int: - >=0 if successful (index of new aoData entry), -1 if failed
+ * Inputs: object:oSettings - dataTables settings object
+ * array:aData - data array to be added
+ * Notes: There are two basic methods for DataTables to get data to display - a JS array
+ * (which is dealt with by this function), and the DOM, which has it's own optimised
+ * function (_fnGatherData). Be careful to make the same changes here as there and vice-versa
+ */
+ function _fnAddData ( oSettings, aDataSupplied )
+ {
+ /* Sanity check the length of the new array */
+ if ( aDataSupplied.length != oSettings.aoColumns.length &&
+ oSettings.iDrawError != oSettings.iDraw )
+ {
+ _fnLog( oSettings, 0, "Added data (size "+aDataSupplied.length+") does not match known "+
+ "number of columns ("+oSettings.aoColumns.length+")" );
+ oSettings.iDrawError = oSettings.iDraw;
+ return -1;
+ }
+
+
+ /* Create the object for storing information about this new row */
+ var aData = aDataSupplied.slice();
+ var iThisIndex = oSettings.aoData.length;
+ oSettings.aoData.push( {
+ "nTr": document.createElement('tr'),
+ "_iId": oSettings.iNextId++,
+ "_aData": aData,
+ "_anHidden": [],
+ "_sRowStripe": ''
+ } );
+
+ /* Create the cells */
+ var nTd, sThisType;
+ for ( var i=0 ; i<aData.length ; i++ )
+ {
+ nTd = document.createElement('td');
+
+ /* Allow null data (from a data array) - simply deal with it as a blank string */
+ if ( aData[i] === null )
+ {
+ aData[i] = '';
+ }
+
+ if ( typeof oSettings.aoColumns[i].fnRender == 'function' )
+ {
+ var sRendered = oSettings.aoColumns[i].fnRender( {
+ "iDataRow": iThisIndex,
+ "iDataColumn": i,
+ "aData": aData,
+ "oSettings": oSettings
+ } );
+ nTd.innerHTML = sRendered;
+ if ( oSettings.aoColumns[i].bUseRendered )
+ {
+ /* Use the rendered data for filtering/sorting */
+ oSettings.aoData[iThisIndex]._aData[i] = sRendered;
+ }
+ }
+ else
+ {
+ nTd.innerHTML = aData[i];
+ }
+
+ /* Cast everything as a string - so we can treat everything equally when sorting */
+ if ( typeof aData[i] != 'string' )
+ {
+ aData[i] += "";
+ }
+ aData[i] = $.trim(aData[i]);
+
+ /* Add user defined class */
+ if ( oSettings.aoColumns[i].sClass !== null )
+ {
+ nTd.className = oSettings.aoColumns[i].sClass;
+ }
+
+ /* See if we should auto-detect the column type */
+ if ( oSettings.aoColumns[i]._bAutoType && oSettings.aoColumns[i].sType != 'string' )
+ {
+ /* Attempt to auto detect the type - same as _fnGatherData() */
+ sThisType = _fnDetectType( oSettings.aoData[iThisIndex]._aData[i] );
+ if ( oSettings.aoColumns[i].sType === null )
+ {
+ oSettings.aoColumns[i].sType = sThisType;
+ }
+ else if ( oSettings.aoColumns[i].sType != sThisType )
+ {
+ /* String is always the 'fallback' option */
+ oSettings.aoColumns[i].sType = 'string';
+ }
+ }
+
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ oSettings.aoData[iThisIndex].nTr.appendChild( nTd );
+ oSettings.aoData[iThisIndex]._anHidden[i] = null;
+ }
+ else
+ {
+ oSettings.aoData[iThisIndex]._anHidden[i] = nTd;
+ }
+ }
+
+ /* Add to the display array */
+ oSettings.aiDisplayMaster.push( iThisIndex );
+ return iThisIndex;
+ }
+
+ /*
+ * Function: _fnGatherData
+ * Purpose: Read in the data from the target table from the DOM
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * Notes: This is a optimised version of _fnAddData (more or less) for reading information
+ * from the DOM. The basic actions must be identical in the two functions.
+ */
+ function _fnGatherData( oSettings )
+ {
+ var iLoop, i, iLen, j, jLen, jInner,
+ nTds, nTrs, nTd, aLocalData, iThisIndex,
+ iRow, iRows, iColumn, iColumns;
+
+ /*
+ * Process by row first
+ * Add the data object for the whole table - storing the tr node. Note - no point in getting
+ * DOM based data if we are going to go and replace it with Ajax source data.
+ */
+ if ( oSettings.sAjaxSource === null )
+ {
+ nTrs = oSettings.nTBody.childNodes;
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+ {
+ if ( nTrs[i].nodeName.toUpperCase() == "TR" )
+ {
+ iThisIndex = oSettings.aoData.length;
+ oSettings.aoData.push( {
+ "nTr": nTrs[i],
+ "_iId": oSettings.iNextId++,
+ "_aData": [],
+ "_anHidden": [],
+ "_sRowStripe": ''
+ } );
+
+ oSettings.aiDisplayMaster.push( iThisIndex );
+
+ aLocalData = oSettings.aoData[iThisIndex]._aData;
+ nTds = nTrs[i].childNodes;
+ jInner = 0;
+
+ for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
+ {
+ if ( nTds[j].nodeName.toUpperCase() == "TD" )
+ {
+ aLocalData[jInner] = $.trim(nTds[j].innerHTML);
+ jInner++;
+ }
+ }
+ }
+ }
+ }
+
+ /* Gather in the TD elements of the Table - note that this is basically the same as
+ * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
+ * setup!
+ */
+ nTrs = _fnGetTrNodes( oSettings );
+ nTds = [];
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+ {
+ for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
+ {
+ nTd = nTrs[i].childNodes[j];
+ if ( nTd.nodeName.toUpperCase() == "TD" )
+ {
+ nTds.push( nTd );
+ }
+ }
+ }
+
+ /* Sanity check */
+ if ( nTds.length != nTrs.length * oSettings.aoColumns.length )
+ {
+ _fnLog( oSettings, 1, "Unexpected number of TD elements. Expected "+
+ (nTrs.length * oSettings.aoColumns.length)+" and got "+nTds.length+". DataTables does "+
+ "not support rowspan / colspan in the table body, and there must be one cell for each "+
+ "row/column combination." );
+ }
+
+ /* Now process by column */
+ for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
+ {
+ /* Get the title of the column - unless there is a user set one */
+ if ( oSettings.aoColumns[iColumn].sTitle === null )
+ {
+ oSettings.aoColumns[iColumn].sTitle = oSettings.aoColumns[iColumn].nTh.innerHTML;
+ }
+
+ var
+ bAutoType = oSettings.aoColumns[iColumn]._bAutoType,
+ bRender = typeof oSettings.aoColumns[iColumn].fnRender == 'function',
+ bClass = oSettings.aoColumns[iColumn].sClass !== null,
+ bVisible = oSettings.aoColumns[iColumn].bVisible,
+ nCell, sThisType, sRendered;
+
+ /* A single loop to rule them all (and be more efficient) */
+ if ( bAutoType || bRender || bClass || !bVisible )
+ {
+ for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
+ {
+ nCell = nTds[ (iRow*iColumns) + iColumn ];
+
+ /* Type detection */
+ if ( bAutoType )
+ {
+ if ( oSettings.aoColumns[iColumn].sType != 'string' )
+ {
+ sThisType = _fnDetectType( oSettings.aoData[iRow]._aData[iColumn] );
+ if ( oSettings.aoColumns[iColumn].sType === null )
+ {
+ oSettings.aoColumns[iColumn].sType = sThisType;
+ }
+ else if ( oSettings.aoColumns[iColumn].sType != sThisType )
+ {
+ /* String is always the 'fallback' option */
+ oSettings.aoColumns[iColumn].sType = 'string';
+ }
+ }
+ }
+
+ /* Rendering */
+ if ( bRender )
+ {
+ sRendered = oSettings.aoColumns[iColumn].fnRender( {
+ "iDataRow": iRow,
+ "iDataColumn": iColumn,
+ "aData": oSettings.aoData[iRow]._aData,
+ "oSettings": oSettings
+ } );
+ nCell.innerHTML = sRendered;
+ if ( oSettings.aoColumns[iColumn].bUseRendered )
+ {
+ /* Use the rendered data for filtering/sorting */
+ oSettings.aoData[iRow]._aData[iColumn] = sRendered;
+ }
+ }
+
+ /* Classes */
+ if ( bClass )
+ {
+ nCell.className += ' '+oSettings.aoColumns[iColumn].sClass;
+ }
+
+ /* Column visability */
+ if ( !bVisible )
+ {
+ oSettings.aoData[iRow]._anHidden[iColumn] = nCell;
+ nCell.parentNode.removeChild( nCell );
+ }
+ else
+ {
+ oSettings.aoData[iRow]._anHidden[iColumn] = null;
+ }
+ }
+ }
+ }
+ }
+
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Drawing functions
+ */
+
+ /*
+ * Function: _fnDrawHead
+ * Purpose: Create the HTML header for the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnDrawHead( oSettings )
+ {
+ var i, nTh, iLen, j, jLen;
+ var anTr = oSettings.nTHead.getElementsByTagName('tr');
+ var iThs = oSettings.nTHead.getElementsByTagName('th').length;
+ var iCorrector = 0;
+ var jqChildren;
+
+ /* If there is a header in place - then use it - otherwise it's going to get nuked... */
+ if ( iThs !== 0 )
+ {
+ /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ nTh = oSettings.aoColumns[i].nTh;
+
+ if ( oSettings.aoColumns[i].sClass !== null )
+ {
+ $(nTh).addClass( oSettings.aoColumns[i].sClass );
+ }
+
+ /* Cache and remove (if needed) any extra elements for this column in the header */
+ for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
+ {
+ jqChildren = $(anTr[j]).children();
+ oSettings.aoColumns[i].anThExtra.push( jqChildren[i-iCorrector] );
+ if ( !oSettings.aoColumns[i].bVisible )
+ {
+ anTr[j].removeChild( jqChildren[i-iCorrector] );
+ }
+ }
+
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ /* Set the title of the column if it is user defined (not what was auto detected) */
+ if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
+ {
+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+ }
+ }
+ else
+ {
+ nTh.parentNode.removeChild( nTh );
+ iCorrector++;
+ }
+ }
+ }
+ else
+ {
+ /* We don't have a header in the DOM - so we are going to have to create one */
+ var nTr = document.createElement( "tr" );
+
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ nTh = oSettings.aoColumns[i].nTh;
+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+
+ if ( oSettings.aoColumns[i].sClass !== null )
+ {
+ $(nTh).addClass( oSettings.aoColumns[i].sClass );
+ }
+
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ nTr.appendChild( nTh );
+ }
+ }
+ $(oSettings.nTHead).html( '' )[0].appendChild( nTr );
+ }
+
+ /* Add the extra markup needed by jQuery UI's themes */
+ if ( oSettings.bJUI )
+ {
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ nTh = oSettings.aoColumns[i].nTh;
+
+ var nDiv = document.createElement('div');
+ nDiv.className = oSettings.oClasses.sSortJUIWrapper;
+ $(nTh).contents().appendTo(nDiv);
+
+ nDiv.appendChild( document.createElement('span') );
+ nTh.appendChild( nDiv );
+ }
+ }
+
+ /* Add sort listener */
+ var fnNoSelect = function (e) {
+ this.onselectstart = function() { return false; };
+ return false;
+ };
+
+ if ( oSettings.oFeatures.bSort )
+ {
+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bSortable !== false )
+ {
+ _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
+
+ /* Take the brutal approach to cancelling text selection in header */
+ $(oSettings.aoColumns[i].nTh).bind( 'mousedown.DT', fnNoSelect );
+ }
+ else
+ {
+ $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
+ }
+ }
+ }
+
+ /* Cache the footer elements */
+ if ( oSettings.nTFoot !== null )
+ {
+ iCorrector = 0;
+ anTr = oSettings.nTFoot.getElementsByTagName('tr');
+ var nTfs = anTr[0].getElementsByTagName('th');
+
+ for ( i=0, iLen=nTfs.length ; i<iLen ; i++ )
+ {
+ if ( typeof oSettings.aoColumns[i] != 'undefined' )
+ {
+ oSettings.aoColumns[i].nTf = nTfs[i-iCorrector];
+
+ if ( oSettings.oClasses.sFooterTH !== "" )
+ {
+ oSettings.aoColumns[i].nTf.className += " "+oSettings.oClasses.sFooterTH;
+ }
+
+ /* Deal with any extra elements for this column from the footer */
+ for ( j=1, jLen=anTr.length ; j<jLen ; j++ )
+ {
+ jqChildren = $(anTr[j]).children();
+ oSettings.aoColumns[i].anTfExtra.push( jqChildren[i-iCorrector] );
+ if ( !oSettings.aoColumns[i].bVisible )
+ {
+ anTr[j].removeChild( jqChildren[i-iCorrector] );
+ }
+ }
+
+ if ( !oSettings.aoColumns[i].bVisible )
+ {
+ nTfs[i-iCorrector].parentNode.removeChild( nTfs[i-iCorrector] );
+ iCorrector++;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Function: _fnDraw
+ * Purpose: Insert the required TR nodes into the table for display
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnDraw( oSettings )
+ {
+ var i, iLen;
+ var anRows = [];
+ var iRowCount = 0;
+ var bRowError = false;
+ var iStrips = oSettings.asStripClasses.length;
+ var iOpenRows = oSettings.aoOpenRows.length;
+
+ oSettings.bDrawing = true;
+
+ /* Check and see if we have an initial draw position from state saving */
+ if ( typeof oSettings.iInitDisplayStart != 'undefined' && oSettings.iInitDisplayStart != -1 )
+ {
+ if ( oSettings.oFeatures.bServerSide )
+ {
+ oSettings._iDisplayStart = oSettings.iInitDisplayStart;
+ }
+ else
+ {
+ oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
+ 0 : oSettings.iInitDisplayStart;
+ }
+ oSettings.iInitDisplayStart = -1;
+ _fnCalculateEnd( oSettings );
+ }
+
+ /* If we are dealing with Ajax - do it here */
+ if ( !oSettings.bDestroying && oSettings.oFeatures.bServerSide &&
+ !_fnAjaxUpdate( oSettings ) )
+ {
+ return;
+ }
+ else if ( !oSettings.oFeatures.bServerSide )
+ {
+ oSettings.iDraw++;
+ }
+
+ if ( oSettings.aiDisplay.length !== 0 )
+ {
+ var iStart = oSettings._iDisplayStart;
+ var iEnd = oSettings._iDisplayEnd;
+
+ if ( oSettings.oFeatures.bServerSide )
+ {
+ iStart = 0;
+ iEnd = oSettings.aoData.length;
+ }
+
+ for ( var j=iStart ; j<iEnd ; j++ )
+ {
+ var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
+ var nRow = aoData.nTr;
+
+ /* Remove the old stripping classes and then add the new one */
+ if ( iStrips !== 0 )
+ {
+ var sStrip = oSettings.asStripClasses[ iRowCount % iStrips ];
+ if ( aoData._sRowStripe != sStrip )
+ {
+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStrip );
+ aoData._sRowStripe = sStrip;
+ }
+ }
+
+ /* Custom row callback function - might want to manipule the row */
+ if ( typeof oSettings.fnRowCallback == "function" )
+ {
+ nRow = oSettings.fnRowCallback.call( oSettings.oInstance, nRow,
+ oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j );
+ if ( !nRow && !bRowError )
+ {
+ _fnLog( oSettings, 0, "A node was not returned by fnRowCallback" );
+ bRowError = true;
+ }
+ }
+
+ anRows.push( nRow );
+ iRowCount++;
+
+ /* If there is an open row - and it is attached to this parent - attach it on redraw */
+ if ( iOpenRows !== 0 )
+ {
+ for ( var k=0 ; k<iOpenRows ; k++ )
+ {
+ if ( nRow == oSettings.aoOpenRows[k].nParent )
+ {
+ anRows.push( oSettings.aoOpenRows[k].nTr );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Table is empty - create a row with an empty message in it */
+ anRows[ 0 ] = document.createElement( 'tr' );
+
+ if ( typeof oSettings.asStripClasses[0] != 'undefined' )
+ {
+ anRows[ 0 ].className = oSettings.asStripClasses[0];
+ }
+
+ var nTd = document.createElement( 'td' );
+ nTd.setAttribute( 'valign', "top" );
+ nTd.colSpan = _fnVisbleColumns( oSettings );
+ nTd.className = oSettings.oClasses.sRowEmpty;
+ if ( typeof oSettings.oLanguage.sEmptyTable != 'undefined' &&
+ oSettings.fnRecordsTotal() === 0 )
+ {
+ nTd.innerHTML = oSettings.oLanguage.sEmptyTable;
+ }
+ else
+ {
+ nTd.innerHTML = oSettings.oLanguage.sZeroRecords.replace(
+ '_MAX_', oSettings.fnFormatNumber(oSettings.fnRecordsTotal()) );
+ }
+
+ anRows[ iRowCount ].appendChild( nTd );
+ }
+
+ /* Callback the header and footer custom funcation if there is one */
+ if ( typeof oSettings.fnHeaderCallback == 'function' )
+ {
+ oSettings.fnHeaderCallback.call( oSettings.oInstance, $('>tr', oSettings.nTHead)[0],
+ _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
+ oSettings.aiDisplay );
+ }
+
+ if ( typeof oSettings.fnFooterCallback == 'function' )
+ {
+ oSettings.fnFooterCallback.call( oSettings.oInstance, $('>tr', oSettings.nTFoot)[0],
+ _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(),
+ oSettings.aiDisplay );
+ }
+
+ /*
+ * Need to remove any old row from the display - note we can't just empty the tbody using
+ * $().html('') since this will unbind the jQuery event handlers (even although the node
+ * still exists!) - equally we can't use innerHTML, since IE throws an exception.
+ */
+ var
+ nAddFrag = document.createDocumentFragment(),
+ nRemoveFrag = document.createDocumentFragment(),
+ nBodyPar, nTrs;
+
+ if ( oSettings.nTBody )
+ {
+ nBodyPar = oSettings.nTBody.parentNode;
+ nRemoveFrag.appendChild( oSettings.nTBody );
+
+ /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
+ * up. When not infinite scroll, always do it.
+ */
+ if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
+ oSettings.bSorted || oSettings.bFiltered )
+ {
+ nTrs = oSettings.nTBody.childNodes;
+ for ( i=nTrs.length-1 ; i>=0 ; i-- )
+ {
+ nTrs[i].parentNode.removeChild( nTrs[i] );
+ }
+ }
+
+ /* Put the draw table into the dom */
+ for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
+ {
+ nAddFrag.appendChild( anRows[i] );
+ }
+
+ oSettings.nTBody.appendChild( nAddFrag );
+ if ( nBodyPar !== null )
+ {
+ nBodyPar.appendChild( oSettings.nTBody );
+ }
+ }
+
+ /* Call all required callback functions for the end of a draw */
+ for ( i=oSettings.aoDrawCallback.length-1 ; i>=0 ; i-- )
+ {
+ oSettings.aoDrawCallback[i].fn.call( oSettings.oInstance, oSettings );
+ }
+
+ /* Draw is complete, sorting and filtering must be as well */
+ oSettings.bSorted = false;
+ oSettings.bFiltered = false;
+ oSettings.bDrawing = false;
+
+ if ( oSettings.oFeatures.bServerSide )
+ {
+ _fnProcessingDisplay( oSettings, false );
+ if ( typeof oSettings._bInitComplete == 'undefined' )
+ {
+ _fnInitComplete( oSettings );
+ }
+ }
+ }
+
+ /*
+ * Function: _fnReDraw
+ * Purpose: Redraw the table - taking account of the various features which are enabled
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnReDraw( oSettings )
+ {
+ if ( oSettings.oFeatures.bSort )
+ {
+ /* Sorting will refilter and draw for us */
+ _fnSort( oSettings, oSettings.oPreviousSearch );
+ }
+ else if ( oSettings.oFeatures.bFilter )
+ {
+ /* Filtering will redraw for us */
+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
+ }
+ else
+ {
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+ }
+
+ /*
+ * Function: _fnAjaxUpdate
+ * Purpose: Update the table using an Ajax call
+ * Returns: bool: block the table drawing or not
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnAjaxUpdate( oSettings )
+ {
+ if ( oSettings.bAjaxDataGet )
+ {
+ _fnProcessingDisplay( oSettings, true );
+ var iColumns = oSettings.aoColumns.length;
+ var aoData = [];
+ var i;
+
+ /* Paging and general */
+ oSettings.iDraw++;
+ aoData.push( { "name": "sEcho", "value": oSettings.iDraw } );
+ aoData.push( { "name": "iColumns", "value": iColumns } );
+ aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } );
+ aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } );
+ aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
+ oSettings._iDisplayLength : -1 } );
+
+ /* Filtering */
+ if ( oSettings.oFeatures.bFilter !== false )
+ {
+ aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
+ aoData.push( { "name": "bRegex", "value": oSettings.oPreviousSearch.bRegex } );
+ for ( i=0 ; i<iColumns ; i++ )
+ {
+ aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } );
+ aoData.push( { "name": "bRegex_"+i, "value": oSettings.aoPreSearchCols[i].bRegex } );
+ aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
+ }
+ }
+
+ /* Sorting */
+ if ( oSettings.oFeatures.bSort !== false )
+ {
+ var iFixed = oSettings.aaSortingFixed !== null ? oSettings.aaSortingFixed.length : 0;
+ var iUser = oSettings.aaSorting.length;
+ aoData.push( { "name": "iSortingCols", "value": iFixed+iUser } );
+ for ( i=0 ; i<iFixed ; i++ )
+ {
+ aoData.push( { "name": "iSortCol_"+i, "value": oSettings.aaSortingFixed[i][0] } );
+ aoData.push( { "name": "sSortDir_"+i, "value": oSettings.aaSortingFixed[i][1] } );
+ }
+
+ for ( i=0 ; i<iUser ; i++ )
+ {
+ aoData.push( { "name": "iSortCol_"+(i+iFixed), "value": oSettings.aaSorting[i][0] } );
+ aoData.push( { "name": "sSortDir_"+(i+iFixed), "value": oSettings.aaSorting[i][1] } );
+ }
+
+ for ( i=0 ; i<iColumns ; i++ )
+ {
+ aoData.push( { "name": "bSortable_"+i, "value": oSettings.aoColumns[i].bSortable } );
+ }
+ }
+
+ oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
+ function(json) {
+ _fnAjaxUpdateDraw( oSettings, json );
+ } );
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ /*
+ * Function: _fnAjaxUpdateDraw
+ * Purpose: Data the data from the server (nuking the old) and redraw the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * object:json - json data return from the server.
+ * The following must be defined:
+ * iTotalRecords, iTotalDisplayRecords, aaData
+ * The following may be defined:
+ * sColumns
+ */
+ function _fnAjaxUpdateDraw ( oSettings, json )
+ {
+ if ( typeof json.sEcho != 'undefined' )
+ {
+ /* Protect against old returns over-writing a new one. Possible when you get
+ * very fast interaction, and later queires are completed much faster
+ */
+ if ( json.sEcho*1 < oSettings.iDraw )
+ {
+ return;
+ }
+ else
+ {
+ oSettings.iDraw = json.sEcho * 1;
+ }
+ }
+
+ if ( !oSettings.oScroll.bInfinite ||
+ (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
+ {
+ _fnClearTable( oSettings );
+ }
+ oSettings._iRecordsTotal = json.iTotalRecords;
+ oSettings._iRecordsDisplay = json.iTotalDisplayRecords;
+
+ /* Determine if reordering is required */
+ var sOrdering = _fnColumnOrdering(oSettings);
+ var bReOrder = (typeof json.sColumns != 'undefined' && sOrdering !== "" && json.sColumns != sOrdering );
+ if ( bReOrder )
+ {
+ var aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
+ }
+
+ for ( var i=0, iLen=json.aaData.length ; i<iLen ; i++ )
+ {
+ if ( bReOrder )
+ {
+ /* If we need to re-order, then create a new array with the correct order and add it */
+ var aData = [];
+ for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
+ {
+ aData.push( json.aaData[i][ aiIndex[j] ] );
+ }
+ _fnAddData( oSettings, aData );
+ }
+ else
+ {
+ /* No re-order required, sever got it "right" - just straight add */
+ _fnAddData( oSettings, json.aaData[i] );
+ }
+ }
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+
+ oSettings.bAjaxDataGet = false;
+ _fnDraw( oSettings );
+ oSettings.bAjaxDataGet = true;
+ _fnProcessingDisplay( oSettings, false );
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Options (features) HTML
+ */
+
+ /*
+ * Function: _fnAddOptionsHtml
+ * Purpose: Add the options to the page HTML for the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnAddOptionsHtml ( oSettings )
+ {
+ /*
+ * Create a temporary, empty, div which we can later on replace with what we have generated
+ * we do it this way to rendering the 'options' html offline - speed :-)
+ */
+ var nHolding = document.createElement( 'div' );
+ oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
+
+ /*
+ * All DataTables are wrapped in a div - this is not currently optional - backwards
+ * compatability. It can be removed if you don't want it.
+ */
+ oSettings.nTableWrapper = document.createElement( 'div' );
+ oSettings.nTableWrapper.className = oSettings.oClasses.sWrapper;
+ if ( oSettings.sTableId !== '' )
+ {
+ oSettings.nTableWrapper.setAttribute( 'id', oSettings.sTableId+'_wrapper' );
+ }
+
+ /* Track where we want to insert the option */
+ var nInsertNode = oSettings.nTableWrapper;
+
+ /* Loop over the user set positioning and place the elements as needed */
+ var aDom = oSettings.sDom.split('');
+ var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
+ for ( var i=0 ; i<aDom.length ; i++ )
+ {
+ iPushFeature = 0;
+ cOption = aDom[i];
+
+ if ( cOption == '<' )
+ {
+ /* New container div */
+ nNewNode = document.createElement( 'div' );
+
+ /* Check to see if we should append an id and/or a class name to the container */
+ cNext = aDom[i+1];
+ if ( cNext == "'" || cNext == '"' )
+ {
+ sAttr = "";
+ j = 2;
+ while ( aDom[i+j] != cNext )
+ {
+ sAttr += aDom[i+j];
+ j++;
+ }
+
+ /* Replace jQuery UI constants */
+ if ( sAttr == "H" )
+ {
+ sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";
+ }
+ else if ( sAttr == "F" )
+ {
+ sAttr = "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";
+ }
+
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
+ * breaks the string into parts and applies them as needed
+ */
+ if ( sAttr.indexOf('.') != -1 )
+ {
+ var aSplit = sAttr.split('.');
+ nNewNode.setAttribute('id', aSplit[0].substr(1, aSplit[0].length-1) );
+ nNewNode.className = aSplit[1];
+ }
+ else if ( sAttr.charAt(0) == "#" )
+ {
+ nNewNode.setAttribute('id', sAttr.substr(1, sAttr.length-1) );
+ }
+ else
+ {
+ nNewNode.className = sAttr;
+ }
+
+ i += j; /* Move along the position array */
+ }
+
+ nInsertNode.appendChild( nNewNode );
+ nInsertNode = nNewNode;
+ }
+ else if ( cOption == '>' )
+ {
+ /* End container div */
+ nInsertNode = nInsertNode.parentNode;
+ }
+ else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
+ {
+ /* Length */
+ nTmp = _fnFeatureHtmlLength( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
+ {
+ /* Filter */
+ nTmp = _fnFeatureHtmlFilter( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
+ {
+ /* pRocessing */
+ nTmp = _fnFeatureHtmlProcessing( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( cOption == 't' )
+ {
+ /* Table */
+ nTmp = _fnFeatureHtmlTable( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( cOption == 'i' && oSettings.oFeatures.bInfo )
+ {
+ /* Info */
+ nTmp = _fnFeatureHtmlInfo( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
+ {
+ /* Pagination */
+ nTmp = _fnFeatureHtmlPaginate( oSettings );
+ iPushFeature = 1;
+ }
+ else if ( _oExt.aoFeatures.length !== 0 )
+ {
+ /* Plug-in features */
+ var aoFeatures = _oExt.aoFeatures;
+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
+ {
+ if ( cOption == aoFeatures[k].cFeature )
+ {
+ nTmp = aoFeatures[k].fnInit( oSettings );
+ if ( nTmp )
+ {
+ iPushFeature = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Add to the 2D features array */
+ if ( iPushFeature == 1 && nTmp !== null )
+ {
+ if ( typeof oSettings.aanFeatures[cOption] != 'object' )
+ {
+ oSettings.aanFeatures[cOption] = [];
+ }
+ oSettings.aanFeatures[cOption].push( nTmp );
+ nInsertNode.appendChild( nTmp );
+ }
+ }
+
+ /* Built our DOM structure - replace the holding div with what we want */
+ nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Filtering
+ */
+
+ /*
+ * Function: _fnFeatureHtmlTable
+ * Purpose: Add any control elements for the table - specifically scrolling
+ * Returns: node: - Node to add to the DOM
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlTable ( oSettings )
+ {
+ /* Chack if scrolling is enabled or not - if not then leave the DOM unaltered */
+ if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
+ {
+ return oSettings.nTable;
+ }
+
+ /*
+ * The HTML structure that we want to generate in this function is:
+ * div - nScroller
+ * div - nScrollHead
+ * div - nScrollHeadInner
+ * table - nScrollHeadTable
+ * thead - nThead
+ * div - nScrollBody
+ * table - oSettings.nTable
+ * thead - nTheadSize
+ * tbody - nTbody
+ * div - nScrollFoot
+ * div - nScrollFootInner
+ * table - nScrollFootTable
+ * tfoot - nTfoot
+ */
+ var
+ nScroller = document.createElement('div'),
+ nScrollHead = document.createElement('div'),
+ nScrollHeadInner = document.createElement('div'),
+ nScrollBody = document.createElement('div'),
+ nScrollFoot = document.createElement('div'),
+ nScrollFootInner = document.createElement('div'),
+ nScrollHeadTable = oSettings.nTable.cloneNode(false),
+ nScrollFootTable = oSettings.nTable.cloneNode(false),
+ nThead = oSettings.nTable.getElementsByTagName('thead')[0],
+ nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null :
+ oSettings.nTable.getElementsByTagName('tfoot')[0],
+ oClasses = (typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI) ?
+ _oExt.oJUIClasses : _oExt.oStdClasses;
+
+ nScrollHead.appendChild( nScrollHeadInner );
+ nScrollFoot.appendChild( nScrollFootInner );
+ nScrollBody.appendChild( oSettings.nTable );
+ nScroller.appendChild( nScrollHead );
+ nScroller.appendChild( nScrollBody );
+ nScrollHeadInner.appendChild( nScrollHeadTable );
+ nScrollHeadTable.appendChild( nThead );
+ if ( nTfoot !== null )
+ {
+ nScroller.appendChild( nScrollFoot );
+ nScrollFootInner.appendChild( nScrollFootTable );
+ nScrollFootTable.appendChild( nTfoot );
+ }
+
+ nScroller.className = oClasses.sScrollWrapper;
+ nScrollHead.className = oClasses.sScrollHead;
+ nScrollHeadInner.className = oClasses.sScrollHeadInner;
+ nScrollBody.className = oClasses.sScrollBody;
+ nScrollFoot.className = oClasses.sScrollFoot;
+ nScrollFootInner.className = oClasses.sScrollFootInner;
+
+ if ( oSettings.oScroll.bAutoCss )
+ {
+ nScrollHead.style.overflow = "hidden";
+ nScrollHead.style.position = "relative";
+ nScrollFoot.style.overflow = "hidden";
+ nScrollBody.style.overflow = "auto";
+ }
+
+ nScrollHead.style.border = "0";
+ nScrollHead.style.width = "100%";
+ nScrollFoot.style.border = "0";
+ nScrollHeadInner.style.width = "150%"; /* will be overwritten */
+
+ /* Modify attributes to respect the clones */
+ nScrollHeadTable.removeAttribute('id');
+ nScrollHeadTable.style.marginLeft = "0";
+ oSettings.nTable.style.marginLeft = "0";
+ if ( nTfoot !== null )
+ {
+ nScrollFootTable.removeAttribute('id');
+ nScrollFootTable.style.marginLeft = "0";
+ }
+
+ /* Move any caption elements from the body to the header */
+ var nCaptions = $('>caption', oSettings.nTable);
+ for ( var i=0, iLen=nCaptions.length ; i<iLen ; i++ )
+ {
+ nScrollHeadTable.appendChild( nCaptions[i] );
+ }
+
+ /*
+ * Sizing
+ */
+ /* When xscrolling add the width and a scroller to move the header with the body */
+ if ( oSettings.oScroll.sX !== "" )
+ {
+ nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
+ nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
+
+ if ( nTfoot !== null )
+ {
+ nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );
+ }
+
+ /* When the body is scrolled, then we also want to scroll the headers */
+ $(nScrollBody).scroll( function (e) {
+ nScrollHead.scrollLeft = this.scrollLeft;
+
+ if ( nTfoot !== null )
+ {
+ nScrollFoot.scrollLeft = this.scrollLeft;
+ }
+ } );
+ }
+
+ /* When yscrolling, add the height */
+ if ( oSettings.oScroll.sY !== "" )
+ {
+ nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
+ }
+
+ /* Redraw - align columns across the tables */
+ oSettings.aoDrawCallback.push( {
+ "fn": _fnScrollDraw,
+ "sName": "scrolling"
+ } );
+
+ /* Infinite scrolling event handlers */
+ if ( oSettings.oScroll.bInfinite )
+ {
+ $(nScrollBody).scroll( function() {
+ /* Use a blocker to stop scrolling from loading more data while other data is still loading */
+ if ( !oSettings.bDrawing )
+ {
+ /* Check if we should load the next data set */
+ if ( $(this).scrollTop() + $(this).height() >
+ $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
+ {
+ /* Only do the redraw if we have to - we might be at the end of the data */
+ if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
+ {
+ _fnPageChange( oSettings, 'next' );
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+ }
+ }
+ } );
+ }
+
+ oSettings.nScrollHead = nScrollHead;
+ oSettings.nScrollFoot = nScrollFoot;
+
+ return nScroller;
+ }
+
+ /*
+ * Function: _fnScrollDraw
+ * Purpose: Update the various tables for resizing
+ * Returns: node: - Node to add to the DOM
+ * Inputs: object:o - dataTables settings object
+ * Notes: It's a bit of a pig this function, but basically the idea to:
+ * 1. Re-create the table inside the scrolling div
+ * 2. Take live measurements from the DOM
+ * 3. Apply the measurements
+ * 4. Clean up
+ */
+ function _fnScrollDraw ( o )
+ {
+ var
+ nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
+ nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
+ nScrollBody = o.nTable.parentNode,
+ i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
+ iWidth, aApplied=[], iSanityWidth;
+
+ /*
+ * 1. Re-create the table inside the scrolling div
+ */
+
+ /* Remove the old minimised thead and tfoot elements in the inner table */
+ var nTheadSize = o.nTable.getElementsByTagName('thead');
+ if ( nTheadSize.length > 0 )
+ {
+ o.nTable.removeChild( nTheadSize[0] );
+ }
+
+ if ( o.nTFoot !== null )
+ {
+ /* Remove the old minimised footer element in the cloned header */
+ var nTfootSize = o.nTable.getElementsByTagName('tfoot');
+ if ( nTfootSize.length > 0 )
+ {
+ o.nTable.removeChild( nTfootSize[0] );
+ }
+ }
+
+ /* Clone the current header and footer elements and then place it into the inner table */
+ nTheadSize = o.nTHead.cloneNode(true);
+ o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
+
+ if ( o.nTFoot !== null )
+ {
+ nTfootSize = o.nTFoot.cloneNode(true);
+ o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
+ }
+
+ /*
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
+ */
+
+ /* Remove old sizing and apply the calculated column widths
+ * Get the unique column headers in the newly created (cloned) header. We want to apply the
+ * calclated sizes to this header
+ */
+ var nThs = _fnGetUniqueThs( nTheadSize );
+ for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
+ {
+ iVis = _fnVisibleToColumnIndex( o, i );
+ nThs[i].style.width = o.aoColumns[iVis].sWidth;
+ }
+
+ if ( o.nTFoot !== null )
+ {
+ _fnApplyToChildren( function(n) {
+ n.style.width = "";
+ }, nTfootSize.getElementsByTagName('tr') );
+ }
+
+ /* Size the table as a whole */
+ iSanityWidth = $(o.nTable).outerWidth();
+ if ( o.oScroll.sX === "" )
+ {
+ /* No x scrolling */
+ o.nTable.style.width = "100%";
+
+ /* I know this is rubbish - but IE7 will make the width of the table when 100% include
+ * the scrollbar - which is shouldn't. This needs feature detection in future - to do
+ */
+ if ( $.browser.msie && $.browser.version <= 7 )
+ {
+ o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth()-o.oScroll.iBarWidth );
+ }
+ }
+ else
+ {
+ if ( o.oScroll.sXInner !== "" )
+ {
+ /* x scroll inner has been given - use it */
+ o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
+ }
+ else if ( iSanityWidth == $(nScrollBody).width() &&
+ $(nScrollBody).height() < $(o.nTable).height() )
+ {
+ /* There is y-scrolling - try to take account of the y scroll bar */
+ o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
+ if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
+ {
+ /* Not possible to take account of it */
+ o.nTable.style.width = _fnStringToCss( iSanityWidth );
+ }
+ }
+ else
+ {
+ /* All else fails */
+ o.nTable.style.width = _fnStringToCss( iSanityWidth );
+ }
+ }
+
+ /* Recalculate the sanity width - now that we've applied the required width, before it was
+ * a temporary variable. This is required because the column width calculation is done
+ * before this table DOM is created.
+ */
+ iSanityWidth = $(o.nTable).outerWidth();
+
+ /* We want the hidden header to have zero height, so remove padding and borders. Then
+ * set the width based on the real headers
+ */
+ anHeadToSize = o.nTHead.getElementsByTagName('tr');
+ anHeadSizers = nTheadSize.getElementsByTagName('tr');
+
+ _fnApplyToChildren( function(nSizer, nToSize) {
+ oStyle = nSizer.style;
+ oStyle.paddingTop = "0";
+ oStyle.paddingBottom = "0";
+ oStyle.borderTopWidth = "0";
+ oStyle.borderBottomWidth = "0";
+ oStyle.height = 0;
+
+ iWidth = $(nSizer).width();
+ nToSize.style.width = _fnStringToCss( iWidth );
+ aApplied.push( iWidth );
+ }, anHeadSizers, anHeadToSize );
+ $(anHeadSizers).height(0);
+
+ if ( o.nTFoot !== null )
+ {
+ /* Clone the current footer and then place it into the body table as a "hidden header" */
+ anFootSizers = nTfootSize.getElementsByTagName('tr');
+ anFootToSize = o.nTFoot.getElementsByTagName('tr');
+
+ _fnApplyToChildren( function(nSizer, nToSize) {
+ oStyle = nSizer.style;
+ oStyle.paddingTop = "0";
+ oStyle.paddingBottom = "0";
+ oStyle.borderTopWidth = "0";
+ oStyle.borderBottomWidth = "0";
+ oStyle.height = 0;
+
+ iWidth = $(nSizer).width();
+ nToSize.style.width = _fnStringToCss( iWidth );
+ aApplied.push( iWidth );
+ }, anFootSizers, anFootToSize );
+ $(anFootSizers).height(0);
+ }
+
+ /*
+ * 3. Apply the measurements
+ */
+
+ /* "Hide" the header and footer that we used for the sizing. We want to also fix their width
+ * to what they currently are
+ */
+ _fnApplyToChildren( function(nSizer) {
+ nSizer.innerHTML = "";
+ nSizer.style.width = _fnStringToCss( aApplied.shift() );
+ }, anHeadSizers );
+
+ if ( o.nTFoot !== null )
+ {
+ _fnApplyToChildren( function(nSizer) {
+ nSizer.innerHTML = "";
+ nSizer.style.width = _fnStringToCss( aApplied.shift() );
+ }, anFootSizers );
+ }
+
+ /* Sanity check that the table is of a sensible width. If not then we are going to get
+ * misalignment
+ */
+ if ( $(o.nTable).outerWidth() < iSanityWidth )
+ {
+ if ( o.oScroll.sX === "" )
+ {
+ _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
+ " misalignment. It is suggested that you enable x-scrolling or increase the width"+
+ " the table has in which to be drawn" );
+ }
+ else if ( o.oScroll.sXInner !== "" )
+ {
+ _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
+ " misalignment. It is suggested that you increase the sScrollXInner property to"+
+ " allow it to draw in a larger area, or simply remove that parameter to allow"+
+ " automatic calculation" );
+ }
+ }
+
+
+ /*
+ * 4. Clean up
+ */
+
+ if ( o.oScroll.sY === "" )
+ {
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
+ * the scrollbar height from the visible display, rather than adding it on. We need to
+ * set the height in order to sort this. Don't want to do it in any other browsers.
+ */
+ if ( $.browser.msie && $.browser.version <= 7 )
+ {
+ nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
+ }
+ }
+
+ if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
+ {
+ nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
+
+ var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
+ o.oScroll.iBarWidth : 0;
+ if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
+ {
+ nScrollBody.style.height = _fnStringToCss( $(o.nTable).height()+iExtra );
+ }
+ }
+
+ /* Finally set the width's of the header and footer tables */
+ var iOuterWidth = $(o.nTable).outerWidth();
+ nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
+ nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth+o.oScroll.iBarWidth );
+
+ if ( o.nTFoot !== null )
+ {
+ var
+ nScrollFootInner = o.nScrollFoot.getElementsByTagName('div')[0],
+ nScrollFootTable = nScrollFootInner.getElementsByTagName('table')[0];
+
+ nScrollFootInner.style.width = _fnStringToCss( o.nTable.offsetWidth+o.oScroll.iBarWidth );
+ nScrollFootTable.style.width = _fnStringToCss( o.nTable.offsetWidth );
+ }
+
+ /* If sorting or filtering has occured, jump the scrolling back to the top */
+ if ( o.bSorted || o.bFiltered )
+ {
+ nScrollBody.scrollTop = 0;
+ }
+ }
+
+ /*
+ * Function: _fnAjustColumnSizing
+ * Purpose: Ajust the table column widths for new data
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * Notes: You would probably want to do a redraw after calling this function!
+ */
+ function _fnAjustColumnSizing ( oSettings )
+ {
+ /* Not interested in doing column width calculation if autowidth is disabled */
+ if ( oSettings.oFeatures.bAutoWidth === false )
+ {
+ return false;
+ }
+
+ _fnCalculateColumnWidths( oSettings );
+ for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
+ }
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Filtering
+ */
+
+ /*
+ * Function: _fnFeatureHtmlFilter
+ * Purpose: Generate the node required for filtering text
+ * Returns: node
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlFilter ( oSettings )
+ {
+ var nFilter = document.createElement( 'div' );
+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.f == "undefined" )
+ {
+ nFilter.setAttribute( 'id', oSettings.sTableId+'_filter' );
+ }
+ nFilter.className = oSettings.oClasses.sFilter;
+ var sSpace = oSettings.oLanguage.sSearch==="" ? "" : " ";
+ nFilter.innerHTML = oSettings.oLanguage.sSearch+sSpace+'<input type="text" />';
+
+ var jqFilter = $("input", nFilter);
+ jqFilter.val( oSettings.oPreviousSearch.sSearch.replace('"','&quot;') );
+ jqFilter.bind( 'keyup.DT', function(e) {
+ /* Update all other filter input elements for the new display */
+ var n = oSettings.aanFeatures.f;
+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+ {
+ if ( n[i] != this.parentNode )
+ {
+ $('input', n[i]).val( this.value );
+ }
+ }
+
+ /* Now do the filter */
+ if ( this.value != oSettings.oPreviousSearch.sSearch )
+ {
+ _fnFilterComplete( oSettings, {
+ "sSearch": this.value,
+ "bRegex": oSettings.oPreviousSearch.bRegex,
+ "bSmart": oSettings.oPreviousSearch.bSmart
+ } );
+ }
+ } );
+
+ jqFilter.bind( 'keypress.DT', function(e) {
+ /* Prevent default */
+ if ( e.keyCode == 13 )
+ {
+ return false;
+ }
+ } );
+
+ return nFilter;
+ }
+
+ /*
+ * Function: _fnFilterComplete
+ * Purpose: Filter the table using both the global filter and column based filtering
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * object:oSearch: search information
+ * int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
+ */
+ function _fnFilterComplete ( oSettings, oInput, iForce )
+ {
+ /* Filter on everything */
+ _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart );
+
+ /* Now do the individual column filter */
+ for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
+ {
+ _fnFilterColumn( oSettings, oSettings.aoPreSearchCols[i].sSearch, i,
+ oSettings.aoPreSearchCols[i].bRegex, oSettings.aoPreSearchCols[i].bSmart );
+ }
+
+ /* Custom filtering */
+ if ( _oExt.afnFiltering.length !== 0 )
+ {
+ _fnFilterCustom( oSettings );
+ }
+
+ /* Tell the draw function we have been filtering */
+ oSettings.bFiltered = true;
+
+ /* Redraw the table */
+ oSettings._iDisplayStart = 0;
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+
+ /* Rebuild search array 'offline' */
+ _fnBuildSearchArray( oSettings, 0 );
+ }
+
+ /*
+ * Function: _fnFilterCustom
+ * Purpose: Apply custom filtering functions
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFilterCustom( oSettings )
+ {
+ var afnFilters = _oExt.afnFiltering;
+ for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
+ {
+ var iCorrector = 0;
+ for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
+ {
+ var iDisIndex = oSettings.aiDisplay[j-iCorrector];
+
+ /* Check if we should use this row based on the filtering function */
+ if ( !afnFilters[i]( oSettings, oSettings.aoData[iDisIndex]._aData, iDisIndex ) )
+ {
+ oSettings.aiDisplay.splice( j-iCorrector, 1 );
+ iCorrector++;
+ }
+ }
+ }
+ }
+
+ /*
+ * Function: _fnFilterColumn
+ * Purpose: Filter the table on a per-column basis
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * string:sInput - string to filter on
+ * int:iColumn - column to filter
+ * bool:bRegex - treat search string as a regular expression or not
+ * bool:bSmart - use smart filtering or not
+ */
+ function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart )
+ {
+ if ( sInput === "" )
+ {
+ return;
+ }
+
+ var iIndexCorrector = 0;
+ var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
+
+ for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
+ {
+ var sData = _fnDataToSearch( oSettings.aoData[ oSettings.aiDisplay[i] ]._aData[iColumn],
+ oSettings.aoColumns[iColumn].sType );
+ if ( ! rpSearch.test( sData ) )
+ {
+ oSettings.aiDisplay.splice( i, 1 );
+ iIndexCorrector++;
+ }
+ }
+ }
+
+ /*
+ * Function: _fnFilter
+ * Purpose: Filter the data table based on user input and draw the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * string:sInput - string to filter on
+ * int:iForce - optional - force a research of the master array (1) or not (undefined or 0)
+ * bool:bRegex - treat as a regular expression or not
+ * bool:bSmart - perform smart filtering or not
+ */
+ function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart )
+ {
+ var i;
+ var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart );
+
+ /* Check if we are forcing or not - optional parameter */
+ if ( typeof iForce == 'undefined' || iForce === null )
+ {
+ iForce = 0;
+ }
+
+ /* Need to take account of custom filtering functions - always filter */
+ if ( _oExt.afnFiltering.length !== 0 )
+ {
+ iForce = 1;
+ }
+
+ /*
+ * If the input is blank - we want the full data set
+ */
+ if ( sInput.length <= 0 )
+ {
+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+ }
+ else
+ {
+ /*
+ * We are starting a new search or the new search string is smaller
+ * then the old one (i.e. delete). Search from the master array
+ */
+ if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
+ oSettings.oPreviousSearch.sSearch.length > sInput.length || iForce == 1 ||
+ sInput.indexOf(oSettings.oPreviousSearch.sSearch) !== 0 )
+ {
+ /* Nuke the old display array - we are going to rebuild it */
+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
+
+ /* Force a rebuild of the search array */
+ _fnBuildSearchArray( oSettings, 1 );
+
+ /* Search through all records to populate the search array
+ * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1
+ * mapping
+ */
+ for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
+ {
+ if ( rpSearch.test(oSettings.asDataSearch[i]) )
+ {
+ oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
+ }
+ }
+ }
+ else
+ {
+ /* Using old search array - refine it - do it this way for speed
+ * Don't have to search the whole master array again
+ */
+ var iIndexCorrector = 0;
+
+ /* Search the current results */
+ for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
+ {
+ if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
+ {
+ oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
+ iIndexCorrector++;
+ }
+ }
+ }
+ }
+ oSettings.oPreviousSearch.sSearch = sInput;
+ oSettings.oPreviousSearch.bRegex = bRegex;
+ oSettings.oPreviousSearch.bSmart = bSmart;
+ }
+
+ /*
+ * Function: _fnBuildSearchArray
+ * Purpose: Create an array which can be quickly search through
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * int:iMaster - use the master data array - optional
+ */
+ function _fnBuildSearchArray ( oSettings, iMaster )
+ {
+ /* Clear out the old data */
+ oSettings.asDataSearch.splice( 0, oSettings.asDataSearch.length );
+
+ var aArray = (typeof iMaster != 'undefined' && iMaster == 1) ?
+ oSettings.aiDisplayMaster : oSettings.aiDisplay;
+
+ for ( var i=0, iLen=aArray.length ; i<iLen ; i++ )
+ {
+ oSettings.asDataSearch[i] = _fnBuildSearchRow( oSettings,
+ oSettings.aoData[ aArray[i] ]._aData );
+ }
+ }
+
+ /*
+ * Function: _fnBuildSearchRow
+ * Purpose: Create a searchable string from a single data row
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * array:aData - aoData[]._aData array to use for the data to search
+ */
+ function _fnBuildSearchRow( oSettings, aData )
+ {
+ var sSearch = '';
+ var nTmp = document.createElement('div');
+
+ for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
+ {
+ if ( oSettings.aoColumns[j].bSearchable )
+ {
+ var sData = aData[j];
+ sSearch += _fnDataToSearch( sData, oSettings.aoColumns[j].sType )+' ';
+ }
+ }
+
+ /* If it looks like there is an HTML entity in the string, attempt to decode it */
+ if ( sSearch.indexOf('&') !== -1 )
+ {
+ nTmp.innerHTML = sSearch;
+ sSearch = nTmp.textContent ? nTmp.textContent : nTmp.innerText;
+
+ /* IE and Opera appear to put an newline where there is a <br> tag - remove it */
+ sSearch = sSearch.replace(/\n/g," ").replace(/\r/g,"");
+ }
+
+ return sSearch;
+ }
+
+ /*
+ * Function: _fnFilterCreateSearch
+ * Purpose: Build a regular expression object suitable for searching a table
+ * Returns: RegExp: - constructed object
+ * Inputs: string:sSearch - string to search for
+ * bool:bRegex - treat as a regular expression or not
+ * bool:bSmart - perform smart filtering or not
+ */
+ function _fnFilterCreateSearch( sSearch, bRegex, bSmart )
+ {
+ var asSearch, sRegExpString;
+
+ if ( bSmart )
+ {
+ /* Generate the regular expression to use. Something along the lines of:
+ * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
+ */
+ asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
+ sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
+ return new RegExp( sRegExpString, "i" );
+ }
+ else
+ {
+ sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
+ return new RegExp( sSearch, "i" );
+ }
+ }
+
+ /*
+ * Function: _fnDataToSearch
+ * Purpose: Convert raw data into something that the user can search on
+ * Returns: string: - search string
+ * Inputs: string:sData - data to be modified
+ * string:sType - data type
+ */
+ function _fnDataToSearch ( sData, sType )
+ {
+ if ( typeof _oExt.ofnSearch[sType] == "function" )
+ {
+ return _oExt.ofnSearch[sType]( sData );
+ }
+ else if ( sType == "html" )
+ {
+ return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
+ }
+ else if ( typeof sData == "string" )
+ {
+ return sData.replace(/\n/g," ");
+ }
+ return sData;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Sorting
+ */
+
+ /*
+ * Function: _fnSort
+ * Purpose: Change the order of the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * bool:bApplyClasses - optional - should we apply classes or not
+ * Notes: We always sort the master array and then apply a filter again
+ * if it is needed. This probably isn't optimal - but atm I can't think
+ * of any other way which is (each has disadvantages). we want to sort aiDisplayMaster -
+ * but according to aoData[]._aData
+ */
+ function _fnSort ( oSettings, bApplyClasses )
+ {
+ var
+ iDataSort, iDataType,
+ i, iLen, j, jLen,
+ aaSort = [],
+ aiOrig = [],
+ oSort = _oExt.oSort,
+ aoData = oSettings.aoData,
+ aoColumns = oSettings.aoColumns;
+
+ /* No sorting required if server-side or no sorting array */
+ if ( !oSettings.oFeatures.bServerSide &&
+ (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
+ {
+ if ( oSettings.aaSortingFixed !== null )
+ {
+ aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
+ }
+ else
+ {
+ aaSort = oSettings.aaSorting.slice();
+ }
+
+ /* If there is a sorting data type, and a fuction belonging to it, then we need to
+ * get the data from the developer's function and apply it for this column
+ */
+ for ( i=0 ; i<aaSort.length ; i++ )
+ {
+ var iColumn = aaSort[i][0];
+ var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
+ var sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
+ if ( typeof _oExt.afnSortData[sDataType] != 'undefined' )
+ {
+ var aData = _oExt.afnSortData[sDataType]( oSettings, iColumn, iVisColumn );
+ for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
+ {
+ aoData[j]._aData[iColumn] = aData[j];
+ }
+ }
+ }
+
+ /* Create a value - key array of the current row positions such that we can use their
+ * current position during the sort, if values match, in order to perform stable sorting
+ */
+ for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
+ {
+ aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
+ }
+
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
+ * follow on it's own, but this is what we want (example two column sorting):
+ * fnLocalSorting = function(a,b){
+ * var iTest;
+ * iTest = oSort['string-asc']('data11', 'data12');
+ * if (iTest !== 0)
+ * return iTest;
+ * iTest = oSort['numeric-desc']('data21', 'data22');
+ * if (iTest !== 0)
+ * return iTest;
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+ * }
+ * Basically we have a test for each sorting column, if the data in that column is equal,
+ * test the next column. If all columns match, then we use a numeric sort on the row
+ * positions in the original data array to provide a stable sort.
+ */
+ var iSortLen = aaSort.length;
+ oSettings.aiDisplayMaster.sort( function ( a, b ) {
+ var iTest;
+ for ( i=0 ; i<iSortLen ; i++ )
+ {
+ iDataSort = aoColumns[ aaSort[i][0] ].iDataSort;
+ iDataType = aoColumns[ iDataSort ].sType;
+ iTest = oSort[ iDataType+"-"+aaSort[i][1] ](
+ aoData[a]._aData[iDataSort],
+ aoData[b]._aData[iDataSort]
+ );
+
+ if ( iTest !== 0 )
+ {
+ return iTest;
+ }
+ }
+
+ return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+ } );
+ }
+
+ /* Alter the sorting classes to take account of the changes */
+ if ( typeof bApplyClasses == 'undefined' || bApplyClasses )
+ {
+ _fnSortingClasses( oSettings );
+ }
+
+ /* Tell the draw function that we have sorted the data */
+ oSettings.bSorted = true;
+
+ /* Copy the master data into the draw array and re-draw */
+ if ( oSettings.oFeatures.bFilter )
+ {
+ /* _fnFilter() will redraw the table for us */
+ _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+ }
+ else
+ {
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+ oSettings._iDisplayStart = 0; /* reset display back to page 0 */
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+ }
+
+ /*
+ * Function: _fnSortAttachListener
+ * Purpose: Attach a sort handler (click) to a node
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * node:nNode - node to attach the handler to
+ * int:iDataIndex - column sorting index
+ * function:fnCallback - callback function - optional
+ */
+ function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
+ {
+ $(nNode).bind( 'click.DT', function (e) {
+ /* If the column is not sortable - don't to anything */
+ if ( oSettings.aoColumns[iDataIndex].bSortable === false )
+ {
+ return;
+ }
+
+ /*
+ * This is a little bit odd I admit... I declare a temporary function inside the scope of
+ * _fnDrawHead and the click handler in order that the code presented here can be used
+ * twice - once for when bProcessing is enabled, and another time for when it is
+ * disabled, as we need to perform slightly different actions.
+ * Basically the issue here is that the Javascript engine in modern browsers don't
+ * appear to allow the rendering engine to update the display while it is still excuting
+ * it's thread (well - it does but only after long intervals). This means that the
+ * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
+ * I force an execution break by using setTimeout - but this breaks the expected
+ * thread continuation for the end-developer's point of view (their code would execute
+ * too early), so we on;y do it when we absolutely have to.
+ */
+ var fnInnerSorting = function () {
+ var iColumn, iNextSort;
+
+ /* If the shift key is pressed then we are multipe column sorting */
+ if ( e.shiftKey )
+ {
+ /* Are we already doing some kind of sort on this column? */
+ var bFound = false;
+ for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
+ {
+ if ( oSettings.aaSorting[i][0] == iDataIndex )
+ {
+ bFound = true;
+ iColumn = oSettings.aaSorting[i][0];
+ iNextSort = oSettings.aaSorting[i][2]+1;
+
+ if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
+ {
+ /* Reached the end of the sorting options, remove from multi-col sort */
+ oSettings.aaSorting.splice( i, 1 );
+ }
+ else
+ {
+ /* Move onto next sorting direction */
+ oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
+ oSettings.aaSorting[i][2] = iNextSort;
+ }
+ break;
+ }
+ }
+
+ /* No sort yet - add it in */
+ if ( bFound === false )
+ {
+ oSettings.aaSorting.push( [ iDataIndex,
+ oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
+ }
+ }
+ else
+ {
+ /* If no shift key then single column sort */
+ if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
+ {
+ iColumn = oSettings.aaSorting[0][0];
+ iNextSort = oSettings.aaSorting[0][2]+1;
+ if ( typeof oSettings.aoColumns[iColumn].asSorting[iNextSort] == 'undefined' )
+ {
+ iNextSort = 0;
+ }
+ oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
+ oSettings.aaSorting[0][2] = iNextSort;
+ }
+ else
+ {
+ oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
+ oSettings.aaSorting.push( [ iDataIndex,
+ oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
+ }
+ }
+
+ /* Run the sort */
+ _fnSort( oSettings );
+ }; /* /fnInnerSorting */
+
+ if ( !oSettings.oFeatures.bProcessing )
+ {
+ fnInnerSorting();
+ }
+ else
+ {
+ _fnProcessingDisplay( oSettings, true );
+ setTimeout( function() {
+ fnInnerSorting();
+ if ( !oSettings.oFeatures.bServerSide )
+ {
+ _fnProcessingDisplay( oSettings, false );
+ }
+ }, 0 );
+ }
+
+ /* Call the user specified callback function - used for async user interaction */
+ if ( typeof fnCallback == 'function' )
+ {
+ fnCallback( oSettings );
+ }
+ } );
+ }
+
+ /*
+ * Function: _fnSortingClasses
+ * Purpose: Set the sortting classes on the header
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * Notes: It is safe to call this function when bSort and bSortClasses are false
+ */
+ function _fnSortingClasses( oSettings )
+ {
+ var i, iLen, j, jLen, iFound;
+ var aaSort, sClass;
+ var iColumns = oSettings.aoColumns.length;
+ var oClasses = oSettings.oClasses;
+
+ for ( i=0 ; i<iColumns ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bSortable )
+ {
+ $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
+ " "+ oSettings.aoColumns[i].sSortingClass );
+ }
+ }
+
+ if ( oSettings.aaSortingFixed !== null )
+ {
+ aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
+ }
+ else
+ {
+ aaSort = oSettings.aaSorting.slice();
+ }
+
+ /* Apply the required classes to the header */
+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bSortable )
+ {
+ sClass = oSettings.aoColumns[i].sSortingClass;
+ iFound = -1;
+ for ( j=0 ; j<aaSort.length ; j++ )
+ {
+ if ( aaSort[j][0] == i )
+ {
+ sClass = ( aaSort[j][1] == "asc" ) ?
+ oClasses.sSortAsc : oClasses.sSortDesc;
+ iFound = j;
+ break;
+ }
+ }
+ $(oSettings.aoColumns[i].nTh).addClass( sClass );
+
+ if ( oSettings.bJUI )
+ {
+ /* jQuery UI uses extra markup */
+ var jqSpan = $("span", oSettings.aoColumns[i].nTh);
+ jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+
+ oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
+
+ var sSpanClass;
+ if ( iFound == -1 )
+ {
+ sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
+ }
+ else if ( aaSort[iFound][1] == "asc" )
+ {
+ sSpanClass = oClasses.sSortJUIAsc;
+ }
+ else
+ {
+ sSpanClass = oClasses.sSortJUIDesc;
+ }
+
+ jqSpan.addClass( sSpanClass );
+ }
+ }
+ else
+ {
+ /* No sorting on this column, so add the base class. This will have been assigned by
+ * _fnAddColumn
+ */
+ $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
+ }
+ }
+
+ /*
+ * Apply the required classes to the table body
+ * Note that this is given as a feature switch since it can significantly slow down a sort
+ * on large data sets (adding and removing of classes is always slow at the best of times..)
+ * Further to this, note that this code is admitadly fairly ugly. It could be made a lot
+ * simpiler using jQuery selectors and add/removeClass, but that is significantly slower
+ * (on the order of 5 times slower) - hence the direct DOM manipulation here.
+ */
+ sClass = oClasses.sSortColumn;
+
+ if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
+ {
+ var nTds = _fnGetTdNodes( oSettings );
+
+ /* Remove the old classes */
+ if ( nTds.length >= iColumns )
+ {
+ for ( i=0 ; i<iColumns ; i++ )
+ {
+ if ( nTds[i].className.indexOf(sClass+"1") != -1 )
+ {
+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+ {
+ nTds[(iColumns*j)+i].className =
+ $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"1", "" ) );
+ }
+ }
+ else if ( nTds[i].className.indexOf(sClass+"2") != -1 )
+ {
+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+ {
+ nTds[(iColumns*j)+i].className =
+ $.trim( nTds[(iColumns*j)+i].className.replace( sClass+"2", "" ) );
+ }
+ }
+ else if ( nTds[i].className.indexOf(sClass+"3") != -1 )
+ {
+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+ {
+ nTds[(iColumns*j)+i].className =
+ $.trim( nTds[(iColumns*j)+i].className.replace( " "+sClass+"3", "" ) );
+ }
+ }
+ }
+ }
+
+ /* Add the new classes to the table */
+ var iClass = 1, iTargetCol;
+ for ( i=0 ; i<aaSort.length ; i++ )
+ {
+ iTargetCol = parseInt( aaSort[i][0], 10 );
+ for ( j=0, jLen=(nTds.length/iColumns) ; j<jLen ; j++ )
+ {
+ nTds[(iColumns*j)+iTargetCol].className += " "+sClass+iClass;
+ }
+
+ if ( iClass < 3 )
+ {
+ iClass++;
+ }
+ }
+ }
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Pagination. Note that most of the paging logic is done in
+ * _oExt.oPagination
+ */
+
+ /*
+ * Function: _fnFeatureHtmlPaginate
+ * Purpose: Generate the node required for default pagination
+ * Returns: node
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlPaginate ( oSettings )
+ {
+ if ( oSettings.oScroll.bInfinite )
+ {
+ return null;
+ }
+
+ var nPaginate = document.createElement( 'div' );
+ nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
+
+ _oExt.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate,
+ function( oSettings ) {
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ }
+ );
+
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
+ if ( typeof oSettings.aanFeatures.p == "undefined" )
+ {
+ oSettings.aoDrawCallback.push( {
+ "fn": function( oSettings ) {
+ _oExt.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
+ _fnCalculateEnd( oSettings );
+ _fnDraw( oSettings );
+ } );
+ },
+ "sName": "pagination"
+ } );
+ }
+ return nPaginate;
+ }
+
+ /*
+ * Function: _fnPageChange
+ * Purpose: Alter the display settings to change the page
+ * Returns: bool:true - page has changed, false - no change (no effect) eg 'first' on page 1
+ * Inputs: object:oSettings - dataTables settings object
+ * string:sAction - paging action to take: "first", "previous", "next" or "last"
+ */
+ function _fnPageChange ( oSettings, sAction )
+ {
+ var iOldStart = oSettings._iDisplayStart;
+
+ if ( sAction == "first" )
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ else if ( sAction == "previous" )
+ {
+ oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
+ oSettings._iDisplayStart - oSettings._iDisplayLength :
+ 0;
+
+ /* Correct for underrun */
+ if ( oSettings._iDisplayStart < 0 )
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ }
+ else if ( sAction == "next" )
+ {
+ if ( oSettings._iDisplayLength >= 0 )
+ {
+ /* Make sure we are not over running the display array */
+ if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
+ {
+ oSettings._iDisplayStart += oSettings._iDisplayLength;
+ }
+ }
+ else
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ }
+ else if ( sAction == "last" )
+ {
+ if ( oSettings._iDisplayLength >= 0 )
+ {
+ var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
+ oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
+ }
+ else
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ }
+ else
+ {
+ _fnLog( oSettings, 0, "Unknown paging action: "+sAction );
+ }
+
+ return iOldStart != oSettings._iDisplayStart;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: HTML info
+ */
+
+ /*
+ * Function: _fnFeatureHtmlInfo
+ * Purpose: Generate the node required for the info display
+ * Returns: node
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlInfo ( oSettings )
+ {
+ var nInfo = document.createElement( 'div' );
+ nInfo.className = oSettings.oClasses.sInfo;
+
+ /* Actions that are to be taken once only for this feature */
+ if ( typeof oSettings.aanFeatures.i == "undefined" )
+ {
+ /* Add draw callback */
+ oSettings.aoDrawCallback.push( {
+ "fn": _fnUpdateInfo,
+ "sName": "information"
+ } );
+
+ /* Add id */
+ if ( oSettings.sTableId !== '' )
+ {
+ nInfo.setAttribute( 'id', oSettings.sTableId+'_info' );
+ }
+ }
+
+ return nInfo;
+ }
+
+ /*
+ * Function: _fnUpdateInfo
+ * Purpose: Update the information elements in the display
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnUpdateInfo ( oSettings )
+ {
+ /* Show information about the table */
+ if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
+ {
+ return;
+ }
+
+ var
+ iStart = oSettings._iDisplayStart+1, iEnd = oSettings.fnDisplayEnd(),
+ iMax = oSettings.fnRecordsTotal(), iTotal = oSettings.fnRecordsDisplay(),
+ sStart = oSettings.fnFormatNumber( iStart ), sEnd = oSettings.fnFormatNumber( iEnd ),
+ sMax = oSettings.fnFormatNumber( iMax ), sTotal = oSettings.fnFormatNumber( iTotal ),
+ sOut;
+
+ /* When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
+ * internally
+ */
+ if ( oSettings.oScroll.bInfinite )
+ {
+ sStart = oSettings.fnFormatNumber( 1 );
+ }
+
+ if ( oSettings.fnRecordsDisplay() === 0 &&
+ oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
+ {
+ /* Empty record set */
+ sOut = oSettings.oLanguage.sInfoEmpty+ oSettings.oLanguage.sInfoPostFix;
+ }
+ else if ( oSettings.fnRecordsDisplay() === 0 )
+ {
+ /* Rmpty record set after filtering */
+ sOut = oSettings.oLanguage.sInfoEmpty +' '+
+ oSettings.oLanguage.sInfoFiltered.replace('_MAX_', sMax)+
+ oSettings.oLanguage.sInfoPostFix;
+ }
+ else if ( oSettings.fnRecordsDisplay() == oSettings.fnRecordsTotal() )
+ {
+ /* Normal record set */
+ sOut = oSettings.oLanguage.sInfo.
+ replace('_START_', sStart).
+ replace('_END_', sEnd).
+ replace('_TOTAL_', sTotal)+
+ oSettings.oLanguage.sInfoPostFix;
+ }
+ else
+ {
+ /* Record set after filtering */
+ sOut = oSettings.oLanguage.sInfo.
+ replace('_START_', sStart).
+ replace('_END_', sEnd).
+ replace('_TOTAL_', sTotal) +' '+
+ oSettings.oLanguage.sInfoFiltered.replace('_MAX_',
+ oSettings.fnFormatNumber(oSettings.fnRecordsTotal()))+
+ oSettings.oLanguage.sInfoPostFix;
+ }
+
+ if ( oSettings.oLanguage.fnInfoCallback !== null )
+ {
+ sOut = oSettings.oLanguage.fnInfoCallback( oSettings, iStart, iEnd, iMax, iTotal, sOut );
+ }
+
+ var n = oSettings.aanFeatures.i;
+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
+ {
+ $(n[i]).html( sOut );
+ }
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Length change
+ */
+
+ /*
+ * Function: _fnFeatureHtmlLength
+ * Purpose: Generate the node required for user display length changing
+ * Returns: node
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlLength ( oSettings )
+ {
+ if ( oSettings.oScroll.bInfinite )
+ {
+ return null;
+ }
+
+ /* This can be overruled by not using the _MENU_ var/macro in the language variable */
+ var sName = (oSettings.sTableId === "") ? "" : 'name="'+oSettings.sTableId+'_length"';
+ var sStdMenu = '<select size="1" '+sName+'>';
+ var i, iLen;
+
+ if ( oSettings.aLengthMenu.length == 2 && typeof oSettings.aLengthMenu[0] == 'object' &&
+ typeof oSettings.aLengthMenu[1] == 'object' )
+ {
+ for ( i=0, iLen=oSettings.aLengthMenu[0].length ; i<iLen ; i++ )
+ {
+ sStdMenu += '<option value="'+oSettings.aLengthMenu[0][i]+'">'+
+ oSettings.aLengthMenu[1][i]+'</option>';
+ }
+ }
+ else
+ {
+ for ( i=0, iLen=oSettings.aLengthMenu.length ; i<iLen ; i++ )
+ {
+ sStdMenu += '<option value="'+oSettings.aLengthMenu[i]+'">'+
+ oSettings.aLengthMenu[i]+'</option>';
+ }
+ }
+ sStdMenu += '</select>';
+
+ var nLength = document.createElement( 'div' );
+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.l == "undefined" )
+ {
+ nLength.setAttribute( 'id', oSettings.sTableId+'_length' );
+ }
+ nLength.className = oSettings.oClasses.sLength;
+ nLength.innerHTML = oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu );
+
+ /*
+ * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
+ * and Stefan Skopnik for fixing the fix!
+ */
+ $('select option[value="'+oSettings._iDisplayLength+'"]',nLength).attr("selected",true);
+
+ $('select', nLength).bind( 'change.DT', function(e) {
+ var iVal = $(this).val();
+
+ /* Update all other length options for the new display */
+ var n = oSettings.aanFeatures.l;
+ for ( i=0, iLen=n.length ; i<iLen ; i++ )
+ {
+ if ( n[i] != this.parentNode )
+ {
+ $('select', n[i]).val( iVal );
+ }
+ }
+
+ /* Redraw the table */
+ oSettings._iDisplayLength = parseInt(iVal, 10);
+ _fnCalculateEnd( oSettings );
+
+ /* If we have space to show extra rows (backing up from the end point - then do so */
+ if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
+ {
+ oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
+ if ( oSettings._iDisplayStart < 0 )
+ {
+ oSettings._iDisplayStart = 0;
+ }
+ }
+
+ if ( oSettings._iDisplayLength == -1 )
+ {
+ oSettings._iDisplayStart = 0;
+ }
+
+ _fnDraw( oSettings );
+ } );
+
+ return nLength;
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Feature: Processing incidator
+ */
+
+ /*
+ * Function: _fnFeatureHtmlProcessing
+ * Purpose: Generate the node required for the processing node
+ * Returns: node
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnFeatureHtmlProcessing ( oSettings )
+ {
+ var nProcessing = document.createElement( 'div' );
+
+ if ( oSettings.sTableId !== '' && typeof oSettings.aanFeatures.r == "undefined" )
+ {
+ nProcessing.setAttribute( 'id', oSettings.sTableId+'_processing' );
+ }
+ nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
+ nProcessing.className = oSettings.oClasses.sProcessing;
+ oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
+
+ return nProcessing;
+ }
+
+ /*
+ * Function: _fnProcessingDisplay
+ * Purpose: Display or hide the processing indicator
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * bool:
+ * true - show the processing indicator
+ * false - don't show
+ */
+ function _fnProcessingDisplay ( oSettings, bShow )
+ {
+ if ( oSettings.oFeatures.bProcessing )
+ {
+ var an = oSettings.aanFeatures.r;
+ for ( var i=0, iLen=an.length ; i<iLen ; i++ )
+ {
+ an[i].style.visibility = bShow ? "visible" : "hidden";
+ }
+ }
+ }
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Support functions
+ */
+
+ /*
+ * Function: _fnVisibleToColumnIndex
+ * Purpose: Covert the index of a visible column to the index in the data array (take account
+ * of hidden columns)
+ * Returns: int:i - the data index
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnVisibleToColumnIndex( oSettings, iMatch )
+ {
+ var iColumn = -1;
+
+ for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible === true )
+ {
+ iColumn++;
+ }
+
+ if ( iColumn == iMatch )
+ {
+ return i;
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ * Function: _fnColumnIndexToVisible
+ * Purpose: Covert the index of an index in the data array and convert it to the visible
+ * column index (take account of hidden columns)
+ * Returns: int:i - the data index
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnColumnIndexToVisible( oSettings, iMatch )
+ {
+ var iVisible = -1;
+ for ( var i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible === true )
+ {
+ iVisible++;
+ }
+
+ if ( i == iMatch )
+ {
+ return oSettings.aoColumns[i].bVisible === true ? iVisible : null;
+ }
+ }
+
+ return null;
+ }
+
+
+ /*
+ * Function: _fnNodeToDataIndex
+ * Purpose: Take a TR element and convert it to an index in aoData
+ * Returns: int:i - index if found, null if not
+ * Inputs: object:s - dataTables settings object
+ * node:n - the TR element to find
+ */
+ function _fnNodeToDataIndex( s, n )
+ {
+ var i, iLen;
+
+ /* Optimisation - see if the nodes which are currently visible match, since that is
+ * the most likely node to be asked for (a selector or event for example)
+ */
+ for ( i=s._iDisplayStart, iLen=s._iDisplayEnd ; i<iLen ; i++ )
+ {
+ if ( s.aoData[ s.aiDisplay[i] ].nTr == n )
+ {
+ return s.aiDisplay[i];
+ }
+ }
+
+ /* Otherwise we are in for a slog through the whole data cache */
+ for ( i=0, iLen=s.aoData.length ; i<iLen ; i++ )
+ {
+ if ( s.aoData[i].nTr == n )
+ {
+ return i;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Function: _fnVisbleColumns
+ * Purpose: Get the number of visible columns
+ * Returns: int:i - the number of visible columns
+ * Inputs: object:oS - dataTables settings object
+ */
+ function _fnVisbleColumns( oS )
+ {
+ var iVis = 0;
+ for ( var i=0 ; i<oS.aoColumns.length ; i++ )
+ {
+ if ( oS.aoColumns[i].bVisible === true )
+ {
+ iVis++;
+ }
+ }
+ return iVis;
+ }
+
+ /*
+ * Function: _fnCalculateEnd
+ * Purpose: Rcalculate the end point based on the start point
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnCalculateEnd( oSettings )
+ {
+ if ( oSettings.oFeatures.bPaginate === false )
+ {
+ oSettings._iDisplayEnd = oSettings.aiDisplay.length;
+ }
+ else
+ {
+ /* Set the end point of the display - based on how many elements there are
+ * still to display
+ */
+ if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
+ oSettings._iDisplayLength == -1 )
+ {
+ oSettings._iDisplayEnd = oSettings.aiDisplay.length;
+ }
+ else
+ {
+ oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
+ }
+ }
+ }
+
+ /*
+ * Function: _fnConvertToWidth
+ * Purpose: Convert a CSS unit width to pixels (e.g. 2em)
+ * Returns: int:iWidth - width in pixels
+ * Inputs: string:sWidth - width to be converted
+ * node:nParent - parent to get the with for (required for
+ * relative widths) - optional
+ */
+ function _fnConvertToWidth ( sWidth, nParent )
+ {
+ if ( !sWidth || sWidth === null || sWidth === '' )
+ {
+ return 0;
+ }
+
+ if ( typeof nParent == "undefined" )
+ {
+ nParent = document.getElementsByTagName('body')[0];
+ }
+
+ var iWidth;
+ var nTmp = document.createElement( "div" );
+ nTmp.style.width = sWidth;
+
+ nParent.appendChild( nTmp );
+ iWidth = nTmp.offsetWidth;
+ nParent.removeChild( nTmp );
+
+ return ( iWidth );
+ }
+
+ /*
+ * Function: _fnCalculateColumnWidths
+ * Purpose: Calculate the width of columns for the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnCalculateColumnWidths ( oSettings )
+ {
+ var iTableWidth = oSettings.nTable.offsetWidth;
+ var iUserInputs = 0;
+ var iTmpWidth;
+ var iVisibleColumns = 0;
+ var iColums = oSettings.aoColumns.length;
+ var i;
+ var oHeaders = $('th', oSettings.nTHead);
+
+ /* Convert any user input sizes into pixel sizes */
+ for ( i=0 ; i<iColums ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ iVisibleColumns++;
+
+ if ( oSettings.aoColumns[i].sWidth !== null )
+ {
+ iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
+ oSettings.nTable.parentNode );
+ if ( iTmpWidth !== null )
+ {
+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
+ }
+
+ iUserInputs++;
+ }
+ }
+ }
+
+ /* If the number of columns in the DOM equals the number that we have to process in
+ * DataTables, then we can use the offsets that are created by the web-browser. No custom
+ * sizes can be set in order for this to happen, nor scrolling used
+ */
+ if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
+ oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
+ {
+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ iTmpWidth = $(oHeaders[i]).width();
+ if ( iTmpWidth !== null )
+ {
+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
+ }
+ }
+ }
+ else
+ {
+ /* Otherwise we are going to have to do some calculations to get the width of each column.
+ * Construct a 1 row table with the widest node in the data, and any user defined widths,
+ * then insert it into the DOM and allow the browser to do all the hard work of
+ * calculating table widths.
+ */
+ var
+ nCalcTmp = oSettings.nTable.cloneNode( false ),
+ nBody = document.createElement( 'tbody' ),
+ nTr = document.createElement( 'tr' ),
+ nDivSizing;
+
+ nCalcTmp.removeAttribute( "id" );
+ nCalcTmp.appendChild( oSettings.nTHead.cloneNode(true) );
+ if ( oSettings.nTFoot !== null )
+ {
+ nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
+ _fnApplyToChildren( function(n) {
+ n.style.width = "";
+ }, nCalcTmp.getElementsByTagName('tr') );
+ }
+
+ nCalcTmp.appendChild( nBody );
+ nBody.appendChild( nTr );
+
+ /* Remove any sizing that was previously applied by the styles */
+ var jqColSizing = $('thead th', nCalcTmp);
+ if ( jqColSizing.length === 0 )
+ {
+ jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
+ }
+ jqColSizing.each( function (i) {
+ this.style.width = "";
+
+ var iIndex = _fnVisibleToColumnIndex( oSettings, i );
+ if ( iIndex !== null && oSettings.aoColumns[iIndex].sWidthOrig !== "" )
+ {
+ this.style.width = oSettings.aoColumns[iIndex].sWidthOrig;
+ }
+ } );
+
+ /* Find the biggest td for each column and put it into the table */
+ for ( i=0 ; i<iColums ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ var nTd = _fnGetWidestNode( oSettings, i );
+ if ( nTd !== null )
+ {
+ nTd = nTd.cloneNode(true);
+ nTr.appendChild( nTd );
+ }
+ }
+ }
+
+ /* Build the table and 'display' it */
+ var nWrapper = oSettings.nTable.parentNode;
+ nWrapper.appendChild( nCalcTmp );
+
+ /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
+ * when not scrolling leave the table width as it is. This results in slightly different,
+ * but I think correct behaviour
+ */
+ if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
+ {
+ nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
+ }
+ else if ( oSettings.oScroll.sX !== "" )
+ {
+ nCalcTmp.style.width = "";
+ if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
+ {
+ nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
+ }
+ }
+ else if ( oSettings.oScroll.sY !== "" )
+ {
+ nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
+ }
+ nCalcTmp.style.visibility = "hidden";
+
+ /* Scrolling considerations */
+ _fnScrollingWidthAdjust( oSettings, nCalcTmp );
+
+ /* Read the width's calculated by the browser and store them for use by the caller. We
+ * first of all try to use the elements in the body, but it is possible that there are
+ * no elements there, under which circumstances we use the header elements
+ */
+ var oNodes = $("tbody tr:eq(0)>td", nCalcTmp);
+ if ( oNodes.length === 0 )
+ {
+ oNodes = $("thead tr:eq(0)>th", nCalcTmp);
+ }
+
+ var iIndex, iCorrector = 0, iWidth;
+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ if ( oSettings.aoColumns[i].bVisible )
+ {
+ iWidth = $(oNodes[iCorrector]).outerWidth();
+ if ( iWidth !== null && iWidth > 0 )
+ {
+ oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
+ }
+ iCorrector++;
+ }
+ }
+
+ oSettings.nTable.style.width = _fnStringToCss( $(nCalcTmp).outerWidth() );
+ nCalcTmp.parentNode.removeChild( nCalcTmp );
+ }
+ }
+
+ /*
+ * Function: _fnScrollingWidthAdjust
+ * Purpose: Adjust a table's width to take account of scrolling
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * node:n - table node
+ */
+ function _fnScrollingWidthAdjust ( oSettings, n )
+ {
+ if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
+ {
+ /* When y-scrolling only, we want to remove the width of the scroll bar so the table
+ * + scroll bar will fit into the area avaialble.
+ */
+ var iOrigWidth = $(n).width();
+ n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
+ }
+ else if ( oSettings.oScroll.sX !== "" )
+ {
+ /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
+ n.style.width = _fnStringToCss( $(n).outerWidth() );
+ }
+ }
+
+ /*
+ * Function: _fnGetWidestNode
+ * Purpose: Get the widest node
+ * Returns: string: - max strlens for each column
+ * Inputs: object:oSettings - dataTables settings object
+ * int:iCol - column of interest
+ * boolean:bFast - Should we use fast (but non-accurate) calculation - optional,
+ * default true
+ * Notes: This operation is _expensive_ (!!!). It requires a lot of DOM interaction, but
+ * this is the only way to reliably get the widest string. For example 'mmm' would be wider
+ * than 'iiii' so we can't just ocunt characters. If this can be optimised it would be good
+ * to do so!
+ */
+ function _fnGetWidestNode( oSettings, iCol, bFast )
+ {
+ /* Use fast not non-accurate calculate based on the strlen */
+ if ( typeof bFast == 'undefined' || bFast )
+ {
+ var iMaxLen = _fnGetMaxLenString( oSettings, iCol );
+ var iFastVis = _fnColumnIndexToVisible( oSettings, iCol);
+ if ( iMaxLen < 0 )
+ {
+ return null;
+ }
+ return oSettings.aoData[iMaxLen].nTr.getElementsByTagName('td')[iFastVis];
+ }
+
+ /* Use the slow approach, but get high quality answers - note that this code is not actually
+ * used by DataTables by default. If you want to use it you can alter the call to
+ * _fnGetWidestNode to pass 'false' as the third argument
+ */
+ var
+ iMax = -1, i, iLen,
+ iMaxIndex = -1,
+ n = document.createElement('div');
+
+ n.style.visibility = "hidden";
+ n.style.position = "absolute";
+ document.body.appendChild( n );
+
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
+ {
+ n.innerHTML = oSettings.aoData[i]._aData[iCol];
+ if ( n.offsetWidth > iMax )
+ {
+ iMax = n.offsetWidth;
+ iMaxIndex = i;
+ }
+ }
+ document.body.removeChild( n );
+
+ if ( iMaxIndex >= 0 )
+ {
+ var iVis = _fnColumnIndexToVisible( oSettings, iCol);
+ var nRet = oSettings.aoData[iMaxIndex].nTr.getElementsByTagName('td')[iVis];
+ if ( nRet )
+ {
+ return nRet;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Function: _fnGetMaxLenString
+ * Purpose: Get the maximum strlen for each data column
+ * Returns: string: - max strlens for each column
+ * Inputs: object:oSettings - dataTables settings object
+ * int:iCol - column of interest
+ */
+ function _fnGetMaxLenString( oSettings, iCol )
+ {
+ var iMax = -1;
+ var iMaxIndex = -1;
+
+ for ( var i=0 ; i<oSettings.aoData.length ; i++ )
+ {
+ var s = oSettings.aoData[i]._aData[iCol];
+ if ( s.length > iMax )
+ {
+ iMax = s.length;
+ iMaxIndex = i;
+ }
+ }
+
+ return iMaxIndex;
+ }
+
+ /*
+ * Function: _fnStringToCss
+ * Purpose: Append a CSS unit (only if required) to a string
+ * Returns: 0 if match, 1 if length is different, 2 if no match
+ * Inputs: array:aArray1 - first array
+ * array:aArray2 - second array
+ */
+ function _fnStringToCss( s )
+ {
+ if ( s === null )
+ {
+ return "0px";
+ }
+
+ if ( typeof s == 'number' )
+ {
+ if ( s < 0 )
+ {
+ return "0px";
+ }
+ return s+"px";
+ }
+
+ /* Check if the last character is not 0-9 */
+ var c = s.charCodeAt( s.length-1 );
+ if (c < 0x30 || c > 0x39)
+ {
+ return s;
+ }
+ return s+"px";
+ }
+
+ /*
+ * Function: _fnArrayCmp
+ * Purpose: Compare two arrays
+ * Returns: 0 if match, 1 if length is different, 2 if no match
+ * Inputs: array:aArray1 - first array
+ * array:aArray2 - second array
+ */
+ function _fnArrayCmp( aArray1, aArray2 )
+ {
+ if ( aArray1.length != aArray2.length )
+ {
+ return 1;
+ }
+
+ for ( var i=0 ; i<aArray1.length ; i++ )
+ {
+ if ( aArray1[i] != aArray2[i] )
+ {
+ return 2;
+ }
+ }
+
+ return 0;
+ }
+
+ /*
+ * Function: _fnDetectType
+ * Purpose: Get the sort type based on an input string
+ * Returns: string: - type (defaults to 'string' if no type can be detected)
+ * Inputs: string:sData - data we wish to know the type of
+ * Notes: This function makes use of the DataTables plugin objct _oExt
+ * (.aTypes) such that new types can easily be added.
+ */
+ function _fnDetectType( sData )
+ {
+ var aTypes = _oExt.aTypes;
+ var iLen = aTypes.length;
+
+ for ( var i=0 ; i<iLen ; i++ )
+ {
+ var sType = aTypes[i]( sData );
+ if ( sType !== null )
+ {
+ return sType;
+ }
+ }
+
+ return 'string';
+ }
+
+ /*
+ * Function: _fnSettingsFromNode
+ * Purpose: Return the settings object for a particular table
+ * Returns: object: Settings object - or null if not found
+ * Inputs: node:nTable - table we are using as a dataTable
+ */
+ function _fnSettingsFromNode ( nTable )
+ {
+ for ( var i=0 ; i<_aoSettings.length ; i++ )
+ {
+ if ( _aoSettings[i].nTable == nTable )
+ {
+ return _aoSettings[i];
+ }
+ }
+
+ return null;
+ }
+
+ /*
+ * Function: _fnGetDataMaster
+ * Purpose: Return an array with the full table data
+ * Returns: array array:aData - Master data array
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnGetDataMaster ( oSettings )
+ {
+ var aData = [];
+ var iLen = oSettings.aoData.length;
+ for ( var i=0 ; i<iLen; i++ )
+ {
+ aData.push( oSettings.aoData[i]._aData );
+ }
+ return aData;
+ }
+
+ /*
+ * Function: _fnGetTrNodes
+ * Purpose: Return an array with the TR nodes for the table
+ * Returns: array: - TR array
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnGetTrNodes ( oSettings )
+ {
+ var aNodes = [];
+ var iLen = oSettings.aoData.length;
+ for ( var i=0 ; i<iLen ; i++ )
+ {
+ aNodes.push( oSettings.aoData[i].nTr );
+ }
+ return aNodes;
+ }
+
+ /*
+ * Function: _fnGetTdNodes
+ * Purpose: Return an array with the TD nodes for the table
+ * Returns: array: - TD array
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnGetTdNodes ( oSettings )
+ {
+ var nTrs = _fnGetTrNodes( oSettings );
+ var nTds = [], nTd;
+ var anReturn = [];
+ var iCorrector;
+ var iRow, iRows, iColumn, iColumns;
+
+ for ( iRow=0, iRows=nTrs.length ; iRow<iRows ; iRow++ )
+ {
+ nTds = [];
+ for ( iColumn=0, iColumns=nTrs[iRow].childNodes.length ; iColumn<iColumns ; iColumn++ )
+ {
+ nTd = nTrs[iRow].childNodes[iColumn];
+ if ( nTd.nodeName.toUpperCase() == "TD" )
+ {
+ nTds.push( nTd );
+ }
+ }
+
+ iCorrector = 0;
+ for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
+ {
+ if ( oSettings.aoColumns[iColumn].bVisible )
+ {
+ anReturn.push( nTds[iColumn-iCorrector] );
+ }
+ else
+ {
+ anReturn.push( oSettings.aoData[iRow]._anHidden[iColumn] );
+ iCorrector++;
+ }
+ }
+ }
+ return anReturn;
+ }
+
+ /*
+ * Function: _fnEscapeRegex
+ * Purpose: scape a string stuch that it can be used in a regular expression
+ * Returns: string: - escaped string
+ * Inputs: string:sVal - string to escape
+ */
+ function _fnEscapeRegex ( sVal )
+ {
+ var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^' ];
+ var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
+ return sVal.replace(reReplace, '\\$1');
+ }
+
+ /*
+ * Function: _fnDeleteIndex
+ * Purpose: Take an array of integers (index array) and remove a target integer (value - not
+ * the key!)
+ * Returns: -
+ * Inputs: a:array int - Index array to target
+ * int:iTarget - value to find
+ */
+ function _fnDeleteIndex( a, iTarget )
+ {
+ var iTargetIndex = -1;
+
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
+ {
+ if ( a[i] == iTarget )
+ {
+ iTargetIndex = i;
+ }
+ else if ( a[i] > iTarget )
+ {
+ a[i]--;
+ }
+ }
+
+ if ( iTargetIndex != -1 )
+ {
+ a.splice( iTargetIndex, 1 );
+ }
+ }
+
+ /*
+ * Function: _fnReOrderIndex
+ * Purpose: Figure out how to reorder a display list
+ * Returns: array int:aiReturn - index list for reordering
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnReOrderIndex ( oSettings, sColumns )
+ {
+ var aColumns = sColumns.split(',');
+ var aiReturn = [];
+
+ for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ for ( var j=0 ; j<iLen ; j++ )
+ {
+ if ( oSettings.aoColumns[i].sName == aColumns[j] )
+ {
+ aiReturn.push( j );
+ break;
+ }
+ }
+ }
+
+ return aiReturn;
+ }
+
+ /*
+ * Function: _fnColumnOrdering
+ * Purpose: Get the column ordering that DataTables expects
+ * Returns: string: - comma separated list of names
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnColumnOrdering ( oSettings )
+ {
+ var sNames = '';
+ for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ {
+ sNames += oSettings.aoColumns[i].sName+',';
+ }
+ if ( sNames.length == iLen )
+ {
+ return "";
+ }
+ return sNames.slice(0, -1);
+ }
+
+ /*
+ * Function: _fnLog
+ * Purpose: Log an error message
+ * Returns: -
+ * Inputs: int:iLevel - log error messages, or display them to the user
+ * string:sMesg - error message
+ */
+ function _fnLog( oSettings, iLevel, sMesg )
+ {
+ var sAlert = oSettings.sTableId === "" ?
+ "DataTables warning: " +sMesg :
+ "DataTables warning (table id = '"+oSettings.sTableId+"'): " +sMesg;
+
+ if ( iLevel === 0 )
+ {
+ if ( _oExt.sErrMode == 'alert' )
+ {
+ alert( sAlert );
+ }
+ else
+ {
+ throw sAlert;
+ }
+ return;
+ }
+ else if ( typeof console != 'undefined' && typeof console.log != 'undefined' )
+ {
+ console.log( sAlert );
+ }
+ }
+
+ /*
+ * Function: _fnClearTable
+ * Purpose: Nuke the table
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnClearTable( oSettings )
+ {
+ oSettings.aoData.splice( 0, oSettings.aoData.length );
+ oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
+ oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
+ _fnCalculateEnd( oSettings );
+ }
+
+ /*
+ * Function: _fnSaveState
+ * Purpose: Save the state of a table in a cookie such that the page can be reloaded
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ */
+ function _fnSaveState ( oSettings )
+ {
+ if ( !oSettings.oFeatures.bStateSave || typeof oSettings.bDestroying != 'undefined' )
+ {
+ return;
+ }
+
+ /* Store the interesting variables */
+ var i, iLen, sTmp;
+ var sValue = "{";
+ sValue += '"iCreate":'+ new Date().getTime()+',';
+ sValue += '"iStart":'+ oSettings._iDisplayStart+',';
+ sValue += '"iEnd":'+ oSettings._iDisplayEnd+',';
+ sValue += '"iLength":'+ oSettings._iDisplayLength+',';
+ sValue += '"sFilter":"'+ encodeURIComponent(oSettings.oPreviousSearch.sSearch)+'",';
+ sValue += '"sFilterEsc":'+ !oSettings.oPreviousSearch.bRegex+',';
+
+ sValue += '"aaSorting":[ ';
+ for ( i=0 ; i<oSettings.aaSorting.length ; i++ )
+ {
+ sValue += '['+oSettings.aaSorting[i][0]+',"'+oSettings.aaSorting[i][1]+'"],';
+ }
+ sValue = sValue.substring(0, sValue.length-1);
+ sValue += "],";
+
+ sValue += '"aaSearchCols":[ ';
+ for ( i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
+ {
+ sValue += '["'+encodeURIComponent(oSettings.aoPreSearchCols[i].sSearch)+
+ '",'+!oSettings.aoPreSearchCols[i].bRegex+'],';
+ }
+ sValue = sValue.substring(0, sValue.length-1);
+ sValue += "],";
+
+ sValue += '"abVisCols":[ ';
+ for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ {
+ sValue += oSettings.aoColumns[i].bVisible+",";
+ }
+ sValue = sValue.substring(0, sValue.length-1);
+ sValue += "]";
+
+ /* Save state from any plug-ins */
+ for ( i=0, iLen=oSettings.aoStateSave.length ; i<iLen ; i++ )
+ {
+ sTmp = oSettings.aoStateSave[i].fn( oSettings, sValue );
+ if ( sTmp !== "" )
+ {
+ sValue = sTmp;
+ }
+ }
+
+ sValue += "}";
+
+ _fnCreateCookie( oSettings.sCookiePrefix+oSettings.sInstance, sValue,
+ oSettings.iCookieDuration, oSettings.sCookiePrefix, oSettings.fnCookieCallback );
+ }
+
+ /*
+ * Function: _fnLoadState
+ * Purpose: Attempt to load a saved table state from a cookie
+ * Returns: -
+ * Inputs: object:oSettings - dataTables settings object
+ * object:oInit - DataTables init object so we can override settings
+ */
+ function _fnLoadState ( oSettings, oInit )
+ {
+ if ( !oSettings.oFeatures.bStateSave )
+ {
+ return;
+ }
+
+ var oData, i, iLen;
+ var sData = _fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
+ if ( sData !== null && sData !== '' )
+ {
+ /* Try/catch the JSON eval - if it is bad then we ignore it - note that 1.7.0 and before
+ * incorrectly used single quotes for some strings - hence the replace below
+ */
+ try
+ {
+ oData = (typeof $.parseJSON == 'function') ?
+ $.parseJSON( sData.replace(/'/g, '"') ) : eval( '('+sData+')' );
+ }
+ catch( e )
+ {
+ return;
+ }
+
+ /* Allow custom and plug-in manipulation functions to alter the data set which was
+ * saved, and also reject any saved state by returning false
+ */
+ for ( i=0, iLen=oSettings.aoStateLoad.length ; i<iLen ; i++ )
+ {
+ if ( !oSettings.aoStateLoad[i].fn( oSettings, oData ) )
+ {
+ return;
+ }
+ }
+
+ /* Store the saved state so it might be accessed at any time (particualrly a plug-in */
+ oSettings.oLoadedState = $.extend( true, {}, oData );
+
+ /* Restore key features */
+ oSettings._iDisplayStart = oData.iStart;
+ oSettings.iInitDisplayStart = oData.iStart;
+ oSettings._iDisplayEnd = oData.iEnd;
+ oSettings._iDisplayLength = oData.iLength;
+ oSettings.oPreviousSearch.sSearch = decodeURIComponent(oData.sFilter);
+ oSettings.aaSorting = oData.aaSorting.slice();
+ oSettings.saved_aaSorting = oData.aaSorting.slice();
+
+ /*
+ * Search filtering - global reference added in 1.4.1
+ * Note that we use a 'not' for the value of the regular expression indicator to maintain
+ * compatibility with pre 1.7 versions, where this was basically inverted. Added in 1.7.0
+ */
+ if ( typeof oData.sFilterEsc != 'undefined' )
+ {
+ oSettings.oPreviousSearch.bRegex = !oData.sFilterEsc;
+ }
+
+ /* Column filtering - added in 1.5.0 beta 6 */
+ if ( typeof oData.aaSearchCols != 'undefined' )
+ {
+ for ( i=0 ; i<oData.aaSearchCols.length ; i++ )
+ {
+ oSettings.aoPreSearchCols[i] = {
+ "sSearch": decodeURIComponent(oData.aaSearchCols[i][0]),
+ "bRegex": !oData.aaSearchCols[i][1]
+ };
+ }
+ }
+
+ /* Column visibility state - added in 1.5.0 beta 10 */
+ if ( typeof oData.abVisCols != 'undefined' )
+ {
+ /* Pass back visibiliy settings to the init handler, but to do not here override
+ * the init object that the user might have passed in
+ */
+ oInit.saved_aoColumns = [];
+ for ( i=0 ; i<oData.abVisCols.length ; i++ )
+ {
+ oInit.saved_aoColumns[i] = {};
+ oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
+ }
+ }
+ }
+ }
+
+ /*
+ * Function: _fnCreateCookie
+ * Purpose: Create a new cookie with a value to store the state of a table
+ * Returns: -
+ * Inputs: string:sName - name of the cookie to create
+ * string:sValue - the value the cookie should take
+ * int:iSecs - duration of the cookie
+ * string:sBaseName - sName is made up of the base + file name - this is the base
+ * function:fnCallback - User definable function to modify the cookie
+ */
+ function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
+ {
+ var date = new Date();
+ date.setTime( date.getTime()+(iSecs*1000) );
+
+ /*
+ * Shocking but true - it would appear IE has major issues with having the path not having
+ * a trailing slash on it. We need the cookie to be available based on the path, so we
+ * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
+ * patch to use at least some of the path
+ */
+ var aParts = window.location.pathname.split('/');
+ var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
+ var sFullCookie, oData;
+
+ if ( fnCallback !== null )
+ {
+ oData = (typeof $.parseJSON == 'function') ?
+ $.parseJSON( sValue ) : eval( '('+sValue+')' );
+ sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
+ aParts.join('/')+"/" );
+ }
+ else
+ {
+ sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
+ "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
+ }
+
+ /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
+ * belonging to DataTables. This is FAR from bullet proof
+ */
+ var sOldName="", iOldTime=9999999999999;
+ var iLength = _fnReadCookie( sNameFile )!==null ? document.cookie.length :
+ sFullCookie.length + document.cookie.length;
+
+ if ( iLength+10 > 4096 ) /* Magic 10 for padding */
+ {
+ var aCookies =document.cookie.split(';');
+ for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
+ {
+ if ( aCookies[i].indexOf( sBaseName ) != -1 )
+ {
+ /* It's a DataTables cookie, so eval it and check the time stamp */
+ var aSplitCookie = aCookies[i].split('=');
+ try { oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' ); }
+ catch( e ) { continue; }
+
+ if ( typeof oData.iCreate != 'undefined' && oData.iCreate < iOldTime )
+ {
+ sOldName = aSplitCookie[0];
+ iOldTime = oData.iCreate;
+ }
+ }
+ }
+
+ if ( sOldName !== "" )
+ {
+ document.cookie = sOldName+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
+ aParts.join('/') + "/";
+ }
+ }
+
+ document.cookie = sFullCookie;
+ }
+
+ /*
+ * Function: _fnReadCookie
+ * Purpose: Read an old cookie to get a cookie with an old table state
+ * Returns: string: - contents of the cookie - or null if no cookie with that name found
+ * Inputs: string:sName - name of the cookie to read
+ */
+ function _fnReadCookie ( sName )
+ {
+ var
+ aParts = window.location.pathname.split('/'),
+ sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
+ sCookieContents = document.cookie.split(';');
+
+ for( var i=0 ; i<sCookieContents.length ; i++ )
+ {
+ var c = sCookieContents[i];
+
+ while (c.charAt(0)==' ')
+ {
+ c = c.substring(1,c.length);
+ }
+
+ if (c.indexOf(sNameEQ) === 0)
+ {
+ return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Function: _fnGetUniqueThs
+ * Purpose: Get an array of unique th elements, one for each column
+ * Returns: array node:aReturn - list of unique ths
+ * Inputs: node:nThead - The thead element for the table
+ */
+ function _fnGetUniqueThs ( nThead )
+ {
+ var nTrs = nThead.getElementsByTagName('tr');
+
+ /* Nice simple case */
+ if ( nTrs.length == 1 )
+ {
+ return nTrs[0].getElementsByTagName('th');
+ }
+
+ /* Otherwise we need to figure out the layout array to get the nodes */
+ var aLayout = [], aReturn = [];
+ var ROWSPAN = 2, COLSPAN = 3, TDELEM = 4;
+ var i, j, k, iLen, jLen, iColumnShifted;
+ var fnShiftCol = function ( a, i, j ) {
+ while ( typeof a[i][j] != 'undefined' ) {
+ j++;
+ }
+ return j;
+ };
+ var fnAddRow = function ( i ) {
+ if ( typeof aLayout[i] == 'undefined' ) {
+ aLayout[i] = [];
+ }
+ };
+
+ /* Calculate a layout array */
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
+ {
+ fnAddRow( i );
+ var iColumn = 0;
+ var nTds = [];
+
+ for ( j=0, jLen=nTrs[i].childNodes.length ; j<jLen ; j++ )
+ {
+ if ( nTrs[i].childNodes[j].nodeName.toUpperCase() == "TD" ||
+ nTrs[i].childNodes[j].nodeName.toUpperCase() == "TH" )
+ {
+ nTds.push( nTrs[i].childNodes[j] );
+ }
+ }
+
+ for ( j=0, jLen=nTds.length ; j<jLen ; j++ )
+ {
+ var iColspan = nTds[j].getAttribute('colspan') * 1;
+ var iRowspan = nTds[j].getAttribute('rowspan') * 1;
+
+ if ( !iColspan || iColspan===0 || iColspan===1 )
+ {
+ iColumnShifted = fnShiftCol( aLayout, i, iColumn );
+ aLayout[i][iColumnShifted] = (nTds[j].nodeName.toUpperCase()=="TD") ? TDELEM : nTds[j];
+ if ( iRowspan || iRowspan===0 || iRowspan===1 )
+ {
+ for ( k=1 ; k<iRowspan ; k++ )
+ {
+ fnAddRow( i+k );
+ aLayout[i+k][iColumnShifted] = ROWSPAN;
+ }
+ }
+ iColumn++;
+ }
+ else
+ {
+ iColumnShifted = fnShiftCol( aLayout, i, iColumn );
+ for ( k=0 ; k<iColspan ; k++ )
+ {
+ aLayout[i][iColumnShifted+k] = COLSPAN;
+ }
+ iColumn += iColspan;
+ }
+ }
+ }
+
+ /* Convert the layout array into a node array */
+ for ( i=0, iLen=aLayout.length ; i<iLen ; i++ )
+ {
+ for ( j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
+ {
+ if ( typeof aLayout[i][j] == 'object' && typeof aReturn[j] == 'undefined' )
+ {
+ aReturn[j] = aLayout[i][j];
+ }
+ }
+ }
+
+ return aReturn;
+ }
+
+ /*
+ * Function: _fnScrollBarWidth
+ * Purpose: Get the width of a scroll bar in this browser being used
+ * Returns: int: - width in pixels
+ * Inputs: -
+ * Notes: All credit for this function belongs to Alexandre Gomes. Thanks for sharing!
+ * http://www.alexandre-gomes.com/?p=115
+ */
+ function _fnScrollBarWidth ()
+ {
+ var inner = document.createElement('p');
+ var style = inner.style;
+ style.width = "100%";
+ style.height = "200px";
+
+ var outer = document.createElement('div');
+ style = outer.style;
+ style.position = "absolute";
+ style.top = "0px";
+ style.left = "0px";
+ style.visibility = "hidden";
+ style.width = "200px";
+ style.height = "150px";
+ style.overflow = "hidden";
+ outer.appendChild(inner);
+
+ document.body.appendChild(outer);
+ var w1 = inner.offsetWidth;
+ outer.style.overflow = 'scroll';
+ var w2 = inner.offsetWidth;
+ if ( w1 == w2 )
+ {
+ w2 = outer.clientWidth;
+ }
+
+ document.body.removeChild(outer);
+ return (w1 - w2);
+ }
+
+ /*
+ * Function: _fnApplyToChildren
+ * Purpose: Apply a given function to the display child nodes of an element array (typically
+ * TD children of TR rows
+ * Returns: - (done by reference)
+ * Inputs: function:fn - Method to apply to the objects
+ * array nodes:an1 - List of elements to look through for display children
+ * array nodes:an2 - Another list (identical structure to the first) - optional
+ */
+ function _fnApplyToChildren( fn, an1, an2 )
+ {
+ for ( var i=0, iLen=an1.length ; i<iLen ; i++ )
+ {
+ for ( var j=0, jLen=an1[i].childNodes.length ; j<jLen ; j++ )
+ {
+ if ( an1[i].childNodes[j].nodeType == 1 )
+ {
+ if ( typeof an2 != 'undefined' )
+ {
+ fn( an1[i].childNodes[j], an2[i].childNodes[j] );
+ }
+ else
+ {
+ fn( an1[i].childNodes[j] );
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Function: _fnMap
+ * Purpose: See if a property is defined on one object, if so assign it to the other object
+ * Returns: - (done by reference)
+ * Inputs: object:oRet - target object
+ * object:oSrc - source object
+ * string:sName - property
+ * string:sMappedName - name to map too - optional, sName used if not given
+ */
+ function _fnMap( oRet, oSrc, sName, sMappedName )
+ {
+ if ( typeof sMappedName == 'undefined' )
+ {
+ sMappedName = sName;
+ }
+ if ( typeof oSrc[sName] != 'undefined' )
+ {
+ oRet[sMappedName] = oSrc[sName];
+ }
+ }
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - API
+ *
+ * I'm not overly happy with this solution - I'd much rather that there was a way of getting
+ * a list of all the private functions and do what we need to dynamically - but that doesn't
+ * appear to be possible. Bonkers. A better solution would be to provide a 'bind' type object
+ * To do - bind type method in DTs 2.x.
+ */
+ this.oApi._fnExternApiFunc = _fnExternApiFunc;
+ this.oApi._fnInitalise = _fnInitalise;
+ this.oApi._fnLanguageProcess = _fnLanguageProcess;
+ this.oApi._fnAddColumn = _fnAddColumn;
+ this.oApi._fnColumnOptions = _fnColumnOptions;
+ this.oApi._fnAddData = _fnAddData;
+ this.oApi._fnGatherData = _fnGatherData;
+ this.oApi._fnDrawHead = _fnDrawHead;
+ this.oApi._fnDraw = _fnDraw;
+ this.oApi._fnReDraw = _fnReDraw;
+ this.oApi._fnAjaxUpdate = _fnAjaxUpdate;
+ this.oApi._fnAjaxUpdateDraw = _fnAjaxUpdateDraw;
+ this.oApi._fnAddOptionsHtml = _fnAddOptionsHtml;
+ this.oApi._fnFeatureHtmlTable = _fnFeatureHtmlTable;
+ this.oApi._fnScrollDraw = _fnScrollDraw;
+ this.oApi._fnAjustColumnSizing = _fnAjustColumnSizing;
+ this.oApi._fnFeatureHtmlFilter = _fnFeatureHtmlFilter;
+ this.oApi._fnFilterComplete = _fnFilterComplete;
+ this.oApi._fnFilterCustom = _fnFilterCustom;
+ this.oApi._fnFilterColumn = _fnFilterColumn;
+ this.oApi._fnFilter = _fnFilter;
+ this.oApi._fnBuildSearchArray = _fnBuildSearchArray;
+ this.oApi._fnBuildSearchRow = _fnBuildSearchRow;
+ this.oApi._fnFilterCreateSearch = _fnFilterCreateSearch;
+ this.oApi._fnDataToSearch = _fnDataToSearch;
+ this.oApi._fnSort = _fnSort;
+ this.oApi._fnSortAttachListener = _fnSortAttachListener;
+ this.oApi._fnSortingClasses = _fnSortingClasses;
+ this.oApi._fnFeatureHtmlPaginate = _fnFeatureHtmlPaginate;
+ this.oApi._fnPageChange = _fnPageChange;
+ this.oApi._fnFeatureHtmlInfo = _fnFeatureHtmlInfo;
+ this.oApi._fnUpdateInfo = _fnUpdateInfo;
+ this.oApi._fnFeatureHtmlLength = _fnFeatureHtmlLength;
+ this.oApi._fnFeatureHtmlProcessing = _fnFeatureHtmlProcessing;
+ this.oApi._fnProcessingDisplay = _fnProcessingDisplay;
+ this.oApi._fnVisibleToColumnIndex = _fnVisibleToColumnIndex;
+ this.oApi._fnColumnIndexToVisible = _fnColumnIndexToVisible;
+ this.oApi._fnNodeToDataIndex = _fnNodeToDataIndex;
+ this.oApi._fnVisbleColumns = _fnVisbleColumns;
+ this.oApi._fnCalculateEnd = _fnCalculateEnd;
+ this.oApi._fnConvertToWidth = _fnConvertToWidth;
+ this.oApi._fnCalculateColumnWidths = _fnCalculateColumnWidths;
+ this.oApi._fnScrollingWidthAdjust = _fnScrollingWidthAdjust;
+ this.oApi._fnGetWidestNode = _fnGetWidestNode;
+ this.oApi._fnGetMaxLenString = _fnGetMaxLenString;
+ this.oApi._fnStringToCss = _fnStringToCss;
+ this.oApi._fnArrayCmp = _fnArrayCmp;
+ this.oApi._fnDetectType = _fnDetectType;
+ this.oApi._fnSettingsFromNode = _fnSettingsFromNode;
+ this.oApi._fnGetDataMaster = _fnGetDataMaster;
+ this.oApi._fnGetTrNodes = _fnGetTrNodes;
+ this.oApi._fnGetTdNodes = _fnGetTdNodes;
+ this.oApi._fnEscapeRegex = _fnEscapeRegex;
+ this.oApi._fnDeleteIndex = _fnDeleteIndex;
+ this.oApi._fnReOrderIndex = _fnReOrderIndex;
+ this.oApi._fnColumnOrdering = _fnColumnOrdering;
+ this.oApi._fnLog = _fnLog;
+ this.oApi._fnClearTable = _fnClearTable;
+ this.oApi._fnSaveState = _fnSaveState;
+ this.oApi._fnLoadState = _fnLoadState;
+ this.oApi._fnCreateCookie = _fnCreateCookie;
+ this.oApi._fnReadCookie = _fnReadCookie;
+ this.oApi._fnGetUniqueThs = _fnGetUniqueThs;
+ this.oApi._fnScrollBarWidth = _fnScrollBarWidth;
+ this.oApi._fnApplyToChildren = _fnApplyToChildren;
+ this.oApi._fnMap = _fnMap;
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Section - Constructor
+ */
+
+ /* Want to be able to reference "this" inside the this.each function */
+ var _that = this;
+ return this.each(function()
+ {
+ var i=0, iLen, j, jLen, k, kLen;
+
+ /* Check to see if we are re-initalising a table */
+ for ( i=0, iLen=_aoSettings.length ; i<iLen ; i++ )
+ {
+ /* Base check on table node */
+ if ( _aoSettings[i].nTable == this )
+ {
+ if ( typeof oInit == 'undefined' ||
+ ( typeof oInit.bRetrieve != 'undefined' && oInit.bRetrieve === true ) )
+ {
+ return _aoSettings[i].oInstance;
+ }
+ else if ( typeof oInit.bDestroy != 'undefined' && oInit.bDestroy === true )
+ {
+ _aoSettings[i].oInstance.fnDestroy();
+ break;
+ }
+ else
+ {
+ _fnLog( _aoSettings[i], 0, "Cannot reinitialise DataTable.\n\n"+
+ "To retrieve the DataTables object for this table, please pass either no arguments "+
+ "to the dataTable() function, or set bRetrieve to true. Alternatively, to destory "+
+ "the old table and create a new one, set bDestroy to true (note that a lot of "+
+ "changes to the configuration can be made through the API which is usually much "+
+ "faster)." );
+ return;
+ }
+ }
+
+ /* If the element we are initialising has the same ID as a table which was previously
+ * initialised, but the table nodes don't match (from before) then we destory the old
+ * instance by simply deleting it. This is under the assumption that the table has been
+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
+ */
+ if ( _aoSettings[i].sTableId !== "" && _aoSettings[i].sTableId == this.getAttribute('id') )
+ {
+ _aoSettings.splice( i, 1 );
+ break;
+ }
+ }
+
+ /* Make a complete and independent copy of the settings object */
+ var oSettings = new classSettings();
+ _aoSettings.push( oSettings );
+
+ var bInitHandedOff = false;
+ var bUsePassedData = false;
+
+ /* Set the id */
+ var sId = this.getAttribute( 'id' );
+ if ( sId !== null )
+ {
+ oSettings.sTableId = sId;
+ oSettings.sInstance = sId;
+ }
+ else
+ {
+ oSettings.sInstance = _oExt._oExternConfig.iNextUnique ++;
+ }
+
+ /* Sanity check */
+ if ( this.nodeName.toLowerCase() != 'table' )
+ {
+ _fnLog( oSettings, 0, "Attempted to initialise DataTables on a node which is not a "+
+ "table: "+this.nodeName );
+ return;
+ }
+
+ /* Set the table node */
+ oSettings.nTable = this;
+
+ /* Keep a reference to the 'this' instance for the table. Note that if this table is being
+ * created with others, we retrieve a unique instance to ease API access.
+ */
+ oSettings.oInstance = _that.length == 1 ? _that : $(this).dataTable();
+
+ /* Bind the API functions to the settings, so we can perform actions whenever oSettings is
+ * available
+ */
+ oSettings.oApi = _that.oApi;
+
+ /* State the table's width for if a destroy is called at a later time */
+ oSettings.sDestroyWidth = $(this).width();
+
+ /* Store the features that we have available */
+ if ( typeof oInit != 'undefined' && oInit !== null )
+ {
+ oSettings.oInit = oInit;
+ _fnMap( oSettings.oFeatures, oInit, "bPaginate" );
+ _fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
+ _fnMap( oSettings.oFeatures, oInit, "bFilter" );
+ _fnMap( oSettings.oFeatures, oInit, "bSort" );
+ _fnMap( oSettings.oFeatures, oInit, "bInfo" );
+ _fnMap( oSettings.oFeatures, oInit, "bProcessing" );
+ _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
+ _fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
+ _fnMap( oSettings.oFeatures, oInit, "bServerSide" );
+ _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
+ _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
+ _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
+ _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
+ _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
+ _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
+ _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
+ _fnMap( oSettings, oInit, "asStripClasses" );
+ _fnMap( oSettings, oInit, "fnRowCallback" );
+ _fnMap( oSettings, oInit, "fnHeaderCallback" );
+ _fnMap( oSettings, oInit, "fnFooterCallback" );
+ _fnMap( oSettings, oInit, "fnCookieCallback" );
+ _fnMap( oSettings, oInit, "fnInitComplete" );
+ _fnMap( oSettings, oInit, "fnServerData" );
+ _fnMap( oSettings, oInit, "fnFormatNumber" );
+ _fnMap( oSettings, oInit, "aaSorting" );
+ _fnMap( oSettings, oInit, "aaSortingFixed" );
+ _fnMap( oSettings, oInit, "aLengthMenu" );
+ _fnMap( oSettings, oInit, "sPaginationType" );
+ _fnMap( oSettings, oInit, "sAjaxSource" );
+ _fnMap( oSettings, oInit, "iCookieDuration" );
+ _fnMap( oSettings, oInit, "sCookiePrefix" );
+ _fnMap( oSettings, oInit, "sDom" );
+ _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
+ _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
+ _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
+ _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
+ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
+
+ /* Callback functions which are array driven */
+ if ( typeof oInit.fnDrawCallback == 'function' )
+ {
+ oSettings.aoDrawCallback.push( {
+ "fn": oInit.fnDrawCallback,
+ "sName": "user"
+ } );
+ }
+
+ if ( typeof oInit.fnStateSaveCallback == 'function' )
+ {
+ oSettings.aoStateSave.push( {
+ "fn": oInit.fnStateSaveCallback,
+ "sName": "user"
+ } );
+ }
+
+ if ( typeof oInit.fnStateLoadCallback == 'function' )
+ {
+ oSettings.aoStateLoad.push( {
+ "fn": oInit.fnStateLoadCallback,
+ "sName": "user"
+ } );
+ }
+
+ if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
+ oSettings.oFeatures.bSortClasses )
+ {
+ /* Enable sort classes for server-side processing. Safe to do it here, since server-side
+ * processing must be enabled by the developer
+ */
+ oSettings.aoDrawCallback.push( {
+ "fn": _fnSortingClasses,
+ "sName": "server_side_sort_classes"
+ } );
+ }
+
+ if ( typeof oInit.bJQueryUI != 'undefined' && oInit.bJQueryUI )
+ {
+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
+ * you want to have multiple tables with multiple independent classes
+ */
+ oSettings.oClasses = _oExt.oJUIClasses;
+
+ if ( typeof oInit.sDom == 'undefined' )
+ {
+ /* Set the DOM to use a layout suitable for jQuery UI's theming */
+ oSettings.sDom = '<"H"lfr>t<"F"ip>';
+ }
+ }
+
+ /* Calculate the scroll bar width and cache it for use later on */
+ if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
+ {
+ oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
+ }
+
+ if ( typeof oInit.iDisplayStart != 'undefined' &&
+ typeof oSettings.iInitDisplayStart == 'undefined' )
+ {
+ /* Display start point, taking into account the save saving */
+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
+ oSettings._iDisplayStart = oInit.iDisplayStart;
+ }
+
+ /* Must be done after everything which can be overridden by a cookie! */
+ if ( typeof oInit.bStateSave != 'undefined' )
+ {
+ oSettings.oFeatures.bStateSave = oInit.bStateSave;
+ _fnLoadState( oSettings, oInit );
+ oSettings.aoDrawCallback.push( {
+ "fn": _fnSaveState,
+ "sName": "state_save"
+ } );
+ }
+
+ if ( typeof oInit.aaData != 'undefined' )
+ {
+ bUsePassedData = true;
+ }
+
+ /* Backwards compatability */
+ /* aoColumns / aoData - remove at some point... */
+ if ( typeof oInit != 'undefined' && typeof oInit.aoData != 'undefined' )
+ {
+ oInit.aoColumns = oInit.aoData;
+ }
+
+ /* Language definitions */
+ if ( typeof oInit.oLanguage != 'undefined' )
+ {
+ if ( typeof oInit.oLanguage.sUrl != 'undefined' && oInit.oLanguage.sUrl !== "" )
+ {
+ /* Get the language definitions from a file */
+ oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
+ $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
+ _fnLanguageProcess( oSettings, json, true ); } );
+ bInitHandedOff = true;
+ }
+ else
+ {
+ _fnLanguageProcess( oSettings, oInit.oLanguage, false );
+ }
+ }
+ /* Warning: The _fnLanguageProcess function is async to the remainder of this function due
+ * to the XHR. We use _bInitialised in _fnLanguageProcess() to check this the processing
+ * below is complete. The reason for spliting it like this is optimisation - we can fire
+ * off the XHR (if needed) and then continue processing the data.
+ */
+ }
+ else
+ {
+ /* Create a dummy object for quick manipulation later on. */
+ oInit = {};
+ }
+
+ /*
+ * Stripes
+ * Add the strip classes now that we know which classes to apply - unless overruled
+ */
+ if ( typeof oInit.asStripClasses == 'undefined' )
+ {
+ oSettings.asStripClasses.push( oSettings.oClasses.sStripOdd );
+ oSettings.asStripClasses.push( oSettings.oClasses.sStripEven );
+ }
+
+ /* Remove row stripe classes if they are already on the table row */
+ var bStripeRemove = false;
+ var anRows = $('>tbody>tr', this);
+ for ( i=0, iLen=oSettings.asStripClasses.length ; i<iLen ; i++ )
+ {
+ if ( anRows.filter(":lt(2)").hasClass( oSettings.asStripClasses[i]) )
+ {
+ bStripeRemove = true;
+ break;
+ }
+ }
+
+ if ( bStripeRemove )
+ {
+ /* Store the classes which we are about to remove so they can be readded on destory */
+ oSettings.asDestoryStrips = [ '', '' ];
+ if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripOdd) )
+ {
+ oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripOdd+" ";
+ }
+ if ( $(anRows[0]).hasClass(oSettings.oClasses.sStripEven) )
+ {
+ oSettings.asDestoryStrips[0] += oSettings.oClasses.sStripEven;
+ }
+ if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripOdd) )
+ {
+ oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripOdd+" ";
+ }
+ if ( $(anRows[1]).hasClass(oSettings.oClasses.sStripEven) )
+ {
+ oSettings.asDestoryStrips[1] += oSettings.oClasses.sStripEven;
+ }
+
+ anRows.removeClass( oSettings.asStripClasses.join(' ') );
+ }
+
+ /*
+ * Columns
+ * See if we should load columns automatically or use defined ones
+ */
+ var nThead = this.getElementsByTagName('thead');
+ var anThs = nThead.length===0 ? [] : _fnGetUniqueThs( nThead[0] );
+ var aoColumnsInit;
+
+ /* If not given a column array, generate one with nulls */
+ if ( typeof oInit.aoColumns == 'undefined' )
+ {
+ aoColumnsInit = [];
+ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
+ {
+ aoColumnsInit.push( null );
+ }
+ }
+ else
+ {
+ aoColumnsInit = oInit.aoColumns;
+ }
+
+ /* Add the columns */
+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
+ {
+ /* Check if we have column visibilty state to restore */
+ if ( typeof oInit.saved_aoColumns != 'undefined' && oInit.saved_aoColumns.length == iLen )
+ {
+ if ( aoColumnsInit[i] === null )
+ {
+ aoColumnsInit[i] = {};
+ }
+ aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
+ }
+
+ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
+ }
+
+ /* Add options from column definations */
+ if ( typeof oInit.aoColumnDefs != 'undefined' )
+ {
+ /* Loop over the column defs array - loop in reverse so first instace has priority */
+ for ( i=oInit.aoColumnDefs.length-1 ; i>=0 ; i-- )
+ {
+ /* Each column def can target multiple columns, as it is an array */
+ var aTargets = oInit.aoColumnDefs[i].aTargets;
+ if ( !$.isArray( aTargets ) )
+ {
+ _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
+ }
+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
+ {
+ if ( typeof aTargets[j] == 'number' && aTargets[j] >= 0 )
+ {
+ /* 0+ integer, left to right column counting. We add columns which are unknown
+ * automatically. Is this the right behaviour for this? We should at least
+ * log it in future. We cannot do this for the negative or class targets, only here.
+ */
+ while( oSettings.aoColumns.length <= aTargets[j] )
+ {
+ _fnAddColumn( oSettings );
+ }
+ _fnColumnOptions( oSettings, aTargets[j], oInit.aoColumnDefs[i] );
+ }
+ else if ( typeof aTargets[j] == 'number' && aTargets[j] < 0 )
+ {
+ /* Negative integer, right to left column counting */
+ _fnColumnOptions( oSettings, oSettings.aoColumns.length+aTargets[j],
+ oInit.aoColumnDefs[i] );
+ }
+ else if ( typeof aTargets[j] == 'string' )
+ {
+ /* Class name matching on TH element */
+ for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
+ {
+ if ( aTargets[j] == "_all" ||
+ oSettings.aoColumns[k].nTh.className.indexOf( aTargets[j] ) != -1 )
+ {
+ _fnColumnOptions( oSettings, k, oInit.aoColumnDefs[i] );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Add options from column array - after the defs array so this has priority */
+ if ( typeof aoColumnsInit != 'undefined' )
+ {
+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
+ {
+ _fnColumnOptions( oSettings, i, aoColumnsInit[i] );
+ }
+ }
+
+ /*
+ * Sorting
+ * Check the aaSorting array
+ */
+ for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
+ {
+ if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
+ {
+ oSettings.aaSorting[i][0] = 0;
+ }
+ var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
+
+ /* Add a default sorting index */
+ if ( typeof oSettings.aaSorting[i][2] == 'undefined' )
+ {
+ oSettings.aaSorting[i][2] = 0;
+ }
+
+ /* If aaSorting is not defined, then we use the first indicator in asSorting */
+ if ( typeof oInit.aaSorting == "undefined" &&
+ typeof oSettings.saved_aaSorting == "undefined" )
+ {
+ oSettings.aaSorting[i][1] = oColumn.asSorting[0];
+ }
+
+ /* Set the current sorting index based on aoColumns.asSorting */
+ for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
+ {
+ if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
+ {
+ oSettings.aaSorting[i][2] = j;
+ break;
+ }
+ }
+ }
+
+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
+ * account, and also will apply sorting disabled classes if disabled
+ */
+ _fnSortingClasses( oSettings );
+
+ /*
+ * Final init
+ * Sanity check that there is a thead and tbody. If not let's just create them
+ */
+ if ( this.getElementsByTagName('thead').length === 0 )
+ {
+ this.appendChild( document.createElement( 'thead' ) );
+ }
+
+ if ( this.getElementsByTagName('tbody').length === 0 )
+ {
+ this.appendChild( document.createElement( 'tbody' ) );
+ }
+
+ oSettings.nTHead = this.getElementsByTagName('thead')[0];
+ oSettings.nTBody = this.getElementsByTagName('tbody')[0];
+ if ( this.getElementsByTagName('tfoot').length > 0 )
+ {
+ oSettings.nTFoot = this.getElementsByTagName('tfoot')[0];
+ }
+
+ /* Check if there is data passing into the constructor */
+ if ( bUsePassedData )
+ {
+ for ( i=0 ; i<oInit.aaData.length ; i++ )
+ {
+ _fnAddData( oSettings, oInit.aaData[ i ] );
+ }
+ }
+ else
+ {
+ /* Grab the data from the page */
+ _fnGatherData( oSettings );
+ }
+
+ /* Copy the data index array */
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+
+ /* Initialisation complete - table can be drawn */
+ oSettings.bInitialised = true;
+
+ /* Check if we need to initialise the table (it might not have been handed off to the
+ * language processor)
+ */
+ if ( bInitHandedOff === false )
+ {
+ _fnInitalise( oSettings );
+ }
+ });
+ };
+})(jQuery, window, document);
+/*!
+ * jQuery Templates Plugin 1.0.0pre
+ * http://github.com/jquery/jquery-tmpl
+ * Requires jQuery 1.4.2
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+(function( jQuery, undefined ){
+ var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
+ newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
+
+ function newTmplItem( options, parentItem, fn, data ) {
+ // Returns a template item data structure for a new rendered instance of a template (a 'template item').
+ // The content field is a hierarchical array of strings and nested items (to be
+ // removed and replaced by nodes field of dom elements, once inserted in DOM).
+ var newItem = {
+ data: data || (data === 0 || data === false) ? data : (parentItem ? parentItem.data : {}),
+ _wrap: parentItem ? parentItem._wrap : null,
+ tmpl: null,
+ parent: parentItem || null,
+ nodes: [],
+ calls: tiCalls,
+ nest: tiNest,
+ wrap: tiWrap,
+ html: tiHtml,
+ update: tiUpdate
+ };
+ if ( options ) {
+ jQuery.extend( newItem, options, { nodes: [], parent: parentItem });
+ }
+ if ( fn ) {
+ // Build the hierarchical content to be used during insertion into DOM
+ newItem.tmpl = fn;
+ newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
+ newItem.key = ++itemKey;
+ // Keep track of new template item, until it is stored as jQuery Data on DOM element
+ (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
+ }
+ return newItem;
+ }
+
+ // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
+ jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+ }, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
+ parent = this.length === 1 && this[0].parentNode;
+
+ appendToTmplItems = newTmplItems || {};
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ ret = this;
+ } else {
+ for ( i = 0, l = insert.length; i < l; i++ ) {
+ cloneIndex = i;
+ elems = (i > 0 ? this.clone(true) : this).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+ cloneIndex = 0;
+ ret = this.pushStack( ret, name, insert.selector );
+ }
+ tmplItems = appendToTmplItems;
+ appendToTmplItems = null;
+ jQuery.tmpl.complete( tmplItems );
+ return ret;
+ };
+ });
+
+ jQuery.fn.extend({
+ // Use first wrapped element as template markup.
+ // Return wrapped set of template items, obtained by rendering template against data.
+ tmpl: function( data, options, parentItem ) {
+ return jQuery.tmpl( this[0], data, options, parentItem );
+ },
+
+ // Find which rendered template item the first wrapped DOM element belongs to
+ tmplItem: function() {
+ return jQuery.tmplItem( this[0] );
+ },
+
+ // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
+ template: function( name ) {
+ return jQuery.template( name, this[0] );
+ },
+
+ domManip: function( args, table, callback, options ) {
+ if ( args[0] && jQuery.isArray( args[0] )) {
+ var dmArgs = jQuery.makeArray( arguments ), elems = args[0], elemsLength = elems.length, i = 0, tmplItem;
+ while ( i < elemsLength && !(tmplItem = jQuery.data( elems[i++], "tmplItem" ))) {}
+ if ( tmplItem && cloneIndex ) {
+ dmArgs[2] = function( fragClone ) {
+ // Handler called by oldManip when rendered template has been inserted into DOM.
+ jQuery.tmpl.afterManip( this, fragClone, callback );
+ };
+ }
+ oldManip.apply( this, dmArgs );
+ } else {
+ oldManip.apply( this, arguments );
+ }
+ cloneIndex = 0;
+ if ( !appendToTmplItems ) {
+ jQuery.tmpl.complete( newTmplItems );
+ }
+ return this;
+ }
+ });
+
+ jQuery.extend({
+ // Return wrapped set of template items, obtained by rendering template against data.
+ tmpl: function( tmpl, data, options, parentItem ) {
+ var ret, topLevel = !parentItem;
+ if ( topLevel ) {
+ // This is a top-level tmpl call (not from a nested template using {{tmpl}})
+ parentItem = topTmplItem;
+ tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
+ wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
+ } else if ( !tmpl ) {
+ // The template item is already associated with DOM - this is a refresh.
+ // Re-evaluate rendered template for the parentItem
+ tmpl = parentItem.tmpl;
+ newTmplItems[parentItem.key] = parentItem;
+ parentItem.nodes = [];
+ if ( parentItem.wrapped ) {
+ updateWrapped( parentItem, parentItem.wrapped );
+ }
+ // Rebuild, without creating a new template item
+ return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
+ }
+ if ( !tmpl ) {
+ return []; // Could throw...
+ }
+ if ( typeof data === "function" ) {
+ data = data.call( parentItem || {} );
+ }
+ if ( options && options.wrapped ) {
+ updateWrapped( options, options.wrapped );
+ }
+ ret = jQuery.isArray( data ) ?
+ jQuery.map( data, function( dataItem ) {
+ return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
+ }) :
+ [ newTmplItem( options, parentItem, tmpl, data ) ];
+ return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
+ },
+
+ // Return rendered template item for an element.
+ tmplItem: function( elem ) {
+ var tmplItem;
+ if ( elem instanceof jQuery ) {
+ elem = elem[0];
+ }
+ while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
+ return tmplItem || topTmplItem;
+ },
+
+ // Set:
+ // Use $.template( name, tmpl ) to cache a named template,
+ // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
+ // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
+
+ // Get:
+ // Use $.template( name ) to access a cached template.
+ // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
+ // will return the compiled template, without adding a name reference.
+ // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
+ // to $.template( null, templateString )
+ template: function( name, tmpl ) {
+ if (tmpl) {
+ // Compile template and associate with name
+ if ( typeof tmpl === "string" ) {
+ // This is an HTML string being passed directly in.
+ tmpl = buildTmplFn( tmpl )
+ } else if ( tmpl instanceof jQuery ) {
+ tmpl = tmpl[0] || {};
+ }
+ if ( tmpl.nodeType ) {
+ // If this is a template block, use cached copy, or generate tmpl function and cache.
+ tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
+ // Issue: In IE, if the container element is not a script block, the innerHTML will remove quotes from attribute values whenever the value does not include white space.
+ // This means that foo="${x}" will not work if the value of x includes white space: foo="${x}" -> foo=value of x.
+ // To correct this, include space in tag: foo="${ x }" -> foo="value of x"
+ }
+ return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
+ }
+ // Return named compiled template
+ return name ? (typeof name !== "string" ? jQuery.template( null, name ):
+ (jQuery.template[name] ||
+ // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec)
+ jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
+ },
+
+ encode: function( text ) {
+ // Do HTML encoding replacing < > & and ' and " by corresponding entities.
+ return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
+ }
+ });
+
+ jQuery.extend( jQuery.tmpl, {
+ tag: {
+ "tmpl": {
+ _default: { $2: "null" },
+ open: "if($notnull_1){__=__.concat($item.nest($1,$2));}"
+ // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
+ // This means that {{tmpl foo}} treats foo as a template (which IS a function).
+ // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
+ },
+ "wrap": {
+ _default: { $2: "null" },
+ open: "$item.calls(__,$1,$2);__=[];",
+ close: "call=$item.calls();__=call._.concat($item.wrap(call,__));"
+ },
+ "each": {
+ _default: { $2: "$index, $value" },
+ open: "if($notnull_1){$.each($1a,function($2){with(this){",
+ close: "}});}"
+ },
+ "if": {
+ open: "if(($notnull_1) && $1a){",
+ close: "}"
+ },
+ "else": {
+ _default: { $1: "true" },
+ open: "}else if(($notnull_1) && $1a){"
+ },
+ "html": {
+ // Unecoded expression evaluation.
+ open: "if($notnull_1){__.push($1a);}"
+ },
+ "=": {
+ // Encoded expression evaluation. Abbreviated form is ${}.
+ _default: { $1: "$data" },
+ open: "if($notnull_1){__.push($.encode($1a));}"
+ },
+ "!": {
+ // Comment tag. Skipped by parser
+ open: ""
+ }
+ },
+
+ // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
+ complete: function( items ) {
+ newTmplItems = {};
+ },
+
+ // Call this from code which overrides domManip, or equivalent
+ // Manage cloning/storing template items etc.
+ afterManip: function afterManip( elem, fragClone, callback ) {
+ // Provides cloned fragment ready for fixup prior to and after insertion into DOM
+ var content = fragClone.nodeType === 11 ?
+ jQuery.makeArray(fragClone.childNodes) :
+ fragClone.nodeType === 1 ? [fragClone] : [];
+
+ // Return fragment to original caller (e.g. append) for DOM insertion
+ callback.call( elem, fragClone );
+
+ // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
+ storeTmplItems( content );
+ cloneIndex++;
+ }
+ });
+
+ //========================== Private helper functions, used by code above ==========================
+
+ function build( tmplItem, nested, content ) {
+ // Convert hierarchical content into flat string array
+ // and finally return array of fragments ready for DOM insertion
+ var frag, ret = content ? jQuery.map( content, function( item ) {
+ return (typeof item === "string") ?
+ // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
+ (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
+ // This is a child template item. Build nested template.
+ build( item, tmplItem, item._ctnt );
+ }) :
+ // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
+ tmplItem;
+ if ( nested ) {
+ return ret;
+ }
+
+ // top-level template
+ ret = ret.join("");
+
+ // Support templates which have initial or final text nodes, or consist only of text
+ // Also support HTML entities within the HTML markup.
+ ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
+ frag = jQuery( middle ).get();
+
+ storeTmplItems( frag );
+ if ( before ) {
+ frag = unencode( before ).concat(frag);
+ }
+ if ( after ) {
+ frag = frag.concat(unencode( after ));
+ }
+ });
+ return frag ? frag : unencode( ret );
+ }
+
+ function unencode( text ) {
+ // Use createElement, since createTextNode will not render HTML entities correctly
+ var el = document.createElement( "div" );
+ el.innerHTML = text;
+ return jQuery.makeArray(el.childNodes);
+ }
+
+ // Generate a reusable function that will serve to render a template against data
+ function buildTmplFn( markup ) {
+ return new Function("jQuery","$item",
+ // Use the variable __ to hold a string array while building the compiled template. (See https://github.com/jquery/jquery-tmpl/issues#issue/10).
+ "var $=jQuery,call,__=[],$data=$item.data;" +
+
+ // Introduce the data as local variables using with(){}
+ "with($data){__.push('" +
+
+ // Convert the template into pure JavaScript
+ jQuery.trim(markup)
+ .replace( /([\\'])/g, "\\$1" )
+ .replace( /[\r\t\n]/g, " " )
+ .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
+ .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
+ function( all, slash, type, fnargs, target, parens, args ) {
+ var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
+ if ( !tag ) {
+ throw "Unknown template tag: " + type;
+ }
+ def = tag._default || [];
+ if ( parens && !/\w$/.test(target)) {
+ target += parens;
+ parens = "";
+ }
+ if ( target ) {
+ target = unescape( target );
+ args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
+ // Support for target being things like a.toLowerCase();
+ // In that case don't call with template item as 'this' pointer. Just evaluate...
+ expr = parens ? (target.indexOf(".") > -1 ? target + unescape( parens ) : ("(" + target + ").call($item" + args)) : target;
+ exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
+ } else {
+ exprAutoFnDetect = expr = def.$1 || "null";
+ }
+ fnargs = unescape( fnargs );
+ return "');" +
+ tag[ slash ? "close" : "open" ]
+ .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
+ .split( "$1a" ).join( exprAutoFnDetect )
+ .split( "$1" ).join( expr )
+ .split( "$2" ).join( fnargs || def.$2 || "" ) +
+ "__.push('";
+ }) +
+ "');}return __;"
+ );
+ }
+ function updateWrapped( options, wrapped ) {
+ // Build the wrapped content.
+ options._wrap = build( options, true,
+ // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
+ jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
+ ).join("");
+ }
+
+ function unescape( args ) {
+ return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
+ }
+ function outerHtml( elem ) {
+ var div = document.createElement("div");
+ div.appendChild( elem.cloneNode(true) );
+ return div.innerHTML;
+ }
+
+ // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
+ function storeTmplItems( content ) {
+ var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
+ for ( i = 0, l = content.length; i < l; i++ ) {
+ if ( (elem = content[i]).nodeType !== 1 ) {
+ continue;
+ }
+ elems = elem.getElementsByTagName("*");
+ for ( m = elems.length - 1; m >= 0; m-- ) {
+ processItemKey( elems[m] );
+ }
+ processItemKey( elem );
+ }
+ function processItemKey( el ) {
+ var pntKey, pntNode = el, pntItem, tmplItem, key;
+ // Ensure that each rendered template inserted into the DOM has its own template item,
+ if ( (key = el.getAttribute( tmplItmAtt ))) {
+ while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
+ if ( pntKey !== key ) {
+ // The next ancestor with a _tmplitem expando is on a different key than this one.
+ // So this is a top-level element within this template item
+ // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
+ pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
+ if ( !(tmplItem = newTmplItems[key]) ) {
+ // The item is for wrapped content, and was copied from the temporary parent wrappedItem.
+ tmplItem = wrappedItems[key];
+ tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode] );
+ tmplItem.key = ++itemKey;
+ newTmplItems[itemKey] = tmplItem;
+ }
+ if ( cloneIndex ) {
+ cloneTmplItem( key );
+ }
+ }
+ el.removeAttribute( tmplItmAtt );
+ } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
+ // This was a rendered element, cloned during append or appendTo etc.
+ // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
+ cloneTmplItem( tmplItem.key );
+ newTmplItems[tmplItem.key] = tmplItem;
+ pntNode = jQuery.data( el.parentNode, "tmplItem" );
+ pntNode = pntNode ? pntNode.key : 0;
+ }
+ if ( tmplItem ) {
+ pntItem = tmplItem;
+ // Find the template item of the parent element.
+ // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
+ while ( pntItem && pntItem.key != pntNode ) {
+ // Add this element as a top-level node for this rendered template item, as well as for any
+ // ancestor items between this item and the item of its parent element
+ pntItem.nodes.push( el );
+ pntItem = pntItem.parent;
+ }
+ // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
+ delete tmplItem._ctnt;
+ delete tmplItem._wrap;
+ // Store template item as jQuery data on the element
+ jQuery.data( el, "tmplItem", tmplItem );
+ }
+ function cloneTmplItem( key ) {
+ key = key + keySuffix;
+ tmplItem = newClonedItems[key] =
+ (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent ));
+ }
+ }
+ }
+
+ //---- Helper functions for template item ----
+
+ function tiCalls( content, tmpl, data, options ) {
+ if ( !content ) {
+ return stack.pop();
+ }
+ stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
+ }
+
+ function tiNest( tmpl, data, options ) {
+ // nested template, using {{tmpl}} tag
+ return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
+ }
+
+ function tiWrap( call, wrapped ) {
+ // nested template, using {{wrap}} tag
+ var options = call.options || {};
+ options.wrapped = wrapped;
+ // Apply the template, which may incorporate wrapped content,
+ return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
+ }
+
+ function tiHtml( filter, textOnly ) {
+ var wrapped = this._wrap;
+ return jQuery.map(
+ jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
+ function(e) {
+ return textOnly ?
+ e.innerText || e.textContent :
+ e.outerHTML || outerHtml(e);
+ });
+ }
+
+ function tiUpdate() {
+ var coll = this.nodes;
+ jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
+ jQuery( coll ).remove();
+ }
+})( jQuery );
+/*!
+ * jQuery UI 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function( $, undefined ) {
+
+// prevent duplicate loading
+// this is only a problem because we proxy existing functions
+// and we don't want to double proxy them
+$.ui = $.ui || {};
+if ( $.ui.version ) {
+ return;
+}
+
+$.extend( $.ui, {
+ version: "1.8.10",
+
+ keyCode: {
+ ALT: 18,
+ BACKSPACE: 8,
+ CAPS_LOCK: 20,
+ COMMA: 188,
+ COMMAND: 91,
+ COMMAND_LEFT: 91, // COMMAND
+ COMMAND_RIGHT: 93,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ MENU: 93, // COMMAND_RIGHT
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SHIFT: 16,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38,
+ WINDOWS: 91 // COMMAND
+ }
+});
+
+// plugins
+$.fn.extend({
+ _focus: $.fn.focus,
+ focus: function( delay, fn ) {
+ return typeof delay === "number" ?
+ this.each(function() {
+ var elem = this;
+ setTimeout(function() {
+ $( elem ).focus();
+ if ( fn ) {
+ fn.call( elem );
+ }
+ }, delay );
+ }) :
+ this._focus.apply( this, arguments );
+ },
+
+ scrollParent: function() {
+ var scrollParent;
+ if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+ scrollParent = this.parents().filter(function() {
+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ } else {
+ scrollParent = this.parents().filter(function() {
+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ }
+
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+ },
+
+ zIndex: function( zIndex ) {
+ if ( zIndex !== undefined ) {
+ return this.css( "zIndex", zIndex );
+ }
+
+ if ( this.length ) {
+ var elem = $( this[ 0 ] ), position, value;
+ while ( elem.length && elem[ 0 ] !== document ) {
+ // Ignore z-index if position is set to a value where z-index is ignored by the browser
+ // This makes behavior of this function consistent across browsers
+ // WebKit always returns auto if the element is positioned
+ position = elem.css( "position" );
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
+ // IE returns 0 when zIndex is not specified
+ // other browsers return a string
+ // we ignore the case of nested elements with an explicit value of 0
+ // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
+ value = parseInt( elem.css( "zIndex" ), 10 );
+ if ( !isNaN( value ) && value !== 0 ) {
+ return value;
+ }
+ }
+ elem = elem.parent();
+ }
+ }
+
+ return 0;
+ },
+
+ disableSelection: function() {
+ return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
+ ".ui-disableSelection", function( event ) {
+ event.preventDefault();
+ });
+ },
+
+ enableSelection: function() {
+ return this.unbind( ".ui-disableSelection" );
+ }
+});
+
+$.each( [ "Width", "Height" ], function( i, name ) {
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
+ type = name.toLowerCase(),
+ orig = {
+ innerWidth: $.fn.innerWidth,
+ innerHeight: $.fn.innerHeight,
+ outerWidth: $.fn.outerWidth,
+ outerHeight: $.fn.outerHeight
+ };
+
+ function reduce( elem, size, border, margin ) {
+ $.each( side, function() {
+ size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
+ if ( border ) {
+ size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
+ }
+ if ( margin ) {
+ size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
+ }
+ });
+ return size;
+ }
+
+ $.fn[ "inner" + name ] = function( size ) {
+ if ( size === undefined ) {
+ return orig[ "inner" + name ].call( this );
+ }
+
+ return this.each(function() {
+ $( this ).css( type, reduce( this, size ) + "px" );
+ });
+ };
+
+ $.fn[ "outer" + name] = function( size, margin ) {
+ if ( typeof size !== "number" ) {
+ return orig[ "outer" + name ].call( this, size );
+ }
+
+ return this.each(function() {
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
+ });
+ };
+});
+
+// selectors
+function visible( element ) {
+ return !$( element ).parents().andSelf().filter(function() {
+ return $.curCSS( this, "visibility" ) === "hidden" ||
+ $.expr.filters.hidden( this );
+ }).length;
+}
+
+$.extend( $.expr[ ":" ], {
+ data: function( elem, i, match ) {
+ return !!$.data( elem, match[ 3 ] );
+ },
+
+ focusable: function( element ) {
+ var nodeName = element.nodeName.toLowerCase(),
+ tabIndex = $.attr( element, "tabindex" );
+ if ( "area" === nodeName ) {
+ var map = element.parentNode,
+ mapName = map.name,
+ img;
+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
+ return false;
+ }
+ img = $( "img[usemap=#" + mapName + "]" )[0];
+ return !!img && visible( img );
+ }
+ return ( /input|select|textarea|button|object/.test( nodeName )
+ ? !element.disabled
+ : "a" == nodeName
+ ? element.href || !isNaN( tabIndex )
+ : !isNaN( tabIndex ))
+ // the element and all of its ancestors must be visible
+ && visible( element );
+ },
+
+ tabbable: function( element ) {
+ var tabIndex = $.attr( element, "tabindex" );
+ return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
+ }
+});
+
+// support
+$(function() {
+ var body = document.body,
+ div = body.appendChild( div = document.createElement( "div" ) );
+
+ $.extend( div.style, {
+ minHeight: "100px",
+ height: "auto",
+ padding: 0,
+ borderWidth: 0
+ });
+
+ $.support.minHeight = div.offsetHeight === 100;
+ $.support.selectstart = "onselectstart" in div;
+
+ // set display to none to avoid a layout bug in IE
+ // http://dev.jquery.com/ticket/4014
+ body.removeChild( div ).style.display = "none";
+});
+
+
+
+
+
+// deprecated
+$.extend( $.ui, {
+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
+ plugin: {
+ add: function( module, option, set ) {
+ var proto = $.ui[ module ].prototype;
+ for ( var i in set ) {
+ proto.plugins[ i ] = proto.plugins[ i ] || [];
+ proto.plugins[ i ].push( [ option, set[ i ] ] );
+ }
+ },
+ call: function( instance, name, args ) {
+ var set = instance.plugins[ name ];
+ if ( !set || !instance.element[ 0 ].parentNode ) {
+ return;
+ }
+
+ for ( var i = 0; i < set.length; i++ ) {
+ if ( instance.options[ set[ i ][ 0 ] ] ) {
+ set[ i ][ 1 ].apply( instance.element, args );
+ }
+ }
+ }
+ },
+
+ // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
+ contains: function( a, b ) {
+ return document.compareDocumentPosition ?
+ a.compareDocumentPosition( b ) & 16 :
+ a !== b && a.contains( b );
+ },
+
+ // only used by resizable
+ hasScroll: function( el, a ) {
+
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
+ if ( $( el ).css( "overflow" ) === "hidden") {
+ return false;
+ }
+
+ var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
+ has = false;
+
+ if ( el[ scroll ] > 0 ) {
+ return true;
+ }
+
+ // TODO: determine which cases actually cause this to happen
+ // if the element doesn't have the scroll set, see if it's possible to
+ // set the scroll
+ el[ scroll ] = 1;
+ has = ( el[ scroll ] > 0 );
+ el[ scroll ] = 0;
+ return has;
+ },
+
+ // these are odd functions, fix the API or move into individual plugins
+ isOverAxis: function( x, reference, size ) {
+ //Determines when x coordinate is over "b" element axis
+ return ( x > reference ) && ( x < ( reference + size ) );
+ },
+ isOver: function( y, x, top, left, height, width ) {
+ //Determines when x, y coordinates is over "b" element
+ return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
+ }
+});
+
+})( jQuery );
+/*!
+ * jQuery UI Widget 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ $( elem ).triggerHandler( "remove" );
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ $( this ).triggerHandler( "remove" );
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name ),
+ methodValue = instance && $.isFunction( instance[options] ) ?
+ instance[ options ].apply( instance, args ) :
+ instance;
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
+// if ( !instance ) {
+// throw "cannot call methods on " + name + " prior to initialization; " +
+// "attempted to call method '" + options + "'";
+// }
+// if ( !$.isFunction( instance[options] ) ) {
+// throw "no such method '" + options + "' for " + name + " widget instance";
+// }
+// var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var callback = this.options[ type ];
+
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ data = data || {};
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if ( event.originalEvent ) {
+ for ( var i = $.event.props.length, prop; i; ) {
+ prop = $.event.props[ --i ];
+ event[ prop ] = event.originalEvent[ prop ];
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+})( jQuery );
+/*!
+ * jQuery UI Mouse 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.mouse", {
+ options: {
+ cancel: ':input,option',
+ distance: 1,
+ delay: 0
+ },
+ _mouseInit: function() {
+ var self = this;
+
+ this.element
+ .bind('mousedown.'+this.widgetName, function(event) {
+ return self._mouseDown(event);
+ })
+ .bind('click.'+this.widgetName, function(event) {
+ if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
+ $.removeData(event.target, self.widgetName + '.preventClickEvent');
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+
+ this.started = false;
+ },
+
+ // TODO: make sure destroying one instance of mouse doesn't mess with
+ // other instances of mouse
+ _mouseDestroy: function() {
+ this.element.unbind('.'+this.widgetName);
+ },
+
+ _mouseDown: function(event) {
+ // don't let more than one widget handle mouseStart
+ // TODO: figure out why we have to use originalEvent
+ event.originalEvent = event.originalEvent || {};
+ if (event.originalEvent.mouseHandled) { return; }
+
+ // we may have missed mouseup (out of window)
+ (this._mouseStarted && this._mouseUp(event));
+
+ this._mouseDownEvent = event;
+
+ var self = this,
+ btnIsLeft = (event.which == 1),
+ elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+ return true;
+ }
+
+ this.mouseDelayMet = !this.options.delay;
+ if (!this.mouseDelayMet) {
+ this._mouseDelayTimer = setTimeout(function() {
+ self.mouseDelayMet = true;
+ }, this.options.delay);
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted = (this._mouseStart(event) !== false);
+ if (!this._mouseStarted) {
+ event.preventDefault();
+ return true;
+ }
+ }
+
+ // these delegates are required to keep context
+ this._mouseMoveDelegate = function(event) {
+ return self._mouseMove(event);
+ };
+ this._mouseUpDelegate = function(event) {
+ return self._mouseUp(event);
+ };
+ $(document)
+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ event.preventDefault();
+ event.originalEvent.mouseHandled = true;
+ return true;
+ },
+
+ _mouseMove: function(event) {
+ // IE mouseup check - mouseup happened when mouse was out of window
+ if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
+ return this._mouseUp(event);
+ }
+
+ if (this._mouseStarted) {
+ this._mouseDrag(event);
+ return event.preventDefault();
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted =
+ (this._mouseStart(this._mouseDownEvent, event) !== false);
+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+ }
+
+ return !this._mouseStarted;
+ },
+
+ _mouseUp: function(event) {
+ $(document)
+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ if (this._mouseStarted) {
+ this._mouseStarted = false;
+
+ if (event.target == this._mouseDownEvent.target) {
+ $.data(event.target, this.widgetName + '.preventClickEvent', true);
+ }
+
+ this._mouseStop(event);
+ }
+
+ return false;
+ },
+
+ _mouseDistanceMet: function(event) {
+ return (Math.max(
+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
+ ) >= this.options.distance
+ );
+ },
+
+ _mouseDelayMet: function(event) {
+ return this.mouseDelayMet;
+ },
+
+ // These are placeholder methods, to be overriden by extending plugin
+ _mouseStart: function(event) {},
+ _mouseDrag: function(event) {},
+ _mouseStop: function(event) {},
+ _mouseCapture: function(event) { return true; }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Position 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $, undefined ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+ verticalPositions = /top|center|bottom/,
+ center = "center",
+ _position = $.fn.position,
+ _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+ if ( !options || !options.of ) {
+ return _position.apply( this, arguments );
+ }
+
+ // make a copy, we don't want to modify arguments
+ options = $.extend( {}, options );
+
+ var target = $( options.of ),
+ targetElem = target[0],
+ collision = ( options.collision || "flip" ).split( " " ),
+ offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+ targetWidth,
+ targetHeight,
+ basePosition;
+
+ if ( targetElem.nodeType === 9 ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: 0, left: 0 };
+ // TODO: use $.isWindow() in 1.9
+ } else if ( targetElem.setTimeout ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+ } else if ( targetElem.preventDefault ) {
+ // force left top to allow flipping
+ options.at = "left top";
+ targetWidth = targetHeight = 0;
+ basePosition = { top: options.of.pageY, left: options.of.pageX };
+ } else {
+ targetWidth = target.outerWidth();
+ targetHeight = target.outerHeight();
+ basePosition = target.offset();
+ }
+
+ // force my and at to have valid horizontal and veritcal positions
+ // if a value is missing or invalid, it will be converted to center
+ $.each( [ "my", "at" ], function() {
+ var pos = ( options[this] || "" ).split( " " );
+ if ( pos.length === 1) {
+ pos = horizontalPositions.test( pos[0] ) ?
+ pos.concat( [center] ) :
+ verticalPositions.test( pos[0] ) ?
+ [ center ].concat( pos ) :
+ [ center, center ];
+ }
+ pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
+ pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
+ options[ this ] = pos;
+ });
+
+ // normalize collision option
+ if ( collision.length === 1 ) {
+ collision[ 1 ] = collision[ 0 ];
+ }
+
+ // normalize offset option
+ offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+ if ( offset.length === 1 ) {
+ offset[ 1 ] = offset[ 0 ];
+ }
+ offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+ if ( options.at[0] === "right" ) {
+ basePosition.left += targetWidth;
+ } else if ( options.at[0] === center ) {
+ basePosition.left += targetWidth / 2;
+ }
+
+ if ( options.at[1] === "bottom" ) {
+ basePosition.top += targetHeight;
+ } else if ( options.at[1] === center ) {
+ basePosition.top += targetHeight / 2;
+ }
+
+ basePosition.left += offset[ 0 ];
+ basePosition.top += offset[ 1 ];
+
+ return this.each(function() {
+ var elem = $( this ),
+ elemWidth = elem.outerWidth(),
+ elemHeight = elem.outerHeight(),
+ marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
+ marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
+ collisionWidth = elemWidth + marginLeft +
+ ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
+ collisionHeight = elemHeight + marginTop +
+ ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
+ position = $.extend( {}, basePosition ),
+ collisionPosition;
+
+ if ( options.my[0] === "right" ) {
+ position.left -= elemWidth;
+ } else if ( options.my[0] === center ) {
+ position.left -= elemWidth / 2;
+ }
+
+ if ( options.my[1] === "bottom" ) {
+ position.top -= elemHeight;
+ } else if ( options.my[1] === center ) {
+ position.top -= elemHeight / 2;
+ }
+
+ // prevent fractions (see #5280)
+ position.left = Math.round( position.left );
+ position.top = Math.round( position.top );
+
+ collisionPosition = {
+ left: position.left - marginLeft,
+ top: position.top - marginTop
+ };
+
+ $.each( [ "left", "top" ], function( i, dir ) {
+ if ( $.ui.position[ collision[i] ] ) {
+ $.ui.position[ collision[i] ][ dir ]( position, {
+ targetWidth: targetWidth,
+ targetHeight: targetHeight,
+ elemWidth: elemWidth,
+ elemHeight: elemHeight,
+ collisionPosition: collisionPosition,
+ collisionWidth: collisionWidth,
+ collisionHeight: collisionHeight,
+ offset: offset,
+ my: options.my,
+ at: options.at
+ });
+ }
+ });
+
+ if ( $.fn.bgiframe ) {
+ elem.bgiframe();
+ }
+ elem.offset( $.extend( position, { using: options.using } ) );
+ });
+};
+
+$.ui.position = {
+ fit: {
+ left: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
+ position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
+ },
+ top: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
+ position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
+ }
+ },
+
+ flip: {
+ left: function( position, data ) {
+ if ( data.at[0] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
+ myOffset = data.my[ 0 ] === "left" ?
+ -data.elemWidth :
+ data.my[ 0 ] === "right" ?
+ data.elemWidth :
+ 0,
+ atOffset = data.at[ 0 ] === "left" ?
+ data.targetWidth :
+ -data.targetWidth,
+ offset = -2 * data.offset[ 0 ];
+ position.left += data.collisionPosition.left < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ },
+ top: function( position, data ) {
+ if ( data.at[1] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
+ myOffset = data.my[ 1 ] === "top" ?
+ -data.elemHeight :
+ data.my[ 1 ] === "bottom" ?
+ data.elemHeight :
+ 0,
+ atOffset = data.at[ 1 ] === "top" ?
+ data.targetHeight :
+ -data.targetHeight,
+ offset = -2 * data.offset[ 1 ];
+ position.top += data.collisionPosition.top < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ }
+ }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+ $.offset.setOffset = function( elem, options ) {
+ // set position first, in-case top/left are set even on static elem
+ if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+ elem.style.position = "relative";
+ }
+ var curElem = $( elem ),
+ curOffset = curElem.offset(),
+ curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
+ curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
+ props = {
+ top: (options.top - curOffset.top) + curTop,
+ left: (options.left - curOffset.left) + curLeft
+ };
+
+ if ( 'using' in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ };
+
+ $.fn.offset = function( options ) {
+ var elem = this[ 0 ];
+ if ( !elem || !elem.ownerDocument ) { return null; }
+ if ( options ) {
+ return this.each(function() {
+ $.offset.setOffset( this, options );
+ });
+ }
+ return _offset.call( this );
+ };
+}
+
+}( jQuery ));
+/*
+ * jQuery UI Draggable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.draggable", $.ui.mouse, {
+ widgetEventPrefix: "drag",
+ options: {
+ addClasses: true,
+ appendTo: "parent",
+ axis: false,
+ connectToSortable: false,
+ containment: false,
+ cursor: "auto",
+ cursorAt: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ iframeFix: false,
+ opacity: false,
+ refreshPositions: false,
+ revert: false,
+ revertDuration: 500,
+ scope: "default",
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ snap: false,
+ snapMode: "both",
+ snapTolerance: 20,
+ stack: false,
+ zIndex: false
+ },
+ _create: function() {
+
+ if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
+ this.element[0].style.position = 'relative';
+
+ (this.options.addClasses && this.element.addClass("ui-draggable"));
+ (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
+
+ this._mouseInit();
+
+ },
+
+ destroy: function() {
+ if(!this.element.data('draggable')) return;
+ this.element
+ .removeData("draggable")
+ .unbind(".draggable")
+ .removeClass("ui-draggable"
+ + " ui-draggable-dragging"
+ + " ui-draggable-disabled");
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseCapture: function(event) {
+
+ var o = this.options;
+
+ // among others, prevent a drag on a resizable-handle
+ if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
+ return false;
+
+ //Quit if we're not on a valid handle
+ this.handle = this._getHandle(event);
+ if (!this.handle)
+ return false;
+
+ return true;
+
+ },
+
+ _mouseStart: function(event) {
+
+ var o = this.options;
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ //If ddmanager is used for droppables, set the global draggable
+ if($.ui.ddmanager)
+ $.ui.ddmanager.current = this;
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Store the helper's css position
+ this.cssPosition = this.helper.css("position");
+ this.scrollParent = this.helper.scrollParent();
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.positionAbs = this.element.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ //Generate the original position
+ this.originalPosition = this.position = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Set a containment if given in the options
+ if(o.containment)
+ this._setContainment();
+
+ //Trigger event + callbacks
+ if(this._trigger("start", event) === false) {
+ this._clear();
+ return false;
+ }
+
+ //Recache the helper size
+ this._cacheHelperProportions();
+
+ //Prepare the droppable offsets
+ if ($.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+
+ this.helper.addClass("ui-draggable-dragging");
+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+ return true;
+ },
+
+ _mouseDrag: function(event, noPropagation) {
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Call plugins and callbacks and use the resulting position if something is returned
+ if (!noPropagation) {
+ var ui = this._uiHash();
+ if(this._trigger('drag', event, ui) === false) {
+ this._mouseUp({});
+ return false;
+ }
+ this.position = ui.position;
+ }
+
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ //If we are using droppables, inform the manager about the drop
+ var dropped = false;
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
+ dropped = $.ui.ddmanager.drop(this, event);
+
+ //if a drop comes from outside (a sortable)
+ if(this.dropped) {
+ dropped = this.dropped;
+ this.dropped = false;
+ }
+
+ //if the original element is removed, don't bother to continue if helper is set to "original"
+ if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original")
+ return false;
+
+ if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
+ var self = this;
+ $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
+ if(self._trigger("stop", event) !== false) {
+ self._clear();
+ }
+ });
+ } else {
+ if(this._trigger("stop", event) !== false) {
+ this._clear();
+ }
+ }
+
+ return false;
+ },
+
+ cancel: function() {
+
+ if(this.helper.is(".ui-draggable-dragging")) {
+ this._mouseUp({});
+ } else {
+ this._clear();
+ }
+
+ return this;
+
+ },
+
+ _getHandle: function(event) {
+
+ var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
+ $(this.options.handle, this.element)
+ .find("*")
+ .andSelf()
+ .each(function() {
+ if(this == event.target) handle = true;
+ });
+
+ return handle;
+
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options;
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
+
+ if(!helper.parents('body').length)
+ helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
+
+ if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
+ helper.css("position", "absolute");
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj == 'string') {
+ obj = obj.split(' ');
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ('left' in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ('right' in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ('top' in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ('bottom' in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+ //Get the offsetParent and cache its position
+ this.offsetParent = this.helper.offsetParent();
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+ po = { top: 0, left: 0 };
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition == "relative") {
+ var p = this.element.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
+ top: (parseInt(this.element.css("marginTop"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var o = this.options;
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
+ (o.containment == 'document' ? 0 : $(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left,
+ (o.containment == 'document' ? 0 : $(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top,
+ (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+ (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+ ];
+
+ if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
+ var ce = $(o.containment)[0]; if(!ce) return;
+ var co = $(o.containment).offset();
+ var over = ($(ce).css("overflow") != 'hidden');
+
+ this.containment = [
+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+ ];
+ } else if(o.containment.constructor == Array) {
+ this.containment = o.containment;
+ }
+
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) pos = this.position;
+ var mod = d == "absolute" ? 1 : -1;
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ return {
+ top: (
+ pos.top // The absolute mouse position
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+ ),
+ left: (
+ pos.left // The absolute mouse position
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+ var pageX = event.pageX;
+ var pageY = event.pageY;
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+ if(this.containment) {
+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+ }
+
+ if(o.grid) {
+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY // The absolute mouse position
+ - this.offset.click.top // Click offset (relative to the element)
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+ ),
+ left: (
+ pageX // The absolute mouse position
+ - this.offset.click.left // Click offset (relative to the element)
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+ )
+ };
+
+ },
+
+ _clear: function() {
+ this.helper.removeClass("ui-draggable-dragging");
+ if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
+ //if($.ui.ddmanager) $.ui.ddmanager.current = null;
+ this.helper = null;
+ this.cancelHelperRemoval = false;
+ },
+
+ // From now on bulk stuff - mainly helpers
+
+ _trigger: function(type, event, ui) {
+ ui = ui || this._uiHash();
+ $.ui.plugin.call(this, type, [event, ui]);
+ if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
+ return $.Widget.prototype._trigger.call(this, type, event, ui);
+ },
+
+ plugins: {},
+
+ _uiHash: function(event) {
+ return {
+ helper: this.helper,
+ position: this.position,
+ originalPosition: this.originalPosition,
+ offset: this.positionAbs
+ };
+ }
+
+});
+
+$.extend($.ui.draggable, {
+ version: "1.8.10"
+});
+
+$.ui.plugin.add("draggable", "connectToSortable", {
+ start: function(event, ui) {
+
+ var inst = $(this).data("draggable"), o = inst.options,
+ uiSortable = $.extend({}, ui, { item: inst.element });
+ inst.sortables = [];
+ $(o.connectToSortable).each(function() {
+ var sortable = $.data(this, 'sortable');
+ if (sortable && !sortable.options.disabled) {
+ inst.sortables.push({
+ instance: sortable,
+ shouldRevert: sortable.options.revert
+ });
+ sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache
+ sortable._trigger("activate", event, uiSortable);
+ }
+ });
+
+ },
+ stop: function(event, ui) {
+
+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
+ var inst = $(this).data("draggable"),
+ uiSortable = $.extend({}, ui, { item: inst.element });
+
+ $.each(inst.sortables, function() {
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+
+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
+
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
+ if(this.shouldRevert) this.instance.options.revert = true;
+
+ //Trigger the stop of the sortable
+ this.instance._mouseStop(event);
+
+ this.instance.options.helper = this.instance.options._helper;
+
+ //If the helper has been the original item, restore properties in the sortable
+ if(inst.options.helper == 'original')
+ this.instance.currentItem.css({ top: 'auto', left: 'auto' });
+
+ } else {
+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
+ this.instance._trigger("deactivate", event, uiSortable);
+ }
+
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var inst = $(this).data("draggable"), self = this;
+
+ var checkPos = function(o) {
+ var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
+ var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
+ var itemHeight = o.height, itemWidth = o.width;
+ var itemTop = o.top, itemLeft = o.left;
+
+ return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
+ };
+
+ $.each(inst.sortables, function(i) {
+
+ //Copy over some variables to allow calling the sortable's native _intersectsWith
+ this.instance.positionAbs = inst.positionAbs;
+ this.instance.helperProportions = inst.helperProportions;
+ this.instance.offset.click = inst.offset.click;
+
+ if(this.instance._intersectsWith(this.instance.containerCache)) {
+
+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
+ if(!this.instance.isOver) {
+
+ this.instance.isOver = 1;
+ //Now we fake the start of dragging for the sortable instance,
+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
+ this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
+ this.instance.options.helper = function() { return ui.helper[0]; };
+
+ event.target = this.instance.currentItem[0];
+ this.instance._mouseCapture(event, true);
+ this.instance._mouseStart(event, true, true);
+
+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
+ this.instance.offset.click.top = inst.offset.click.top;
+ this.instance.offset.click.left = inst.offset.click.left;
+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
+
+ inst._trigger("toSortable", event);
+ inst.dropped = this.instance.element; //draggable revert needs that
+ //hack so receive/update callbacks work (mostly)
+ inst.currentItem = inst.element;
+ this.instance.fromOutside = inst;
+
+ }
+
+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
+ if(this.instance.currentItem) this.instance._mouseDrag(event);
+
+ } else {
+
+ //If it doesn't intersect with the sortable, and it intersected before,
+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+ this.instance.cancelHelperRemoval = true;
+
+ //Prevent reverting on this forced stop
+ this.instance.options.revert = false;
+
+ // The out event needs to be triggered independently
+ this.instance._trigger('out', event, this.instance._uiHash(this.instance));
+
+ this.instance._mouseStop(event, true);
+ this.instance.options.helper = this.instance.options._helper;
+
+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
+ this.instance.currentItem.remove();
+ if(this.instance.placeholder) this.instance.placeholder.remove();
+
+ inst._trigger("fromSortable", event);
+ inst.dropped = false; //draggable revert needs that
+ }
+
+ };
+
+ });
+
+ }
+});
+
+$.ui.plugin.add("draggable", "cursor", {
+ start: function(event, ui) {
+ var t = $('body'), o = $(this).data('draggable').options;
+ if (t.css("cursor")) o._cursor = t.css("cursor");
+ t.css("cursor", o.cursor);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data('draggable').options;
+ if (o._cursor) $('body').css("cursor", o._cursor);
+ }
+});
+
+$.ui.plugin.add("draggable", "iframeFix", {
+ start: function(event, ui) {
+ var o = $(this).data('draggable').options;
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+ $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
+ .css({
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+ position: "absolute", opacity: "0.001", zIndex: 1000
+ })
+ .css($(this).offset())
+ .appendTo("body");
+ });
+ },
+ stop: function(event, ui) {
+ $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
+ }
+});
+
+$.ui.plugin.add("draggable", "opacity", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data('draggable').options;
+ if(t.css("opacity")) o._opacity = t.css("opacity");
+ t.css('opacity', o.opacity);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data('draggable').options;
+ if(o._opacity) $(ui.helper).css('opacity', o._opacity);
+ }
+});
+
+$.ui.plugin.add("draggable", "scroll", {
+ start: function(event, ui) {
+ var i = $(this).data("draggable");
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
+ },
+ drag: function(event, ui) {
+
+ var i = $(this).data("draggable"), o = i.options, scrolled = false;
+
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
+
+ if(!o.axis || o.axis != 'x') {
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
+ else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
+ }
+
+ if(!o.axis || o.axis != 'y') {
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
+ else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
+ }
+
+ } else {
+
+ if(!o.axis || o.axis != 'x') {
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+ }
+
+ if(!o.axis || o.axis != 'y') {
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+ }
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(i, event);
+
+ }
+});
+
+$.ui.plugin.add("draggable", "snap", {
+ start: function(event, ui) {
+
+ var i = $(this).data("draggable"), o = i.options;
+ i.snapElements = [];
+
+ $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
+ var $t = $(this); var $o = $t.offset();
+ if(this != i.element[0]) i.snapElements.push({
+ item: this,
+ width: $t.outerWidth(), height: $t.outerHeight(),
+ top: $o.top, left: $o.left
+ });
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var inst = $(this).data("draggable"), o = inst.options;
+ var d = o.snapTolerance;
+
+ var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
+
+ for (var i = inst.snapElements.length - 1; i >= 0; i--){
+
+ var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
+ t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
+
+ //Yes, I know, this is insane ;)
+ if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
+ if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ inst.snapElements[i].snapping = false;
+ continue;
+ }
+
+ if(o.snapMode != 'inner') {
+ var ts = Math.abs(t - y2) <= d;
+ var bs = Math.abs(b - y1) <= d;
+ var ls = Math.abs(l - x2) <= d;
+ var rs = Math.abs(r - x1) <= d;
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
+ }
+
+ var first = (ts || bs || ls || rs);
+
+ if(o.snapMode != 'outer') {
+ var ts = Math.abs(t - y1) <= d;
+ var bs = Math.abs(b - y2) <= d;
+ var ls = Math.abs(l - x1) <= d;
+ var rs = Math.abs(r - x2) <= d;
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
+ }
+
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
+
+ };
+
+ }
+});
+
+$.ui.plugin.add("draggable", "stack", {
+ start: function(event, ui) {
+
+ var o = $(this).data("draggable").options;
+
+ var group = $.makeArray($(o.stack)).sort(function(a,b) {
+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
+ });
+ if (!group.length) { return; }
+
+ var min = parseInt(group[0].style.zIndex) || 0;
+ $(group).each(function(i) {
+ this.style.zIndex = min + i;
+ });
+
+ this[0].style.zIndex = min + group.length;
+
+ }
+});
+
+$.ui.plugin.add("draggable", "zIndex", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data("draggable").options;
+ if(t.css("zIndex")) o._zIndex = t.css("zIndex");
+ t.css('zIndex', o.zIndex);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data("draggable").options;
+ if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
+ }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Droppable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.droppable", {
+ widgetEventPrefix: "drop",
+ options: {
+ accept: '*',
+ activeClass: false,
+ addClasses: true,
+ greedy: false,
+ hoverClass: false,
+ scope: 'default',
+ tolerance: 'intersect'
+ },
+ _create: function() {
+
+ var o = this.options, accept = o.accept;
+ this.isover = 0; this.isout = 1;
+
+ this.accept = $.isFunction(accept) ? accept : function(d) {
+ return d.is(accept);
+ };
+
+ //Store the droppable's proportions
+ this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
+
+ // Add the reference and positions to the manager
+ $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
+ $.ui.ddmanager.droppables[o.scope].push(this);
+
+ (o.addClasses && this.element.addClass("ui-droppable"));
+
+ },
+
+ destroy: function() {
+ var drop = $.ui.ddmanager.droppables[this.options.scope];
+ for ( var i = 0; i < drop.length; i++ )
+ if ( drop[i] == this )
+ drop.splice(i, 1);
+
+ this.element
+ .removeClass("ui-droppable ui-droppable-disabled")
+ .removeData("droppable")
+ .unbind(".droppable");
+
+ return this;
+ },
+
+ _setOption: function(key, value) {
+
+ if(key == 'accept') {
+ this.accept = $.isFunction(value) ? value : function(d) {
+ return d.is(value);
+ };
+ }
+ $.Widget.prototype._setOption.apply(this, arguments);
+ },
+
+ _activate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) this.element.addClass(this.options.activeClass);
+ (draggable && this._trigger('activate', event, this.ui(draggable)));
+ },
+
+ _deactivate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
+ (draggable && this._trigger('deactivate', event, this.ui(draggable)));
+ },
+
+ _over: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
+ this._trigger('over', event, this.ui(draggable));
+ }
+
+ },
+
+ _out: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
+ this._trigger('out', event, this.ui(draggable));
+ }
+
+ },
+
+ _drop: function(event,custom) {
+
+ var draggable = custom || $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
+
+ var childrenIntersection = false;
+ this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
+ var inst = $.data(this, 'droppable');
+ if(
+ inst.options.greedy
+ && !inst.options.disabled
+ && inst.options.scope == draggable.options.scope
+ && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
+ && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
+ ) { childrenIntersection = true; return false; }
+ });
+ if(childrenIntersection) return false;
+
+ if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
+ this._trigger('drop', event, this.ui(draggable));
+ return this.element;
+ }
+
+ return false;
+
+ },
+
+ ui: function(c) {
+ return {
+ draggable: (c.currentItem || c.element),
+ helper: c.helper,
+ position: c.position,
+ offset: c.positionAbs
+ };
+ }
+
+});
+
+$.extend($.ui.droppable, {
+ version: "1.8.10"
+});
+
+$.ui.intersect = function(draggable, droppable, toleranceMode) {
+
+ if (!droppable.offset) return false;
+
+ var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
+ var l = droppable.offset.left, r = l + droppable.proportions.width,
+ t = droppable.offset.top, b = t + droppable.proportions.height;
+
+ switch (toleranceMode) {
+ case 'fit':
+ return (l <= x1 && x2 <= r
+ && t <= y1 && y2 <= b);
+ break;
+ case 'intersect':
+ return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
+ && x2 - (draggable.helperProportions.width / 2) < r // Left Half
+ && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
+ && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
+ break;
+ case 'pointer':
+ var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
+ isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
+ return isOver;
+ break;
+ case 'touch':
+ return (
+ (y1 >= t && y1 <= b) || // Top edge touching
+ (y2 >= t && y2 <= b) || // Bottom edge touching
+ (y1 < t && y2 > b) // Surrounded vertically
+ ) && (
+ (x1 >= l && x1 <= r) || // Left edge touching
+ (x2 >= l && x2 <= r) || // Right edge touching
+ (x1 < l && x2 > r) // Surrounded horizontally
+ );
+ break;
+ default:
+ return false;
+ break;
+ }
+
+};
+
+/*
+ This manager tracks offsets of draggables and droppables
+*/
+$.ui.ddmanager = {
+ current: null,
+ droppables: { 'default': [] },
+ prepareOffsets: function(t, event) {
+
+ var m = $.ui.ddmanager.droppables[t.options.scope] || [];
+ var type = event ? event.type : null; // workaround for #2317
+ var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
+
+ droppablesLoop: for (var i = 0; i < m.length; i++) {
+
+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
+ for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
+ m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
+
+ m[i].offset = m[i].element.offset();
+ m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
+
+ if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
+
+ }
+
+ },
+ drop: function(draggable, event) {
+
+ var dropped = false;
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
+
+ if(!this.options) return;
+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
+ dropped = dropped || this._drop.call(this, event);
+
+ if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ this.isout = 1; this.isover = 0;
+ this._deactivate.call(this, event);
+ }
+
+ });
+ return dropped;
+
+ },
+ drag: function(draggable, event) {
+
+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
+ if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
+
+ //Run through all droppables and check their positions based on specific tolerance options
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
+
+ if(this.options.disabled || this.greedyChild || !this.visible) return;
+ var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
+
+ var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
+ if(!c) return;
+
+ var parentInstance;
+ if (this.options.greedy) {
+ var parent = this.element.parents(':data(droppable):eq(0)');
+ if (parent.length) {
+ parentInstance = $.data(parent[0], 'droppable');
+ parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
+ }
+ }
+
+ // we just moved into a greedy child
+ if (parentInstance && c == 'isover') {
+ parentInstance['isover'] = 0;
+ parentInstance['isout'] = 1;
+ parentInstance._out.call(parentInstance, event);
+ }
+
+ this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
+ this[c == "isover" ? "_over" : "_out"].call(this, event);
+
+ // we just moved out of a greedy child
+ if (parentInstance && c == 'isout') {
+ parentInstance['isout'] = 0;
+ parentInstance['isover'] = 1;
+ parentInstance._over.call(parentInstance, event);
+ }
+ });
+
+ }
+};
+
+})(jQuery);
+/*
+ * jQuery UI Resizable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.resizable", $.ui.mouse, {
+ widgetEventPrefix: "resize",
+ options: {
+ alsoResize: false,
+ animate: false,
+ animateDuration: "slow",
+ animateEasing: "swing",
+ aspectRatio: false,
+ autoHide: false,
+ containment: false,
+ ghost: false,
+ grid: false,
+ handles: "e,s,se",
+ helper: false,
+ maxHeight: null,
+ maxWidth: null,
+ minHeight: 10,
+ minWidth: 10,
+ zIndex: 1000
+ },
+ _create: function() {
+
+ var self = this, o = this.options;
+ this.element.addClass("ui-resizable");
+
+ $.extend(this, {
+ _aspectRatio: !!(o.aspectRatio),
+ aspectRatio: o.aspectRatio,
+ originalElement: this.element,
+ _proportionallyResizeElements: [],
+ _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
+ });
+
+ //Wrap the element if it cannot hold child nodes
+ if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
+
+ //Opera fix for relative positioning
+ if (/relative/.test(this.element.css('position')) && $.browser.opera)
+ this.element.css({ position: 'relative', top: 'auto', left: 'auto' });
+
+ //Create a wrapper element and set the wrapper to the new current internal element
+ this.element.wrap(
+ $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
+ position: this.element.css('position'),
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight(),
+ top: this.element.css('top'),
+ left: this.element.css('left')
+ })
+ );
+
+ //Overwrite the original this.element
+ this.element = this.element.parent().data(
+ "resizable", this.element.data('resizable')
+ );
+
+ this.elementIsWrapper = true;
+
+ //Move margins to the wrapper
+ this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
+ this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
+
+ //Prevent Safari textarea resize
+ this.originalResizeStyle = this.originalElement.css('resize');
+ this.originalElement.css('resize', 'none');
+
+ //Push the actual element to our proportionallyResize internal array
+ this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
+
+ // avoid IE jump (hard set the margin)
+ this.originalElement.css({ margin: this.originalElement.css('margin') });
+
+ // fix handlers offset
+ this._proportionallyResize();
+
+ }
+
+ this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
+ if(this.handles.constructor == String) {
+
+ if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
+ var n = this.handles.split(","); this.handles = {};
+
+ for(var i = 0; i < n.length; i++) {
+
+ var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
+ var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
+
+ // increase zIndex of sw, se, ne, nw axis
+ //TODO : this modifies original option
+ if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
+
+ //TODO : What's going on here?
+ if ('se' == handle) {
+ axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
+ };
+
+ //Insert into internal handles object and append to element
+ this.handles[handle] = '.ui-resizable-'+handle;
+ this.element.append(axis);
+ }
+
+ }
+
+ this._renderAxis = function(target) {
+
+ target = target || this.element;
+
+ for(var i in this.handles) {
+
+ if(this.handles[i].constructor == String)
+ this.handles[i] = $(this.handles[i], this.element).show();
+
+ //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
+ if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
+
+ var axis = $(this.handles[i], this.element), padWrapper = 0;
+
+ //Checking the correct pad and border
+ padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
+
+ //The padding type i have to apply...
+ var padPos = [ 'padding',
+ /ne|nw|n/.test(i) ? 'Top' :
+ /se|sw|s/.test(i) ? 'Bottom' :
+ /^e$/.test(i) ? 'Right' : 'Left' ].join("");
+
+ target.css(padPos, padWrapper);
+
+ this._proportionallyResize();
+
+ }
+
+ //TODO: What's that good for? There's not anything to be executed left
+ if(!$(this.handles[i]).length)
+ continue;
+
+ }
+ };
+
+ //TODO: make renderAxis a prototype function
+ this._renderAxis(this.element);
+
+ this._handles = $('.ui-resizable-handle', this.element)
+ .disableSelection();
+
+ //Matching axis name
+ this._handles.mouseover(function() {
+ if (!self.resizing) {
+ if (this.className)
+ var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
+ //Axis, default = se
+ self.axis = axis && axis[1] ? axis[1] : 'se';
+ }
+ });
+
+ //If we want to auto hide the elements
+ if (o.autoHide) {
+ this._handles.hide();
+ $(this.element)
+ .addClass("ui-resizable-autohide")
+ .hover(function() {
+ $(this).removeClass("ui-resizable-autohide");
+ self._handles.show();
+ },
+ function(){
+ if (!self.resizing) {
+ $(this).addClass("ui-resizable-autohide");
+ self._handles.hide();
+ }
+ });
+ }
+
+ //Initialize the mouse interaction
+ this._mouseInit();
+
+ },
+
+ destroy: function() {
+
+ this._mouseDestroy();
+
+ var _destroy = function(exp) {
+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
+ .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
+ };
+
+ //TODO: Unwrap at same DOM position
+ if (this.elementIsWrapper) {
+ _destroy(this.element);
+ var wrapper = this.element;
+ wrapper.after(
+ this.originalElement.css({
+ position: wrapper.css('position'),
+ width: wrapper.outerWidth(),
+ height: wrapper.outerHeight(),
+ top: wrapper.css('top'),
+ left: wrapper.css('left')
+ })
+ ).remove();
+ }
+
+ this.originalElement.css('resize', this.originalResizeStyle);
+ _destroy(this.originalElement);
+
+ return this;
+ },
+
+ _mouseCapture: function(event) {
+ var handle = false;
+ for (var i in this.handles) {
+ if ($(this.handles[i])[0] == event.target) {
+ handle = true;
+ }
+ }
+
+ return !this.options.disabled && handle;
+ },
+
+ _mouseStart: function(event) {
+
+ var o = this.options, iniPos = this.element.position(), el = this.element;
+
+ this.resizing = true;
+ this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
+
+ // bugfix for http://dev.jquery.com/ticket/1749
+ if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
+ el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
+ }
+
+ //Opera fixing relative position
+ if ($.browser.opera && (/relative/).test(el.css('position')))
+ el.css({ position: 'relative', top: 'auto', left: 'auto' });
+
+ this._renderProxy();
+
+ var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
+
+ if (o.containment) {
+ curleft += $(o.containment).scrollLeft() || 0;
+ curtop += $(o.containment).scrollTop() || 0;
+ }
+
+ //Store needed variables
+ this.offset = this.helper.offset();
+ this.position = { left: curleft, top: curtop };
+ this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.originalPosition = { left: curleft, top: curtop };
+ this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
+ this.originalMousePosition = { left: event.pageX, top: event.pageY };
+
+ //Aspect Ratio
+ this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
+
+ var cursor = $('.ui-resizable-' + this.axis).css('cursor');
+ $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
+
+ el.addClass("ui-resizable-resizing");
+ this._propagate("start", event);
+ return true;
+ },
+
+ _mouseDrag: function(event) {
+
+ //Increase performance, avoid regex
+ var el = this.helper, o = this.options, props = {},
+ self = this, smp = this.originalMousePosition, a = this.axis;
+
+ var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
+ var trigger = this._change[a];
+ if (!trigger) return false;
+
+ // Calculate the attrs that will be change
+ var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
+
+ if (this._aspectRatio || event.shiftKey)
+ data = this._updateRatio(data, event);
+
+ data = this._respectSize(data, event);
+
+ // plugins callbacks need to be called first
+ this._propagate("resize", event);
+
+ el.css({
+ top: this.position.top + "px", left: this.position.left + "px",
+ width: this.size.width + "px", height: this.size.height + "px"
+ });
+
+ if (!this._helper && this._proportionallyResizeElements.length)
+ this._proportionallyResize();
+
+ this._updateCache(data);
+
+ // calling the user callback at the end
+ this._trigger('resize', event, this.ui());
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ this.resizing = false;
+ var o = this.options, self = this;
+
+ if(this._helper) {
+ var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+ soffsetw = ista ? 0 : self.sizeDiff.width;
+
+ var s = { width: (self.helper.width() - soffsetw), height: (self.helper.height() - soffseth) },
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+ if (!o.animate)
+ this.element.css($.extend(s, { top: top, left: left }));
+
+ self.helper.height(self.size.height);
+ self.helper.width(self.size.width);
+
+ if (this._helper && !o.animate) this._proportionallyResize();
+ }
+
+ $('body').css('cursor', 'auto');
+
+ this.element.removeClass("ui-resizable-resizing");
+
+ this._propagate("stop", event);
+
+ if (this._helper) this.helper.remove();
+ return false;
+
+ },
+
+ _updateCache: function(data) {
+ var o = this.options;
+ this.offset = this.helper.offset();
+ if (isNumber(data.left)) this.position.left = data.left;
+ if (isNumber(data.top)) this.position.top = data.top;
+ if (isNumber(data.height)) this.size.height = data.height;
+ if (isNumber(data.width)) this.size.width = data.width;
+ },
+
+ _updateRatio: function(data, event) {
+
+ var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
+
+ if (data.height) data.width = (csize.height * this.aspectRatio);
+ else if (data.width) data.height = (csize.width / this.aspectRatio);
+
+ if (a == 'sw') {
+ data.left = cpos.left + (csize.width - data.width);
+ data.top = null;
+ }
+ if (a == 'nw') {
+ data.top = cpos.top + (csize.height - data.height);
+ data.left = cpos.left + (csize.width - data.width);
+ }
+
+ return data;
+ },
+
+ _respectSize: function(data, event) {
+
+ var el = this.helper, o = this.options, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
+
+ if (isminw) data.width = o.minWidth;
+ if (isminh) data.height = o.minHeight;
+ if (ismaxw) data.width = o.maxWidth;
+ if (ismaxh) data.height = o.maxHeight;
+
+ var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
+ var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
+
+ if (isminw && cw) data.left = dw - o.minWidth;
+ if (ismaxw && cw) data.left = dw - o.maxWidth;
+ if (isminh && ch) data.top = dh - o.minHeight;
+ if (ismaxh && ch) data.top = dh - o.maxHeight;
+
+ // fixing jump error on top/left - bug #2330
+ var isNotwh = !data.width && !data.height;
+ if (isNotwh && !data.left && data.top) data.top = null;
+ else if (isNotwh && !data.top && data.left) data.left = null;
+
+ return data;
+ },
+
+ _proportionallyResize: function() {
+
+ var o = this.options;
+ if (!this._proportionallyResizeElements.length) return;
+ var element = this.helper || this.element;
+
+ for (var i=0; i < this._proportionallyResizeElements.length; i++) {
+
+ var prel = this._proportionallyResizeElements[i];
+
+ if (!this.borderDif) {
+ var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
+ p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
+
+ this.borderDif = $.map(b, function(v, i) {
+ var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
+ return border + padding;
+ });
+ }
+
+ if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
+ continue;
+
+ prel.css({
+ height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
+ width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
+ });
+
+ };
+
+ },
+
+ _renderProxy: function() {
+
+ var el = this.element, o = this.options;
+ this.elementOffset = el.offset();
+
+ if(this._helper) {
+
+ this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
+
+ // fix ie6 offset TODO: This seems broken
+ var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
+ pxyoffset = ( ie6 ? 2 : -1 );
+
+ this.helper.addClass(this._helper).css({
+ width: this.element.outerWidth() + pxyoffset,
+ height: this.element.outerHeight() + pxyoffset,
+ position: 'absolute',
+ left: this.elementOffset.left - ie6offset +'px',
+ top: this.elementOffset.top - ie6offset +'px',
+ zIndex: ++o.zIndex //TODO: Don't modify option
+ });
+
+ this.helper
+ .appendTo("body")
+ .disableSelection();
+
+ } else {
+ this.helper = this.element;
+ }
+
+ },
+
+ _change: {
+ e: function(event, dx, dy) {
+ return { width: this.originalSize.width + dx };
+ },
+ w: function(event, dx, dy) {
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+ return { left: sp.left + dx, width: cs.width - dx };
+ },
+ n: function(event, dx, dy) {
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+ return { top: sp.top + dy, height: cs.height - dy };
+ },
+ s: function(event, dx, dy) {
+ return { height: this.originalSize.height + dy };
+ },
+ se: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ sw: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ },
+ ne: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ nw: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ }
+ },
+
+ _propagate: function(n, event) {
+ $.ui.plugin.call(this, n, [event, this.ui()]);
+ (n != "resize" && this._trigger(n, event, this.ui()));
+ },
+
+ plugins: {},
+
+ ui: function() {
+ return {
+ originalElement: this.originalElement,
+ element: this.element,
+ helper: this.helper,
+ position: this.position,
+ size: this.size,
+ originalSize: this.originalSize,
+ originalPosition: this.originalPosition
+ };
+ }
+
+});
+
+$.extend($.ui.resizable, {
+ version: "1.8.10"
+});
+
+/*
+ * Resizable Extensions
+ */
+
+$.ui.plugin.add("resizable", "alsoResize", {
+
+ start: function (event, ui) {
+ var self = $(this).data("resizable"), o = self.options;
+
+ var _store = function (exp) {
+ $(exp).each(function() {
+ var el = $(this);
+ el.data("resizable-alsoresize", {
+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
+ left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10),
+ position: el.css('position') // to reset Opera on stop()
+ });
+ });
+ };
+
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
+ }else{
+ _store(o.alsoResize);
+ }
+ },
+
+ resize: function (event, ui) {
+ var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
+
+ var delta = {
+ height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
+ top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
+ },
+
+ _alsoResize = function (exp, c) {
+ $(exp).each(function() {
+ var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
+
+ $.each(css, function (i, prop) {
+ var sum = (start[prop]||0) + (delta[prop]||0);
+ if (sum && sum >= 0)
+ style[prop] = sum || null;
+ });
+
+ // Opera fixing relative position
+ if ($.browser.opera && /relative/.test(el.css('position'))) {
+ self._revertToRelativePosition = true;
+ el.css({ position: 'absolute', top: 'auto', left: 'auto' });
+ }
+
+ el.css(style);
+ });
+ };
+
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
+ }else{
+ _alsoResize(o.alsoResize);
+ }
+ },
+
+ stop: function (event, ui) {
+ var self = $(this).data("resizable"), o = self.options;
+
+ var _reset = function (exp) {
+ $(exp).each(function() {
+ var el = $(this);
+ // reset position for Opera - no need to verify it was changed
+ el.css({ position: el.data("resizable-alsoresize").position });
+ });
+ };
+
+ if (self._revertToRelativePosition) {
+ self._revertToRelativePosition = false;
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
+ $.each(o.alsoResize, function (exp) { _reset(exp); });
+ }else{
+ _reset(o.alsoResize);
+ }
+ }
+
+ $(this).removeData("resizable-alsoresize");
+ }
+});
+
+$.ui.plugin.add("resizable", "animate", {
+
+ stop: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options;
+
+ var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+ soffsetw = ista ? 0 : self.sizeDiff.width;
+
+ var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+ self.element.animate(
+ $.extend(style, top && left ? { top: top, left: left } : {}), {
+ duration: o.animateDuration,
+ easing: o.animateEasing,
+ step: function() {
+
+ var data = {
+ width: parseInt(self.element.css('width'), 10),
+ height: parseInt(self.element.css('height'), 10),
+ top: parseInt(self.element.css('top'), 10),
+ left: parseInt(self.element.css('left'), 10)
+ };
+
+ if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
+
+ // propagating resize, and updating values for each animation step
+ self._updateCache(data);
+ self._propagate("resize", event);
+
+ }
+ }
+ );
+ }
+
+});
+
+$.ui.plugin.add("resizable", "containment", {
+
+ start: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options, el = self.element;
+ var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
+ if (!ce) return;
+
+ self.containerElement = $(ce);
+
+ if (/document/.test(oc) || oc == document) {
+ self.containerOffset = { left: 0, top: 0 };
+ self.containerPosition = { left: 0, top: 0 };
+
+ self.parentData = {
+ element: $(document), left: 0, top: 0,
+ width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
+ };
+ }
+
+ // i'm a node, so compute top, left, right, bottom
+ else {
+ var element = $(ce), p = [];
+ $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
+
+ self.containerOffset = element.offset();
+ self.containerPosition = element.position();
+ self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
+
+ var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
+
+ self.parentData = {
+ element: ce, left: co.left, top: co.top, width: width, height: height
+ };
+ }
+ },
+
+ resize: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options,
+ ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
+ pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
+
+ if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
+
+ if (cp.left < (self._helper ? co.left : 0)) {
+ self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
+ if (pRatio) self.size.height = self.size.width / o.aspectRatio;
+ self.position.left = o.helper ? co.left : 0;
+ }
+
+ if (cp.top < (self._helper ? co.top : 0)) {
+ self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
+ if (pRatio) self.size.width = self.size.height * o.aspectRatio;
+ self.position.top = self._helper ? co.top : 0;
+ }
+
+ self.offset.left = self.parentData.left+self.position.left;
+ self.offset.top = self.parentData.top+self.position.top;
+
+ var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
+ hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
+
+ var isParent = self.containerElement.get(0) == self.element.parent().get(0),
+ isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
+
+ if(isParent && isOffsetRelative) woset -= self.parentData.left;
+
+ if (woset + self.size.width >= self.parentData.width) {
+ self.size.width = self.parentData.width - woset;
+ if (pRatio) self.size.height = self.size.width / self.aspectRatio;
+ }
+
+ if (hoset + self.size.height >= self.parentData.height) {
+ self.size.height = self.parentData.height - hoset;
+ if (pRatio) self.size.width = self.size.height * self.aspectRatio;
+ }
+ },
+
+ stop: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options, cp = self.position,
+ co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
+
+ var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
+
+ if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+ if (self._helper && !o.animate && (/static/).test(ce.css('position')))
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+ }
+});
+
+$.ui.plugin.add("resizable", "ghost", {
+
+ start: function(event, ui) {
+
+ var self = $(this).data("resizable"), o = self.options, cs = self.size;
+
+ self.ghost = self.originalElement.clone();
+ self.ghost
+ .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
+ .addClass('ui-resizable-ghost')
+ .addClass(typeof o.ghost == 'string' ? o.ghost : '');
+
+ self.ghost.appendTo(self.helper);
+
+ },
+
+ resize: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options;
+ if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
+ },
+
+ stop: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options;
+ if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
+ }
+
+});
+
+$.ui.plugin.add("resizable", "grid", {
+
+ resize: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
+ o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
+ var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
+
+ if (/^(se|s|e)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ }
+ else if (/^(ne)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.top = op.top - oy;
+ }
+ else if (/^(sw)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.left = op.left - ox;
+ }
+ else {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.top = op.top - oy;
+ self.position.left = op.left - ox;
+ }
+ }
+
+});
+
+var num = function(v) {
+ return parseInt(v, 10) || 0;
+};
+
+var isNumber = function(value) {
+ return !isNaN(parseInt(value, 10));
+};
+
+})(jQuery);
+/*
+ * jQuery UI Selectable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.selectable", $.ui.mouse, {
+ options: {
+ appendTo: 'body',
+ autoRefresh: true,
+ distance: 0,
+ filter: '*',
+ tolerance: 'touch'
+ },
+ _create: function() {
+ var self = this;
+
+ this.element.addClass("ui-selectable");
+
+ this.dragged = false;
+
+ // cache selectee children based on filter
+ var selectees;
+ this.refresh = function() {
+ selectees = $(self.options.filter, self.element[0]);
+ selectees.each(function() {
+ var $this = $(this);
+ var pos = $this.offset();
+ $.data(this, "selectable-item", {
+ element: this,
+ $element: $this,
+ left: pos.left,
+ top: pos.top,
+ right: pos.left + $this.outerWidth(),
+ bottom: pos.top + $this.outerHeight(),
+ startselected: false,
+ selected: $this.hasClass('ui-selected'),
+ selecting: $this.hasClass('ui-selecting'),
+ unselecting: $this.hasClass('ui-unselecting')
+ });
+ });
+ };
+ this.refresh();
+
+ this.selectees = selectees.addClass("ui-selectee");
+
+ this._mouseInit();
+
+ this.helper = $("<div class='ui-selectable-helper'></div>");
+ },
+
+ destroy: function() {
+ this.selectees
+ .removeClass("ui-selectee")
+ .removeData("selectable-item");
+ this.element
+ .removeClass("ui-selectable ui-selectable-disabled")
+ .removeData("selectable")
+ .unbind(".selectable");
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseStart: function(event) {
+ var self = this;
+
+ this.opos = [event.pageX, event.pageY];
+
+ if (this.options.disabled)
+ return;
+
+ var options = this.options;
+
+ this.selectees = $(options.filter, this.element[0]);
+
+ this._trigger("start", event);
+
+ $(options.appendTo).append(this.helper);
+ // position helper (lasso)
+ this.helper.css({
+ "left": event.clientX,
+ "top": event.clientY,
+ "width": 0,
+ "height": 0
+ });
+
+ if (options.autoRefresh) {
+ this.refresh();
+ }
+
+ this.selectees.filter('.ui-selected').each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.startselected = true;
+ if (!event.metaKey) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ });
+
+ $(event.target).parents().andSelf().each(function() {
+ var selectee = $.data(this, "selectable-item");
+ if (selectee) {
+ var doSelect = !event.metaKey || !selectee.$element.hasClass('ui-selected');
+ selectee.$element
+ .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
+ .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
+ selectee.unselecting = !doSelect;
+ selectee.selecting = doSelect;
+ selectee.selected = doSelect;
+ // selectable (UN)SELECTING callback
+ if (doSelect) {
+ self._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ } else {
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ return false;
+ }
+ });
+
+ },
+
+ _mouseDrag: function(event) {
+ var self = this;
+ this.dragged = true;
+
+ if (this.options.disabled)
+ return;
+
+ var options = this.options;
+
+ var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
+ if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
+ if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
+ this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
+
+ this.selectees.each(function() {
+ var selectee = $.data(this, "selectable-item");
+ //prevent helper from being selected if appendTo: selectable
+ if (!selectee || selectee.element == self.element[0])
+ return;
+ var hit = false;
+ if (options.tolerance == 'touch') {
+ hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
+ } else if (options.tolerance == 'fit') {
+ hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
+ }
+
+ if (hit) {
+ // SELECT
+ if (selectee.selected) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+ }
+ if (selectee.unselecting) {
+ selectee.$element.removeClass('ui-unselecting');
+ selectee.unselecting = false;
+ }
+ if (!selectee.selecting) {
+ selectee.$element.addClass('ui-selecting');
+ selectee.selecting = true;
+ // selectable SELECTING callback
+ self._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ }
+ } else {
+ // UNSELECT
+ if (selectee.selecting) {
+ if (event.metaKey && selectee.startselected) {
+ selectee.$element.removeClass('ui-selecting');
+ selectee.selecting = false;
+ selectee.$element.addClass('ui-selected');
+ selectee.selected = true;
+ } else {
+ selectee.$element.removeClass('ui-selecting');
+ selectee.selecting = false;
+ if (selectee.startselected) {
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ }
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ if (selectee.selected) {
+ if (!event.metaKey && !selectee.startselected) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ }
+ });
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+ var self = this;
+
+ this.dragged = false;
+
+ var options = this.options;
+
+ $('.ui-unselecting', this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass('ui-unselecting');
+ selectee.unselecting = false;
+ selectee.startselected = false;
+ self._trigger("unselected", event, {
+ unselected: selectee.element
+ });
+ });
+ $('.ui-selecting', this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
+ selectee.selecting = false;
+ selectee.selected = true;
+ selectee.startselected = true;
+ self._trigger("selected", event, {
+ selected: selectee.element
+ });
+ });
+ this._trigger("stop", event);
+
+ this.helper.remove();
+
+ return false;
+ }
+
+});
+
+$.extend($.ui.selectable, {
+ version: "1.8.10"
+});
+
+})(jQuery);
+/*
+ * jQuery UI Sortable 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.sortable", $.ui.mouse, {
+ widgetEventPrefix: "sort",
+ options: {
+ appendTo: "parent",
+ axis: false,
+ connectWith: false,
+ containment: false,
+ cursor: 'auto',
+ cursorAt: false,
+ dropOnEmpty: true,
+ forcePlaceholderSize: false,
+ forceHelperSize: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ items: '> *',
+ opacity: false,
+ placeholder: false,
+ revert: false,
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ scope: "default",
+ tolerance: "intersect",
+ zIndex: 1000
+ },
+ _create: function() {
+
+ var o = this.options;
+ this.containerCache = {};
+ this.element.addClass("ui-sortable");
+
+ //Get the items
+ this.refresh();
+
+ //Let's determine if the items are floating
+ this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
+
+ //Let's determine the parent's offset
+ this.offset = this.element.offset();
+
+ //Initialize mouse events for interaction
+ this._mouseInit();
+
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass("ui-sortable ui-sortable-disabled")
+ .removeData("sortable")
+ .unbind(".sortable");
+ this._mouseDestroy();
+
+ for ( var i = this.items.length - 1; i >= 0; i-- )
+ this.items[i].item.removeData("sortable-item");
+
+ return this;
+ },
+
+ _setOption: function(key, value){
+ if ( key === "disabled" ) {
+ this.options[ key ] = value;
+
+ this.widget()
+ [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
+ } else {
+ // Don't call widget base _setOption for disable as it adds ui-state-disabled class
+ $.Widget.prototype._setOption.apply(this, arguments);
+ }
+ },
+
+ _mouseCapture: function(event, overrideHandle) {
+
+ if (this.reverting) {
+ return false;
+ }
+
+ if(this.options.disabled || this.options.type == 'static') return false;
+
+ //We have to refresh the items data once first
+ this._refreshItems(event);
+
+ //Find out if the clicked node (or one of its parents) is a actual item in this.items
+ var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
+ if($.data(this, 'sortable-item') == self) {
+ currentItem = $(this);
+ return false;
+ }
+ });
+ if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
+
+ if(!currentItem) return false;
+ if(this.options.handle && !overrideHandle) {
+ var validHandle = false;
+
+ $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
+ if(!validHandle) return false;
+ }
+
+ this.currentItem = currentItem;
+ this._removeCurrentsFromItems();
+ return true;
+
+ },
+
+ _mouseStart: function(event, overrideHandle, noActivation) {
+
+ var o = this.options, self = this;
+ this.currentContainer = this;
+
+ //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
+ this.refreshPositions();
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Get the next scrolling parent
+ this.scrollParent = this.helper.scrollParent();
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.currentItem.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ // Only after we got the offset, we can change the helper's position to absolute
+ // TODO: Still need to figure out a way to make relative sorting possible
+ this.helper.css("position", "absolute");
+ this.cssPosition = this.helper.css("position");
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ //Generate the original position
+ this.originalPosition = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Cache the former DOM position
+ this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
+
+ //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
+ if(this.helper[0] != this.currentItem[0]) {
+ this.currentItem.hide();
+ }
+
+ //Create the placeholder
+ this._createPlaceholder();
+
+ //Set a containment if given in the options
+ if(o.containment)
+ this._setContainment();
+
+ if(o.cursor) { // cursor option
+ if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
+ $('body').css("cursor", o.cursor);
+ }
+
+ if(o.opacity) { // opacity option
+ if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
+ this.helper.css("opacity", o.opacity);
+ }
+
+ if(o.zIndex) { // zIndex option
+ if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
+ this.helper.css("zIndex", o.zIndex);
+ }
+
+ //Prepare scrolling
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
+ this.overflowOffset = this.scrollParent.offset();
+
+ //Call callbacks
+ this._trigger("start", event, this._uiHash());
+
+ //Recache the helper size
+ if(!this._preserveHelperProportions)
+ this._cacheHelperProportions();
+
+
+ //Post 'activate' events to possible containers
+ if(!noActivation) {
+ for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
+ }
+
+ //Prepare possible droppables
+ if($.ui.ddmanager)
+ $.ui.ddmanager.current = this;
+
+ if ($.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+
+ this.dragging = true;
+
+ this.helper.addClass("ui-sortable-helper");
+ this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+ return true;
+
+ },
+
+ _mouseDrag: function(event) {
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ if (!this.lastPositionAbs) {
+ this.lastPositionAbs = this.positionAbs;
+ }
+
+ //Do scrolling
+ if(this.options.scroll) {
+ var o = this.options, scrolled = false;
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
+
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
+
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
+
+ } else {
+
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+ }
+
+ //Regenerate the absolute position used for position checks
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Set the helper position
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+
+ //Rearrange
+ for (var i = this.items.length - 1; i >= 0; i--) {
+
+ //Cache variables and intersection, continue if no intersection
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
+ if (!intersection) continue;
+
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
+ && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
+ && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
+ ) {
+
+ this.direction = intersection == 1 ? "down" : "up";
+
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
+ this._rearrange(event, item);
+ } else {
+ break;
+ }
+
+ this._trigger("change", event, this._uiHash());
+ break;
+ }
+ }
+
+ //Post events to containers
+ this._contactContainers(event);
+
+ //Interconnect with droppables
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+ //Call callbacks
+ this._trigger('sort', event, this._uiHash());
+
+ this.lastPositionAbs = this.positionAbs;
+ return false;
+
+ },
+
+ _mouseStop: function(event, noPropagation) {
+
+ if(!event) return;
+
+ //If we are using droppables, inform the manager about the drop
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
+ $.ui.ddmanager.drop(this, event);
+
+ if(this.options.revert) {
+ var self = this;
+ var cur = self.placeholder.offset();
+
+ self.reverting = true;
+
+ $(this.helper).animate({
+ left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
+ top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
+ }, parseInt(this.options.revert, 10) || 500, function() {
+ self._clear(event);
+ });
+ } else {
+ this._clear(event, noPropagation);
+ }
+
+ return false;
+
+ },
+
+ cancel: function() {
+
+ var self = this;
+
+ if(this.dragging) {
+
+ this._mouseUp({ target: null });
+
+ if(this.options.helper == "original")
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ else
+ this.currentItem.show();
+
+ //Post deactivating events to containers
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ this.containers[i]._trigger("deactivate", null, self._uiHash(this));
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", null, self._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ if (this.placeholder) {
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+ if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
+
+ $.extend(this, {
+ helper: null,
+ dragging: false,
+ reverting: false,
+ _noFinalSort: null
+ });
+
+ if(this.domPosition.prev) {
+ $(this.domPosition.prev).after(this.currentItem);
+ } else {
+ $(this.domPosition.parent).prepend(this.currentItem);
+ }
+ }
+
+ return this;
+
+ },
+
+ serialize: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected);
+ var str = []; o = o || {};
+
+ $(items).each(function() {
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
+ if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
+ });
+
+ if(!str.length && o.key) {
+ str.push(o.key + '=');
+ }
+
+ return str.join('&');
+
+ },
+
+ toArray: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected);
+ var ret = []; o = o || {};
+
+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
+ return ret;
+
+ },
+
+ /* Be careful with the following core functions */
+ _intersectsWith: function(item) {
+
+ var x1 = this.positionAbs.left,
+ x2 = x1 + this.helperProportions.width,
+ y1 = this.positionAbs.top,
+ y2 = y1 + this.helperProportions.height;
+
+ var l = item.left,
+ r = l + item.width,
+ t = item.top,
+ b = t + item.height;
+
+ var dyClick = this.offset.click.top,
+ dxClick = this.offset.click.left;
+
+ var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
+
+ if( this.options.tolerance == "pointer"
+ || this.options.forcePointerForContainers
+ || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
+ ) {
+ return isOverElement;
+ } else {
+
+ return (l < x1 + (this.helperProportions.width / 2) // Right Half
+ && x2 - (this.helperProportions.width / 2) < r // Left Half
+ && t < y1 + (this.helperProportions.height / 2) // Bottom Half
+ && y2 - (this.helperProportions.height / 2) < b ); // Top Half
+
+ }
+ },
+
+ _intersectsWithPointer: function(item) {
+
+ var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
+ isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
+ isOverElement = isOverElementHeight && isOverElementWidth,
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (!isOverElement)
+ return false;
+
+ return this.floating ?
+ ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
+ : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
+
+ },
+
+ _intersectsWithSides: function(item) {
+
+ var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
+ isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (this.floating && horizontalDirection) {
+ return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
+ } else {
+ return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
+ }
+
+ },
+
+ _getDragVerticalDirection: function() {
+ var delta = this.positionAbs.top - this.lastPositionAbs.top;
+ return delta != 0 && (delta > 0 ? "down" : "up");
+ },
+
+ _getDragHorizontalDirection: function() {
+ var delta = this.positionAbs.left - this.lastPositionAbs.left;
+ return delta != 0 && (delta > 0 ? "right" : "left");
+ },
+
+ refresh: function(event) {
+ this._refreshItems(event);
+ this.refreshPositions();
+ return this;
+ },
+
+ _connectWith: function() {
+ var options = this.options;
+ return options.connectWith.constructor == String
+ ? [options.connectWith]
+ : options.connectWith;
+ },
+
+ _getItemsAsjQuery: function(connected) {
+
+ var self = this;
+ var items = [];
+ var queries = [];
+ var connectWith = this._connectWith();
+
+ if(connectWith && connected) {
+ for (var i = connectWith.length - 1; i >= 0; i--){
+ var cur = $(connectWith[i]);
+ for (var j = cur.length - 1; j >= 0; j--){
+ var inst = $.data(cur[j], 'sortable');
+ if(inst && inst != this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
+ }
+ };
+ };
+ }
+
+ queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
+
+ for (var i = queries.length - 1; i >= 0; i--){
+ queries[i][0].each(function() {
+ items.push(this);
+ });
+ };
+
+ return $(items);
+
+ },
+
+ _removeCurrentsFromItems: function() {
+
+ var list = this.currentItem.find(":data(sortable-item)");
+
+ for (var i=0; i < this.items.length; i++) {
+
+ for (var j=0; j < list.length; j++) {
+ if(list[j] == this.items[i].item[0])
+ this.items.splice(i,1);
+ };
+
+ };
+
+ },
+
+ _refreshItems: function(event) {
+
+ this.items = [];
+ this.containers = [this];
+ var items = this.items;
+ var self = this;
+ var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
+ var connectWith = this._connectWith();
+
+ if(connectWith) {
+ for (var i = connectWith.length - 1; i >= 0; i--){
+ var cur = $(connectWith[i]);
+ for (var j = cur.length - 1; j >= 0; j--){
+ var inst = $.data(cur[j], 'sortable');
+ if(inst && inst != this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
+ this.containers.push(inst);
+ }
+ };
+ };
+ }
+
+ for (var i = queries.length - 1; i >= 0; i--) {
+ var targetData = queries[i][1];
+ var _queries = queries[i][0];
+
+ for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
+ var item = $(_queries[j]);
+
+ item.data('sortable-item', targetData); // Data for target checking (mouse manager)
+
+ items.push({
+ item: item,
+ instance: targetData,
+ width: 0, height: 0,
+ left: 0, top: 0
+ });
+ };
+ };
+
+ },
+
+ refreshPositions: function(fast) {
+
+ //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
+ if(this.offsetParent && this.helper) {
+ this.offset.parent = this._getParentOffset();
+ }
+
+ for (var i = this.items.length - 1; i >= 0; i--){
+ var item = this.items[i];
+
+ var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
+
+ if (!fast) {
+ item.width = t.outerWidth();
+ item.height = t.outerHeight();
+ }
+
+ var p = t.offset();
+ item.left = p.left;
+ item.top = p.top;
+ };
+
+ if(this.options.custom && this.options.custom.refreshContainers) {
+ this.options.custom.refreshContainers.call(this);
+ } else {
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ var p = this.containers[i].element.offset();
+ this.containers[i].containerCache.left = p.left;
+ this.containers[i].containerCache.top = p.top;
+ this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
+ this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
+ };
+ }
+
+ return this;
+ },
+
+ _createPlaceholder: function(that) {
+
+ var self = that || this, o = self.options;
+
+ if(!o.placeholder || o.placeholder.constructor == String) {
+ var className = o.placeholder;
+ o.placeholder = {
+ element: function() {
+
+ var el = $(document.createElement(self.currentItem[0].nodeName))
+ .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
+ .removeClass("ui-sortable-helper")[0];
+
+ if(!className)
+ el.style.visibility = "hidden";
+
+ return el;
+ },
+ update: function(container, p) {
+
+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
+ if(className && !o.forcePlaceholderSize) return;
+
+ //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
+ if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
+ if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
+ }
+ };
+ }
+
+ //Create the placeholder
+ self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
+
+ //Append it after the actual current item
+ self.currentItem.after(self.placeholder);
+
+ //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
+ o.placeholder.update(self, self.placeholder);
+
+ },
+
+ _contactContainers: function(event) {
+
+ // get innermost container that intersects with item
+ var innermostContainer = null, innermostIndex = null;
+
+
+ for (var i = this.containers.length - 1; i >= 0; i--){
+
+ // never consider a container that's located within the item itself
+ if($.ui.contains(this.currentItem[0], this.containers[i].element[0]))
+ continue;
+
+ if(this._intersectsWith(this.containers[i].containerCache)) {
+
+ // if we've already found a container and it's more "inner" than this, then continue
+ if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0]))
+ continue;
+
+ innermostContainer = this.containers[i];
+ innermostIndex = i;
+
+ } else {
+ // container doesn't intersect. trigger "out" event if necessary
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", event, this._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ // if no intersecting containers found, return
+ if(!innermostContainer) return;
+
+ // move the item into the container if it's not there already
+ if(this.containers.length === 1) {
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ } else if(this.currentContainer != this.containers[innermostIndex]) {
+
+ //When entering a new container, we will find the item with the least distance and append our item near it
+ var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
+ for (var j = this.items.length - 1; j >= 0; j--) {
+ if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
+ var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top'];
+ if(Math.abs(cur - base) < dist) {
+ dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
+ }
+ }
+
+ if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
+ return;
+
+ this.currentContainer = this.containers[innermostIndex];
+ itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
+ this._trigger("change", event, this._uiHash());
+ this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
+
+ //Update the placeholder
+ this.options.placeholder.update(this.currentContainer, this.placeholder);
+
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ }
+
+
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options;
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
+
+ if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
+ $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
+
+ if(helper[0] == this.currentItem[0])
+ this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
+
+ if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
+ if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj == 'string') {
+ obj = obj.split(' ');
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ('left' in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ('right' in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ('top' in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ('bottom' in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+
+ //Get the offsetParent and cache its position
+ this.offsetParent = this.helper.offsetParent();
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+ po = { top: 0, left: 0 };
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition == "relative") {
+ var p = this.currentItem.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
+ top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var o = this.options;
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
+ 0 - this.offset.relative.left - this.offset.parent.left,
+ 0 - this.offset.relative.top - this.offset.parent.top,
+ $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+ ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+ ];
+
+ if(!(/^(document|window|parent)$/).test(o.containment)) {
+ var ce = $(o.containment)[0];
+ var co = $(o.containment).offset();
+ var over = ($(ce).css("overflow") != 'hidden');
+
+ this.containment = [
+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+ ];
+ }
+
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) pos = this.position;
+ var mod = d == "absolute" ? 1 : -1;
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ return {
+ top: (
+ pos.top // The absolute mouse position
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+ ),
+ left: (
+ pos.left // The absolute mouse position
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ // This is another very weird special case that only happens for relative elements:
+ // 1. If the css position is relative
+ // 2. and the scroll parent is the document or similar to the offset parent
+ // we have to refresh the relative offset during the scroll so there are no jumps
+ if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
+ this.offset.relative = this._getRelativeOffset();
+ }
+
+ var pageX = event.pageX;
+ var pageY = event.pageY;
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+ if(this.containment) {
+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+ }
+
+ if(o.grid) {
+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY // The absolute mouse position
+ - this.offset.click.top // Click offset (relative to the element)
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+ ),
+ left: (
+ pageX // The absolute mouse position
+ - this.offset.click.left // Click offset (relative to the element)
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+ )
+ };
+
+ },
+
+ _rearrange: function(event, i, a, hardRefresh) {
+
+ a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
+
+ //Various things done here to improve the performance:
+ // 1. we create a setTimeout, that calls refreshPositions
+ // 2. on the instance, we have a counter variable, that get's higher after every append
+ // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
+ // 4. this lets only the last addition to the timeout stack through
+ this.counter = this.counter ? ++this.counter : 1;
+ var self = this, counter = this.counter;
+
+ window.setTimeout(function() {
+ if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
+ },0);
+
+ },
+
+ _clear: function(event, noPropagation) {
+
+ this.reverting = false;
+ // We delay all events that have to be triggered to after the point where the placeholder has been removed and
+ // everything else normalized again
+ var delayedTriggers = [], self = this;
+
+ // We first have to update the dom position of the actual currentItem
+ // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
+ if(!this._noFinalSort && this.currentItem[0].parentNode) this.placeholder.before(this.currentItem);
+ this._noFinalSort = null;
+
+ if(this.helper[0] == this.currentItem[0]) {
+ for(var i in this._storedCSS) {
+ if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
+ }
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ } else {
+ this.currentItem.show();
+ }
+
+ if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
+ if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
+ if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
+ if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) {
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ }
+ };
+ };
+
+ //Post events to containers
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ if(this.containers[i].containerCache.over) {
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ //Do what was originally in plugins
+ if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
+ if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
+ if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
+
+ this.dragging = false;
+ if(this.cancelHelperRemoval) {
+ if(!noPropagation) {
+ this._trigger("beforeStop", event, this._uiHash());
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+ return false;
+ }
+
+ if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
+
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+
+ if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
+
+ if(!noPropagation) {
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+
+ this.fromOutside = false;
+ return true;
+
+ },
+
+ _trigger: function() {
+ if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
+ this.cancel();
+ }
+ },
+
+ _uiHash: function(inst) {
+ var self = inst || this;
+ return {
+ helper: self.helper,
+ placeholder: self.placeholder || $([]),
+ position: self.position,
+ originalPosition: self.originalPosition,
+ offset: self.positionAbs,
+ item: self.currentItem,
+ sender: inst ? inst.element : null
+ };
+ }
+
+});
+
+$.extend($.ui.sortable, {
+ version: "1.8.10"
+});
+
+})(jQuery);
+/*
+ * jQuery UI Accordion 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget( "ui.accordion", {
+ options: {
+ active: 0,
+ animated: "slide",
+ autoHeight: true,
+ clearStyle: false,
+ collapsible: false,
+ event: "click",
+ fillSpace: false,
+ header: "> li > :first-child,> :not(li):even",
+ icons: {
+ header: "ui-icon-triangle-1-e",
+ headerSelected: "ui-icon-triangle-1-s"
+ },
+ navigation: false,
+ navigationFilter: function() {
+ return this.href.toLowerCase() === location.href.toLowerCase();
+ }
+ },
+
+ _create: function() {
+ var self = this,
+ options = self.options;
+
+ self.running = 0;
+
+ self.element
+ .addClass( "ui-accordion ui-widget ui-helper-reset" )
+ // in lack of child-selectors in CSS
+ // we need to mark top-LIs in a UL-accordion for some IE-fix
+ .children( "li" )
+ .addClass( "ui-accordion-li-fix" );
+
+ self.headers = self.element.find( options.header )
+ .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
+ .bind( "mouseenter.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-hover" );
+ })
+ .bind( "mouseleave.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( "ui-state-hover" );
+ })
+ .bind( "focus.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-focus" );
+ })
+ .bind( "blur.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( "ui-state-focus" );
+ });
+
+ self.headers.next()
+ .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );
+
+ if ( options.navigation ) {
+ var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
+ if ( current.length ) {
+ var header = current.closest( ".ui-accordion-header" );
+ if ( header.length ) {
+ // anchor within header
+ self.active = header;
+ } else {
+ // anchor within content
+ self.active = current.closest( ".ui-accordion-content" ).prev();
+ }
+ }
+ }
+
+ self.active = self._findActive( self.active || options.active )
+ .addClass( "ui-state-default ui-state-active" )
+ .toggleClass( "ui-corner-all" )
+ .toggleClass( "ui-corner-top" );
+ self.active.next().addClass( "ui-accordion-content-active" );
+
+ self._createIcons();
+ self.resize();
+
+ // ARIA
+ self.element.attr( "role", "tablist" );
+
+ self.headers
+ .attr( "role", "tab" )
+ .bind( "keydown.accordion", function( event ) {
+ return self._keydown( event );
+ })
+ .next()
+ .attr( "role", "tabpanel" );
+
+ self.headers
+ .not( self.active || "" )
+ .attr({
+ "aria-expanded": "false",
+ tabIndex: -1
+ })
+ .next()
+ .hide();
+
+ // make sure at least one header is in the tab order
+ if ( !self.active.length ) {
+ self.headers.eq( 0 ).attr( "tabIndex", 0 );
+ } else {
+ self.active
+ .attr({
+ "aria-expanded": "true",
+ tabIndex: 0
+ });
+ }
+
+ // only need links in tab order for Safari
+ if ( !$.browser.safari ) {
+ self.headers.find( "a" ).attr( "tabIndex", -1 );
+ }
+
+ if ( options.event ) {
+ self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
+ self._clickHandler.call( self, event, this );
+ event.preventDefault();
+ });
+ }
+ },
+
+ _createIcons: function() {
+ var options = this.options;
+ if ( options.icons ) {
+ $( "<span></span>" )
+ .addClass( "ui-icon " + options.icons.header )
+ .prependTo( this.headers );
+ this.active.children( ".ui-icon" )
+ .toggleClass(options.icons.header)
+ .toggleClass(options.icons.headerSelected);
+ this.element.addClass( "ui-accordion-icons" );
+ }
+ },
+
+ _destroyIcons: function() {
+ this.headers.children( ".ui-icon" ).remove();
+ this.element.removeClass( "ui-accordion-icons" );
+ },
+
+ destroy: function() {
+ var options = this.options;
+
+ this.element
+ .removeClass( "ui-accordion ui-widget ui-helper-reset" )
+ .removeAttr( "role" );
+
+ this.headers
+ .unbind( ".accordion" )
+ .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-expanded" )
+ .removeAttr( "tabIndex" );
+
+ this.headers.find( "a" ).removeAttr( "tabIndex" );
+ this._destroyIcons();
+ var contents = this.headers.next()
+ .css( "display", "" )
+ .removeAttr( "role" )
+ .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" );
+ if ( options.autoHeight || options.fillHeight ) {
+ contents.css( "height", "" );
+ }
+
+ return $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+
+ if ( key == "active" ) {
+ this.activate( value );
+ }
+ if ( key == "icons" ) {
+ this._destroyIcons();
+ if ( value ) {
+ this._createIcons();
+ }
+ }
+ // #5332 - opacity doesn't cascade to positioned elements in IE
+ // so we need to add the disabled class to the headers and panels
+ if ( key == "disabled" ) {
+ this.headers.add(this.headers.next())
+ [ value ? "addClass" : "removeClass" ](
+ "ui-accordion-disabled ui-state-disabled" );
+ }
+ },
+
+ _keydown: function( event ) {
+ if ( this.options.disabled || event.altKey || event.ctrlKey ) {
+ return;
+ }
+
+ var keyCode = $.ui.keyCode,
+ length = this.headers.length,
+ currentIndex = this.headers.index( event.target ),
+ toFocus = false;
+
+ switch ( event.keyCode ) {
+ case keyCode.RIGHT:
+ case keyCode.DOWN:
+ toFocus = this.headers[ ( currentIndex + 1 ) % length ];
+ break;
+ case keyCode.LEFT:
+ case keyCode.UP:
+ toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
+ break;
+ case keyCode.SPACE:
+ case keyCode.ENTER:
+ this._clickHandler( { target: event.target }, event.target );
+ event.preventDefault();
+ }
+
+ if ( toFocus ) {
+ $( event.target ).attr( "tabIndex", -1 );
+ $( toFocus ).attr( "tabIndex", 0 );
+ toFocus.focus();
+ return false;
+ }
+
+ return true;
+ },
+
+ resize: function() {
+ var options = this.options,
+ maxHeight;
+
+ if ( options.fillSpace ) {
+ if ( $.browser.msie ) {
+ var defOverflow = this.element.parent().css( "overflow" );
+ this.element.parent().css( "overflow", "hidden");
+ }
+ maxHeight = this.element.parent().height();
+ if ($.browser.msie) {
+ this.element.parent().css( "overflow", defOverflow );
+ }
+
+ this.headers.each(function() {
+ maxHeight -= $( this ).outerHeight( true );
+ });
+
+ this.headers.next()
+ .each(function() {
+ $( this ).height( Math.max( 0, maxHeight -
+ $( this ).innerHeight() + $( this ).height() ) );
+ })
+ .css( "overflow", "auto" );
+ } else if ( options.autoHeight ) {
+ maxHeight = 0;
+ this.headers.next()
+ .each(function() {
+ maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
+ })
+ .height( maxHeight );
+ }
+
+ return this;
+ },
+
+ activate: function( index ) {
+ // TODO this gets called on init, changing the option without an explicit call for that
+ this.options.active = index;
+ // call clickHandler with custom event
+ var active = this._findActive( index )[ 0 ];
+ this._clickHandler( { target: active }, active );
+
+ return this;
+ },
+
+ _findActive: function( selector ) {
+ return selector
+ ? typeof selector === "number"
+ ? this.headers.filter( ":eq(" + selector + ")" )
+ : this.headers.not( this.headers.not( selector ) )
+ : selector === false
+ ? $( [] )
+ : this.headers.filter( ":eq(0)" );
+ },
+
+ // TODO isn't event.target enough? why the separate target argument?
+ _clickHandler: function( event, target ) {
+ var options = this.options;
+ if ( options.disabled ) {
+ return;
+ }
+
+ // called only when using activate(false) to close all parts programmatically
+ if ( !event.target ) {
+ if ( !options.collapsible ) {
+ return;
+ }
+ this.active
+ .removeClass( "ui-state-active ui-corner-top" )
+ .addClass( "ui-state-default ui-corner-all" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.headerSelected )
+ .addClass( options.icons.header );
+ this.active.next().addClass( "ui-accordion-content-active" );
+ var toHide = this.active.next(),
+ data = {
+ options: options,
+ newHeader: $( [] ),
+ oldHeader: options.active,
+ newContent: $( [] ),
+ oldContent: toHide
+ },
+ toShow = ( this.active = $( [] ) );
+ this._toggle( toShow, toHide, data );
+ return;
+ }
+
+ // get the click target
+ var clicked = $( event.currentTarget || target ),
+ clickedIsActive = clicked[0] === this.active[0];
+
+ // TODO the option is changed, is that correct?
+ // TODO if it is correct, shouldn't that happen after determining that the click is valid?
+ options.active = options.collapsible && clickedIsActive ?
+ false :
+ this.headers.index( clicked );
+
+ // if animations are still active, or the active header is the target, ignore click
+ if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
+ return;
+ }
+
+ // find elements to show and hide
+ var active = this.active,
+ toShow = clicked.next(),
+ toHide = this.active.next(),
+ data = {
+ options: options,
+ newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
+ oldHeader: this.active,
+ newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
+ oldContent: toHide
+ },
+ down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
+
+ // when the call to ._toggle() comes after the class changes
+ // it causes a very odd bug in IE 8 (see #6720)
+ this.active = clickedIsActive ? $([]) : clicked;
+ this._toggle( toShow, toHide, data, clickedIsActive, down );
+
+ // switch classes
+ active
+ .removeClass( "ui-state-active ui-corner-top" )
+ .addClass( "ui-state-default ui-corner-all" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.headerSelected )
+ .addClass( options.icons.header );
+ if ( !clickedIsActive ) {
+ clicked
+ .removeClass( "ui-state-default ui-corner-all" )
+ .addClass( "ui-state-active ui-corner-top" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.header )
+ .addClass( options.icons.headerSelected );
+ clicked
+ .next()
+ .addClass( "ui-accordion-content-active" );
+ }
+
+ return;
+ },
+
+ _toggle: function( toShow, toHide, data, clickedIsActive, down ) {
+ var self = this,
+ options = self.options;
+
+ self.toShow = toShow;
+ self.toHide = toHide;
+ self.data = data;
+
+ var complete = function() {
+ if ( !self ) {
+ return;
+ }
+ return self._completed.apply( self, arguments );
+ };
+
+ // trigger changestart event
+ self._trigger( "changestart", null, self.data );
+
+ // count elements to animate
+ self.running = toHide.size() === 0 ? toShow.size() : toHide.size();
+
+ if ( options.animated ) {
+ var animOptions = {};
+
+ if ( options.collapsible && clickedIsActive ) {
+ animOptions = {
+ toShow: $( [] ),
+ toHide: toHide,
+ complete: complete,
+ down: down,
+ autoHeight: options.autoHeight || options.fillSpace
+ };
+ } else {
+ animOptions = {
+ toShow: toShow,
+ toHide: toHide,
+ complete: complete,
+ down: down,
+ autoHeight: options.autoHeight || options.fillSpace
+ };
+ }
+
+ if ( !options.proxied ) {
+ options.proxied = options.animated;
+ }
+
+ if ( !options.proxiedDuration ) {
+ options.proxiedDuration = options.duration;
+ }
+
+ options.animated = $.isFunction( options.proxied ) ?
+ options.proxied( animOptions ) :
+ options.proxied;
+
+ options.duration = $.isFunction( options.proxiedDuration ) ?
+ options.proxiedDuration( animOptions ) :
+ options.proxiedDuration;
+
+ var animations = $.ui.accordion.animations,
+ duration = options.duration,
+ easing = options.animated;
+
+ if ( easing && !animations[ easing ] && !$.easing[ easing ] ) {
+ easing = "slide";
+ }
+ if ( !animations[ easing ] ) {
+ animations[ easing ] = function( options ) {
+ this.slide( options, {
+ easing: easing,
+ duration: duration || 700
+ });
+ };
+ }
+
+ animations[ easing ]( animOptions );
+ } else {
+ if ( options.collapsible && clickedIsActive ) {
+ toShow.toggle();
+ } else {
+ toHide.hide();
+ toShow.show();
+ }
+
+ complete( true );
+ }
+
+ // TODO assert that the blur and focus triggers are really necessary, remove otherwise
+ toHide.prev()
+ .attr({
+ "aria-expanded": "false",
+ tabIndex: -1
+ })
+ .blur();
+ toShow.prev()
+ .attr({
+ "aria-expanded": "true",
+ tabIndex: 0
+ })
+ .focus();
+ },
+
+ _completed: function( cancel ) {
+ this.running = cancel ? 0 : --this.running;
+ if ( this.running ) {
+ return;
+ }
+
+ if ( this.options.clearStyle ) {
+ this.toShow.add( this.toHide ).css({
+ height: "",
+ overflow: ""
+ });
+ }
+
+ // other classes are removed before the animation; this one needs to stay until completed
+ this.toHide.removeClass( "ui-accordion-content-active" );
+ // Work around for rendering bug in IE (#5421)
+ if ( this.toHide.length ) {
+ this.toHide.parent()[0].className = this.toHide.parent()[0].className;
+ }
+
+ this._trigger( "change", null, this.data );
+ }
+});
+
+$.extend( $.ui.accordion, {
+ version: "1.8.10",
+ animations: {
+ slide: function( options, additions ) {
+ options = $.extend({
+ easing: "swing",
+ duration: 300
+ }, options, additions );
+ if ( !options.toHide.size() ) {
+ options.toShow.animate({
+ height: "show",
+ paddingTop: "show",
+ paddingBottom: "show"
+ }, options );
+ return;
+ }
+ if ( !options.toShow.size() ) {
+ options.toHide.animate({
+ height: "hide",
+ paddingTop: "hide",
+ paddingBottom: "hide"
+ }, options );
+ return;
+ }
+ var overflow = options.toShow.css( "overflow" ),
+ percentDone = 0,
+ showProps = {},
+ hideProps = {},
+ fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
+ originalWidth;
+ // fix width before calculating height of hidden element
+ var s = options.toShow;
+ originalWidth = s[0].style.width;
+ s.width( parseInt( s.parent().width(), 10 )
+ - parseInt( s.css( "paddingLeft" ), 10 )
+ - parseInt( s.css( "paddingRight" ), 10 )
+ - ( parseInt( s.css( "borderLeftWidth" ), 10 ) || 0 )
+ - ( parseInt( s.css( "borderRightWidth" ), 10) || 0 ) );
+
+ $.each( fxAttrs, function( i, prop ) {
+ hideProps[ prop ] = "hide";
+
+ var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ );
+ showProps[ prop ] = {
+ value: parts[ 1 ],
+ unit: parts[ 2 ] || "px"
+ };
+ });
+ options.toShow.css({ height: 0, overflow: "hidden" }).show();
+ options.toHide
+ .filter( ":hidden" )
+ .each( options.complete )
+ .end()
+ .filter( ":visible" )
+ .animate( hideProps, {
+ step: function( now, settings ) {
+ // only calculate the percent when animating height
+ // IE gets very inconsistent results when animating elements
+ // with small values, which is common for padding
+ if ( settings.prop == "height" ) {
+ percentDone = ( settings.end - settings.start === 0 ) ? 0 :
+ ( settings.now - settings.start ) / ( settings.end - settings.start );
+ }
+
+ options.toShow[ 0 ].style[ settings.prop ] =
+ ( percentDone * showProps[ settings.prop ].value )
+ + showProps[ settings.prop ].unit;
+ },
+ duration: options.duration,
+ easing: options.easing,
+ complete: function() {
+ if ( !options.autoHeight ) {
+ options.toShow.css( "height", "" );
+ }
+ options.toShow.css({
+ width: originalWidth,
+ overflow: overflow
+ });
+ options.complete();
+ }
+ });
+ },
+ bounceslide: function( options ) {
+ this.slide( options, {
+ easing: options.down ? "easeOutBounce" : "swing",
+ duration: options.down ? 1000 : 200
+ });
+ }
+ }
+});
+
+})( jQuery );
+/*
+ * jQuery UI Autocomplete 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function( $, undefined ) {
+
+// used to prevent race conditions with remote data sources
+var requestIndex = 0;
+
+$.widget( "ui.autocomplete", {
+ options: {
+ appendTo: "body",
+ delay: 300,
+ minLength: 1,
+ position: {
+ my: "left top",
+ at: "left bottom",
+ collision: "none"
+ },
+ source: null
+ },
+
+ pending: 0,
+
+ _create: function() {
+ var self = this,
+ doc = this.element[ 0 ].ownerDocument,
+ suppressKeyPress;
+
+ this.element
+ .addClass( "ui-autocomplete-input" )
+ .attr( "autocomplete", "off" )
+ // TODO verify these actually work as intended
+ .attr({
+ role: "textbox",
+ "aria-autocomplete": "list",
+ "aria-haspopup": "true"
+ })
+ .bind( "keydown.autocomplete", function( event ) {
+ if ( self.options.disabled || self.element.attr( "readonly" ) ) {
+ return;
+ }
+
+ suppressKeyPress = false;
+ var keyCode = $.ui.keyCode;
+ switch( event.keyCode ) {
+ case keyCode.PAGE_UP:
+ self._move( "previousPage", event );
+ break;
+ case keyCode.PAGE_DOWN:
+ self._move( "nextPage", event );
+ break;
+ case keyCode.UP:
+ self._move( "previous", event );
+ // prevent moving cursor to beginning of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.DOWN:
+ self._move( "next", event );
+ // prevent moving cursor to end of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.ENTER:
+ case keyCode.NUMPAD_ENTER:
+ // when menu is open and has focus
+ if ( self.menu.active ) {
+ // #6055 - Opera still allows the keypress to occur
+ // which causes forms to submit
+ suppressKeyPress = true;
+ event.preventDefault();
+ }
+ //passthrough - ENTER and TAB both select the current element
+ case keyCode.TAB:
+ if ( !self.menu.active ) {
+ return;
+ }
+ self.menu.select( event );
+ break;
+ case keyCode.ESCAPE:
+ self.element.val( self.term );
+ self.close( event );
+ break;
+ default:
+ // keypress is triggered before the input value is changed
+ clearTimeout( self.searching );
+ self.searching = setTimeout(function() {
+ // only search if the value has changed
+ if ( self.term != self.element.val() ) {
+ self.selectedItem = null;
+ self.search( null, event );
+ }
+ }, self.options.delay );
+ break;
+ }
+ })
+ .bind( "keypress.autocomplete", function( event ) {
+ if ( suppressKeyPress ) {
+ suppressKeyPress = false;
+ event.preventDefault();
+ }
+ })
+ .bind( "focus.autocomplete", function() {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ self.selectedItem = null;
+ self.previous = self.element.val();
+ })
+ .bind( "blur.autocomplete", function( event ) {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ clearTimeout( self.searching );
+ // clicks on the menu (or a button to trigger a search) will cause a blur event
+ self.closing = setTimeout(function() {
+ self.close( event );
+ self._change( event );
+ }, 150 );
+ });
+ this._initSource();
+ this.response = function() {
+ return self._response.apply( self, arguments );
+ };
+ this.menu = $( "<ul></ul>" )
+ .addClass( "ui-autocomplete" )
+ .appendTo( $( this.options.appendTo || "body", doc )[0] )
+ // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
+ .mousedown(function( event ) {
+ // clicking on the scrollbar causes focus to shift to the body
+ // but we can't detect a mouseup or a click immediately afterward
+ // so we have to track the next mousedown and close the menu if
+ // the user clicks somewhere outside of the autocomplete
+ var menuElement = self.menu.element[ 0 ];
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
+ setTimeout(function() {
+ $( document ).one( 'mousedown', function( event ) {
+ if ( event.target !== self.element[ 0 ] &&
+ event.target !== menuElement &&
+ !$.ui.contains( menuElement, event.target ) ) {
+ self.close();
+ }
+ });
+ }, 1 );
+ }
+
+ // use another timeout to make sure the blur-event-handler on the input was already triggered
+ setTimeout(function() {
+ clearTimeout( self.closing );
+ }, 13);
+ })
+ .menu({
+ focus: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" );
+ if ( false !== self._trigger( "focus", event, { item: item } ) ) {
+ // use value to match what will end up in the input, if it was a key event
+ if ( /^key/.test(event.originalEvent.type) ) {
+ self.element.val( item.value );
+ }
+ }
+ },
+ selected: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" ),
+ previous = self.previous;
+
+ // only trigger when focus was lost (click on menu)
+ if ( self.element[0] !== doc.activeElement ) {
+ self.element.focus();
+ self.previous = previous;
+ // #6109 - IE triggers two focus events and the second
+ // is asynchronous, so we need to reset the previous
+ // term synchronously and asynchronously :-(
+ setTimeout(function() {
+ self.previous = previous;
+ self.selectedItem = item;
+ }, 1);
+ }
+
+ if ( false !== self._trigger( "select", event, { item: item } ) ) {
+ self.element.val( item.value );
+ }
+ // reset the term after the select event
+ // this allows custom select handling to work properly
+ self.term = self.element.val();
+
+ self.close( event );
+ self.selectedItem = item;
+ },
+ blur: function( event, ui ) {
+ // don't set the value of the text field if it's already correct
+ // this prevents moving the cursor unnecessarily
+ if ( self.menu.element.is(":visible") &&
+ ( self.element.val() !== self.term ) ) {
+ self.element.val( self.term );
+ }
+ }
+ })
+ .zIndex( this.element.zIndex() + 1 )
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+ .css({ top: 0, left: 0 })
+ .hide()
+ .data( "menu" );
+ if ( $.fn.bgiframe ) {
+ this.menu.element.bgiframe();
+ }
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-autocomplete-input" )
+ .removeAttr( "autocomplete" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-autocomplete" )
+ .removeAttr( "aria-haspopup" );
+ this.menu.element.remove();
+ $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+ if ( key === "source" ) {
+ this._initSource();
+ }
+ if ( key === "appendTo" ) {
+ this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
+ }
+ if ( key === "disabled" && value && this.xhr ) {
+ this.xhr.abort();
+ }
+ },
+
+ _initSource: function() {
+ var self = this,
+ array,
+ url;
+ if ( $.isArray(this.options.source) ) {
+ array = this.options.source;
+ this.source = function( request, response ) {
+ response( $.ui.autocomplete.filter(array, request.term) );
+ };
+ } else if ( typeof this.options.source === "string" ) {
+ url = this.options.source;
+ this.source = function( request, response ) {
+ if ( self.xhr ) {
+ self.xhr.abort();
+ }
+ self.xhr = $.ajax({
+ url: url,
+ data: request,
+ dataType: "json",
+ autocompleteRequest: ++requestIndex,
+ success: function( data, status ) {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( data );
+ }
+ },
+ error: function() {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( [] );
+ }
+ }
+ });
+ };
+ } else {
+ this.source = this.options.source;
+ }
+ },
+
+ search: function( value, event ) {
+ value = value != null ? value : this.element.val();
+
+ // always save the actual value, not the one passed as an argument
+ this.term = this.element.val();
+
+ if ( value.length < this.options.minLength ) {
+ return this.close( event );
+ }
+
+ clearTimeout( this.closing );
+ if ( this._trigger( "search", event ) === false ) {
+ return;
+ }
+
+ return this._search( value );
+ },
+
+ _search: function( value ) {
+ this.pending++;
+ this.element.addClass( "ui-autocomplete-loading" );
+
+ this.source( { term: value }, this.response );
+ },
+
+ _response: function( content ) {
+ if ( !this.options.disabled && content && content.length ) {
+ content = this._normalize( content );
+ this._suggest( content );
+ this._trigger( "open" );
+ } else {
+ this.close();
+ }
+ this.pending--;
+ if ( !this.pending ) {
+ this.element.removeClass( "ui-autocomplete-loading" );
+ }
+ },
+
+ close: function( event ) {
+ clearTimeout( this.closing );
+ if ( this.menu.element.is(":visible") ) {
+ this.menu.element.hide();
+ this.menu.deactivate();
+ this._trigger( "close", event );
+ }
+ },
+
+ _change: function( event ) {
+ if ( this.previous !== this.element.val() ) {
+ this._trigger( "change", event, { item: this.selectedItem } );
+ }
+ },
+
+ _normalize: function( items ) {
+ // assume all items have the right format when the first item is complete
+ if ( items.length && items[0].label && items[0].value ) {
+ return items;
+ }
+ return $.map( items, function(item) {
+ if ( typeof item === "string" ) {
+ return {
+ label: item,
+ value: item
+ };
+ }
+ return $.extend({
+ label: item.label || item.value,
+ value: item.value || item.label
+ }, item );
+ });
+ },
+
+ _suggest: function( items ) {
+ var ul = this.menu.element
+ .empty()
+ .zIndex( this.element.zIndex() + 1 );
+ this._renderMenu( ul, items );
+ // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
+ this.menu.deactivate();
+ this.menu.refresh();
+
+ // size and position menu
+ ul.show();
+ this._resizeMenu();
+ ul.position( $.extend({
+ of: this.element
+ }, this.options.position ));
+ },
+
+ _resizeMenu: function() {
+ var ul = this.menu.element;
+ ul.outerWidth( Math.max(
+ ul.width( "" ).outerWidth(),
+ this.element.outerWidth()
+ ) );
+ },
+
+ _renderMenu: function( ul, items ) {
+ var self = this;
+ $.each( items, function( index, item ) {
+ self._renderItem( ul, item );
+ });
+ },
+
+ _renderItem: function( ul, item) {
+ return $( "<li></li>" )
+ .data( "item.autocomplete", item )
+ .append( $( "<a></a>" ).text( item.label ) )
+ .appendTo( ul );
+ },
+
+ _move: function( direction, event ) {
+ if ( !this.menu.element.is(":visible") ) {
+ this.search( null, event );
+ return;
+ }
+ if ( this.menu.first() && /^previous/.test(direction) ||
+ this.menu.last() && /^next/.test(direction) ) {
+ this.element.val( this.term );
+ this.menu.deactivate();
+ return;
+ }
+ this.menu[ direction ]( event );
+ },
+
+ widget: function() {
+ return this.menu.element;
+ }
+});
+
+$.extend( $.ui.autocomplete, {
+ escapeRegex: function( value ) {
+ return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ },
+ filter: function(array, term) {
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
+ return $.grep( array, function(value) {
+ return matcher.test( value.label || value.value || value );
+ });
+ }
+});
+
+}( jQuery ));
+
+/*
+ * jQuery UI Menu (not officially released)
+ *
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
+ * as long as you're okay with migrating your code later on. We can help with that, too.
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.menu", {
+ _create: function() {
+ var self = this;
+ this.element
+ .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
+ .attr({
+ role: "listbox",
+ "aria-activedescendant": "ui-active-menuitem"
+ })
+ .click(function( event ) {
+ if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
+ return;
+ }
+ // temporary
+ event.preventDefault();
+ self.select( event );
+ });
+ this.refresh();
+ },
+
+ refresh: function() {
+ var self = this;
+
+ // don't refresh list items that are already adapted
+ var items = this.element.children("li:not(.ui-menu-item):has(a)")
+ .addClass("ui-menu-item")
+ .attr("role", "menuitem");
+
+ items.children("a")
+ .addClass("ui-corner-all")
+ .attr("tabindex", -1)
+ // mouseenter doesn't work with event delegation
+ .mouseenter(function( event ) {
+ self.activate( event, $(this).parent() );
+ })
+ .mouseleave(function() {
+ self.deactivate();
+ });
+ },
+
+ activate: function( event, item ) {
+ this.deactivate();
+ if (this.hasScroll()) {
+ var offset = item.offset().top - this.element.offset().top,
+ scroll = this.element.attr("scrollTop"),
+ elementHeight = this.element.height();
+ if (offset < 0) {
+ this.element.attr("scrollTop", scroll + offset);
+ } else if (offset >= elementHeight) {
+ this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
+ }
+ }
+ this.active = item.eq(0)
+ .children("a")
+ .addClass("ui-state-hover")
+ .attr("id", "ui-active-menuitem")
+ .end();
+ this._trigger("focus", event, { item: item });
+ },
+
+ deactivate: function() {
+ if (!this.active) { return; }
+
+ this.active.children("a")
+ .removeClass("ui-state-hover")
+ .removeAttr("id");
+ this._trigger("blur");
+ this.active = null;
+ },
+
+ next: function(event) {
+ this.move("next", ".ui-menu-item:first", event);
+ },
+
+ previous: function(event) {
+ this.move("prev", ".ui-menu-item:last", event);
+ },
+
+ first: function() {
+ return this.active && !this.active.prevAll(".ui-menu-item").length;
+ },
+
+ last: function() {
+ return this.active && !this.active.nextAll(".ui-menu-item").length;
+ },
+
+ move: function(direction, edge, event) {
+ if (!this.active) {
+ this.activate(event, this.element.children(edge));
+ return;
+ }
+ var next = this.active[direction + "All"](".ui-menu-item").eq(0);
+ if (next.length) {
+ this.activate(event, next);
+ } else {
+ this.activate(event, this.element.children(edge));
+ }
+ },
+
+ // TODO merge with previousPage
+ nextPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.last()) {
+ this.activate(event, this.element.children(".ui-menu-item:first"));
+ return;
+ }
+ var base = this.active.offset().top,
+ height = this.element.height(),
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base - height + $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:last");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.last() ? ":first" : ":last"));
+ }
+ },
+
+ // TODO merge with nextPage
+ previousPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.first()) {
+ this.activate(event, this.element.children(".ui-menu-item:last"));
+ return;
+ }
+
+ var base = this.active.offset().top,
+ height = this.element.height();
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base + height - $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:first");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.first() ? ":last" : ":first"));
+ }
+ },
+
+ hasScroll: function() {
+ return this.element.height() < this.element.attr("scrollHeight");
+ },
+
+ select: function( event ) {
+ this._trigger("selected", event, { item: this.active });
+ }
+});
+
+}(jQuery));
+/*
+ * jQuery UI Button 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+var lastActive,
+ baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
+ stateClasses = "ui-state-hover ui-state-active ",
+ typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
+ formResetHandler = function( event ) {
+ $( ":ui-button", event.target.form ).each(function() {
+ var inst = $( this ).data( "button" );
+ setTimeout(function() {
+ inst.refresh();
+ }, 1 );
+ });
+ },
+ radioGroup = function( radio ) {
+ var name = radio.name,
+ form = radio.form,
+ radios = $( [] );
+ if ( name ) {
+ if ( form ) {
+ radios = $( form ).find( "[name='" + name + "']" );
+ } else {
+ radios = $( "[name='" + name + "']", radio.ownerDocument )
+ .filter(function() {
+ return !this.form;
+ });
+ }
+ }
+ return radios;
+ };
+
+$.widget( "ui.button", {
+ options: {
+ disabled: null,
+ text: true,
+ label: null,
+ icons: {
+ primary: null,
+ secondary: null
+ }
+ },
+ _create: function() {
+ this.element.closest( "form" )
+ .unbind( "reset.button" )
+ .bind( "reset.button", formResetHandler );
+
+ if ( typeof this.options.disabled !== "boolean" ) {
+ this.options.disabled = this.element.attr( "disabled" );
+ }
+
+ this._determineButtonType();
+ this.hasTitle = !!this.buttonElement.attr( "title" );
+
+ var self = this,
+ options = this.options,
+ toggleButton = this.type === "checkbox" || this.type === "radio",
+ hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ),
+ focusClass = "ui-state-focus";
+
+ if ( options.label === null ) {
+ options.label = this.buttonElement.html();
+ }
+
+ if ( this.element.is( ":disabled" ) ) {
+ options.disabled = true;
+ }
+
+ this.buttonElement
+ .addClass( baseClasses )
+ .attr( "role", "button" )
+ .bind( "mouseenter.button", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-hover" );
+ if ( this === lastActive ) {
+ $( this ).addClass( "ui-state-active" );
+ }
+ })
+ .bind( "mouseleave.button", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( hoverClass );
+ })
+ .bind( "focus.button", function() {
+ // no need to check disabled, focus won't be triggered anyway
+ $( this ).addClass( focusClass );
+ })
+ .bind( "blur.button", function() {
+ $( this ).removeClass( focusClass );
+ });
+
+ if ( toggleButton ) {
+ this.element.bind( "change.button", function() {
+ self.refresh();
+ });
+ }
+
+ if ( this.type === "checkbox" ) {
+ this.buttonElement.bind( "click.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).toggleClass( "ui-state-active" );
+ self.buttonElement.attr( "aria-pressed", self.element[0].checked );
+ });
+ } else if ( this.type === "radio" ) {
+ this.buttonElement.bind( "click.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).addClass( "ui-state-active" );
+ self.buttonElement.attr( "aria-pressed", true );
+
+ var radio = self.element[ 0 ];
+ radioGroup( radio )
+ .not( radio )
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", false );
+ });
+ } else {
+ this.buttonElement
+ .bind( "mousedown.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).addClass( "ui-state-active" );
+ lastActive = this;
+ $( document ).one( "mouseup", function() {
+ lastActive = null;
+ });
+ })
+ .bind( "mouseup.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).removeClass( "ui-state-active" );
+ })
+ .bind( "keydown.button", function(event) {
+ if ( options.disabled ) {
+ return false;
+ }
+ if ( event.keyCode == $.ui.keyCode.SPACE || event.keyCode == $.ui.keyCode.ENTER ) {
+ $( this ).addClass( "ui-state-active" );
+ }
+ })
+ .bind( "keyup.button", function() {
+ $( this ).removeClass( "ui-state-active" );
+ });
+
+ if ( this.buttonElement.is("a") ) {
+ this.buttonElement.keyup(function(event) {
+ if ( event.keyCode === $.ui.keyCode.SPACE ) {
+ // TODO pass through original event correctly (just as 2nd argument doesn't work)
+ $( this ).click();
+ }
+ });
+ }
+ }
+
+ // TODO: pull out $.Widget's handling for the disabled option into
+ // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
+ // be overridden by individual plugins
+ this._setOption( "disabled", options.disabled );
+ },
+
+ _determineButtonType: function() {
+
+ if ( this.element.is(":checkbox") ) {
+ this.type = "checkbox";
+ } else {
+ if ( this.element.is(":radio") ) {
+ this.type = "radio";
+ } else {
+ if ( this.element.is("input") ) {
+ this.type = "input";
+ } else {
+ this.type = "button";
+ }
+ }
+ }
+
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ // we don't search against the document in case the element
+ // is disconnected from the DOM
+ this.buttonElement = this.element.parents().last()
+ .find( "label[for=" + this.element.attr("id") + "]" );
+ this.element.addClass( "ui-helper-hidden-accessible" );
+
+ var checked = this.element.is( ":checked" );
+ if ( checked ) {
+ this.buttonElement.addClass( "ui-state-active" );
+ }
+ this.buttonElement.attr( "aria-pressed", checked );
+ } else {
+ this.buttonElement = this.element;
+ }
+ },
+
+ widget: function() {
+ return this.buttonElement;
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-helper-hidden-accessible" );
+ this.buttonElement
+ .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
+ .removeAttr( "role" )
+ .removeAttr( "aria-pressed" )
+ .html( this.buttonElement.find(".ui-button-text").html() );
+
+ if ( !this.hasTitle ) {
+ this.buttonElement.removeAttr( "title" );
+ }
+
+ $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+ if ( key === "disabled" ) {
+ if ( value ) {
+ this.element.attr( "disabled", true );
+ } else {
+ this.element.removeAttr( "disabled" );
+ }
+ }
+ this._resetButton();
+ },
+
+ refresh: function() {
+ var isDisabled = this.element.is( ":disabled" );
+ if ( isDisabled !== this.options.disabled ) {
+ this._setOption( "disabled", isDisabled );
+ }
+ if ( this.type === "radio" ) {
+ radioGroup( this.element[0] ).each(function() {
+ if ( $( this ).is( ":checked" ) ) {
+ $( this ).button( "widget" )
+ .addClass( "ui-state-active" )
+ .attr( "aria-pressed", true );
+ } else {
+ $( this ).button( "widget" )
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", false );
+ }
+ });
+ } else if ( this.type === "checkbox" ) {
+ if ( this.element.is( ":checked" ) ) {
+ this.buttonElement
+ .addClass( "ui-state-active" )
+ .attr( "aria-pressed", true );
+ } else {
+ this.buttonElement
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", false );
+ }
+ }
+ },
+
+ _resetButton: function() {
+ if ( this.type === "input" ) {
+ if ( this.options.label ) {
+ this.element.val( this.options.label );
+ }
+ return;
+ }
+ var buttonElement = this.buttonElement.removeClass( typeClasses ),
+ buttonText = $( "<span></span>" )
+ .addClass( "ui-button-text" )
+ .html( this.options.label )
+ .appendTo( buttonElement.empty() )
+ .text(),
+ icons = this.options.icons,
+ multipleIcons = icons.primary && icons.secondary,
+ buttonClasses = [];
+
+ if ( icons.primary || icons.secondary ) {
+ buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
+
+ if ( icons.primary ) {
+ buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
+ }
+
+ if ( icons.secondary ) {
+ buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
+ }
+
+ if ( !this.options.text ) {
+ buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
+ buttonElement.removeClass( "ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary" );
+
+ if ( !this.hasTitle ) {
+ buttonElement.attr( "title", buttonText );
+ }
+ }
+ } else {
+ buttonClasses.push( "ui-button-text-only" );
+ }
+ buttonElement.addClass( buttonClasses.join( " " ) );
+ }
+});
+
+$.widget( "ui.buttonset", {
+ options: {
+ items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)"
+ },
+
+ _create: function() {
+ this.element.addClass( "ui-buttonset" );
+ },
+
+ _init: function() {
+ this.refresh();
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "disabled" ) {
+ this.buttons.button( "option", key, value );
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+ },
+
+ refresh: function() {
+ this.buttons = this.element.find( this.options.items )
+ .filter( ":ui-button" )
+ .button( "refresh" )
+ .end()
+ .not( ":ui-button" )
+ .button()
+ .end()
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
+ .filter( ":first" )
+ .addClass( "ui-corner-left" )
+ .end()
+ .filter( ":last" )
+ .addClass( "ui-corner-right" )
+ .end()
+ .end();
+ },
+
+ destroy: function() {
+ this.element.removeClass( "ui-buttonset" );
+ this.buttons
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-corner-left ui-corner-right" )
+ .end()
+ .button( "destroy" );
+
+ $.Widget.prototype.destroy.call( this );
+ }
+});
+
+}( jQuery ) );
+/*
+ * jQuery UI Dialog 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */
+(function( $, undefined ) {
+
+var uiDialogClasses =
+ 'ui-dialog ' +
+ 'ui-widget ' +
+ 'ui-widget-content ' +
+ 'ui-corner-all ',
+ sizeRelatedOptions = {
+ buttons: true,
+ height: true,
+ maxHeight: true,
+ maxWidth: true,
+ minHeight: true,
+ minWidth: true,
+ width: true
+ },
+ resizableRelatedOptions = {
+ maxHeight: true,
+ maxWidth: true,
+ minHeight: true,
+ minWidth: true
+ };
+
+$.widget("ui.dialog", {
+ options: {
+ autoOpen: true,
+ buttons: {},
+ closeOnEscape: true,
+ closeText: 'close',
+ dialogClass: '',
+ draggable: true,
+ hide: null,
+ height: 'auto',
+ maxHeight: false,
+ maxWidth: false,
+ minHeight: 150,
+ minWidth: 150,
+ modal: false,
+ position: {
+ my: 'center',
+ at: 'center',
+ collision: 'fit',
+ // ensure that the titlebar is never outside the document
+ using: function(pos) {
+ var topOffset = $(this).css(pos).offset().top;
+ if (topOffset < 0) {
+ $(this).css('top', pos.top - topOffset);
+ }
+ }
+ },
+ resizable: true,
+ show: null,
+ stack: true,
+ title: '',
+ width: 300,
+ zIndex: 1000
+ },
+
+ _create: function() {
+ this.originalTitle = this.element.attr('title');
+ // #5742 - .attr() might return a DOMElement
+ if ( typeof this.originalTitle !== "string" ) {
+ this.originalTitle = "";
+ }
+
+ this.options.title = this.options.title || this.originalTitle;
+ var self = this,
+ options = self.options,
+
+ title = options.title || '&#160;',
+ titleId = $.ui.dialog.getTitleId(self.element),
+
+ uiDialog = (self.uiDialog = $('<div></div>'))
+ .appendTo(document.body)
+ .hide()
+ .addClass(uiDialogClasses + options.dialogClass)
+ .css({
+ zIndex: options.zIndex
+ })
+ // setting tabIndex makes the div focusable
+ // setting outline to 0 prevents a border on focus in Mozilla
+ .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
+ if (options.closeOnEscape && event.keyCode &&
+ event.keyCode === $.ui.keyCode.ESCAPE) {
+
+ self.close(event);
+ event.preventDefault();
+ }
+ })
+ .attr({
+ role: 'dialog',
+ 'aria-labelledby': titleId
+ })
+ .mousedown(function(event) {
+ self.moveToTop(false, event);
+ }),
+
+ uiDialogContent = self.element
+ .show()
+ .removeAttr('title')
+ .addClass(
+ 'ui-dialog-content ' +
+ 'ui-widget-content')
+ .appendTo(uiDialog),
+
+ uiDialogTitlebar = (self.uiDialogTitlebar = $('<div></div>'))
+ .addClass(
+ 'ui-dialog-titlebar ' +
+ 'ui-widget-header ' +
+ 'ui-corner-all ' +
+ 'ui-helper-clearfix'
+ )
+ .prependTo(uiDialog),
+
+ uiDialogTitlebarClose = $('<a href="#"></a>')
+ .addClass(
+ 'ui-dialog-titlebar-close ' +
+ 'ui-corner-all'
+ )
+ .attr('role', 'button')
+ .hover(
+ function() {
+ uiDialogTitlebarClose.addClass('ui-state-hover');
+ },
+ function() {
+ uiDialogTitlebarClose.removeClass('ui-state-hover');
+ }
+ )
+ .focus(function() {
+ uiDialogTitlebarClose.addClass('ui-state-focus');
+ })
+ .blur(function() {
+ uiDialogTitlebarClose.removeClass('ui-state-focus');
+ })
+ .click(function(event) {
+ self.close(event);
+ return false;
+ })
+ .appendTo(uiDialogTitlebar),
+
+ uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('<span></span>'))
+ .addClass(
+ 'ui-icon ' +
+ 'ui-icon-closethick'
+ )
+ .text(options.closeText)
+ .appendTo(uiDialogTitlebarClose),
+
+ uiDialogTitle = $('<span></span>')
+ .addClass('ui-dialog-title')
+ .attr('id', titleId)
+ .html(title)
+ .prependTo(uiDialogTitlebar);
+
+ //handling of deprecated beforeclose (vs beforeClose) option
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+ //TODO: remove in 1.9pre
+ if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) {
+ options.beforeClose = options.beforeclose;
+ }
+
+ uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
+
+ if (options.draggable && $.fn.draggable) {
+ self._makeDraggable();
+ }
+ if (options.resizable && $.fn.resizable) {
+ self._makeResizable();
+ }
+
+ self._createButtons(options.buttons);
+ self._isOpen = false;
+
+ if ($.fn.bgiframe) {
+ uiDialog.bgiframe();
+ }
+ },
+
+ _init: function() {
+ if ( this.options.autoOpen ) {
+ this.open();
+ }
+ },
+
+ destroy: function() {
+ var self = this;
+
+ if (self.overlay) {
+ self.overlay.destroy();
+ }
+ self.uiDialog.hide();
+ self.element
+ .unbind('.dialog')
+ .removeData('dialog')
+ .removeClass('ui-dialog-content ui-widget-content')
+ .hide().appendTo('body');
+ self.uiDialog.remove();
+
+ if (self.originalTitle) {
+ self.element.attr('title', self.originalTitle);
+ }
+
+ return self;
+ },
+
+ widget: function() {
+ return this.uiDialog;
+ },
+
+ close: function(event) {
+ var self = this,
+ maxZ, thisZ;
+
+ if (false === self._trigger('beforeClose', event)) {
+ return;
+ }
+
+ if (self.overlay) {
+ self.overlay.destroy();
+ }
+ self.uiDialog.unbind('keypress.ui-dialog');
+
+ self._isOpen = false;
+
+ if (self.options.hide) {
+ self.uiDialog.hide(self.options.hide, function() {
+ self._trigger('close', event);
+ });
+ } else {
+ self.uiDialog.hide();
+ self._trigger('close', event);
+ }
+
+ $.ui.dialog.overlay.resize();
+
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+ if (self.options.modal) {
+ maxZ = 0;
+ $('.ui-dialog').each(function() {
+ if (this !== self.uiDialog[0]) {
+ thisZ = $(this).css('z-index');
+ if(!isNaN(thisZ)) {
+ maxZ = Math.max(maxZ, thisZ);
+ }
+ }
+ });
+ $.ui.dialog.maxZ = maxZ;
+ }
+
+ return self;
+ },
+
+ isOpen: function() {
+ return this._isOpen;
+ },
+
+ // the force parameter allows us to move modal dialogs to their correct
+ // position on open
+ moveToTop: function(force, event) {
+ var self = this,
+ options = self.options,
+ saveScroll;
+
+ if ((options.modal && !force) ||
+ (!options.stack && !options.modal)) {
+ return self._trigger('focus', event);
+ }
+
+ if (options.zIndex > $.ui.dialog.maxZ) {
+ $.ui.dialog.maxZ = options.zIndex;
+ }
+ if (self.overlay) {
+ $.ui.dialog.maxZ += 1;
+ self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ);
+ }
+
+ //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
+ // http://ui.jquery.com/bugs/ticket/3193
+ saveScroll = { scrollTop: self.element.attr('scrollTop'), scrollLeft: self.element.attr('scrollLeft') };
+ $.ui.dialog.maxZ += 1;
+ self.uiDialog.css('z-index', $.ui.dialog.maxZ);
+ self.element.attr(saveScroll);
+ self._trigger('focus', event);
+
+ return self;
+ },
+
+ open: function() {
+ if (this._isOpen) { return; }
+
+ var self = this,
+ options = self.options,
+ uiDialog = self.uiDialog;
+
+ self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
+ self._size();
+ self._position(options.position);
+ uiDialog.show(options.show);
+ self.moveToTop(true);
+
+ // prevent tabbing out of modal dialogs
+ if (options.modal) {
+ uiDialog.bind('keypress.ui-dialog', function(event) {
+ if (event.keyCode !== $.ui.keyCode.TAB) {
+ return;
+ }
+
+ var tabbables = $(':tabbable', this),
+ first = tabbables.filter(':first'),
+ last = tabbables.filter(':last');
+
+ if (event.target === last[0] && !event.shiftKey) {
+ first.focus(1);
+ return false;
+ } else if (event.target === first[0] && event.shiftKey) {
+ last.focus(1);
+ return false;
+ }
+ });
+ }
+
+ // set focus to the first tabbable element in the content area or the first button
+ // if there are no tabbable elements, set focus on the dialog itself
+ $(self.element.find(':tabbable').get().concat(
+ uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat(
+ uiDialog.get()))).eq(0).focus();
+
+ self._isOpen = true;
+ self._trigger('open');
+
+ return self;
+ },
+
+ _createButtons: function(buttons) {
+ var self = this,
+ hasButtons = false,
+ uiDialogButtonPane = $('<div></div>')
+ .addClass(
+ 'ui-dialog-buttonpane ' +
+ 'ui-widget-content ' +
+ 'ui-helper-clearfix'
+ ),
+ uiButtonSet = $( "<div></div>" )
+ .addClass( "ui-dialog-buttonset" )
+ .appendTo( uiDialogButtonPane );
+
+ // if we already have a button pane, remove it
+ self.uiDialog.find('.ui-dialog-buttonpane').remove();
+
+ if (typeof buttons === 'object' && buttons !== null) {
+ $.each(buttons, function() {
+ return !(hasButtons = true);
+ });
+ }
+ if (hasButtons) {
+ $.each(buttons, function(name, props) {
+ props = $.isFunction( props ) ?
+ { click: props, text: name } :
+ props;
+ var button = $('<button type="button"></button>')
+ .attr( props, true )
+ .unbind('click')
+ .click(function() {
+ props.click.apply(self.element[0], arguments);
+ })
+ .appendTo(uiButtonSet);
+ if ($.fn.button) {
+ button.button();
+ }
+ });
+ uiDialogButtonPane.appendTo(self.uiDialog);
+ }
+ },
+
+ _makeDraggable: function() {
+ var self = this,
+ options = self.options,
+ doc = $(document),
+ heightBeforeDrag;
+
+ function filteredUi(ui) {
+ return {
+ position: ui.position,
+ offset: ui.offset
+ };
+ }
+
+ self.uiDialog.draggable({
+ cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
+ handle: '.ui-dialog-titlebar',
+ containment: 'document',
+ start: function(event, ui) {
+ heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
+ $(this).height($(this).height()).addClass("ui-dialog-dragging");
+ self._trigger('dragStart', event, filteredUi(ui));
+ },
+ drag: function(event, ui) {
+ self._trigger('drag', event, filteredUi(ui));
+ },
+ stop: function(event, ui) {
+ options.position = [ui.position.left - doc.scrollLeft(),
+ ui.position.top - doc.scrollTop()];
+ $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
+ self._trigger('dragStop', event, filteredUi(ui));
+ $.ui.dialog.overlay.resize();
+ }
+ });
+ },
+
+ _makeResizable: function(handles) {
+ handles = (handles === undefined ? this.options.resizable : handles);
+ var self = this,
+ options = self.options,
+ // .ui-resizable has position: relative defined in the stylesheet
+ // but dialogs have to use absolute or fixed positioning
+ position = self.uiDialog.css('position'),
+ resizeHandles = (typeof handles === 'string' ?
+ handles :
+ 'n,e,s,w,se,sw,ne,nw'
+ );
+
+ function filteredUi(ui) {
+ return {
+ originalPosition: ui.originalPosition,
+ originalSize: ui.originalSize,
+ position: ui.position,
+ size: ui.size
+ };
+ }
+
+ self.uiDialog.resizable({
+ cancel: '.ui-dialog-content',
+ containment: 'document',
+ alsoResize: self.element,
+ maxWidth: options.maxWidth,
+ maxHeight: options.maxHeight,
+ minWidth: options.minWidth,
+ minHeight: self._minHeight(),
+ handles: resizeHandles,
+ start: function(event, ui) {
+ $(this).addClass("ui-dialog-resizing");
+ self._trigger('resizeStart', event, filteredUi(ui));
+ },
+ resize: function(event, ui) {
+ self._trigger('resize', event, filteredUi(ui));
+ },
+ stop: function(event, ui) {
+ $(this).removeClass("ui-dialog-resizing");
+ options.height = $(this).height();
+ options.width = $(this).width();
+ self._trigger('resizeStop', event, filteredUi(ui));
+ $.ui.dialog.overlay.resize();
+ }
+ })
+ .css('position', position)
+ .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
+ },
+
+ _minHeight: function() {
+ var options = this.options;
+
+ if (options.height === 'auto') {
+ return options.minHeight;
+ } else {
+ return Math.min(options.minHeight, options.height);
+ }
+ },
+
+ _position: function(position) {
+ var myAt = [],
+ offset = [0, 0],
+ isVisible;
+
+ if (position) {
+ // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
+ // if (typeof position == 'string' || $.isArray(position)) {
+ // myAt = $.isArray(position) ? position : position.split(' ');
+
+ if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) {
+ myAt = position.split ? position.split(' ') : [position[0], position[1]];
+ if (myAt.length === 1) {
+ myAt[1] = myAt[0];
+ }
+
+ $.each(['left', 'top'], function(i, offsetPosition) {
+ if (+myAt[i] === myAt[i]) {
+ offset[i] = myAt[i];
+ myAt[i] = offsetPosition;
+ }
+ });
+
+ position = {
+ my: myAt.join(" "),
+ at: myAt.join(" "),
+ offset: offset.join(" ")
+ };
+ }
+
+ position = $.extend({}, $.ui.dialog.prototype.options.position, position);
+ } else {
+ position = $.ui.dialog.prototype.options.position;
+ }
+
+ // need to show the dialog to get the actual offset in the position plugin
+ isVisible = this.uiDialog.is(':visible');
+ if (!isVisible) {
+ this.uiDialog.show();
+ }
+ this.uiDialog
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+ .css({ top: 0, left: 0 })
+ .position($.extend({ of: window }, position));
+ if (!isVisible) {
+ this.uiDialog.hide();
+ }
+ },
+
+ _setOptions: function( options ) {
+ var self = this,
+ resizableOptions = {},
+ resize = false;
+
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+
+ if ( key in sizeRelatedOptions ) {
+ resize = true;
+ }
+ if ( key in resizableRelatedOptions ) {
+ resizableOptions[ key ] = value;
+ }
+ });
+
+ if ( resize ) {
+ this._size();
+ }
+ if ( this.uiDialog.is( ":data(resizable)" ) ) {
+ this.uiDialog.resizable( "option", resizableOptions );
+ }
+ },
+
+ _setOption: function(key, value){
+ var self = this,
+ uiDialog = self.uiDialog;
+
+ switch (key) {
+ //handling of deprecated beforeclose (vs beforeClose) option
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+ //TODO: remove in 1.9pre
+ case "beforeclose":
+ key = "beforeClose";
+ break;
+ case "buttons":
+ self._createButtons(value);
+ break;
+ case "closeText":
+ // ensure that we always pass a string
+ self.uiDialogTitlebarCloseText.text("" + value);
+ break;
+ case "dialogClass":
+ uiDialog
+ .removeClass(self.options.dialogClass)
+ .addClass(uiDialogClasses + value);
+ break;
+ case "disabled":
+ if (value) {
+ uiDialog.addClass('ui-dialog-disabled');
+ } else {
+ uiDialog.removeClass('ui-dialog-disabled');
+ }
+ break;
+ case "draggable":
+ var isDraggable = uiDialog.is( ":data(draggable)" );
+ if ( isDraggable && !value ) {
+ uiDialog.draggable( "destroy" );
+ }
+
+ if ( !isDraggable && value ) {
+ self._makeDraggable();
+ }
+ break;
+ case "position":
+ self._position(value);
+ break;
+ case "resizable":
+ // currently resizable, becoming non-resizable
+ var isResizable = uiDialog.is( ":data(resizable)" );
+ if (isResizable && !value) {
+ uiDialog.resizable('destroy');
+ }
+
+ // currently resizable, changing handles
+ if (isResizable && typeof value === 'string') {
+ uiDialog.resizable('option', 'handles', value);
+ }
+
+ // currently non-resizable, becoming resizable
+ if (!isResizable && value !== false) {
+ self._makeResizable(value);
+ }
+ break;
+ case "title":
+ // convert whatever was passed in o a string, for html() to not throw up
+ $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || '&#160;'));
+ break;
+ }
+
+ $.Widget.prototype._setOption.apply(self, arguments);
+ },
+
+ _size: function() {
+ /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
+ * divs will both have width and height set, so we need to reset them
+ */
+ var options = this.options,
+ nonContentHeight,
+ minContentHeight,
+ isVisible = this.uiDialog.is( ":visible" );
+
+ // reset content sizing
+ this.element.show().css({
+ width: 'auto',
+ minHeight: 0,
+ height: 0
+ });
+
+ if (options.minWidth > options.width) {
+ options.width = options.minWidth;
+ }
+
+ // reset wrapper sizing
+ // determine the height of all the non-content elements
+ nonContentHeight = this.uiDialog.css({
+ height: 'auto',
+ width: options.width
+ })
+ .height();
+ minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
+
+ if ( options.height === "auto" ) {
+ // only needed for IE6 support
+ if ( $.support.minHeight ) {
+ this.element.css({
+ minHeight: minContentHeight,
+ height: "auto"
+ });
+ } else {
+ this.uiDialog.show();
+ var autoHeight = this.element.css( "height", "auto" ).height();
+ if ( !isVisible ) {
+ this.uiDialog.hide();
+ }
+ this.element.height( Math.max( autoHeight, minContentHeight ) );
+ }
+ } else {
+ this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
+ }
+
+ if (this.uiDialog.is(':data(resizable)')) {
+ this.uiDialog.resizable('option', 'minHeight', this._minHeight());
+ }
+ }
+});
+
+$.extend($.ui.dialog, {
+ version: "1.8.10",
+
+ uuid: 0,
+ maxZ: 0,
+
+ getTitleId: function($el) {
+ var id = $el.attr('id');
+ if (!id) {
+ this.uuid += 1;
+ id = this.uuid;
+ }
+ return 'ui-dialog-title-' + id;
+ },
+
+ overlay: function(dialog) {
+ this.$el = $.ui.dialog.overlay.create(dialog);
+ }
+});
+
+$.extend($.ui.dialog.overlay, {
+ instances: [],
+ // reuse old instances due to IE memory leak with alpha transparency (see #5185)
+ oldInstances: [],
+ maxZ: 0,
+ events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
+ function(event) { return event + '.dialog-overlay'; }).join(' '),
+ create: function(dialog) {
+ if (this.instances.length === 0) {
+ // prevent use of anchors and inputs
+ // we use a setTimeout in case the overlay is created from an
+ // event that we're going to be cancelling (see #2804)
+ setTimeout(function() {
+ // handle $(el).dialog().dialog('close') (see #4065)
+ if ($.ui.dialog.overlay.instances.length) {
+ $(document).bind($.ui.dialog.overlay.events, function(event) {
+ // stop events if the z-index of the target is < the z-index of the overlay
+ // we cannot return true when we don't want to cancel the event (#3523)
+ if ($(event.target).zIndex() < $.ui.dialog.overlay.maxZ) {
+ return false;
+ }
+ });
+ }
+ }, 1);
+
+ // allow closing by pressing the escape key
+ $(document).bind('keydown.dialog-overlay', function(event) {
+ if (dialog.options.closeOnEscape && event.keyCode &&
+ event.keyCode === $.ui.keyCode.ESCAPE) {
+
+ dialog.close(event);
+ event.preventDefault();
+ }
+ });
+
+ // handle window resize
+ $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
+ }
+
+ var $el = (this.oldInstances.pop() || $('<div></div>').addClass('ui-widget-overlay'))
+ .appendTo(document.body)
+ .css({
+ width: this.width(),
+ height: this.height()
+ });
+
+ if ($.fn.bgiframe) {
+ $el.bgiframe();
+ }
+
+ this.instances.push($el);
+ return $el;
+ },
+
+ destroy: function($el) {
+ var indexOf = $.inArray($el, this.instances);
+ if (indexOf != -1){
+ this.oldInstances.push(this.instances.splice(indexOf, 1)[0]);
+ }
+
+ if (this.instances.length === 0) {
+ $([document, window]).unbind('.dialog-overlay');
+ }
+
+ $el.remove();
+
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+ var maxZ = 0;
+ $.each(this.instances, function() {
+ maxZ = Math.max(maxZ, this.css('z-index'));
+ });
+ this.maxZ = maxZ;
+ },
+
+ height: function() {
+ var scrollHeight,
+ offsetHeight;
+ // handle IE 6
+ if ($.browser.msie && $.browser.version < 7) {
+ scrollHeight = Math.max(
+ document.documentElement.scrollHeight,
+ document.body.scrollHeight
+ );
+ offsetHeight = Math.max(
+ document.documentElement.offsetHeight,
+ document.body.offsetHeight
+ );
+
+ if (scrollHeight < offsetHeight) {
+ return $(window).height() + 'px';
+ } else {
+ return scrollHeight + 'px';
+ }
+ // handle "good" browsers
+ } else {
+ return $(document).height() + 'px';
+ }
+ },
+
+ width: function() {
+ var scrollWidth,
+ offsetWidth;
+ // handle IE 6
+ if ($.browser.msie && $.browser.version < 7) {
+ scrollWidth = Math.max(
+ document.documentElement.scrollWidth,
+ document.body.scrollWidth
+ );
+ offsetWidth = Math.max(
+ document.documentElement.offsetWidth,
+ document.body.offsetWidth
+ );
+
+ if (scrollWidth < offsetWidth) {
+ return $(window).width() + 'px';
+ } else {
+ return scrollWidth + 'px';
+ }
+ // handle "good" browsers
+ } else {
+ return $(document).width() + 'px';
+ }
+ },
+
+ resize: function() {
+ /* If the dialog is draggable and the user drags it past the
+ * right edge of the window, the document becomes wider so we
+ * need to stretch the overlay. If the user then drags the
+ * dialog back to the left, the document will become narrower,
+ * so we need to shrink the overlay to the appropriate size.
+ * This is handled by shrinking the overlay before setting it
+ * to the full document size.
+ */
+ var $overlays = $([]);
+ $.each($.ui.dialog.overlay.instances, function() {
+ $overlays = $overlays.add(this);
+ });
+
+ $overlays.css({
+ width: 0,
+ height: 0
+ }).css({
+ width: $.ui.dialog.overlay.width(),
+ height: $.ui.dialog.overlay.height()
+ });
+ }
+});
+
+$.extend($.ui.dialog.overlay.prototype, {
+ destroy: function() {
+ $.ui.dialog.overlay.destroy(this.$el);
+ }
+});
+
+}(jQuery));
+/*
+ * jQuery UI Slider 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget( "ui.slider", $.ui.mouse, {
+
+ widgetEventPrefix: "slide",
+
+ options: {
+ animate: false,
+ distance: 0,
+ max: 100,
+ min: 0,
+ orientation: "horizontal",
+ range: false,
+ step: 1,
+ value: 0,
+ values: null
+ },
+
+ _create: function() {
+ var self = this,
+ o = this.options;
+
+ this._keySliding = false;
+ this._mouseSliding = false;
+ this._animateOff = true;
+ this._handleIndex = null;
+ this._detectOrientation();
+ this._mouseInit();
+
+ this.element
+ .addClass( "ui-slider" +
+ " ui-slider-" + this.orientation +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all" );
+
+ if ( o.disabled ) {
+ this.element.addClass( "ui-slider-disabled ui-disabled" );
+ }
+
+ this.range = $([]);
+
+ if ( o.range ) {
+ if ( o.range === true ) {
+ this.range = $( "<div></div>" );
+ if ( !o.values ) {
+ o.values = [ this._valueMin(), this._valueMin() ];
+ }
+ if ( o.values.length && o.values.length !== 2 ) {
+ o.values = [ o.values[0], o.values[0] ];
+ }
+ } else {
+ this.range = $( "<div></div>" );
+ }
+
+ this.range
+ .appendTo( this.element )
+ .addClass( "ui-slider-range" );
+
+ if ( o.range === "min" || o.range === "max" ) {
+ this.range.addClass( "ui-slider-range-" + o.range );
+ }
+
+ // note: this isn't the most fittingly semantic framework class for this element,
+ // but worked best visually with a variety of themes
+ this.range.addClass( "ui-widget-header" );
+ }
+
+ if ( $( ".ui-slider-handle", this.element ).length === 0 ) {
+ $( "<a href='#'></a>" )
+ .appendTo( this.element )
+ .addClass( "ui-slider-handle" );
+ }
+
+ if ( o.values && o.values.length ) {
+ while ( $(".ui-slider-handle", this.element).length < o.values.length ) {
+ $( "<a href='#'></a>" )
+ .appendTo( this.element )
+ .addClass( "ui-slider-handle" );
+ }
+ }
+
+ this.handles = $( ".ui-slider-handle", this.element )
+ .addClass( "ui-state-default" +
+ " ui-corner-all" );
+
+ this.handle = this.handles.eq( 0 );
+
+ this.handles.add( this.range ).filter( "a" )
+ .click(function( event ) {
+ event.preventDefault();
+ })
+ .hover(function() {
+ if ( !o.disabled ) {
+ $( this ).addClass( "ui-state-hover" );
+ }
+ }, function() {
+ $( this ).removeClass( "ui-state-hover" );
+ })
+ .focus(function() {
+ if ( !o.disabled ) {
+ $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
+ $( this ).addClass( "ui-state-focus" );
+ } else {
+ $( this ).blur();
+ }
+ })
+ .blur(function() {
+ $( this ).removeClass( "ui-state-focus" );
+ });
+
+ this.handles.each(function( i ) {
+ $( this ).data( "index.ui-slider-handle", i );
+ });
+
+ this.handles
+ .keydown(function( event ) {
+ var ret = true,
+ index = $( this ).data( "index.ui-slider-handle" ),
+ allowed,
+ curVal,
+ newVal,
+ step;
+
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ case $.ui.keyCode.END:
+ case $.ui.keyCode.PAGE_UP:
+ case $.ui.keyCode.PAGE_DOWN:
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ ret = false;
+ if ( !self._keySliding ) {
+ self._keySliding = true;
+ $( this ).addClass( "ui-state-active" );
+ allowed = self._start( event, index );
+ if ( allowed === false ) {
+ return;
+ }
+ }
+ break;
+ }
+
+ step = self.options.step;
+ if ( self.options.values && self.options.values.length ) {
+ curVal = newVal = self.values( index );
+ } else {
+ curVal = newVal = self.value();
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ newVal = self._valueMin();
+ break;
+ case $.ui.keyCode.END:
+ newVal = self._valueMax();
+ break;
+ case $.ui.keyCode.PAGE_UP:
+ newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ if ( curVal === self._valueMax() ) {
+ return;
+ }
+ newVal = self._trimAlignValue( curVal + step );
+ break;
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ if ( curVal === self._valueMin() ) {
+ return;
+ }
+ newVal = self._trimAlignValue( curVal - step );
+ break;
+ }
+
+ self._slide( event, index, newVal );
+
+ return ret;
+
+ })
+ .keyup(function( event ) {
+ var index = $( this ).data( "index.ui-slider-handle" );
+
+ if ( self._keySliding ) {
+ self._keySliding = false;
+ self._stop( event, index );
+ self._change( event, index );
+ $( this ).removeClass( "ui-state-active" );
+ }
+
+ });
+
+ this._refreshValue();
+
+ this._animateOff = false;
+ },
+
+ destroy: function() {
+ this.handles.remove();
+ this.range.remove();
+
+ this.element
+ .removeClass( "ui-slider" +
+ " ui-slider-horizontal" +
+ " ui-slider-vertical" +
+ " ui-slider-disabled" +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all" )
+ .removeData( "slider" )
+ .unbind( ".slider" );
+
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseCapture: function( event ) {
+ var o = this.options,
+ position,
+ normValue,
+ distance,
+ closestHandle,
+ self,
+ index,
+ allowed,
+ offset,
+ mouseOverHandle;
+
+ if ( o.disabled ) {
+ return false;
+ }
+
+ this.elementSize = {
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight()
+ };
+ this.elementOffset = this.element.offset();
+
+ position = { x: event.pageX, y: event.pageY };
+ normValue = this._normValueFromMouse( position );
+ distance = this._valueMax() - this._valueMin() + 1;
+ self = this;
+ this.handles.each(function( i ) {
+ var thisDistance = Math.abs( normValue - self.values(i) );
+ if ( distance > thisDistance ) {
+ distance = thisDistance;
+ closestHandle = $( this );
+ index = i;
+ }
+ });
+
+ // workaround for bug #3736 (if both handles of a range are at 0,
+ // the first is always used as the one with least distance,
+ // and moving it is obviously prevented by preventing negative ranges)
+ if( o.range === true && this.values(1) === o.min ) {
+ index += 1;
+ closestHandle = $( this.handles[index] );
+ }
+
+ allowed = this._start( event, index );
+ if ( allowed === false ) {
+ return false;
+ }
+ this._mouseSliding = true;
+
+ self._handleIndex = index;
+
+ closestHandle
+ .addClass( "ui-state-active" )
+ .focus();
+
+ offset = closestHandle.offset();
+ mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
+ this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+ left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
+ top: event.pageY - offset.top -
+ ( closestHandle.height() / 2 ) -
+ ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
+ ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
+ ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
+ };
+
+ if ( !this.handles.hasClass( "ui-state-hover" ) ) {
+ this._slide( event, index, normValue );
+ }
+ this._animateOff = true;
+ return true;
+ },
+
+ _mouseStart: function( event ) {
+ return true;
+ },
+
+ _mouseDrag: function( event ) {
+ var position = { x: event.pageX, y: event.pageY },
+ normValue = this._normValueFromMouse( position );
+
+ this._slide( event, this._handleIndex, normValue );
+
+ return false;
+ },
+
+ _mouseStop: function( event ) {
+ this.handles.removeClass( "ui-state-active" );
+ this._mouseSliding = false;
+
+ this._stop( event, this._handleIndex );
+ this._change( event, this._handleIndex );
+
+ this._handleIndex = null;
+ this._clickOffset = null;
+ this._animateOff = false;
+
+ return false;
+ },
+
+ _detectOrientation: function() {
+ this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
+ },
+
+ _normValueFromMouse: function( position ) {
+ var pixelTotal,
+ pixelMouse,
+ percentMouse,
+ valueTotal,
+ valueMouse;
+
+ if ( this.orientation === "horizontal" ) {
+ pixelTotal = this.elementSize.width;
+ pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
+ } else {
+ pixelTotal = this.elementSize.height;
+ pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
+ }
+
+ percentMouse = ( pixelMouse / pixelTotal );
+ if ( percentMouse > 1 ) {
+ percentMouse = 1;
+ }
+ if ( percentMouse < 0 ) {
+ percentMouse = 0;
+ }
+ if ( this.orientation === "vertical" ) {
+ percentMouse = 1 - percentMouse;
+ }
+
+ valueTotal = this._valueMax() - this._valueMin();
+ valueMouse = this._valueMin() + percentMouse * valueTotal;
+
+ return this._trimAlignValue( valueMouse );
+ },
+
+ _start: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+ return this._trigger( "start", event, uiHash );
+ },
+
+ _slide: function( event, index, newVal ) {
+ var otherVal,
+ newValues,
+ allowed;
+
+ if ( this.options.values && this.options.values.length ) {
+ otherVal = this.values( index ? 0 : 1 );
+
+ if ( ( this.options.values.length === 2 && this.options.range === true ) &&
+ ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
+ ) {
+ newVal = otherVal;
+ }
+
+ if ( newVal !== this.values( index ) ) {
+ newValues = this.values();
+ newValues[ index ] = newVal;
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal,
+ values: newValues
+ } );
+ otherVal = this.values( index ? 0 : 1 );
+ if ( allowed !== false ) {
+ this.values( index, newVal, true );
+ }
+ }
+ } else {
+ if ( newVal !== this.value() ) {
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal
+ } );
+ if ( allowed !== false ) {
+ this.value( newVal );
+ }
+ }
+ }
+ },
+
+ _stop: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ this._trigger( "stop", event, uiHash );
+ },
+
+ _change: function( event, index ) {
+ if ( !this._keySliding && !this._mouseSliding ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ this._trigger( "change", event, uiHash );
+ }
+ },
+
+ value: function( newValue ) {
+ if ( arguments.length ) {
+ this.options.value = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, 0 );
+ }
+
+ return this._value();
+ },
+
+ values: function( index, newValue ) {
+ var vals,
+ newValues,
+ i;
+
+ if ( arguments.length > 1 ) {
+ this.options.values[ index ] = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, index );
+ }
+
+ if ( arguments.length ) {
+ if ( $.isArray( arguments[ 0 ] ) ) {
+ vals = this.options.values;
+ newValues = arguments[ 0 ];
+ for ( i = 0; i < vals.length; i += 1 ) {
+ vals[ i ] = this._trimAlignValue( newValues[ i ] );
+ this._change( null, i );
+ }
+ this._refreshValue();
+ } else {
+ if ( this.options.values && this.options.values.length ) {
+ return this._values( index );
+ } else {
+ return this.value();
+ }
+ }
+ } else {
+ return this._values();
+ }
+ },
+
+ _setOption: function( key, value ) {
+ var i,
+ valsLength = 0;
+
+ if ( $.isArray( this.options.values ) ) {
+ valsLength = this.options.values.length;
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+
+ switch ( key ) {
+ case "disabled":
+ if ( value ) {
+ this.handles.filter( ".ui-state-focus" ).blur();
+ this.handles.removeClass( "ui-state-hover" );
+ this.handles.attr( "disabled", "disabled" );
+ this.element.addClass( "ui-disabled" );
+ } else {
+ this.handles.removeAttr( "disabled" );
+ this.element.removeClass( "ui-disabled" );
+ }
+ break;
+ case "orientation":
+ this._detectOrientation();
+ this.element
+ .removeClass( "ui-slider-horizontal ui-slider-vertical" )
+ .addClass( "ui-slider-" + this.orientation );
+ this._refreshValue();
+ break;
+ case "value":
+ this._animateOff = true;
+ this._refreshValue();
+ this._change( null, 0 );
+ this._animateOff = false;
+ break;
+ case "values":
+ this._animateOff = true;
+ this._refreshValue();
+ for ( i = 0; i < valsLength; i += 1 ) {
+ this._change( null, i );
+ }
+ this._animateOff = false;
+ break;
+ }
+ },
+
+ //internal value getter
+ // _value() returns value trimmed by min and max, aligned by step
+ _value: function() {
+ var val = this.options.value;
+ val = this._trimAlignValue( val );
+
+ return val;
+ },
+
+ //internal values getter
+ // _values() returns array of values trimmed by min and max, aligned by step
+ // _values( index ) returns single value trimmed by min and max, aligned by step
+ _values: function( index ) {
+ var val,
+ vals,
+ i;
+
+ if ( arguments.length ) {
+ val = this.options.values[ index ];
+ val = this._trimAlignValue( val );
+
+ return val;
+ } else {
+ // .slice() creates a copy of the array
+ // this copy gets trimmed by min and max and then returned
+ vals = this.options.values.slice();
+ for ( i = 0; i < vals.length; i+= 1) {
+ vals[ i ] = this._trimAlignValue( vals[ i ] );
+ }
+
+ return vals;
+ }
+ },
+
+ // returns the step-aligned value that val is closest to, between (inclusive) min and max
+ _trimAlignValue: function( val ) {
+ if ( val <= this._valueMin() ) {
+ return this._valueMin();
+ }
+ if ( val >= this._valueMax() ) {
+ return this._valueMax();
+ }
+ var step = ( this.options.step > 0 ) ? this.options.step : 1,
+ valModStep = (val - this._valueMin()) % step;
+ alignValue = val - valModStep;
+
+ if ( Math.abs(valModStep) * 2 >= step ) {
+ alignValue += ( valModStep > 0 ) ? step : ( -step );
+ }
+
+ // Since JavaScript has problems with large floats, round
+ // the final value to 5 digits after the decimal point (see #4124)
+ return parseFloat( alignValue.toFixed(5) );
+ },
+
+ _valueMin: function() {
+ return this.options.min;
+ },
+
+ _valueMax: function() {
+ return this.options.max;
+ },
+
+ _refreshValue: function() {
+ var oRange = this.options.range,
+ o = this.options,
+ self = this,
+ animate = ( !this._animateOff ) ? o.animate : false,
+ valPercent,
+ _set = {},
+ lastValPercent,
+ value,
+ valueMin,
+ valueMax;
+
+ if ( this.options.values && this.options.values.length ) {
+ this.handles.each(function( i, j ) {
+ valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100;
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+ if ( self.options.range === true ) {
+ if ( self.orientation === "horizontal" ) {
+ if ( i === 0 ) {
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ } else {
+ if ( i === 0 ) {
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ }
+ lastValPercent = valPercent;
+ });
+ } else {
+ value = this.value();
+ valueMin = this._valueMin();
+ valueMax = this._valueMax();
+ valPercent = ( valueMax !== valueMin ) ?
+ ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
+ 0;
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+
+ if ( oRange === "min" && this.orientation === "horizontal" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "horizontal" ) {
+ this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ if ( oRange === "min" && this.orientation === "vertical" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "vertical" ) {
+ this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ }
+
+});
+
+$.extend( $.ui.slider, {
+ version: "1.8.10"
+});
+
+}(jQuery));
+/*
+ * jQuery UI Tabs 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+var tabId = 0,
+ listId = 0;
+
+function getNextTabId() {
+ return ++tabId;
+}
+
+function getNextListId() {
+ return ++listId;
+}
+
+$.widget( "ui.tabs", {
+ options: {
+ add: null,
+ ajaxOptions: null,
+ cache: false,
+ cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
+ collapsible: false,
+ disable: null,
+ disabled: [],
+ enable: null,
+ event: "click",
+ fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
+ idPrefix: "ui-tabs-",
+ load: null,
+ panelTemplate: "<div></div>",
+ remove: null,
+ select: null,
+ show: null,
+ spinner: "<em>Loading&#8230;</em>",
+ tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
+ },
+
+ _create: function() {
+ this._tabify( true );
+ },
+
+ _setOption: function( key, value ) {
+ if ( key == "selected" ) {
+ if (this.options.collapsible && value == this.options.selected ) {
+ return;
+ }
+ this.select( value );
+ } else {
+ this.options[ key ] = value;
+ this._tabify();
+ }
+ },
+
+ _tabId: function( a ) {
+ return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
+ this.options.idPrefix + getNextTabId();
+ },
+
+ _sanitizeSelector: function( hash ) {
+ // we need this because an id may contain a ":"
+ return hash.replace( /:/g, "\\:" );
+ },
+
+ _cookie: function() {
+ var cookie = this.cookie ||
+ ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );
+ return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );
+ },
+
+ _ui: function( tab, panel ) {
+ return {
+ tab: tab,
+ panel: panel,
+ index: this.anchors.index( tab )
+ };
+ },
+
+ _cleanup: function() {
+ // restore all former loading tabs labels
+ this.lis.filter( ".ui-state-processing" )
+ .removeClass( "ui-state-processing" )
+ .find( "span:data(label.tabs)" )
+ .each(function() {
+ var el = $( this );
+ el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );
+ });
+ },
+
+ _tabify: function( init ) {
+ var self = this,
+ o = this.options,
+ fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
+
+ this.list = this.element.find( "ol,ul" ).eq( 0 );
+ this.lis = $( " > li:has(a[href])", this.list );
+ this.anchors = this.lis.map(function() {
+ return $( "a", this )[ 0 ];
+ });
+ this.panels = $( [] );
+
+ this.anchors.each(function( i, a ) {
+ var href = $( a ).attr( "href" );
+ // For dynamically created HTML that contains a hash as href IE < 8 expands
+ // such href to the full page url with hash and then misinterprets tab as ajax.
+ // Same consideration applies for an added tab with a fragment identifier
+ // since a[href=#fragment-identifier] does unexpectedly not match.
+ // Thus normalize href attribute...
+ var hrefBase = href.split( "#" )[ 0 ],
+ baseEl;
+ if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
+ ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
+ href = a.hash;
+ a.href = href;
+ }
+
+ // inline tab
+ if ( fragmentId.test( href ) ) {
+ self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
+ // remote tab
+ // prevent loading the page itself if href is just "#"
+ } else if ( href && href !== "#" ) {
+ // required for restore on destroy
+ $.data( a, "href.tabs", href );
+
+ // TODO until #3808 is fixed strip fragment identifier from url
+ // (IE fails to load from such url)
+ $.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
+
+ var id = self._tabId( a );
+ a.href = "#" + id;
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
+ .insertAfter( self.panels[ i - 1 ] || self.list );
+ $panel.data( "destroy.tabs", true );
+ }
+ self.panels = self.panels.add( $panel );
+ // invalid tab href
+ } else {
+ o.disabled.push( i );
+ }
+ });
+
+ // initialization from scratch
+ if ( init ) {
+ // attach necessary classes for styling
+ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
+ this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+ this.lis.addClass( "ui-state-default ui-corner-top" );
+ this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
+
+ // Selected tab
+ // use "selected" option or try to retrieve:
+ // 1. from fragment identifier in url
+ // 2. from cookie
+ // 3. from selected class attribute on <li>
+ if ( o.selected === undefined ) {
+ if ( location.hash ) {
+ this.anchors.each(function( i, a ) {
+ if ( a.hash == location.hash ) {
+ o.selected = i;
+ return false;
+ }
+ });
+ }
+ if ( typeof o.selected !== "number" && o.cookie ) {
+ o.selected = parseInt( self._cookie(), 10 );
+ }
+ if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+ o.selected = o.selected || ( this.lis.length ? 0 : -1 );
+ } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
+ o.selected = -1;
+ }
+
+ // sanity check - default to first tab...
+ o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
+ ? o.selected
+ : 0;
+
+ // Take disabling tabs via class attribute from HTML
+ // into account and update option properly.
+ // A selected tab cannot become disabled.
+ o.disabled = $.unique( o.disabled.concat(
+ $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
+ return self.lis.index( n );
+ })
+ ) ).sort();
+
+ if ( $.inArray( o.selected, o.disabled ) != -1 ) {
+ o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );
+ }
+
+ // highlight selected tab
+ this.panels.addClass( "ui-tabs-hide" );
+ this.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ // check for length avoids error when initializing empty list
+ if ( o.selected >= 0 && this.anchors.length ) {
+ self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
+ this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
+
+ // seems to be expected behavior that the show callback is fired
+ self.element.queue( "tabs", function() {
+ self._trigger( "show", null,
+ self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
+ });
+
+ this.load( o.selected );
+ }
+
+ // clean up to avoid memory leaks in certain versions of IE 6
+ // TODO: namespace this event
+ $( window ).bind( "unload", function() {
+ self.lis.add( self.anchors ).unbind( ".tabs" );
+ self.lis = self.anchors = self.panels = null;
+ });
+ // update selected after add/remove
+ } else {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+
+ // update collapsible
+ // TODO: use .toggleClass()
+ this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );
+
+ // set or update cookie after init and add/remove respectively
+ if ( o.cookie ) {
+ this._cookie( o.selected, o.cookie );
+ }
+
+ // disable tabs
+ for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
+ $( li )[ $.inArray( i, o.disabled ) != -1 &&
+ // TODO: use .toggleClass()
+ !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );
+ }
+
+ // reset cache if switching from cached to not cached
+ if ( o.cache === false ) {
+ this.anchors.removeData( "cache.tabs" );
+ }
+
+ // remove all handlers before, tabify may run on existing tabs after add or option change
+ this.lis.add( this.anchors ).unbind( ".tabs" );
+
+ if ( o.event !== "mouseover" ) {
+ var addState = function( state, el ) {
+ if ( el.is( ":not(.ui-state-disabled)" ) ) {
+ el.addClass( "ui-state-" + state );
+ }
+ };
+ var removeState = function( state, el ) {
+ el.removeClass( "ui-state-" + state );
+ };
+ this.lis.bind( "mouseover.tabs" , function() {
+ addState( "hover", $( this ) );
+ });
+ this.lis.bind( "mouseout.tabs", function() {
+ removeState( "hover", $( this ) );
+ });
+ this.anchors.bind( "focus.tabs", function() {
+ addState( "focus", $( this ).closest( "li" ) );
+ });
+ this.anchors.bind( "blur.tabs", function() {
+ removeState( "focus", $( this ).closest( "li" ) );
+ });
+ }
+
+ // set up animations
+ var hideFx, showFx;
+ if ( o.fx ) {
+ if ( $.isArray( o.fx ) ) {
+ hideFx = o.fx[ 0 ];
+ showFx = o.fx[ 1 ];
+ } else {
+ hideFx = showFx = o.fx;
+ }
+ }
+
+ // Reset certain styles left over from animation
+ // and prevent IE's ClearType bug...
+ function resetStyle( $el, fx ) {
+ $el.css( "display", "" );
+ if ( !$.support.opacity && fx.opacity ) {
+ $el[ 0 ].style.removeAttribute( "filter" );
+ }
+ }
+
+ // Show a tab...
+ var showTab = showFx
+ ? function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way
+ .animate( showFx, showFx.duration || "normal", function() {
+ resetStyle( $show, showFx );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ });
+ }
+ : function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.removeClass( "ui-tabs-hide" );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ };
+
+ // Hide a tab, $show is optional...
+ var hideTab = hideFx
+ ? function( clicked, $hide ) {
+ $hide.animate( hideFx, hideFx.duration || "normal", function() {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ resetStyle( $hide, hideFx );
+ self.element.dequeue( "tabs" );
+ });
+ }
+ : function( clicked, $hide, $show ) {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ self.element.dequeue( "tabs" );
+ };
+
+ // attach tab event handler, unbind to avoid duplicates from former tabifying...
+ this.anchors.bind( o.event + ".tabs", function() {
+ var el = this,
+ $li = $(el).closest( "li" ),
+ $hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
+ $show = self.element.find( self._sanitizeSelector( el.hash ) );
+
+ // If tab is already selected and not collapsible or tab disabled or
+ // or is already loading or click callback returns false stop here.
+ // Check if click handler returns false last so that it is not executed
+ // for a disabled or loading tab!
+ if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||
+ $li.hasClass( "ui-state-disabled" ) ||
+ $li.hasClass( "ui-state-processing" ) ||
+ self.panels.filter( ":animated" ).length ||
+ self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {
+ this.blur();
+ return false;
+ }
+
+ o.selected = self.anchors.index( this );
+
+ self.abort();
+
+ // if tab may be closed
+ if ( o.collapsible ) {
+ if ( $li.hasClass( "ui-tabs-selected" ) ) {
+ o.selected = -1;
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ }).dequeue( "tabs" );
+
+ this.blur();
+ return false;
+ } else if ( !$hide.length ) {
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
+ self.load( self.anchors.index( this ) );
+
+ this.blur();
+ return false;
+ }
+ }
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ // show new tab
+ if ( $show.length ) {
+ if ( $hide.length ) {
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ });
+ }
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ self.load( self.anchors.index( this ) );
+ } else {
+ throw "jQuery UI Tabs: Mismatching fragment identifier.";
+ }
+
+ // Prevent IE from keeping other link focussed when using the back button
+ // and remove dotted border from clicked link. This is controlled via CSS
+ // in modern browsers; blur() removes focus from address bar in Firefox
+ // which can become a usability and annoying problem with tabs('rotate').
+ if ( $.browser.msie ) {
+ this.blur();
+ }
+ });
+
+ // disable click in any case
+ this.anchors.bind( "click.tabs", function(){
+ return false;
+ });
+ },
+
+ _getIndex: function( index ) {
+ // meta-function to give users option to provide a href string instead of a numerical index.
+ // also sanitizes numerical indexes to valid values.
+ if ( typeof index == "string" ) {
+ index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) );
+ }
+
+ return index;
+ },
+
+ destroy: function() {
+ var o = this.options;
+
+ this.abort();
+
+ this.element
+ .unbind( ".tabs" )
+ .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" )
+ .removeData( "tabs" );
+
+ this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+
+ this.anchors.each(function() {
+ var href = $.data( this, "href.tabs" );
+ if ( href ) {
+ this.href = href;
+ }
+ var $this = $( this ).unbind( ".tabs" );
+ $.each( [ "href", "load", "cache" ], function( i, prefix ) {
+ $this.removeData( prefix + ".tabs" );
+ });
+ });
+
+ this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {
+ if ( $.data( this, "destroy.tabs" ) ) {
+ $( this ).remove();
+ } else {
+ $( this ).removeClass([
+ "ui-state-default",
+ "ui-corner-top",
+ "ui-tabs-selected",
+ "ui-state-active",
+ "ui-state-hover",
+ "ui-state-focus",
+ "ui-state-disabled",
+ "ui-tabs-panel",
+ "ui-widget-content",
+ "ui-corner-bottom",
+ "ui-tabs-hide"
+ ].join( " " ) );
+ }
+ });
+
+ if ( o.cookie ) {
+ this._cookie( null, o.cookie );
+ }
+
+ return this;
+ },
+
+ add: function( url, label, index ) {
+ if ( index === undefined ) {
+ index = this.anchors.length;
+ }
+
+ var self = this,
+ o = this.options,
+ $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),
+ id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );
+
+ $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );
+
+ // try to find an existing element before creating a new one
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .data( "destroy.tabs", true );
+ }
+ $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );
+
+ if ( index >= this.lis.length ) {
+ $li.appendTo( this.list );
+ $panel.appendTo( this.list[ 0 ].parentNode );
+ } else {
+ $li.insertBefore( this.lis[ index ] );
+ $panel.insertBefore( this.panels[ index ] );
+ }
+
+ o.disabled = $.map( o.disabled, function( n, i ) {
+ return n >= index ? ++n : n;
+ });
+
+ this._tabify();
+
+ if ( this.anchors.length == 1 ) {
+ o.selected = 0;
+ $li.addClass( "ui-tabs-selected ui-state-active" );
+ $panel.removeClass( "ui-tabs-hide" );
+ this.element.queue( "tabs", function() {
+ self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );
+ });
+
+ this.load( 0 );
+ }
+
+ this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ remove: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options,
+ $li = this.lis.eq( index ).remove(),
+ $panel = this.panels.eq( index ).remove();
+
+ // If selected tab was removed focus tab to the right or
+ // in case the last tab was removed the tab to the left.
+ if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {
+ this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
+ }
+
+ o.disabled = $.map(
+ $.grep( o.disabled, function(n, i) {
+ return n != index;
+ }),
+ function( n, i ) {
+ return n >= index ? --n : n;
+ });
+
+ this._tabify();
+
+ this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
+ return this;
+ },
+
+ enable: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options;
+ if ( $.inArray( index, o.disabled ) == -1 ) {
+ return;
+ }
+
+ this.lis.eq( index ).removeClass( "ui-state-disabled" );
+ o.disabled = $.grep( o.disabled, function( n, i ) {
+ return n != index;
+ });
+
+ this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ disable: function( index ) {
+ index = this._getIndex( index );
+ var self = this, o = this.options;
+ // cannot disable already selected tab
+ if ( index != o.selected ) {
+ this.lis.eq( index ).addClass( "ui-state-disabled" );
+
+ o.disabled.push( index );
+ o.disabled.sort();
+
+ this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ }
+
+ return this;
+ },
+
+ select: function( index ) {
+ index = this._getIndex( index );
+ if ( index == -1 ) {
+ if ( this.options.collapsible && this.options.selected != -1 ) {
+ index = this.options.selected;
+ } else {
+ return this;
+ }
+ }
+ this.anchors.eq( index ).trigger( this.options.event + ".tabs" );
+ return this;
+ },
+
+ load: function( index ) {
+ index = this._getIndex( index );
+ var self = this,
+ o = this.options,
+ a = this.anchors.eq( index )[ 0 ],
+ url = $.data( a, "load.tabs" );
+
+ this.abort();
+
+ // not remote or from cache
+ if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {
+ this.element.dequeue( "tabs" );
+ return;
+ }
+
+ // load remote from here on
+ this.lis.eq( index ).addClass( "ui-state-processing" );
+
+ if ( o.spinner ) {
+ var span = $( "span", a );
+ span.data( "label.tabs", span.html() ).html( o.spinner );
+ }
+
+ this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {
+ url: url,
+ success: function( r, s ) {
+ self.element.find( self._sanitizeSelector( a.hash ) ).html( r );
+
+ // take care of tab labels
+ self._cleanup();
+
+ if ( o.cache ) {
+ $.data( a, "cache.tabs", true );
+ }
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ o.ajaxOptions.success( r, s );
+ }
+ catch ( e ) {}
+ },
+ error: function( xhr, s, e ) {
+ // take care of tab labels
+ self._cleanup();
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ // Passing index avoid a race condition when this method is
+ // called after the user has selected another tab.
+ // Pass the anchor that initiated this request allows
+ // loadError to manipulate the tab content panel via $(a.hash)
+ o.ajaxOptions.error( xhr, s, index, a );
+ }
+ catch ( e ) {}
+ }
+ } ) );
+
+ // last, so that load event is fired before show...
+ self.element.dequeue( "tabs" );
+
+ return this;
+ },
+
+ abort: function() {
+ // stop possibly running animations
+ this.element.queue( [] );
+ this.panels.stop( false, true );
+
+ // "tabs" queue must not contain more than two elements,
+ // which are the callbacks for the latest clicked tab...
+ this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );
+
+ // terminate pending requests from other tabs
+ if ( this.xhr ) {
+ this.xhr.abort();
+ delete this.xhr;
+ }
+
+ // take care of tab labels
+ this._cleanup();
+ return this;
+ },
+
+ url: function( index, url ) {
+ this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );
+ return this;
+ },
+
+ length: function() {
+ return this.anchors.length;
+ }
+});
+
+$.extend( $.ui.tabs, {
+ version: "1.8.10"
+});
+
+/*
+ * Tabs Extensions
+ */
+
+/*
+ * Rotate
+ */
+$.extend( $.ui.tabs.prototype, {
+ rotation: null,
+ rotate: function( ms, continuing ) {
+ var self = this,
+ o = this.options;
+
+ var rotate = self._rotate || ( self._rotate = function( e ) {
+ clearTimeout( self.rotation );
+ self.rotation = setTimeout(function() {
+ var t = o.selected;
+ self.select( ++t < self.anchors.length ? t : 0 );
+ }, ms );
+
+ if ( e ) {
+ e.stopPropagation();
+ }
+ });
+
+ var stop = self._unrotate || ( self._unrotate = !continuing
+ ? function(e) {
+ if (e.clientX) { // in case of a true click
+ self.rotate(null);
+ }
+ }
+ : function( e ) {
+ t = o.selected;
+ rotate();
+ });
+
+ // start rotation
+ if ( ms ) {
+ this.element.bind( "tabsshow", rotate );
+ this.anchors.bind( o.event + ".tabs", stop );
+ rotate();
+ // stop rotation
+ } else {
+ clearTimeout( self.rotation );
+ this.element.unbind( "tabsshow", rotate );
+ this.anchors.unbind( o.event + ".tabs", stop );
+ delete this._rotate;
+ delete this._unrotate;
+ }
+
+ return this;
+ }
+});
+
+})( jQuery );
+/*
+ * jQuery UI Datepicker 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */
+(function( $, undefined ) {
+
+$.extend($.ui, { datepicker: { version: "1.8.10" } });
+
+var PROP_NAME = 'datepicker';
+var dpuuid = new Date().getTime();
+
+/* Date picker manager.
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
+ Settings for (groups of) date pickers are maintained in an instance object,
+ allowing multiple different settings on the same page. */
+
+function Datepicker() {
+ this.debug = false; // Change this to true to start debugging
+ this._curInst = null; // The current instance in use
+ this._keyEvent = false; // If the last event was a key event
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
+ this._inDialog = false; // True if showing within a "dialog", false if not
+ this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
+ this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
+ this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
+ this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
+ this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
+ this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
+ this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
+ this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
+ this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
+ this.regional = []; // Available regional settings, indexed by language code
+ this.regional[''] = { // Default regional settings
+ closeText: 'Done', // Display text for close link
+ prevText: 'Prev', // Display text for previous month link
+ nextText: 'Next', // Display text for next month link
+ currentText: 'Today', // Display text for current month link
+ monthNames: ['January','February','March','April','May','June',
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
+ dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
+ weekHeader: 'Wk', // Column header for week of the year
+ dateFormat: 'mm/dd/yy', // See format options on parseDate
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
+ isRTL: false, // True if right-to-left language, false if left-to-right
+ showMonthAfterYear: false, // True if the year select precedes month, false for month then year
+ yearSuffix: '' // Additional text to append to the year in the month headers
+ };
+ this._defaults = { // Global defaults for all the date picker instances
+ showOn: 'focus', // 'focus' for popup on focus,
+ // 'button' for trigger button, or 'both' for either
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
+ showOptions: {}, // Options for enhanced animations
+ defaultDate: null, // Used when field is blank: actual date,
+ // +/-number for offset from today, null for today
+ appendText: '', // Display text following the input box, e.g. showing the format
+ buttonText: '...', // Text for trigger button
+ buttonImage: '', // URL for trigger button image
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
+ hideIfNoPrevNext: false, // True to hide next/previous month links
+ // if not applicable, false to just disable them
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
+ gotoCurrent: false, // True if today link goes back to current selection instead
+ changeMonth: false, // True if month can be selected directly, false if only prev/next
+ changeYear: false, // True if year can be selected directly, false if only prev/next
+ yearRange: 'c-10:c+10', // Range of years to display in drop-down,
+ // either relative to today's year (-nn:+nn), relative to currently displayed year
+ // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
+ selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
+ showWeek: false, // True to show week of the year, false to not show it
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
+ // takes a Date and returns the number of the week for it
+ shortYearCutoff: '+10', // Short year values < this are in the current century,
+ // > this are in the previous century,
+ // string value starting with '+' for current year + value
+ minDate: null, // The earliest selectable date, or null for no limit
+ maxDate: null, // The latest selectable date, or null for no limit
+ duration: 'fast', // Duration of display/closure
+ beforeShowDay: null, // Function that takes a date and returns an array with
+ // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
+ // [2] = cell title (optional), e.g. $.datepicker.noWeekends
+ beforeShow: null, // Function that takes an input field and
+ // returns a set of custom settings for the date picker
+ onSelect: null, // Define a callback function when a date is selected
+ onChangeMonthYear: null, // Define a callback function when the month or year is changed
+ onClose: null, // Define a callback function when the datepicker is closed
+ numberOfMonths: 1, // Number of months to show at a time
+ showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
+ stepMonths: 1, // Number of months to step back/forward
+ stepBigMonths: 12, // Number of months to step back/forward for the big links
+ altField: '', // Selector for an alternate field to store selected dates into
+ altFormat: '', // The date format to use for the alternate field
+ constrainInput: true, // The input is constrained by the current date format
+ showButtonPanel: false, // True to show button panel, false to not show it
+ autoSize: false // True to size the input for the date format, false to leave as is
+ };
+ $.extend(this._defaults, this.regional['']);
+ this.dpDiv = $('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>');
+}
+
+$.extend(Datepicker.prototype, {
+ /* Class name added to elements to indicate already configured with a date picker. */
+ markerClassName: 'hasDatepicker',
+
+ /* Debug logging (if enabled). */
+ log: function () {
+ if (this.debug)
+ console.log.apply('', arguments);
+ },
+
+ // TODO rename to "widget" when switching to widget factory
+ _widgetDatepicker: function() {
+ return this.dpDiv;
+ },
+
+ /* Override the default settings for all instances of the date picker.
+ @param settings object - the new settings to use as defaults (anonymous object)
+ @return the manager object */
+ setDefaults: function(settings) {
+ extendRemove(this._defaults, settings || {});
+ return this;
+ },
+
+ /* Attach the date picker to a jQuery selection.
+ @param target element - the target input field or division or span
+ @param settings object - the new settings to use for this date picker instance (anonymous) */
+ _attachDatepicker: function(target, settings) {
+ // check for settings on the control itself - in namespace 'date:'
+ var inlineSettings = null;
+ for (var attrName in this._defaults) {
+ var attrValue = target.getAttribute('date:' + attrName);
+ if (attrValue) {
+ inlineSettings = inlineSettings || {};
+ try {
+ inlineSettings[attrName] = eval(attrValue);
+ } catch (err) {
+ inlineSettings[attrName] = attrValue;
+ }
+ }
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ var inline = (nodeName == 'div' || nodeName == 'span');
+ if (!target.id) {
+ this.uuid += 1;
+ target.id = 'dp' + this.uuid;
+ }
+ var inst = this._newInst($(target), inline);
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
+ if (nodeName == 'input') {
+ this._connectDatepicker(target, inst);
+ } else if (inline) {
+ this._inlineDatepicker(target, inst);
+ }
+ },
+
+ /* Create a new instance object. */
+ _newInst: function(target, inline) {
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
+ return {id: id, input: target, // associated target
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
+ drawMonth: 0, drawYear: 0, // month being drawn
+ inline: inline, // is datepicker inline or not
+ dpDiv: (!inline ? this.dpDiv : // presentation div
+ $('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))};
+ },
+
+ /* Attach the date picker to an input field. */
+ _connectDatepicker: function(target, inst) {
+ var input = $(target);
+ inst.append = $([]);
+ inst.trigger = $([]);
+ if (input.hasClass(this.markerClassName))
+ return;
+ this._attachments(input, inst);
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).
+ keypress(this._doKeyPress).keyup(this._doKeyUp).
+ bind("setData.datepicker", function(event, key, value) {
+ inst.settings[key] = value;
+ }).bind("getData.datepicker", function(event, key) {
+ return this._get(inst, key);
+ });
+ this._autoSize(inst);
+ $.data(target, PROP_NAME, inst);
+ },
+
+ /* Make attachments based on settings. */
+ _attachments: function(input, inst) {
+ var appendText = this._get(inst, 'appendText');
+ var isRTL = this._get(inst, 'isRTL');
+ if (inst.append)
+ inst.append.remove();
+ if (appendText) {
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
+ input[isRTL ? 'before' : 'after'](inst.append);
+ }
+ input.unbind('focus', this._showDatepicker);
+ if (inst.trigger)
+ inst.trigger.remove();
+ var showOn = this._get(inst, 'showOn');
+ if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
+ input.focus(this._showDatepicker);
+ if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
+ var buttonText = this._get(inst, 'buttonText');
+ var buttonImage = this._get(inst, 'buttonImage');
+ inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
+ $('<img/>').addClass(this._triggerClass).
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
+ $('<button type="button"></button>').addClass(this._triggerClass).
+ html(buttonImage == '' ? buttonText : $('<img/>').attr(
+ { src:buttonImage, alt:buttonText, title:buttonText })));
+ input[isRTL ? 'before' : 'after'](inst.trigger);
+ inst.trigger.click(function() {
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
+ $.datepicker._hideDatepicker();
+ else
+ $.datepicker._showDatepicker(input[0]);
+ return false;
+ });
+ }
+ },
+
+ /* Apply the maximum length for the date format. */
+ _autoSize: function(inst) {
+ if (this._get(inst, 'autoSize') && !inst.inline) {
+ var date = new Date(2009, 12 - 1, 20); // Ensure double digits
+ var dateFormat = this._get(inst, 'dateFormat');
+ if (dateFormat.match(/[DM]/)) {
+ var findMax = function(names) {
+ var max = 0;
+ var maxI = 0;
+ for (var i = 0; i < names.length; i++) {
+ if (names[i].length > max) {
+ max = names[i].length;
+ maxI = i;
+ }
+ }
+ return maxI;
+ };
+ date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
+ 'monthNames' : 'monthNamesShort'))));
+ date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
+ 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
+ }
+ inst.input.attr('size', this._formatDate(inst, date).length);
+ }
+ },
+
+ /* Attach an inline date picker to a div. */
+ _inlineDatepicker: function(target, inst) {
+ var divSpan = $(target);
+ if (divSpan.hasClass(this.markerClassName))
+ return;
+ divSpan.addClass(this.markerClassName).append(inst.dpDiv).
+ bind("setData.datepicker", function(event, key, value){
+ inst.settings[key] = value;
+ }).bind("getData.datepicker", function(event, key){
+ return this._get(inst, key);
+ });
+ $.data(target, PROP_NAME, inst);
+ this._setDate(inst, this._getDefaultDate(inst), true);
+ this._updateDatepicker(inst);
+ this._updateAlternate(inst);
+ inst.dpDiv.show();
+ },
+
+ /* Pop-up the date picker in a "dialog" box.
+ @param input element - ignored
+ @param date string or Date - the initial date to display
+ @param onSelect function - the function to call when a date is selected
+ @param settings object - update the dialog date picker instance's settings (anonymous object)
+ @param pos int[2] - coordinates for the dialog's position within the screen or
+ event - with x/y coordinates or
+ leave empty for default (screen centre)
+ @return the manager object */
+ _dialogDatepicker: function(input, date, onSelect, settings, pos) {
+ var inst = this._dialogInst; // internal instance
+ if (!inst) {
+ this.uuid += 1;
+ var id = 'dp' + this.uuid;
+ this._dialogInput = $('<input type="text" id="' + id +
+ '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
+ this._dialogInput.keydown(this._doKeyDown);
+ $('body').append(this._dialogInput);
+ inst = this._dialogInst = this._newInst(this._dialogInput, false);
+ inst.settings = {};
+ $.data(this._dialogInput[0], PROP_NAME, inst);
+ }
+ extendRemove(inst.settings, settings || {});
+ date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
+ this._dialogInput.val(date);
+
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
+ if (!this._pos) {
+ var browserWidth = document.documentElement.clientWidth;
+ var browserHeight = document.documentElement.clientHeight;
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
+ this._pos = // should use actual width/height below
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
+ }
+
+ // move input on screen for focus, but hidden behind dialog
+ this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
+ inst.settings.onSelect = onSelect;
+ this._inDialog = true;
+ this.dpDiv.addClass(this._dialogClass);
+ this._showDatepicker(this._dialogInput[0]);
+ if ($.blockUI)
+ $.blockUI(this.dpDiv);
+ $.data(this._dialogInput[0], PROP_NAME, inst);
+ return this;
+ },
+
+ /* Detach a datepicker from its control.
+ @param target element - the target input field or division or span */
+ _destroyDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ $.removeData(target, PROP_NAME);
+ if (nodeName == 'input') {
+ inst.append.remove();
+ inst.trigger.remove();
+ $target.removeClass(this.markerClassName).
+ unbind('focus', this._showDatepicker).
+ unbind('keydown', this._doKeyDown).
+ unbind('keypress', this._doKeyPress).
+ unbind('keyup', this._doKeyUp);
+ } else if (nodeName == 'div' || nodeName == 'span')
+ $target.removeClass(this.markerClassName).empty();
+ },
+
+ /* Enable the date picker to a jQuery selection.
+ @param target element - the target input field or division or span */
+ _enableDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ if (nodeName == 'input') {
+ target.disabled = false;
+ inst.trigger.filter('button').
+ each(function() { this.disabled = false; }).end().
+ filter('img').css({opacity: '1.0', cursor: ''});
+ }
+ else if (nodeName == 'div' || nodeName == 'span') {
+ var inline = $target.children('.' + this._inlineClass);
+ inline.children().removeClass('ui-state-disabled');
+ }
+ this._disabledInputs = $.map(this._disabledInputs,
+ function(value) { return (value == target ? null : value); }); // delete entry
+ },
+
+ /* Disable the date picker to a jQuery selection.
+ @param target element - the target input field or division or span */
+ _disableDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ if (nodeName == 'input') {
+ target.disabled = true;
+ inst.trigger.filter('button').
+ each(function() { this.disabled = true; }).end().
+ filter('img').css({opacity: '0.5', cursor: 'default'});
+ }
+ else if (nodeName == 'div' || nodeName == 'span') {
+ var inline = $target.children('.' + this._inlineClass);
+ inline.children().addClass('ui-state-disabled');
+ }
+ this._disabledInputs = $.map(this._disabledInputs,
+ function(value) { return (value == target ? null : value); }); // delete entry
+ this._disabledInputs[this._disabledInputs.length] = target;
+ },
+
+ /* Is the first field in a jQuery collection disabled as a datepicker?
+ @param target element - the target input field or division or span
+ @return boolean - true if disabled, false if enabled */
+ _isDisabledDatepicker: function(target) {
+ if (!target) {
+ return false;
+ }
+ for (var i = 0; i < this._disabledInputs.length; i++) {
+ if (this._disabledInputs[i] == target)
+ return true;
+ }
+ return false;
+ },
+
+ /* Retrieve the instance data for the target control.
+ @param target element - the target input field or division or span
+ @return object - the associated instance data
+ @throws error if a jQuery problem getting data */
+ _getInst: function(target) {
+ try {
+ return $.data(target, PROP_NAME);
+ }
+ catch (err) {
+ throw 'Missing instance data for this datepicker';
+ }
+ },
+
+ /* Update or retrieve the settings for a date picker attached to an input field or division.
+ @param target element - the target input field or division or span
+ @param name object - the new settings to update or
+ string - the name of the setting to change or retrieve,
+ when retrieving also 'all' for all instance settings or
+ 'defaults' for all global defaults
+ @param value any - the new value for the setting
+ (omit if above is an object or to retrieve a value) */
+ _optionDatepicker: function(target, name, value) {
+ var inst = this._getInst(target);
+ if (arguments.length == 2 && typeof name == 'string') {
+ return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
+ this._get(inst, name)) : null));
+ }
+ var settings = name || {};
+ if (typeof name == 'string') {
+ settings = {};
+ settings[name] = value;
+ }
+ if (inst) {
+ if (this._curInst == inst) {
+ this._hideDatepicker();
+ }
+ var date = this._getDateDatepicker(target, true);
+ extendRemove(inst.settings, settings);
+ this._attachments($(target), inst);
+ this._autoSize(inst);
+ this._setDateDatepicker(target, date);
+ this._updateDatepicker(inst);
+ }
+ },
+
+ // change method deprecated
+ _changeDatepicker: function(target, name, value) {
+ this._optionDatepicker(target, name, value);
+ },
+
+ /* Redraw the date picker attached to an input field or division.
+ @param target element - the target input field or division or span */
+ _refreshDatepicker: function(target) {
+ var inst = this._getInst(target);
+ if (inst) {
+ this._updateDatepicker(inst);
+ }
+ },
+
+ /* Set the dates for a jQuery selection.
+ @param target element - the target input field or division or span
+ @param date Date - the new date */
+ _setDateDatepicker: function(target, date) {
+ var inst = this._getInst(target);
+ if (inst) {
+ this._setDate(inst, date);
+ this._updateDatepicker(inst);
+ this._updateAlternate(inst);
+ }
+ },
+
+ /* Get the date(s) for the first entry in a jQuery selection.
+ @param target element - the target input field or division or span
+ @param noDefault boolean - true if no default date is to be used
+ @return Date - the current date */
+ _getDateDatepicker: function(target, noDefault) {
+ var inst = this._getInst(target);
+ if (inst && !inst.inline)
+ this._setDateFromField(inst, noDefault);
+ return (inst ? this._getDate(inst) : null);
+ },
+
+ /* Handle keystrokes. */
+ _doKeyDown: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ var handled = true;
+ var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
+ inst._keyEvent = true;
+ if ($.datepicker._datepickerShowing)
+ switch (event.keyCode) {
+ case 9: $.datepicker._hideDatepicker();
+ handled = false;
+ break; // hide on tab out
+ case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
+ $.datepicker._currentClass + ')', inst.dpDiv);
+ if (sel[0])
+ $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
+ else
+ $.datepicker._hideDatepicker();
+ return false; // don't submit the form
+ break; // select the value on enter
+ case 27: $.datepicker._hideDatepicker();
+ break; // hide on escape
+ case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ -$.datepicker._get(inst, 'stepBigMonths') :
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
+ break; // previous month/year on page up/+ ctrl
+ case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ +$.datepicker._get(inst, 'stepBigMonths') :
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
+ break; // next month/year on page down/+ ctrl
+ case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
+ handled = event.ctrlKey || event.metaKey;
+ break; // clear on ctrl or command +end
+ case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
+ handled = event.ctrlKey || event.metaKey;
+ break; // current on ctrl or command +home
+ case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
+ handled = event.ctrlKey || event.metaKey;
+ // -1 day on ctrl or command +left
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ -$.datepicker._get(inst, 'stepBigMonths') :
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
+ // next month/year on alt +left on Mac
+ break;
+ case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
+ handled = event.ctrlKey || event.metaKey;
+ break; // -1 week on ctrl or command +up
+ case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
+ handled = event.ctrlKey || event.metaKey;
+ // +1 day on ctrl or command +right
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ +$.datepicker._get(inst, 'stepBigMonths') :
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
+ // next month/year on alt +right
+ break;
+ case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
+ handled = event.ctrlKey || event.metaKey;
+ break; // +1 week on ctrl or command +down
+ default: handled = false;
+ }
+ else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
+ $.datepicker._showDatepicker(this);
+ else {
+ handled = false;
+ }
+ if (handled) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ /* Filter entered characters - based on date format. */
+ _doKeyPress: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ if ($.datepicker._get(inst, 'constrainInput')) {
+ var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
+ var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
+ return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
+ }
+ },
+
+ /* Synchronise manual entry and field/alternate field. */
+ _doKeyUp: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ if (inst.input.val() != inst.lastVal) {
+ try {
+ var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
+ (inst.input ? inst.input.val() : null),
+ $.datepicker._getFormatConfig(inst));
+ if (date) { // only if valid
+ $.datepicker._setDateFromField(inst);
+ $.datepicker._updateAlternate(inst);
+ $.datepicker._updateDatepicker(inst);
+ }
+ }
+ catch (event) {
+ $.datepicker.log(event);
+ }
+ }
+ return true;
+ },
+
+ /* Pop-up the date picker for a given input field.
+ @param input element - the input field attached to the date picker or
+ event - if triggered by focus */
+ _showDatepicker: function(input) {
+ input = input.target || input;
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
+ input = $('input', input.parentNode)[0];
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
+ return;
+ var inst = $.datepicker._getInst(input);
+ if ($.datepicker._curInst && $.datepicker._curInst != inst) {
+ $.datepicker._curInst.dpDiv.stop(true, true);
+ }
+ var beforeShow = $.datepicker._get(inst, 'beforeShow');
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
+ inst.lastVal = null;
+ $.datepicker._lastInput = input;
+ $.datepicker._setDateFromField(inst);
+ if ($.datepicker._inDialog) // hide cursor
+ input.value = '';
+ if (!$.datepicker._pos) { // position below input
+ $.datepicker._pos = $.datepicker._findPos(input);
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
+ }
+ var isFixed = false;
+ $(input).parents().each(function() {
+ isFixed |= $(this).css('position') == 'fixed';
+ return !isFixed;
+ });
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
+ $.datepicker._pos[0] -= document.documentElement.scrollLeft;
+ $.datepicker._pos[1] -= document.documentElement.scrollTop;
+ }
+ var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
+ $.datepicker._pos = null;
+ //to avoid flashes on Firefox
+ inst.dpDiv.empty();
+ // determine sizing offscreen
+ inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
+ $.datepicker._updateDatepicker(inst);
+ // fix width for dynamic number of date pickers
+ // and adjust position before showing
+ offset = $.datepicker._checkOffset(inst, offset, isFixed);
+ inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
+ left: offset.left + 'px', top: offset.top + 'px'});
+ if (!inst.inline) {
+ var showAnim = $.datepicker._get(inst, 'showAnim');
+ var duration = $.datepicker._get(inst, 'duration');
+ var postProcess = function() {
+ $.datepicker._datepickerShowing = true;
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !! cover.length ){
+ var borders = $.datepicker._getBorders(inst.dpDiv);
+ cover.css({left: -borders[0], top: -borders[1],
+ width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
+ }
+ };
+ inst.dpDiv.zIndex($(input).zIndex()+1);
+ if ($.effects && $.effects[showAnim])
+ inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
+ else
+ inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
+ if (!showAnim || !duration)
+ postProcess();
+ if (inst.input.is(':visible') && !inst.input.is(':disabled'))
+ inst.input.focus();
+ $.datepicker._curInst = inst;
+ }
+ },
+
+ /* Generate the date picker content. */
+ _updateDatepicker: function(inst) {
+ var self = this;
+ var borders = $.datepicker._getBorders(inst.dpDiv);
+ inst.dpDiv.empty().append(this._generateHTML(inst));
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
+ cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
+ }
+ inst.dpDiv.find('button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a')
+ .bind('mouseout', function(){
+ $(this).removeClass('ui-state-hover');
+ if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).removeClass('ui-datepicker-prev-hover');
+ if(this.className.indexOf('ui-datepicker-next') != -1) $(this).removeClass('ui-datepicker-next-hover');
+ })
+ .bind('mouseover', function(){
+ if (!self._isDisabledDatepicker( inst.inline ? inst.dpDiv.parent()[0] : inst.input[0])) {
+ $(this).parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
+ $(this).addClass('ui-state-hover');
+ if(this.className.indexOf('ui-datepicker-prev') != -1) $(this).addClass('ui-datepicker-prev-hover');
+ if(this.className.indexOf('ui-datepicker-next') != -1) $(this).addClass('ui-datepicker-next-hover');
+ }
+ })
+ .end()
+ .find('.' + this._dayOverClass + ' a')
+ .trigger('mouseover')
+ .end();
+ var numMonths = this._getNumberOfMonths(inst);
+ var cols = numMonths[1];
+ var width = 17;
+ if (cols > 1)
+ inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
+ else
+ inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
+ inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
+ 'Class']('ui-datepicker-multi');
+ inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
+ 'Class']('ui-datepicker-rtl');
+ if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
+ // #6694 - don't focus the input if it's already focused
+ // this breaks the change event in IE
+ inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
+ inst.input.focus();
+ // deffered render of the years select (to avoid flashes on Firefox)
+ if( inst.yearshtml ){
+ var origyearshtml = inst.yearshtml;
+ setTimeout(function(){
+ //assure that inst.yearshtml didn't change.
+ if( origyearshtml === inst.yearshtml ){
+ inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
+ }
+ origyearshtml = inst.yearshtml = null;
+ }, 0);
+ }
+ },
+
+ /* Retrieve the size of left and top borders for an element.
+ @param elem (jQuery object) the element of interest
+ @return (number[2]) the left and top borders */
+ _getBorders: function(elem) {
+ var convert = function(value) {
+ return {thin: 1, medium: 2, thick: 3}[value] || value;
+ };
+ return [parseFloat(convert(elem.css('border-left-width'))),
+ parseFloat(convert(elem.css('border-top-width')))];
+ },
+
+ /* Check positioning to remain on screen. */
+ _checkOffset: function(inst, offset, isFixed) {
+ var dpWidth = inst.dpDiv.outerWidth();
+ var dpHeight = inst.dpDiv.outerHeight();
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
+
+ offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
+
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
+ offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
+ offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
+ Math.abs(dpHeight + inputHeight) : 0);
+
+ return offset;
+ },
+
+ /* Find an object's position on the screen. */
+ _findPos: function(obj) {
+ var inst = this._getInst(obj);
+ var isRTL = this._get(inst, 'isRTL');
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
+ }
+ var position = $(obj).offset();
+ return [position.left, position.top];
+ },
+
+ /* Hide the date picker from view.
+ @param input element - the input field attached to the date picker */
+ _hideDatepicker: function(input) {
+ var inst = this._curInst;
+ if (!inst || (input && inst != $.data(input, PROP_NAME)))
+ return;
+ if (this._datepickerShowing) {
+ var showAnim = this._get(inst, 'showAnim');
+ var duration = this._get(inst, 'duration');
+ var postProcess = function() {
+ $.datepicker._tidyDialog(inst);
+ this._curInst = null;
+ };
+ if ($.effects && $.effects[showAnim])
+ inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
+ else
+ inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
+ if (!showAnim)
+ postProcess();
+ var onClose = this._get(inst, 'onClose');
+ if (onClose)
+ onClose.apply((inst.input ? inst.input[0] : null),
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
+ this._datepickerShowing = false;
+ this._lastInput = null;
+ if (this._inDialog) {
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
+ if ($.blockUI) {
+ $.unblockUI();
+ $('body').append(this.dpDiv);
+ }
+ }
+ this._inDialog = false;
+ }
+ },
+
+ /* Tidy up after a dialog display. */
+ _tidyDialog: function(inst) {
+ inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
+ },
+
+ /* Close date picker if clicked elsewhere. */
+ _checkExternalClick: function(event) {
+ if (!$.datepicker._curInst)
+ return;
+ var $target = $(event.target);
+ if ($target[0].id != $.datepicker._mainDivId &&
+ $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
+ !$target.hasClass($.datepicker.markerClassName) &&
+ !$target.hasClass($.datepicker._triggerClass) &&
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
+ $.datepicker._hideDatepicker();
+ },
+
+ /* Adjust one of the date sub-fields. */
+ _adjustDate: function(id, offset, period) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ if (this._isDisabledDatepicker(target[0])) {
+ return;
+ }
+ this._adjustInstDate(inst, offset +
+ (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
+ period);
+ this._updateDatepicker(inst);
+ },
+
+ /* Action for current link. */
+ _gotoToday: function(id) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
+ inst.selectedDay = inst.currentDay;
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
+ inst.drawYear = inst.selectedYear = inst.currentYear;
+ }
+ else {
+ var date = new Date();
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ }
+ this._notifyChange(inst);
+ this._adjustDate(target);
+ },
+
+ /* Action for selecting a new month/year. */
+ _selectMonthYear: function(id, select, period) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ inst._selectingMonthYear = false;
+ inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
+ inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
+ parseInt(select.options[select.selectedIndex].value,10);
+ this._notifyChange(inst);
+ this._adjustDate(target);
+ },
+
+ /* Restore input focus after not changing month/year. */
+ _clickMonthYear: function(id) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ if (inst.input && inst._selectingMonthYear) {
+ setTimeout(function() {
+ inst.input.focus();
+ }, 0);
+ }
+ inst._selectingMonthYear = !inst._selectingMonthYear;
+ },
+
+ /* Action for selecting a day. */
+ _selectDay: function(id, month, year, td) {
+ var target = $(id);
+ if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
+ return;
+ }
+ var inst = this._getInst(target[0]);
+ inst.selectedDay = inst.currentDay = $('a', td).html();
+ inst.selectedMonth = inst.currentMonth = month;
+ inst.selectedYear = inst.currentYear = year;
+ this._selectDate(id, this._formatDate(inst,
+ inst.currentDay, inst.currentMonth, inst.currentYear));
+ },
+
+ /* Erase the input field and hide the date picker. */
+ _clearDate: function(id) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ this._selectDate(target, '');
+ },
+
+ /* Update the input field with the selected date. */
+ _selectDate: function(id, dateStr) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
+ if (inst.input)
+ inst.input.val(dateStr);
+ this._updateAlternate(inst);
+ var onSelect = this._get(inst, 'onSelect');
+ if (onSelect)
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
+ else if (inst.input)
+ inst.input.trigger('change'); // fire the change event
+ if (inst.inline)
+ this._updateDatepicker(inst);
+ else {
+ this._hideDatepicker();
+ this._lastInput = inst.input[0];
+ if (typeof(inst.input[0]) != 'object')
+ inst.input.focus(); // restore focus
+ this._lastInput = null;
+ }
+ },
+
+ /* Update any alternate field to synchronise with the main field. */
+ _updateAlternate: function(inst) {
+ var altField = this._get(inst, 'altField');
+ if (altField) { // update alternate field too
+ var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
+ var date = this._getDate(inst);
+ var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
+ $(altField).each(function() { $(this).val(dateStr); });
+ }
+ },
+
+ /* Set as beforeShowDay function to prevent selection of weekends.
+ @param date Date - the date to customise
+ @return [boolean, string] - is this date selectable?, what is its CSS class? */
+ noWeekends: function(date) {
+ var day = date.getDay();
+ return [(day > 0 && day < 6), ''];
+ },
+
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
+ @param date Date - the date to get the week for
+ @return number - the number of the week within the year that contains this date */
+ iso8601Week: function(date) {
+ var checkDate = new Date(date.getTime());
+ // Find Thursday of this week starting on Monday
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
+ var time = checkDate.getTime();
+ checkDate.setMonth(0); // Compare with Jan 1
+ checkDate.setDate(1);
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
+ },
+
+ /* Parse a string value into a date object.
+ See formatDate below for the possible formats.
+
+ @param format string - the expected format of the date
+ @param value string - the date in the above format
+ @param settings Object - attributes include:
+ shortYearCutoff number - the cutoff year for determining the century (optional)
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
+ dayNames string[7] - names of the days from Sunday (optional)
+ monthNamesShort string[12] - abbreviated names of the months (optional)
+ monthNames string[12] - names of the months (optional)
+ @return Date - the extracted date value or null if value is blank */
+ parseDate: function (format, value, settings) {
+ if (format == null || value == null)
+ throw 'Invalid arguments';
+ value = (typeof value == 'object' ? value.toString() : value + '');
+ if (value == '')
+ return null;
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
+ var year = -1;
+ var month = -1;
+ var day = -1;
+ var doy = -1;
+ var literal = false;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ // Extract a number from the string value
+ var getNumber = function(match) {
+ var isDoubled = lookAhead(match);
+ var size = (match == '@' ? 14 : (match == '!' ? 20 :
+ (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
+ var digits = new RegExp('^\\d{1,' + size + '}');
+ var num = value.substring(iValue).match(digits);
+ if (!num)
+ throw 'Missing number at position ' + iValue;
+ iValue += num[0].length;
+ return parseInt(num[0], 10);
+ };
+ // Extract a name from the string value and convert to an index
+ var getName = function(match, shortNames, longNames) {
+ var names = (lookAhead(match) ? longNames : shortNames);
+ for (var i = 0; i < names.length; i++) {
+ if (value.substr(iValue, names[i].length).toLowerCase() == names[i].toLowerCase()) {
+ iValue += names[i].length;
+ return i + 1;
+ }
+ }
+ throw 'Unknown name at position ' + iValue;
+ };
+ // Confirm that a literal character matches the string value
+ var checkLiteral = function() {
+ if (value.charAt(iValue) != format.charAt(iFormat))
+ throw 'Unexpected literal at position ' + iValue;
+ iValue++;
+ };
+ var iValue = 0;
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ checkLiteral();
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd':
+ day = getNumber('d');
+ break;
+ case 'D':
+ getName('D', dayNamesShort, dayNames);
+ break;
+ case 'o':
+ doy = getNumber('o');
+ break;
+ case 'm':
+ month = getNumber('m');
+ break;
+ case 'M':
+ month = getName('M', monthNamesShort, monthNames);
+ break;
+ case 'y':
+ year = getNumber('y');
+ break;
+ case '@':
+ var date = new Date(getNumber('@'));
+ year = date.getFullYear();
+ month = date.getMonth() + 1;
+ day = date.getDate();
+ break;
+ case '!':
+ var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
+ year = date.getFullYear();
+ month = date.getMonth() + 1;
+ day = date.getDate();
+ break;
+ case "'":
+ if (lookAhead("'"))
+ checkLiteral();
+ else
+ literal = true;
+ break;
+ default:
+ checkLiteral();
+ }
+ }
+ if (year == -1)
+ year = new Date().getFullYear();
+ else if (year < 100)
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
+ (year <= shortYearCutoff ? 0 : -100);
+ if (doy > -1) {
+ month = 1;
+ day = doy;
+ do {
+ var dim = this._getDaysInMonth(year, month - 1);
+ if (day <= dim)
+ break;
+ month++;
+ day -= dim;
+ } while (true);
+ }
+ var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
+ throw 'Invalid date'; // E.g. 31/02/*
+ return date;
+ },
+
+ /* Standard date formats. */
+ ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
+ COOKIE: 'D, dd M yy',
+ ISO_8601: 'yy-mm-dd',
+ RFC_822: 'D, d M y',
+ RFC_850: 'DD, dd-M-y',
+ RFC_1036: 'D, d M y',
+ RFC_1123: 'D, d M yy',
+ RFC_2822: 'D, d M yy',
+ RSS: 'D, d M y', // RFC 822
+ TICKS: '!',
+ TIMESTAMP: '@',
+ W3C: 'yy-mm-dd', // ISO 8601
+
+ _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
+ Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
+
+ /* Format a date object into a string value.
+ The format can be combinations of the following:
+ d - day of month (no leading zero)
+ dd - day of month (two digit)
+ o - day of year (no leading zeros)
+ oo - day of year (three digit)
+ D - day name short
+ DD - day name long
+ m - month of year (no leading zero)
+ mm - month of year (two digit)
+ M - month name short
+ MM - month name long
+ y - year (two digit)
+ yy - year (four digit)
+ @ - Unix timestamp (ms since 01/01/1970)
+ ! - Windows ticks (100ns since 01/01/0001)
+ '...' - literal text
+ '' - single quote
+
+ @param format string - the desired format of the date
+ @param date Date - the date value to format
+ @param settings Object - attributes include:
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
+ dayNames string[7] - names of the days from Sunday (optional)
+ monthNamesShort string[12] - abbreviated names of the months (optional)
+ monthNames string[12] - names of the months (optional)
+ @return string - the date in the above format */
+ formatDate: function (format, date, settings) {
+ if (!date)
+ return '';
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ // Format a number, with leading zero if necessary
+ var formatNumber = function(match, value, len) {
+ var num = '' + value;
+ if (lookAhead(match))
+ while (num.length < len)
+ num = '0' + num;
+ return num;
+ };
+ // Format a name, short or long as requested
+ var formatName = function(match, value, shortNames, longNames) {
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
+ };
+ var output = '';
+ var literal = false;
+ if (date)
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ output += format.charAt(iFormat);
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd':
+ output += formatNumber('d', date.getDate(), 2);
+ break;
+ case 'D':
+ output += formatName('D', date.getDay(), dayNamesShort, dayNames);
+ break;
+ case 'o':
+ output += formatNumber('o',
+ (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000, 3);
+ break;
+ case 'm':
+ output += formatNumber('m', date.getMonth() + 1, 2);
+ break;
+ case 'M':
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
+ break;
+ case 'y':
+ output += (lookAhead('y') ? date.getFullYear() :
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
+ break;
+ case '@':
+ output += date.getTime();
+ break;
+ case '!':
+ output += date.getTime() * 10000 + this._ticksTo1970;
+ break;
+ case "'":
+ if (lookAhead("'"))
+ output += "'";
+ else
+ literal = true;
+ break;
+ default:
+ output += format.charAt(iFormat);
+ }
+ }
+ return output;
+ },
+
+ /* Extract all possible characters from the date format. */
+ _possibleChars: function (format) {
+ var chars = '';
+ var literal = false;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ for (var iFormat = 0; iFormat < format.length; iFormat++)
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ chars += format.charAt(iFormat);
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd': case 'm': case 'y': case '@':
+ chars += '0123456789';
+ break;
+ case 'D': case 'M':
+ return null; // Accept anything
+ case "'":
+ if (lookAhead("'"))
+ chars += "'";
+ else
+ literal = true;
+ break;
+ default:
+ chars += format.charAt(iFormat);
+ }
+ return chars;
+ },
+
+ /* Get a setting value, defaulting if necessary. */
+ _get: function(inst, name) {
+ return inst.settings[name] !== undefined ?
+ inst.settings[name] : this._defaults[name];
+ },
+
+ /* Parse existing date and initialise date picker. */
+ _setDateFromField: function(inst, noDefault) {
+ if (inst.input.val() == inst.lastVal) {
+ return;
+ }
+ var dateFormat = this._get(inst, 'dateFormat');
+ var dates = inst.lastVal = inst.input ? inst.input.val() : null;
+ var date, defaultDate;
+ date = defaultDate = this._getDefaultDate(inst);
+ var settings = this._getFormatConfig(inst);
+ try {
+ date = this.parseDate(dateFormat, dates, settings) || defaultDate;
+ } catch (event) {
+ this.log(event);
+ dates = (noDefault ? '' : dates);
+ }
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ inst.currentDay = (dates ? date.getDate() : 0);
+ inst.currentMonth = (dates ? date.getMonth() : 0);
+ inst.currentYear = (dates ? date.getFullYear() : 0);
+ this._adjustInstDate(inst);
+ },
+
+ /* Retrieve the default date shown on opening. */
+ _getDefaultDate: function(inst) {
+ return this._restrictMinMax(inst,
+ this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
+ },
+
+ /* A date may be specified as an exact value or a relative one. */
+ _determineDate: function(inst, date, defaultDate) {
+ var offsetNumeric = function(offset) {
+ var date = new Date();
+ date.setDate(date.getDate() + offset);
+ return date;
+ };
+ var offsetString = function(offset) {
+ try {
+ return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
+ offset, $.datepicker._getFormatConfig(inst));
+ }
+ catch (e) {
+ // Ignore
+ }
+ var date = (offset.toLowerCase().match(/^c/) ?
+ $.datepicker._getDate(inst) : null) || new Date();
+ var year = date.getFullYear();
+ var month = date.getMonth();
+ var day = date.getDate();
+ var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
+ var matches = pattern.exec(offset);
+ while (matches) {
+ switch (matches[2] || 'd') {
+ case 'd' : case 'D' :
+ day += parseInt(matches[1],10); break;
+ case 'w' : case 'W' :
+ day += parseInt(matches[1],10) * 7; break;
+ case 'm' : case 'M' :
+ month += parseInt(matches[1],10);
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
+ break;
+ case 'y': case 'Y' :
+ year += parseInt(matches[1],10);
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
+ break;
+ }
+ matches = pattern.exec(offset);
+ }
+ return new Date(year, month, day);
+ };
+ var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
+ newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
+ if (newDate) {
+ newDate.setHours(0);
+ newDate.setMinutes(0);
+ newDate.setSeconds(0);
+ newDate.setMilliseconds(0);
+ }
+ return this._daylightSavingAdjust(newDate);
+ },
+
+ /* Handle switch to/from daylight saving.
+ Hours may be non-zero on daylight saving cut-over:
+ > 12 when midnight changeover, but then cannot generate
+ midnight datetime, so jump to 1AM, otherwise reset.
+ @param date (Date) the date to check
+ @return (Date) the corrected date */
+ _daylightSavingAdjust: function(date) {
+ if (!date) return null;
+ date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
+ return date;
+ },
+
+ /* Set the date(s) directly. */
+ _setDate: function(inst, date, noChange) {
+ var clear = !date;
+ var origMonth = inst.selectedMonth;
+ var origYear = inst.selectedYear;
+ var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
+ inst.selectedDay = inst.currentDay = newDate.getDate();
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
+ inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
+ if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
+ this._notifyChange(inst);
+ this._adjustInstDate(inst);
+ if (inst.input) {
+ inst.input.val(clear ? '' : this._formatDate(inst));
+ }
+ },
+
+ /* Retrieve the date(s) directly. */
+ _getDate: function(inst) {
+ var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
+ this._daylightSavingAdjust(new Date(
+ inst.currentYear, inst.currentMonth, inst.currentDay)));
+ return startDate;
+ },
+
+ /* Generate the HTML for the current state of the date picker. */
+ _generateHTML: function(inst) {
+ var today = new Date();
+ today = this._daylightSavingAdjust(
+ new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
+ var isRTL = this._get(inst, 'isRTL');
+ var showButtonPanel = this._get(inst, 'showButtonPanel');
+ var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
+ var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
+ var numMonths = this._getNumberOfMonths(inst);
+ var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
+ var stepMonths = this._get(inst, 'stepMonths');
+ var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
+ var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
+ new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ var drawMonth = inst.drawMonth - showCurrentAtPos;
+ var drawYear = inst.drawYear;
+ if (drawMonth < 0) {
+ drawMonth += 12;
+ drawYear--;
+ }
+ if (maxDate) {
+ var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
+ maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
+ maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
+ while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
+ drawMonth--;
+ if (drawMonth < 0) {
+ drawMonth = 11;
+ drawYear--;
+ }
+ }
+ }
+ inst.drawMonth = drawMonth;
+ inst.drawYear = drawYear;
+ var prevText = this._get(inst, 'prevText');
+ prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
+ this._getFormatConfig(inst)));
+ var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
+ '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
+ ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
+ var nextText = this._get(inst, 'nextText');
+ nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
+ this._getFormatConfig(inst)));
+ var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
+ '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
+ ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
+ var currentText = this._get(inst, 'currentText');
+ var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
+ currentText = (!navigationAsDateFormat ? currentText :
+ this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
+ var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
+ var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
+ (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
+ '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
+ var firstDay = parseInt(this._get(inst, 'firstDay'),10);
+ firstDay = (isNaN(firstDay) ? 0 : firstDay);
+ var showWeek = this._get(inst, 'showWeek');
+ var dayNames = this._get(inst, 'dayNames');
+ var dayNamesShort = this._get(inst, 'dayNamesShort');
+ var dayNamesMin = this._get(inst, 'dayNamesMin');
+ var monthNames = this._get(inst, 'monthNames');
+ var monthNamesShort = this._get(inst, 'monthNamesShort');
+ var beforeShowDay = this._get(inst, 'beforeShowDay');
+ var showOtherMonths = this._get(inst, 'showOtherMonths');
+ var selectOtherMonths = this._get(inst, 'selectOtherMonths');
+ var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
+ var defaultDate = this._getDefaultDate(inst);
+ var html = '';
+ for (var row = 0; row < numMonths[0]; row++) {
+ var group = '';
+ for (var col = 0; col < numMonths[1]; col++) {
+ var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
+ var cornerClass = ' ui-corner-all';
+ var calender = '';
+ if (isMultiMonth) {
+ calender += '<div class="ui-datepicker-group';
+ if (numMonths[1] > 1)
+ switch (col) {
+ case 0: calender += ' ui-datepicker-group-first';
+ cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
+ case numMonths[1]-1: calender += ' ui-datepicker-group-last';
+ cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
+ default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
+ }
+ calender += '">';
+ }
+ calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
+ (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
+ (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
+ this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
+ row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
+ '</div><table class="ui-datepicker-calendar"><thead>' +
+ '<tr>';
+ var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
+ for (var dow = 0; dow < 7; dow++) { // days of the week
+ var day = (dow + firstDay) % 7;
+ thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
+ '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
+ }
+ calender += thead + '</tr></thead><tbody>';
+ var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
+ if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
+ inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
+ var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
+ var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
+ var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
+ for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
+ calender += '<tr>';
+ var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
+ this._get(inst, 'calculateWeek')(printDate) + '</td>');
+ for (var dow = 0; dow < 7; dow++) { // create date picker days
+ var daySettings = (beforeShowDay ?
+ beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
+ var otherMonth = (printDate.getMonth() != drawMonth);
+ var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
+ (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
+ tbody += '<td class="' +
+ ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
+ (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
+ ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
+ (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
+ // or defaultDate is current printedDate and defaultDate is selectedDate
+ ' ' + this._dayOverClass : '') + // highlight selected day
+ (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
+ (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
+ (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
+ (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
+ ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
+ (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
+ inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
+ (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
+ (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
+ (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
+ (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
+ (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
+ '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
+ printDate.setDate(printDate.getDate() + 1);
+ printDate = this._daylightSavingAdjust(printDate);
+ }
+ calender += tbody + '</tr>';
+ }
+ drawMonth++;
+ if (drawMonth > 11) {
+ drawMonth = 0;
+ drawYear++;
+ }
+ calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
+ ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
+ group += calender;
+ }
+ html += group;
+ }
+ html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
+ '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
+ inst._keyEvent = false;
+ return html;
+ },
+
+ /* Generate the month and year header. */
+ _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
+ secondary, monthNames, monthNamesShort) {
+ var changeMonth = this._get(inst, 'changeMonth');
+ var changeYear = this._get(inst, 'changeYear');
+ var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
+ var html = '<div class="ui-datepicker-title">';
+ var monthHtml = '';
+ // month selection
+ if (secondary || !changeMonth)
+ monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
+ else {
+ var inMinYear = (minDate && minDate.getFullYear() == drawYear);
+ var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
+ monthHtml += '<select class="ui-datepicker-month" ' +
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
+ 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
+ '>';
+ for (var month = 0; month < 12; month++) {
+ if ((!inMinYear || month >= minDate.getMonth()) &&
+ (!inMaxYear || month <= maxDate.getMonth()))
+ monthHtml += '<option value="' + month + '"' +
+ (month == drawMonth ? ' selected="selected"' : '') +
+ '>' + monthNamesShort[month] + '</option>';
+ }
+ monthHtml += '</select>';
+ }
+ if (!showMonthAfterYear)
+ html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
+ // year selection
+ inst.yearshtml = '';
+ if (secondary || !changeYear)
+ html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
+ else {
+ // determine range of years to display
+ var years = this._get(inst, 'yearRange').split(':');
+ var thisYear = new Date().getFullYear();
+ var determineYear = function(value) {
+ var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
+ (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
+ parseInt(value, 10)));
+ return (isNaN(year) ? thisYear : year);
+ };
+ var year = determineYear(years[0]);
+ var endYear = Math.max(year, determineYear(years[1] || ''));
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
+ inst.yearshtml += '<select class="ui-datepicker-year" ' +
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
+ 'onclick="DP_jQuery_' + dpuuid + '.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
+ '>';
+ for (; year <= endYear; year++) {
+ inst.yearshtml += '<option value="' + year + '"' +
+ (year == drawYear ? ' selected="selected"' : '') +
+ '>' + year + '</option>';
+ }
+ inst.yearshtml += '</select>';
+ //when showing there is no need for later update
+ if( ! $.browser.mozilla ){
+ html += inst.yearshtml;
+ inst.yearshtml = null;
+ } else {
+ // will be replaced later with inst.yearshtml
+ html += '<select class="ui-datepicker-year"><option value="' + drawYear + '" selected="selected">' + drawYear + '</option></select>';
+ }
+ }
+ html += this._get(inst, 'yearSuffix');
+ if (showMonthAfterYear)
+ html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
+ html += '</div>'; // Close datepicker_header
+ return html;
+ },
+
+ /* Adjust one of the date sub-fields. */
+ _adjustInstDate: function(inst, offset, period) {
+ var year = inst.drawYear + (period == 'Y' ? offset : 0);
+ var month = inst.drawMonth + (period == 'M' ? offset : 0);
+ var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
+ (period == 'D' ? offset : 0);
+ var date = this._restrictMinMax(inst,
+ this._daylightSavingAdjust(new Date(year, month, day)));
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ if (period == 'M' || period == 'Y')
+ this._notifyChange(inst);
+ },
+
+ /* Ensure a date is within any min/max bounds. */
+ _restrictMinMax: function(inst, date) {
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ var newDate = (minDate && date < minDate ? minDate : date);
+ newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
+ return newDate;
+ },
+
+ /* Notify change of month/year. */
+ _notifyChange: function(inst) {
+ var onChange = this._get(inst, 'onChangeMonthYear');
+ if (onChange)
+ onChange.apply((inst.input ? inst.input[0] : null),
+ [inst.selectedYear, inst.selectedMonth + 1, inst]);
+ },
+
+ /* Determine the number of months to show. */
+ _getNumberOfMonths: function(inst) {
+ var numMonths = this._get(inst, 'numberOfMonths');
+ return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
+ },
+
+ /* Determine the current maximum date - ensure no time components are set. */
+ _getMinMaxDate: function(inst, minMax) {
+ return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
+ },
+
+ /* Find the number of days in a given month. */
+ _getDaysInMonth: function(year, month) {
+ return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
+ },
+
+ /* Find the day of the week of the first of a month. */
+ _getFirstDayOfMonth: function(year, month) {
+ return new Date(year, month, 1).getDay();
+ },
+
+ /* Determines if we should allow a "next/prev" month display change. */
+ _canAdjustMonth: function(inst, offset, curYear, curMonth) {
+ var numMonths = this._getNumberOfMonths(inst);
+ var date = this._daylightSavingAdjust(new Date(curYear,
+ curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
+ if (offset < 0)
+ date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
+ return this._isInRange(inst, date);
+ },
+
+ /* Is the given date in the accepted range? */
+ _isInRange: function(inst, date) {
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ return ((!minDate || date.getTime() >= minDate.getTime()) &&
+ (!maxDate || date.getTime() <= maxDate.getTime()));
+ },
+
+ /* Provide the configuration settings for formatting/parsing. */
+ _getFormatConfig: function(inst) {
+ var shortYearCutoff = this._get(inst, 'shortYearCutoff');
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
+ return {shortYearCutoff: shortYearCutoff,
+ dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
+ monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
+ },
+
+ /* Format the given date for display. */
+ _formatDate: function(inst, day, month, year) {
+ if (!day) {
+ inst.currentDay = inst.selectedDay;
+ inst.currentMonth = inst.selectedMonth;
+ inst.currentYear = inst.selectedYear;
+ }
+ var date = (day ? (typeof day == 'object' ? day :
+ this._daylightSavingAdjust(new Date(year, month, day))) :
+ this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
+ return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
+ }
+});
+
+/* jQuery extend now ignores nulls! */
+function extendRemove(target, props) {
+ $.extend(target, props);
+ for (var name in props)
+ if (props[name] == null || props[name] == undefined)
+ target[name] = props[name];
+ return target;
+};
+
+/* Determine whether an object is an array. */
+function isArray(a) {
+ return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
+ (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
+};
+
+/* Invoke the datepicker functionality.
+ @param options string - a command, optionally followed by additional parameters or
+ Object - settings for attaching new datepicker functionality
+ @return jQuery object */
+$.fn.datepicker = function(options){
+
+ /* Verify an empty collection wasn't passed - Fixes #6976 */
+ if ( !this.length ) {
+ return this;
+ }
+
+ /* Initialise the date picker. */
+ if (!$.datepicker.initialized) {
+ $(document).mousedown($.datepicker._checkExternalClick).
+ find('body').append($.datepicker.dpDiv);
+ $.datepicker.initialized = true;
+ }
+
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
+ if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
+ return $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this[0]].concat(otherArgs));
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
+ return $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this[0]].concat(otherArgs));
+ return this.each(function() {
+ typeof options == 'string' ?
+ $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this].concat(otherArgs)) :
+ $.datepicker._attachDatepicker(this, options);
+ });
+};
+
+$.datepicker = new Datepicker(); // singleton instance
+$.datepicker.initialized = false;
+$.datepicker.uuid = new Date().getTime();
+$.datepicker.version = "1.8.10";
+
+// Workaround for #4055
+// Add another global to avoid noConflict issues with inline event handlers
+window['DP_jQuery_' + dpuuid] = $;
+
+})(jQuery);
+/*
+ * jQuery UI Progressbar 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget( "ui.progressbar", {
+ options: {
+ value: 0,
+ max: 100
+ },
+
+ min: 0,
+
+ _create: function() {
+ this.element
+ .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .attr({
+ role: "progressbar",
+ "aria-valuemin": this.min,
+ "aria-valuemax": this.options.max,
+ "aria-valuenow": this._value()
+ });
+
+ this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
+ .appendTo( this.element );
+
+ this.oldValue = this._value();
+ this._refreshValue();
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-valuemin" )
+ .removeAttr( "aria-valuemax" )
+ .removeAttr( "aria-valuenow" );
+
+ this.valueDiv.remove();
+
+ $.Widget.prototype.destroy.apply( this, arguments );
+ },
+
+ value: function( newValue ) {
+ if ( newValue === undefined ) {
+ return this._value();
+ }
+
+ this._setOption( "value", newValue );
+ return this;
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "value" ) {
+ this.options.value = value;
+ this._refreshValue();
+ if ( this._value() === this.options.max ) {
+ this._trigger( "complete" );
+ }
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+ },
+
+ _value: function() {
+ var val = this.options.value;
+ // normalize invalid value
+ if ( typeof val !== "number" ) {
+ val = 0;
+ }
+ return Math.min( this.options.max, Math.max( this.min, val ) );
+ },
+
+ _percentage: function() {
+ return 100 * this._value() / this.options.max;
+ },
+
+ _refreshValue: function() {
+ var value = this.value();
+ var percentage = this._percentage();
+
+ if ( this.oldValue !== value ) {
+ this.oldValue = value;
+ this._trigger( "change" );
+ }
+
+ this.valueDiv
+ .toggleClass( "ui-corner-right", value === this.options.max )
+ .width( percentage.toFixed(0) + "%" );
+ this.element.attr( "aria-valuenow", value );
+ }
+});
+
+$.extend( $.ui.progressbar, {
+ version: "1.8.10"
+});
+
+})( jQuery );
+/*
+ * jQuery UI Effects 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+;jQuery.effects || (function($, undefined) {
+
+$.effects = {};
+
+
+
+/******************************************************************************/
+/****************************** COLOR ANIMATIONS ******************************/
+/******************************************************************************/
+
+// override the animation for color styles
+$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor',
+ 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'],
+function(i, attr) {
+ $.fx.step[attr] = function(fx) {
+ if (!fx.colorInit) {
+ fx.start = getColor(fx.elem, attr);
+ fx.end = getRGB(fx.end);
+ fx.colorInit = true;
+ }
+
+ fx.elem.style[attr] = 'rgb(' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')';
+ };
+});
+
+// Color Conversion functions from highlightFade
+// By Blair Mitchelmore
+// http://jquery.offput.ca/highlightFade/
+
+// Parse strings looking for color tuples [255,255,255]
+function getRGB(color) {
+ var result;
+
+ // Check if we're already dealing with an array of colors
+ if ( color && color.constructor == Array && color.length == 3 )
+ return color;
+
+ // Look for rgb(num,num,num)
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+ return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
+
+ // Look for rgb(num%,num%,num%)
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+ // Look for #a0b1c2
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+ // Look for #fff
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+ if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
+ return colors['transparent'];
+
+ // Otherwise, we're most likely dealing with a named color
+ return colors[$.trim(color).toLowerCase()];
+}
+
+function getColor(elem, attr) {
+ var color;
+
+ do {
+ color = $.curCSS(elem, attr);
+
+ // Keep going until we find an element that has color, or we hit the body
+ if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )
+ break;
+
+ attr = "backgroundColor";
+ } while ( elem = elem.parentNode );
+
+ return getRGB(color);
+};
+
+// Some named colors to work with
+// From Interface by Stefan Petre
+// http://interface.eyecon.ro/
+
+var colors = {
+ aqua:[0,255,255],
+ azure:[240,255,255],
+ beige:[245,245,220],
+ black:[0,0,0],
+ blue:[0,0,255],
+ brown:[165,42,42],
+ cyan:[0,255,255],
+ darkblue:[0,0,139],
+ darkcyan:[0,139,139],
+ darkgrey:[169,169,169],
+ darkgreen:[0,100,0],
+ darkkhaki:[189,183,107],
+ darkmagenta:[139,0,139],
+ darkolivegreen:[85,107,47],
+ darkorange:[255,140,0],
+ darkorchid:[153,50,204],
+ darkred:[139,0,0],
+ darksalmon:[233,150,122],
+ darkviolet:[148,0,211],
+ fuchsia:[255,0,255],
+ gold:[255,215,0],
+ green:[0,128,0],
+ indigo:[75,0,130],
+ khaki:[240,230,140],
+ lightblue:[173,216,230],
+ lightcyan:[224,255,255],
+ lightgreen:[144,238,144],
+ lightgrey:[211,211,211],
+ lightpink:[255,182,193],
+ lightyellow:[255,255,224],
+ lime:[0,255,0],
+ magenta:[255,0,255],
+ maroon:[128,0,0],
+ navy:[0,0,128],
+ olive:[128,128,0],
+ orange:[255,165,0],
+ pink:[255,192,203],
+ purple:[128,0,128],
+ violet:[128,0,128],
+ red:[255,0,0],
+ silver:[192,192,192],
+ white:[255,255,255],
+ yellow:[255,255,0],
+ transparent: [255,255,255]
+};
+
+
+
+/******************************************************************************/
+/****************************** CLASS ANIMATIONS ******************************/
+/******************************************************************************/
+
+var classAnimationActions = ['add', 'remove', 'toggle'],
+ shorthandStyles = {
+ border: 1,
+ borderBottom: 1,
+ borderColor: 1,
+ borderLeft: 1,
+ borderRight: 1,
+ borderTop: 1,
+ borderWidth: 1,
+ margin: 1,
+ padding: 1
+ };
+
+function getElementStyles() {
+ var style = document.defaultView
+ ? document.defaultView.getComputedStyle(this, null)
+ : this.currentStyle,
+ newStyle = {},
+ key,
+ camelCase;
+
+ // webkit enumerates style porperties
+ if (style && style.length && style[0] && style[style[0]]) {
+ var len = style.length;
+ while (len--) {
+ key = style[len];
+ if (typeof style[key] == 'string') {
+ camelCase = key.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+ newStyle[camelCase] = style[key];
+ }
+ }
+ } else {
+ for (key in style) {
+ if (typeof style[key] === 'string') {
+ newStyle[key] = style[key];
+ }
+ }
+ }
+
+ return newStyle;
+}
+
+function filterStyles(styles) {
+ var name, value;
+ for (name in styles) {
+ value = styles[name];
+ if (
+ // ignore null and undefined values
+ value == null ||
+ // ignore functions (when does this occur?)
+ $.isFunction(value) ||
+ // shorthand styles that need to be expanded
+ name in shorthandStyles ||
+ // ignore scrollbars (break in IE)
+ (/scrollbar/).test(name) ||
+
+ // only colors or values that can be converted to numbers
+ (!(/color/i).test(name) && isNaN(parseFloat(value)))
+ ) {
+ delete styles[name];
+ }
+ }
+
+ return styles;
+}
+
+function styleDifference(oldStyle, newStyle) {
+ var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459
+ name;
+
+ for (name in newStyle) {
+ if (oldStyle[name] != newStyle[name]) {
+ diff[name] = newStyle[name];
+ }
+ }
+
+ return diff;
+}
+
+$.effects.animateClass = function(value, duration, easing, callback) {
+ if ($.isFunction(easing)) {
+ callback = easing;
+ easing = null;
+ }
+
+ return this.queue('fx', function() {
+ var that = $(this),
+ originalStyleAttr = that.attr('style') || ' ',
+ originalStyle = filterStyles(getElementStyles.call(this)),
+ newStyle,
+ className = that.attr('className');
+
+ $.each(classAnimationActions, function(i, action) {
+ if (value[action]) {
+ that[action + 'Class'](value[action]);
+ }
+ });
+ newStyle = filterStyles(getElementStyles.call(this));
+ that.attr('className', className);
+
+ that.animate(styleDifference(originalStyle, newStyle), duration, easing, function() {
+ $.each(classAnimationActions, function(i, action) {
+ if (value[action]) { that[action + 'Class'](value[action]); }
+ });
+ // work around bug in IE by clearing the cssText before setting it
+ if (typeof that.attr('style') == 'object') {
+ that.attr('style').cssText = '';
+ that.attr('style').cssText = originalStyleAttr;
+ } else {
+ that.attr('style', originalStyleAttr);
+ }
+ if (callback) { callback.apply(this, arguments); }
+ });
+
+ // $.animate adds a function to the end of the queue
+ // but we want it at the front
+ var queue = $.queue(this),
+ anim = queue.splice(queue.length - 1, 1)[0];
+ queue.splice(1, 0, anim);
+ $.dequeue(this);
+ });
+};
+
+$.fn.extend({
+ _addClass: $.fn.addClass,
+ addClass: function(classNames, speed, easing, callback) {
+ return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
+ },
+
+ _removeClass: $.fn.removeClass,
+ removeClass: function(classNames,speed,easing,callback) {
+ return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
+ },
+
+ _toggleClass: $.fn.toggleClass,
+ toggleClass: function(classNames, force, speed, easing, callback) {
+ if ( typeof force == "boolean" || force === undefined ) {
+ if ( !speed ) {
+ // without speed parameter;
+ return this._toggleClass(classNames, force);
+ } else {
+ return $.effects.animateClass.apply(this, [(force?{add:classNames}:{remove:classNames}),speed,easing,callback]);
+ }
+ } else {
+ // without switch parameter;
+ return $.effects.animateClass.apply(this, [{ toggle: classNames },force,speed,easing]);
+ }
+ },
+
+ switchClass: function(remove,add,speed,easing,callback) {
+ return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
+ }
+});
+
+
+
+/******************************************************************************/
+/*********************************** EFFECTS **********************************/
+/******************************************************************************/
+
+$.extend($.effects, {
+ version: "1.8.10",
+
+ // Saves a set of properties in a data storage
+ save: function(element, set) {
+ for(var i=0; i < set.length; i++) {
+ if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]);
+ }
+ },
+
+ // Restores a set of previously saved properties from a data storage
+ restore: function(element, set) {
+ for(var i=0; i < set.length; i++) {
+ if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i]));
+ }
+ },
+
+ setMode: function(el, mode) {
+ if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
+ return mode;
+ },
+
+ getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
+ // this should be a little more flexible in the future to handle a string & hash
+ var y, x;
+ switch (origin[0]) {
+ case 'top': y = 0; break;
+ case 'middle': y = 0.5; break;
+ case 'bottom': y = 1; break;
+ default: y = origin[0] / original.height;
+ };
+ switch (origin[1]) {
+ case 'left': x = 0; break;
+ case 'center': x = 0.5; break;
+ case 'right': x = 1; break;
+ default: x = origin[1] / original.width;
+ };
+ return {x: x, y: y};
+ },
+
+ // Wraps the element around a wrapper that copies position properties
+ createWrapper: function(element) {
+
+ // if the element is already wrapped, return it
+ if (element.parent().is('.ui-effects-wrapper')) {
+ return element.parent();
+ }
+
+ // wrap the element
+ var props = {
+ width: element.outerWidth(true),
+ height: element.outerHeight(true),
+ 'float': element.css('float')
+ },
+ wrapper = $('<div></div>')
+ .addClass('ui-effects-wrapper')
+ .css({
+ fontSize: '100%',
+ background: 'transparent',
+ border: 'none',
+ margin: 0,
+ padding: 0
+ });
+
+ element.wrap(wrapper);
+ wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
+
+ // transfer positioning properties to the wrapper
+ if (element.css('position') == 'static') {
+ wrapper.css({ position: 'relative' });
+ element.css({ position: 'relative' });
+ } else {
+ $.extend(props, {
+ position: element.css('position'),
+ zIndex: element.css('z-index')
+ });
+ $.each(['top', 'left', 'bottom', 'right'], function(i, pos) {
+ props[pos] = element.css(pos);
+ if (isNaN(parseInt(props[pos], 10))) {
+ props[pos] = 'auto';
+ }
+ });
+ element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' });
+ }
+
+ return wrapper.css(props).show();
+ },
+
+ removeWrapper: function(element) {
+ if (element.parent().is('.ui-effects-wrapper'))
+ return element.parent().replaceWith(element);
+ return element;
+ },
+
+ setTransition: function(element, list, factor, value) {
+ value = value || {};
+ $.each(list, function(i, x){
+ unit = element.cssUnit(x);
+ if (unit[0] > 0) value[x] = unit[0] * factor + unit[1];
+ });
+ return value;
+ }
+});
+
+
+function _normalizeArguments(effect, options, speed, callback) {
+ // shift params for method overloading
+ if (typeof effect == 'object') {
+ callback = options;
+ speed = null;
+ options = effect;
+ effect = options.effect;
+ }
+ if ($.isFunction(options)) {
+ callback = options;
+ speed = null;
+ options = {};
+ }
+ if (typeof options == 'number' || $.fx.speeds[options]) {
+ callback = speed;
+ speed = options;
+ options = {};
+ }
+ if ($.isFunction(speed)) {
+ callback = speed;
+ speed = null;
+ }
+
+ options = options || {};
+
+ speed = speed || options.duration;
+ speed = $.fx.off ? 0 : typeof speed == 'number'
+ ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default;
+
+ callback = callback || options.complete;
+
+ return [effect, options, speed, callback];
+}
+
+function standardSpeed( speed ) {
+ // valid standard speeds
+ if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
+ return true;
+ }
+
+ // invalid strings - treat as "normal" speed
+ if ( typeof speed === "string" && !$.effects[ speed ] ) {
+ return true;
+ }
+
+ return false;
+}
+
+$.fn.extend({
+ effect: function(effect, options, speed, callback) {
+ var args = _normalizeArguments.apply(this, arguments),
+ // TODO: make effects take actual parameters instead of a hash
+ args2 = {
+ options: args[1],
+ duration: args[2],
+ callback: args[3]
+ },
+ mode = args2.options.mode,
+ effectMethod = $.effects[effect];
+
+ if ( $.fx.off || !effectMethod ) {
+ // delegate to the original method (e.g., .show()) if possible
+ if ( mode ) {
+ return this[ mode ]( args2.duration, args2.callback );
+ } else {
+ return this.each(function() {
+ if ( args2.callback ) {
+ args2.callback.call( this );
+ }
+ });
+ }
+ }
+
+ return effectMethod.call(this, args2);
+ },
+
+ _show: $.fn.show,
+ show: function(speed) {
+ if ( standardSpeed( speed ) ) {
+ return this._show.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'show';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ _hide: $.fn.hide,
+ hide: function(speed) {
+ if ( standardSpeed( speed ) ) {
+ return this._hide.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'hide';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ // jQuery core overloads toggle and creates _toggle
+ __toggle: $.fn.toggle,
+ toggle: function(speed) {
+ if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
+ return this.__toggle.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'toggle';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ // helper functions
+ cssUnit: function(key) {
+ var style = this.css(key), val = [];
+ $.each( ['em','px','%','pt'], function(i, unit){
+ if(style.indexOf(unit) > 0)
+ val = [parseFloat(style), unit];
+ });
+ return val;
+ }
+});
+
+
+
+/******************************************************************************/
+/*********************************** EASING ***********************************/
+/******************************************************************************/
+
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+// t: current time, b: begInnIng value, c: change In value, d: duration
+$.easing.jswing = $.easing.swing;
+
+$.extend($.easing,
+{
+ def: 'easeOutQuad',
+ swing: function (x, t, b, c, d) {
+ //alert($.easing.default);
+ return $.easing[$.easing.def](x, t, b, c, d);
+ },
+ easeInQuad: function (x, t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+ easeOutQuad: function (x, t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+ easeInOutQuad: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+ easeInCubic: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t + b;
+ },
+ easeOutCubic: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+ },
+ easeInOutCubic: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ },
+ easeInQuart: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+ easeOutQuart: function (x, t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+ easeInOutQuart: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+ easeInQuint: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t*t + b;
+ },
+ easeOutQuint: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ },
+ easeInOutQuint: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
+ },
+ easeInSine: function (x, t, b, c, d) {
+ return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ },
+ easeOutSine: function (x, t, b, c, d) {
+ return c * Math.sin(t/d * (Math.PI/2)) + b;
+ },
+ easeInOutSine: function (x, t, b, c, d) {
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ },
+ easeInExpo: function (x, t, b, c, d) {
+ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ },
+ easeOutExpo: function (x, t, b, c, d) {
+ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ },
+ easeInOutExpo: function (x, t, b, c, d) {
+ if (t==0) return b;
+ if (t==d) return b+c;
+ if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+ return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ },
+ easeInCirc: function (x, t, b, c, d) {
+ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ },
+ easeOutCirc: function (x, t, b, c, d) {
+ return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ },
+ easeInOutCirc: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
+ return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ },
+ easeInElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+ easeOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+ easeInOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+ easeInBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+ easeOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+ easeInOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+ easeInBounce: function (x, t, b, c, d) {
+ return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
+ },
+ easeOutBounce: function (x, t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+ },
+ easeInOutBounce: function (x, t, b, c, d) {
+ if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
+ return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2001 Robert Penner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+})(jQuery);
+/*
+ * jQuery UI Effects Blind 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.blind = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'vertical'; // Default direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var ref = (direction == 'vertical') ? 'height' : 'width';
+ var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width();
+ if(mode == 'show') wrapper.css(ref, 0); // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref] = mode == 'show' ? distance : 0;
+
+ // Animate
+ wrapper.animate(animation, o.duration, o.options.easing, function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ });
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Bounce 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.bounce = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var direction = o.options.direction || 'up'; // Default direction
+ var distance = o.options.distance || 20; // Default distance
+ var times = o.options.times || 5; // Default # of times
+ var speed = o.duration || 250; // Default speed per bounce
+ if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
+ if (mode == 'hide') distance = distance / (times * 2);
+ if (mode != 'hide') times--;
+
+ // Animate
+ if (mode == 'show') { // Show Bounce
+ var animation = {opacity: 1};
+ animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation, speed / 2, o.options.easing);
+ distance = distance / 2;
+ times--;
+ };
+ for (var i = 0; i < times; i++) { // Bounces
+ var animation1 = {}, animation2 = {};
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
+ distance = (mode == 'hide') ? distance * 2 : distance / 2;
+ };
+ if (mode == 'hide') { // Last Bounce
+ var animation = {opacity: 0};
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ el.animate(animation, speed / 2, o.options.easing, function(){
+ el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ } else {
+ var animation1 = {}, animation2 = {};
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ };
+ el.queue('fx', function() { el.dequeue(); });
+ el.dequeue();
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Clip 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.clip = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','height','width'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'vertical'; // Default direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var animate = el[0].tagName == 'IMG' ? wrapper : el;
+ var ref = {
+ size: (direction == 'vertical') ? 'height' : 'width',
+ position: (direction == 'vertical') ? 'top' : 'left'
+ };
+ var distance = (direction == 'vertical') ? animate.height() : animate.width();
+ if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref.size] = mode == 'show' ? distance : 0;
+ animation[ref.position] = mode == 'show' ? 0 : distance / 2;
+
+ // Animate
+ animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Drop 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.drop = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','opacity'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default Direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2);
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
+
+ // Animation
+ var animation = {opacity: mode == 'show' ? 1 : 0};
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
+
+ // Animate
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Explode 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.explode = function(o) {
+
+ return this.queue(function() {
+
+ var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
+ var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
+
+ o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode;
+ var el = $(this).show().css('visibility', 'hidden');
+ var offset = el.offset();
+
+ //Substract the margins - not fixing the problem yet.
+ offset.top -= parseInt(el.css("marginTop"),10) || 0;
+ offset.left -= parseInt(el.css("marginLeft"),10) || 0;
+
+ var width = el.outerWidth(true);
+ var height = el.outerHeight(true);
+
+ for(var i=0;i<rows;i++) { // =
+ for(var j=0;j<cells;j++) { // ||
+ el
+ .clone()
+ .appendTo('body')
+ .wrap('<div></div>')
+ .css({
+ position: 'absolute',
+ visibility: 'visible',
+ left: -j*(width/cells),
+ top: -i*(height/rows)
+ })
+ .parent()
+ .addClass('ui-effects-explode')
+ .css({
+ position: 'absolute',
+ overflow: 'hidden',
+ width: width/cells,
+ height: height/rows,
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0),
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0),
+ opacity: o.options.mode == 'show' ? 0 : 1
+ }).animate({
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)),
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)),
+ opacity: o.options.mode == 'show' ? 1 : 0
+ }, o.duration || 500);
+ }
+ }
+
+ // Set a timeout, to call the callback approx. when the other animations have finished
+ setTimeout(function() {
+
+ o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide();
+ if(o.callback) o.callback.apply(el[0]); // Callback
+ el.dequeue();
+
+ $('div.ui-effects-explode').remove();
+
+ }, o.duration || 500);
+
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Fade 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.fade = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'hide');
+
+ elem.animate({ opacity: mode }, {
+ queue: false,
+ duration: o.duration,
+ easing: o.options.easing,
+ complete: function() {
+ (o.callback && o.callback.apply(this, arguments));
+ elem.dequeue();
+ }
+ });
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Fold 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.fold = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var size = o.options.size || 15; // Default fold size
+ var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value
+ var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2;
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var widthFirst = ((mode == 'show') != horizFirst);
+ var ref = widthFirst ? ['width', 'height'] : ['height', 'width'];
+ var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()];
+ var percent = /([0-9]+)%/.exec(size);
+ if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1];
+ if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift
+
+ // Animation
+ var animation1 = {}, animation2 = {};
+ animation1[ref[0]] = mode == 'show' ? distance[0] : size;
+ animation2[ref[1]] = mode == 'show' ? distance[1] : 0;
+
+ // Animate
+ wrapper.animate(animation1, duration, o.options.easing)
+ .animate(animation2, duration, o.options.easing, function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ });
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Highlight 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.highlight = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ props = ['backgroundImage', 'backgroundColor', 'opacity'],
+ mode = $.effects.setMode(elem, o.options.mode || 'show'),
+ animation = {
+ backgroundColor: elem.css('backgroundColor')
+ };
+
+ if (mode == 'hide') {
+ animation.opacity = 0;
+ }
+
+ $.effects.save(elem, props);
+ elem
+ .show()
+ .css({
+ backgroundImage: 'none',
+ backgroundColor: o.options.color || '#ffff99'
+ })
+ .animate(animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.options.easing,
+ complete: function() {
+ (mode == 'hide' && elem.hide());
+ $.effects.restore(elem, props);
+ (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter'));
+ (o.callback && o.callback.apply(this, arguments));
+ elem.dequeue();
+ }
+ });
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Pulsate 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.pulsate = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'show');
+ times = ((o.options.times || 5) * 2) - 1;
+ duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2,
+ isVisible = elem.is(':visible'),
+ animateTo = 0;
+
+ if (!isVisible) {
+ elem.css('opacity', 0).show();
+ animateTo = 1;
+ }
+
+ if ((mode == 'hide' && isVisible) || (mode == 'show' && !isVisible)) {
+ times--;
+ }
+
+ for (var i = 0; i < times; i++) {
+ elem.animate({ opacity: animateTo }, duration, o.options.easing);
+ animateTo = (animateTo + 1) % 2;
+ }
+
+ elem.animate({ opacity: animateTo }, duration, o.options.easing, function() {
+ if (animateTo == 0) {
+ elem.hide();
+ }
+ (o.callback && o.callback.apply(this, arguments));
+ });
+
+ elem
+ .queue('fx', function() { elem.dequeue(); })
+ .dequeue();
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Scale 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.puff = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'hide'),
+ percent = parseInt(o.options.percent, 10) || 150,
+ factor = percent / 100,
+ original = { height: elem.height(), width: elem.width() };
+
+ $.extend(o.options, {
+ fade: true,
+ mode: mode,
+ percent: mode == 'hide' ? percent : 100,
+ from: mode == 'hide'
+ ? original
+ : {
+ height: original.height * factor,
+ width: original.width * factor
+ }
+ });
+
+ elem.effect('scale', o.options, o.duration, o.callback);
+ elem.dequeue();
+ });
+};
+
+$.effects.scale = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this);
+
+ // Set options
+ var options = $.extend(true, {}, o.options);
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent
+ var direction = o.options.direction || 'both'; // Set default axis
+ var origin = o.options.origin; // The origin of the scaling
+ if (mode != 'effect') { // Set default origin and restore for show/hide
+ options.origin = origin || ['middle','center'];
+ options.restore = true;
+ }
+ var original = {height: el.height(), width: el.width()}; // Save original
+ el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state
+
+ // Adjust
+ var factor = { // Set scaling factor
+ y: direction != 'horizontal' ? (percent / 100) : 1,
+ x: direction != 'vertical' ? (percent / 100) : 1
+ };
+ el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state
+
+ if (o.options.fade) { // Fade option to support puff
+ if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;};
+ if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;};
+ };
+
+ // Animation
+ options.from = el.from; options.to = el.to; options.mode = mode;
+
+ // Animate
+ el.effect('size', options, o.duration, o.callback);
+ el.dequeue();
+ });
+
+};
+
+$.effects.size = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity'];
+ var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore
+ var props2 = ['width','height','overflow']; // Copy for children
+ var cProps = ['fontSize'];
+ var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
+ var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var restore = o.options.restore || false; // Default restore
+ var scale = o.options.scale || 'both'; // Default scale mode
+ var origin = o.options.origin; // The origin of the sizing
+ var original = {height: el.height(), width: el.width()}; // Save original
+ el.from = o.options.from || original; // Default from state
+ el.to = o.options.to || original; // Default to state
+ // Adjust
+ if (origin) { // Calculate baseline shifts
+ var baseline = $.effects.getBaseline(origin, original);
+ el.from.top = (original.height - el.from.height) * baseline.y;
+ el.from.left = (original.width - el.from.width) * baseline.x;
+ el.to.top = (original.height - el.to.height) * baseline.y;
+ el.to.left = (original.width - el.to.width) * baseline.x;
+ };
+ var factor = { // Set scaling factor
+ from: {y: el.from.height / original.height, x: el.from.width / original.width},
+ to: {y: el.to.height / original.height, x: el.to.width / original.width}
+ };
+ if (scale == 'box' || scale == 'both') { // Scale the css box
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ props = props.concat(vProps);
+ el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from);
+ el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to);
+ };
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
+ props = props.concat(hProps);
+ el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from);
+ el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to);
+ };
+ };
+ if (scale == 'content' || scale == 'both') { // Scale the content
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ props = props.concat(cProps);
+ el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from);
+ el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to);
+ };
+ };
+ $.effects.save(el, restore ? props : props1); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ el.css('overflow','hidden').css(el.from); // Shift
+
+ // Animate
+ if (scale == 'content' || scale == 'both') { // Scale the children
+ vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size
+ hProps = hProps.concat(['marginLeft','marginRight']); // Add margins
+ props2 = props.concat(vProps).concat(hProps); // Concat
+ el.find("*[width]").each(function(){
+ child = $(this);
+ if (restore) $.effects.save(child, props2);
+ var c_original = {height: child.height(), width: child.width()}; // Save original
+ child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x};
+ child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x};
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from);
+ child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to);
+ };
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
+ child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from);
+ child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to);
+ };
+ child.css(child.from); // Shift children
+ child.animate(child.to, o.duration, o.options.easing, function(){
+ if (restore) $.effects.restore(child, props2); // Restore children
+ }); // Animate children
+ });
+ };
+
+ // Animate
+ el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if (el.to.opacity === 0) {
+ el.css('opacity', el.from.opacity);
+ }
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Shake 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.shake = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default direction
+ var distance = o.options.distance || 20; // Default distance
+ var times = o.options.times || 3; // Default # of times
+ var speed = o.duration || o.options.duration || 140; // Default speed per shake
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+
+ // Animation
+ var animation = {}, animation1 = {}, animation2 = {};
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2;
+ animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2;
+
+ // Animate
+ el.animate(animation, speed, o.options.easing);
+ for (var i = 1; i < times; i++) { // Shakes
+ el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing);
+ };
+ el.animate(animation1, speed, o.options.easing).
+ animate(animation, speed / 2, o.options.easing, function(){ // Last shake
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ el.queue('fx', function() { el.dequeue(); });
+ el.dequeue();
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Slide 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.slide = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default Direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
+ if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
+
+ // Animate
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Transfer 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.transfer = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ target = $(o.options.to),
+ endPosition = target.offset(),
+ animation = {
+ top: endPosition.top,
+ left: endPosition.left,
+ height: target.innerHeight(),
+ width: target.innerWidth()
+ },
+ startPosition = elem.offset(),
+ transfer = $('<div class="ui-effects-transfer"></div>')
+ .appendTo(document.body)
+ .addClass(o.options.className)
+ .css({
+ top: startPosition.top,
+ left: startPosition.left,
+ height: elem.innerHeight(),
+ width: elem.innerWidth(),
+ position: 'absolute'
+ })
+ .animate(animation, o.duration, o.options.easing, function() {
+ transfer.remove();
+ (o.callback && o.callback.apply(elem[0], arguments));
+ elem.dequeue();
+ });
+ });
+};
+
+})(jQuery);
+/*
+ Watermark plugin for jQuery
+ Version: 3.1.1
+ http://jquery-watermark.googlecode.com/
+
+ Copyright (c) 2009-2011 Todd Northrop
+ http://www.speednet.biz/
+
+ January 10, 2011
+
+ Requires: jQuery 1.2.3+
+
+ Dual licensed under the MIT or GPL Version 2 licenses.
+ See mit-license.txt and gpl2-license.txt in the project root for details.
+------------------------------------------------------*/
+
+(function ($, window, undefined) {
+
+var
+ // String constants for data names
+ dataFlag = "watermark",
+ dataClass = "watermarkClass",
+ dataFocus = "watermarkFocus",
+ dataFormSubmit = "watermarkSubmit",
+ dataMaxLen = "watermarkMaxLength",
+ dataPassword = "watermarkPassword",
+ dataText = "watermarkText",
+
+ // Copy of native jQuery regex use to strip return characters from element value
+ rreturn = /\r/g,
+
+ // Includes only elements with watermark defined
+ selWatermarkDefined = ":data(" + dataFlag + ")",
+
+ // Includes only elements capable of having watermark
+ selWatermarkAble = ":text,:password,:search,textarea",
+
+ // triggerFns:
+ // Array of function names to look for in the global namespace.
+ // Any such functions found will be hijacked to trigger a call to
+ // hideAll() any time they are called. The default value is the
+ // ASP.NET function that validates the controls on the page
+ // prior to a postback.
+ //
+ // Am I missing other important trigger function(s) to look for?
+ // Please leave me feedback:
+ // http://code.google.com/p/jquery-watermark/issues/list
+ triggerFns = [
+ "Page_ClientValidate"
+ ],
+
+ // Holds a value of true if a watermark was displayed since the last
+ // hideAll() was executed. Avoids repeatedly calling hideAll().
+ pageDirty = false,
+
+ // Detects if the browser can handle native placeholders
+ hasNativePlaceholder = ("placeholder" in document.createElement("input"));
+
+// Best practice: this plugin adds only one method to the jQuery object.
+// Also ensures that the watermark code is only added once.
+$.watermark = $.watermark || {
+
+ // Current version number of the plugin
+ version: "3.1.1",
+
+ runOnce: true,
+
+ // Default options used when watermarks are instantiated.
+ // Can be changed to affect the default behavior for all
+ // new or updated watermarks.
+ options: {
+
+ // Default class name for all watermarks
+ className: "watermark",
+
+ // If true, plugin will detect and use native browser support for
+ // watermarks, if available. (e.g., WebKit's placeholder attribute.)
+ useNative: true,
+
+ // If true, all watermarks will be hidden during the window's
+ // beforeunload event. This is done mainly because WebKit
+ // browsers remember the watermark text during navigation
+ // and try to restore the watermark text after the user clicks
+ // the Back button. We can avoid this by hiding the text before
+ // the browser has a chance to save it. The regular unload event
+ // was tried, but it seems the browser saves the text before
+ // that event kicks off, because it didn't work.
+ hideBeforeUnload: true
+ },
+
+ // Hide one or more watermarks by specifying any selector type
+ // i.e., DOM element, string selector, jQuery matched set, etc.
+ hide: function (selector) {
+ $(selector).filter(selWatermarkDefined).each(
+ function () {
+ $.watermark._hide($(this));
+ }
+ );
+ },
+
+ // Internal use only.
+ _hide: function ($input, focus) {
+ var elem = $input[0],
+ inputVal = (elem.value || "").replace(rreturn, ""),
+ inputWm = $input.data(dataText) || "",
+ maxLen = $input.data(dataMaxLen) || 0,
+ className = $input.data(dataClass);
+
+ if ((inputWm.length) && (inputVal == inputWm)) {
+ elem.value = "";
+
+ // Password type?
+ if ($input.data(dataPassword)) {
+
+ if (($input.attr("type") || "") === "text") {
+ var $pwd = $input.data(dataPassword) || [],
+ $wrap = $input.parent() || [];
+
+ if (($pwd.length) && ($wrap.length)) {
+ $wrap[0].removeChild($input[0]); // Can't use jQuery methods, because they destroy data
+ $wrap[0].appendChild($pwd[0]);
+ $input = $pwd;
+ }
+ }
+ }
+
+ if (maxLen) {
+ $input.attr("maxLength", maxLen);
+ $input.removeData(dataMaxLen);
+ }
+
+ if (focus) {
+ $input.attr("autocomplete", "off"); // Avoid NS_ERROR_XPC_JS_THREW_STRING error in Firefox
+
+ window.setTimeout(
+ function () {
+ $input.select(); // Fix missing cursor in IE
+ }
+ , 1);
+ }
+ }
+
+ className && $input.removeClass(className);
+ },
+
+ // Display one or more watermarks by specifying any selector type
+ // i.e., DOM element, string selector, jQuery matched set, etc.
+ // If conditions are not right for displaying a watermark, ensures that watermark is not shown.
+ show: function (selector) {
+ $(selector).filter(selWatermarkDefined).each(
+ function () {
+ $.watermark._show($(this));
+ }
+ );
+ },
+
+ // Internal use only.
+ _show: function ($input) {
+ var elem = $input[0],
+ val = (elem.value || "").replace(rreturn, ""),
+ text = $input.data(dataText) || "",
+ type = $input.attr("type") || "",
+ className = $input.data(dataClass);
+
+ if (((val.length == 0) || (val == text)) && (!$input.data(dataFocus))) {
+ pageDirty = true;
+
+ // Password type?
+ if ($input.data(dataPassword)) {
+
+ if (type === "password") {
+ var $pwd = $input.data(dataPassword) || [],
+ $wrap = $input.parent() || [];
+
+ if (($pwd.length) && ($wrap.length)) {
+ $wrap[0].removeChild($input[0]); // Can't use jQuery methods, because they destroy data
+ $wrap[0].appendChild($pwd[0]);
+ $input = $pwd;
+ $input.attr("maxLength", text.length);
+ elem = $input[0];
+ }
+ }
+ }
+
+ // Ensure maxLength big enough to hold watermark (input of type="text" or type="search" only)
+ if ((type === "text") || (type === "search")) {
+ var maxLen = $input.attr("maxLength") || 0;
+
+ if ((maxLen > 0) && (text.length > maxLen)) {
+ $input.data(dataMaxLen, maxLen);
+ $input.attr("maxLength", text.length);
+ }
+ }
+
+ className && $input.addClass(className);
+ elem.value = text;
+ }
+ else {
+ $.watermark._hide($input);
+ }
+ },
+
+ // Hides all watermarks on the current page.
+ hideAll: function () {
+ if (pageDirty) {
+ $.watermark.hide(selWatermarkAble);
+ pageDirty = false;
+ }
+ },
+
+ // Displays all watermarks on the current page.
+ showAll: function () {
+ $.watermark.show(selWatermarkAble);
+ }
+};
+
+$.fn.watermark = $.fn.watermark || function (text, options) {
+ /// <summary>
+ /// Set watermark text and class name on all input elements of type="text/password/search" and
+ /// textareas within the matched set. If className is not specified in options, the default is
+ /// "watermark". Within the matched set, only input elements with type="text/password/search"
+ /// and textareas are affected; all other elements are ignored.
+ /// </summary>
+ /// <returns type="jQuery">
+ /// Returns the original jQuery matched set (not just the input and texarea elements).
+ /// </returns>
+ /// <param name="text" type="String">
+ /// Text to display as a watermark when the input or textarea element has an empty value and does not
+ /// have focus. The first time watermark() is called on an element, if this argument is empty (or not
+ /// a String type), then the watermark will have the net effect of only changing the class name when
+ /// the input or textarea element's value is empty and it does not have focus.
+ /// </param>
+ /// <param name="options" type="Object" optional="true">
+ /// Provides the ability to override the default watermark options ($.watermark.options). For backward
+ /// compatibility, if a string value is supplied, it is used as the class name that overrides the class
+ /// name in $.watermark.options.className. Properties include:
+ /// className: When the watermark is visible, the element will be styled using this class name.
+ /// useNative (Boolean or Function): Specifies if native browser support for watermarks will supersede
+ /// plugin functionality. If useNative is a function, the return value from the function will
+ /// determine if native support is used. The function is passed one argument -- a jQuery object
+ /// containing the element being tested as the only element in its matched set -- and the DOM
+ /// element being tested is the object on which the function is invoked (the value of "this").
+ /// </param>
+ /// <remarks>
+ /// The effect of changing the text and class name on an input element is called a watermark because
+ /// typically light gray text is used to provide a hint as to what type of input is required. However,
+ /// the appearance of the watermark can be something completely different: simply change the CSS style
+ /// pertaining to the supplied class name.
+ ///
+ /// The first time watermark() is called on an element, the watermark text and class name are initialized,
+ /// and the focus and blur events are hooked in order to control the display of the watermark. Also, as
+ /// of version 3.0, drag and drop events are hooked to guard against dropped text being appended to the
+ /// watermark. If native watermark support is provided by the browser, it is detected and used, unless
+ /// the useNative option is set to false.
+ ///
+ /// Subsequently, watermark() can be called again on an element in order to change the watermark text
+ /// and/or class name, and it can also be called without any arguments in order to refresh the display.
+ ///
+ /// For example, after changing the value of the input or textarea element programmatically, watermark()
+ /// should be called without any arguments to refresh the display, because the change event is only
+ /// triggered by user actions, not by programmatic changes to an input or textarea element's value.
+ ///
+ /// The one exception to programmatic updates is for password input elements: you are strongly cautioned
+ /// against changing the value of a password input element programmatically (after the page loads).
+ /// The reason is that some fairly hairy code is required behind the scenes to make the watermarks bypass
+ /// IE security and switch back and forth between clear text (for watermarks) and obscured text (for
+ /// passwords). It is *possible* to make programmatic changes, but it must be done in a certain way, and
+ /// overall it is not recommended.
+ /// </remarks>
+
+ if (!this.length) {
+ return this;
+ }
+
+ var hasClass = false,
+ hasText = (typeof(text) === "string");
+
+ if (hasText) {
+ text = text.replace(rreturn, "");
+ }
+
+ if (typeof(options) === "object") {
+ hasClass = (typeof(options.className) === "string");
+ options = $.extend({}, $.watermark.options, options);
+ }
+ else if (typeof(options) === "string") {
+ hasClass = true;
+ options = $.extend({}, $.watermark.options, {className: options});
+ }
+ else {
+ options = $.watermark.options;
+ }
+
+ if (typeof(options.useNative) !== "function") {
+ options.useNative = options.useNative? function () { return true; } : function () { return false; };
+ }
+
+ return this.each(
+ function () {
+ var $input = $(this);
+
+ if (!$input.is(selWatermarkAble)) {
+ return;
+ }
+
+ // Watermark already initialized?
+ if ($input.data(dataFlag)) {
+
+ // If re-defining text or class, first remove existing watermark, then make changes
+ if (hasText || hasClass) {
+ $.watermark._hide($input);
+
+ if (hasText) {
+ $input.data(dataText, text);
+ }
+
+ if (hasClass) {
+ $input.data(dataClass, options.className);
+ }
+ }
+ }
+ else {
+
+ // Detect and use native browser support, if enabled in options
+ if (
+ (hasNativePlaceholder)
+ && (options.useNative.call(this, $input))
+ && (($input.attr("tagName") || "") !== "TEXTAREA")
+ ) {
+ // className is not set because current placeholder standard doesn't
+ // have a separate class name property for placeholders (watermarks).
+ if (hasText) {
+ $input.attr("placeholder", text);
+ }
+
+ // Only set data flag for non-native watermarks
+ // [purposely commented-out] -> $input.data(dataFlag, 1);
+ return;
+ }
+
+ $input.data(dataText, hasText? text : "");
+ $input.data(dataClass, options.className);
+ $input.data(dataFlag, 1); // Flag indicates watermark was initialized
+
+ // Special processing for password type
+ if (($input.attr("type") || "") === "password") {
+ var $wrap = $input.wrap("<span>").parent(),
+ $wm = $($wrap.html().replace(/type=["']?password["']?/i, 'type="text"'));
+
+ $wm.data(dataText, $input.data(dataText));
+ $wm.data(dataClass, $input.data(dataClass));
+ $wm.data(dataFlag, 1);
+ $wm.attr("maxLength", text.length);
+
+ $wm.focus(
+ function () {
+ $.watermark._hide($wm, true);
+ }
+ ).bind("dragenter",
+ function () {
+ $.watermark._hide($wm);
+ }
+ ).bind("dragend",
+ function () {
+ window.setTimeout(function () { $wm.blur(); }, 1);
+ }
+ );
+ $input.blur(
+ function () {
+ $.watermark._show($input);
+ }
+ ).bind("dragleave",
+ function () {
+ $.watermark._show($input);
+ }
+ );
+
+ $wm.data(dataPassword, $input);
+ $input.data(dataPassword, $wm);
+ }
+ else {
+
+ $input.focus(
+ function () {
+ $input.data(dataFocus, 1);
+ $.watermark._hide($input, true);
+ }
+ ).blur(
+ function () {
+ $input.data(dataFocus, 0);
+ $.watermark._show($input);
+ }
+ ).bind("dragenter",
+ function () {
+ $.watermark._hide($input);
+ }
+ ).bind("dragleave",
+ function () {
+ $.watermark._show($input);
+ }
+ ).bind("dragend",
+ function () {
+ window.setTimeout(function () { $.watermark._show($input); }, 1);
+ }
+ ).bind("drop",
+ // Firefox makes this lovely function necessary because the dropped text
+ // is merged with the watermark before the drop event is called.
+ function (evt) {
+ var elem = $input[0],
+ dropText = evt.originalEvent.dataTransfer.getData("Text");
+
+ if ((elem.value || "").replace(rreturn, "").replace(dropText, "") === $input.data(dataText)) {
+ elem.value = dropText;
+ }
+
+ $input.focus();
+ }
+ );
+ }
+
+ // In order to reliably clear all watermarks before form submission,
+ // we need to replace the form's submit function with our own
+ // function. Otherwise watermarks won't be cleared when the form
+ // is submitted programmatically.
+ if (this.form) {
+ var form = this.form,
+ $form = $(form);
+
+ if (!$form.data(dataFormSubmit)) {
+ $form.submit($.watermark.hideAll);
+
+ // form.submit exists for all browsers except Google Chrome
+ // (see "else" below for explanation)
+ if (form.submit) {
+ $form.data(dataFormSubmit, form.submit);
+
+ form.submit = (function (f, $f) {
+ return function () {
+ var nativeSubmit = $f.data(dataFormSubmit);
+
+ $.watermark.hideAll();
+
+ if (nativeSubmit.apply) {
+ nativeSubmit.apply(f, Array.prototype.slice.call(arguments));
+ }
+ else {
+ nativeSubmit();
+ }
+ };
+ })(form, $form);
+ }
+ else {
+ $form.data(dataFormSubmit, 1);
+
+ // This strangeness is due to the fact that Google Chrome's
+ // form.submit function is not visible to JavaScript (identifies
+ // as "undefined"). I had to invent a solution here because hours
+ // of Googling (ironically) for an answer did not turn up anything
+ // useful. Within my own form.submit function I delete the form's
+ // submit function, and then call the non-existent function --
+ // which, in the world of Google Chrome, still exists.
+ form.submit = (function (f) {
+ return function () {
+ $.watermark.hideAll();
+ delete f.submit;
+ f.submit();
+ };
+ })(form);
+ }
+ }
+ }
+ }
+
+ $.watermark._show($input);
+ }
+ );
+};
+
+// The code included within the following if structure is guaranteed to only run once,
+// even if the watermark script file is included multiple times in the page.
+if ($.watermark.runOnce) {
+ $.watermark.runOnce = false;
+
+ $.extend($.expr[":"], {
+
+ // Extends jQuery with a custom selector - ":search" for determining is the element
+ // is a "search" input type.
+ search: function (elem) {
+ return "search" === (elem.type || "");
+ },
+
+ // Extends jQuery with a custom selector - ":data(...)"
+ // :data(<name>) Includes elements that have a specific name defined in the jQuery data
+ // collection. (Only the existence of the name is checked; the value is ignored.)
+ // A more sophisticated version of the :data() custom selector originally part of this plugin
+ // was removed for compatibility with jQuery UI. The original code can be found in the SVN
+ // source listing in the file, "jquery.data.js".
+ data: function( elem, i, match ) {
+ return !!$.data( elem, match[ 3 ] );
+ }
+ });
+
+ // Overloads the jQuery .val() function to return the underlying input value on
+ // watermarked input elements. When .val() is being used to set values, this
+ // function ensures watermarks are properly set/removed after the values are set.
+ // Uses self-executing function to override the default jQuery function.
+ (function (valOld) {
+
+ $.fn.val = function () {
+
+ // Best practice: return immediately if empty matched set
+ if ( !this.length ) {
+ return arguments.length? this : undefined;
+ }
+
+ // If no args, then we're getting the value of the first element;
+ // otherwise we're setting values for all elements in matched set
+ if ( !arguments.length ) {
+
+ // If element is watermarked, get the underlying value;
+ // otherwise use native jQuery .val()
+ if ( this.data(dataFlag) ) {
+ var v = (this[0].value || "").replace(rreturn, "");
+ return (v === (this.data(dataText) || ""))? "" : v;
+ }
+ else {
+ return valOld.apply( this, arguments );
+ }
+ }
+ else {
+ valOld.apply( this, arguments );
+ $.watermark.show(this);
+ return this;
+ }
+ };
+
+ })($.fn.val);
+
+ // Hijack any functions found in the triggerFns list
+ if (triggerFns.length) {
+
+ // Wait until DOM is ready before searching
+ $(function () {
+ var i, name, fn;
+
+ for (i=triggerFns.length-1; i>=0; i--) {
+ name = triggerFns[i];
+ fn = window[name];
+
+ if (typeof(fn) === "function") {
+ window[name] = (function (origFn) {
+ return function () {
+ $.watermark.hideAll();
+ return origFn.apply(null, Array.prototype.slice.call(arguments));
+ };
+ })(fn);
+ }
+ }
+ });
+ }
+
+ $(window).bind("beforeunload", function () {
+ if ($.watermark.options.hideBeforeUnload) {
+ $.watermark.hideAll();
+ }
+ });
+}
+
+})(jQuery, window);
+/**
+ * This jQuery plugin will gather the comments within
+ * the current jQuery collection, returning all the
+ * comments in a new jQuery collection.
+ *
+ * NOTE: Comments are wrapped in DIV tags.
+ *
+ * @source http://www.bennadel.com/blog/1563-jQuery-Comments-Plug-in-To-Access-HTML-Comments-For-DOM-Templating.htm
+ */
+(function($) {
+ $.fn.comments = function( blnDeep ){
+ var blnDeep = (blnDeep || false);
+ var jComments = $( [] );
+
+ // Loop over each node to search its children for
+ // comment nodes and element nodes (if deep search).
+ this.each(
+ function( intI, objNode ){
+ var objChildNode = objNode.firstChild;
+ var strParentID = $( this ).attr( "id" );
+
+ // Keep looping over the top-level children
+ // while we have a node to examine.
+ while (objChildNode){
+
+ // Check to see if this node is a comment.
+ if (objChildNode.nodeType === 8){
+
+ // We found a comment node. Add it to
+ // the nodes collection wrapped in a
+ // DIV (as we may have HTML).
+ jComments = jComments.add(
+ "<div rel='" + strParentID + "'>" +
+ objChildNode.nodeValue +
+ "</div>"
+ );
+
+ } else if (
+ blnDeep &&
+ (objChildNode.nodeType === 1)
+ ) {
+
+ // Traverse this node deeply.
+ jComments = jComments.add(
+ $( objChildNode ).comments( true )
+ );
+
+ }
+
+ // Move to the next sibling.
+ objChildNode = objChildNode.nextSibling;
+
+ }
+
+ }
+ );
+
+ // Return the jQuery comments collection.
+ return( jComments );
+ }
+})(jQuery);
+/*!
+ * MockJax - jQuery Plugin to Mock Ajax requests
+ *
+ * Version: 1.4.0
+ * Released: 2011-02-04
+ * Source: http://github.com/appendto/jquery-mockjax
+ * Docs: http://enterprisejquery.com/2010/07/mock-your-ajax-requests-with-mockjax-for-rapid-development
+ * Plugin: mockjax
+ * Author: Jonathan Sharp (http://jdsharp.com)
+ * License: MIT,GPL
+ *
+ * Copyright (c) 2010 appendTo LLC.
+ * Dual licensed under the MIT or GPL licenses.
+ * http://appendto.com/open-source-licenses
+ */
+(function($) {
+ var _ajax = $.ajax,
+ mockHandlers = [];
+
+ function parseXML(xml) {
+ if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
+ DOMParser = function() { };
+ DOMParser.prototype.parseFromString = function( xmlString ) {
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
+ doc.async = 'false';
+ doc.loadXML( xmlString );
+ return doc;
+ };
+ }
+
+ try {
+ var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
+ if ( $.isXMLDoc( xmlDoc ) ) {
+ var err = $('parsererror', xmlDoc);
+ if ( err.length == 1 ) {
+ throw('Error: ' + $(xmlDoc).text() );
+ }
+ } else {
+ throw('Unable to parse XML');
+ }
+ } catch( e ) {
+ var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
+ $(document).trigger('xmlParseError', [ msg ]);
+ return undefined;
+ }
+ return xmlDoc;
+ }
+
+ $.extend({
+ ajax: function(origSettings) {
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
+ mock = false;
+ // Iterate over our mock handlers (in registration order) until we find
+ // one that is willing to intercept the request
+ $.each(mockHandlers, function(k, v) {
+ if ( !mockHandlers[k] ) {
+ return;
+ }
+ var m = null;
+ // If the mock was registered with a function, let the function decide if we
+ // want to mock this request
+ if ( $.isFunction(mockHandlers[k]) ) {
+ m = mockHandlers[k](s);
+ } else {
+ m = mockHandlers[k];
+ // Inspect the URL of the request and check if the mock handler's url
+ // matches the url for this ajax request
+ if ( $.isFunction(m.url.test) ) {
+ // The user provided a regex for the url, test it
+ if ( !m.url.test( s.url ) ) {
+ m = null;
+ }
+ } else {
+ // Look for a simple wildcard '*' or a direct URL match
+ var star = m.url.indexOf('*');
+ if ( ( m.url != '*' && m.url != s.url && star == -1 ) ||
+ ( star > -1 && m.url.substr(0, star) != s.url.substr(0, star) ) ) {
+ // The url we tested did not match the wildcard *
+ m = null;
+ }
+ }
+ if ( m ) {
+ // Inspect the data submitted in the request (either POST body or GET query string)
+ if ( m.data && s.data ) {
+ var identical = false;
+ // Deep inspect the identity of the objects
+ (function ident(mock, live) {
+ // Test for situations where the data is a querystring (not an object)
+ if (typeof live === 'string') {
+ // Querystring may be a regex
+ identical = $.isFunction( mock.test ) ? mock.test(live) : mock == live;
+ return identical;
+ }
+ $.each(mock, function(k, v) {
+ if ( live[k] === undefined ) {
+ identical = false;
+ return false;
+ } else {
+ identical = true;
+ if ( typeof live[k] == 'object' ) {
+ return ident(mock[k], live[k]);
+ } else {
+ if ( $.isFunction( mock[k].test ) ) {
+ identical = mock[k].test(live[k]);
+ } else {
+ identical = ( mock[k] == live[k] );
+ }
+ return identical;
+ }
+ }
+ });
+ })(m.data, s.data);
+ // They're not identical, do not mock this request
+ if ( identical == false ) {
+ m = null;
+ }
+ }
+ // Inspect the request type
+ if ( m && m.type && m.type != s.type ) {
+ // The request type doesn't match (GET vs. POST)
+ m = null;
+ }
+ }
+ }
+ if ( m ) {
+ mock = true;
+
+ // Handle console logging
+ var c = $.extend({}, $.mockjaxSettings, m);
+ if ( c.log && $.isFunction(c.log) ) {
+ c.log('MOCK ' + s.type.toUpperCase() + ': ' + s.url, $.extend({}, s));
+ }
+
+ var jsre = /=\?(&|$)/, jsc = (new Date()).getTime();
+
+ // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
+ // because there isn't an easy hook for the cross domain script tag of jsonp
+ if ( s.dataType === "jsonp" ) {
+ if ( s.type.toUpperCase() === "GET" ) {
+ if ( !jsre.test( s.url ) ) {
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+ }
+ } else if ( !s.data || !jsre.test(s.data) ) {
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+ }
+ s.dataType = "json";
+ }
+
+ // Build temporary JSONP function
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+ // Replace the =? sequence both in the query string and the data
+ if ( s.data ) {
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+ }
+
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+ // We need to make sure
+ // that a JSONP style response is executed properly
+ s.dataType = "script";
+
+ // Handle JSONP-style loading
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+ data = tmp;
+ success();
+ complete();
+ // Garbage collect
+ window[ jsonp ] = undefined;
+
+ try {
+ delete window[ jsonp ];
+ } catch(e) {}
+
+ if ( head ) {
+ head.removeChild( script );
+ }
+ };
+ }
+
+ var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+ parts = rurl.exec( s.url ),
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+ // Test if we are going to create a script tag (if so, intercept & mock)
+ if ( s.dataType === "script" && s.type.toUpperCase() === "GET" && remote ) {
+ // Synthesize the mock request for adding a script tag
+ var callbackContext = origSettings && origSettings.context || s;
+
+ function success() {
+ // If a local callback was specified, fire it and pass it the data
+ if ( s.success ) {
+ s.success.call( callbackContext, ( m.response ? m.response.toString() : m.responseText || ''), status, {} );
+ }
+
+ // Fire the global callback
+ if ( s.global ) {
+ trigger( "ajaxSuccess", [{}, s] );
+ }
+ }
+
+ function complete() {
+ // Process result
+ if ( s.complete ) {
+ s.complete.call( callbackContext, {} , status );
+ }
+
+ // The request was completed
+ if ( s.global ) {
+ trigger( "ajaxComplete", [{}, s] );
+ }
+
+ // Handle the global AJAX counter
+ if ( s.global && ! --jQuery.active ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+
+ function trigger(type, args) {
+ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+ }
+
+ if ( m.response && $.isFunction(m.response) ) {
+ m.response(origSettings);
+ } else {
+ $.globalEval(m.responseText);
+ }
+ success();
+ complete();
+ return false;
+ }
+ mock = _ajax.call($, $.extend(true, {}, origSettings, {
+ // Mock the XHR object
+ xhr: function() {
+ // Extend with our default mockjax settings
+ m = $.extend({}, $.mockjaxSettings, m);
+
+ if ( m.contentType ) {
+ m.headers['content-type'] = m.contentType;
+ }
+
+ // Return our mock xhr object
+ return {
+ status: m.status,
+ readyState: 1,
+ open: function() { },
+ send: function() {
+ // This is a substitute for < 1.4 which lacks $.proxy
+ var process = (function(that) {
+ return function() {
+ return (function() {
+ // The request has returned
+ this.status = m.status;
+ this.readyState = 4;
+
+ // We have an executable function, call it to give
+ // the mock handler a chance to update it's data
+ if ( $.isFunction(m.response) ) {
+ m.response(origSettings);
+ }
+ // Copy over our mock to our xhr object before passing control back to
+ // jQuery's onreadystatechange callback
+ if ( s.dataType == 'json' && ( typeof m.responseText == 'object' ) ) {
+ this.responseText = JSON.stringify(m.responseText);
+ } else if ( s.dataType == 'xml' ) {
+ if ( typeof m.responseXML == 'string' ) {
+ this.responseXML = parseXML(m.responseXML);
+ } else {
+ this.responseXML = m.responseXML;
+ }
+ } else {
+ this.responseText = m.responseText;
+ }
+ // jQuery < 1.4 doesn't have onreadystate change for xhr
+ if ( $.isFunction(this.onreadystatechange) ) {
+ this.onreadystatechange( m.isTimeout ? 'timeout' : undefined );
+ }
+ }).apply(that);
+ };
+ })(this);
+
+ if ( m.proxy ) {
+ // We're proxying this request and loading in an external file instead
+ _ajax({
+ global: false,
+ url: m.proxy,
+ type: m.proxyType,
+ data: m.data,
+ dataType: s.dataType,
+ complete: function(xhr, txt) {
+ m.responseXML = xhr.responseXML;
+ m.responseText = xhr.responseText;
+ this.responseTimer = setTimeout(process, m.responseTime || 0);
+ }
+ });
+ } else {
+ // type == 'POST' || 'GET' || 'DELETE'
+ if ( s.async === false ) {
+ // TODO: Blocking delay
+ process();
+ } else {
+ this.responseTimer = setTimeout(process, m.responseTime || 50);
+ }
+ }
+ },
+ abort: function() {
+ clearTimeout(this.responseTimer);
+ },
+ setRequestHeader: function() { },
+ getResponseHeader: function(header) {
+ // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
+ if ( m.headers && m.headers[header] ) {
+ // Return arbitrary headers
+ return m.headers[header];
+ } else if ( header.toLowerCase() == 'last-modified' ) {
+ return m.lastModified || (new Date()).toString();
+ } else if ( header.toLowerCase() == 'etag' ) {
+ return m.etag || '';
+ } else if ( header.toLowerCase() == 'content-type' ) {
+ return m.contentType || 'text/plain';
+ }
+ },
+ getAllResponseHeaders: function() {
+ var headers = '';
+ $.each(m.headers, function(k, v) {
+ headers += k + ': ' + v + "\n";
+ });
+ return headers;
+ }
+ };
+ }
+ }));
+ return false;
+ }
+ });
+ // We don't have a mock request, trigger a normal request
+ if ( !mock ) {
+ return _ajax.apply($, arguments);
+ } else {
+ return mock;
+ }
+ }
+ });
+
+ $.mockjaxSettings = {
+ //url: null,
+ //type: 'GET',
+ log: function(msg) {
+ window['console'] && window.console.log && window.console.log(msg);
+ },
+ status: 200,
+ responseTime: 500,
+ isTimeout: false,
+ contentType: 'text/plain',
+ response: '',
+ responseText: '',
+ responseXML: '',
+ proxy: '',
+ proxyType: 'GET',
+
+ lastModified: null,
+ etag: '',
+ headers: {
+ etag: 'IJF@H#@923uf8023hFO@I#H#',
+ 'content-type' : 'text/plain'
+ }
+ };
+
+ $.mockjax = function(settings) {
+ var i = mockHandlers.length;
+ mockHandlers[i] = settings;
+ return i;
+ };
+ $.mockjaxClear = function(i) {
+ if ( arguments.length == 1 ) {
+ mockHandlers[i] = null;
+ } else {
+ mockHandlers = [];
+ }
+ };
+})(jQuery);
diff --git a/res/pages/js/register.js b/res/pages/js/register.js
new file mode 100644
index 00000000..c76405b5
--- /dev/null
+++ b/res/pages/js/register.js
@@ -0,0 +1,105 @@
+new function() {
+ $.fn.validator = {
+ init: function(o) {
+ if(o.name == 'username') { this.username(o) };
+ if(o.name == 'passone') { this.passone(o) };
+ if(o.name == 'passtwo') { this.passtwo(o) };
+ if(o.name == 'human') { this.human(o) };
+
+ },
+ passone: function (o) {
+ var empty = /^$/;
+ var pass = /^[\x21-\x7E]*$/;
+ if (o.value.match(empty)) {
+ doError(o, 'You must specify a password.');
+ }
+ else if (!o.value.match(pass)) {
+ doError(o, 'At this time our system only supports passwords which consist of valid ASCII characters, excluding spaces and control codes.');
+ } else {
+ doSuccess(o);
+ };
+ },
+ passtwo: function (o) {
+ if (o.value == $("#passone").val()) {
+ doSuccess(o);
+ } else {
+ doError(o, 'Please type your password a second time to make sure it was entered correctly.');
+ };
+ },
+ human: function (o) {
+ var human = /^[0-9a-zA-z]{10}$/;
+ if (o.value.match(human)) {
+ doSuccess(o);
+ } else {
+ doError(o, 'In order for us to verify that you are a human being and not an automated program we need you type the characters you see in the image below. The answer will be exactly ten characters and only contain letters and numbers.');
+ };
+ },
+ username: function (o) {
+ var name = /^[a-zA-z][0-9a-zA-z\_]*$/;
+ if (o.value.match(name)) {
+ doSuccess(o);
+ } else {
+ doError(o, 'The username you selected is invalid. The username must start with a letter and only contain letters, numbers and underscores. Please correct the problem and try again.');
+ };
+ }
+ };
+
+ function doSuccess(o) {
+ $('#' + o.id).removeClass("red");
+ $('#' + o.id).addClass("white");
+ $('#' + o.id + '_msg').remove();
+ }
+
+ function doError(o,m) {
+ $('#' + o.id).removeClass("white");
+ $('#' + o.id).addClass("red");
+ $('#' + o.id + '_msg').remove();
+ $('#' + o.id).after('<div id="' + o.id + '_msg">' + m + '</div>');
+ }
+};
+
+function prettyMoney(amount) {
+ amount -= 0;
+ amount = (Math.round(amount * 100)) / 100;
+ return (amount == Math.floor(amount)) ? amount + '.00' : ( (amount * 10 == Math.floor(amount * 10)) ? amount + '0' : amount);
+}
+
+$(document).ready(function() {
+
+ if ($.browser.mozilla && document.getElementById("reg_step2") != null) {
+ $("div#reg_step2 table#plans").wrap("<div id='plans_wrapper' style='padding: 0px; margin: 0px; width: 737px;'></div>");
+ $("div#reg_step2 div#plans_wrapper").corner("round");
+ }
+
+ $("input").focus(function() {
+ $(this).removeClass("red");
+ $(this).removeClass("white");
+ $(this).addClass("gray");
+ });
+
+ $("input").blur(function() {
+ $(this).removeClass("gray");
+ $(this).addClass("white");
+ $(this).validator.init(this);
+ });
+
+ $("form").submit(function() {
+ var output = true;
+ $(this).find("input").blur();
+ $(this).find(".red").each(function() { output = false; });
+ return output;
+ });
+
+ $("form#step2").submit(function() {
+ if ($("input#basic").attr("checked") != true && $("input#personal").attr("checked") != true) {
+ $('div#plans_msg').remove();
+ $('div#buttons').before('<div id="plans_msg"><br />Please select a plan before continuing.</div>');
+ return false;
+ }
+ return true;
+ });
+
+ $("table#plans input").click(function() {
+ $('div#plans_msg').remove();
+ });
+});
diff --git a/res/pages/js/site.js b/res/pages/js/site.js
new file mode 100644
index 00000000..a58aca2d
--- /dev/null
+++ b/res/pages/js/site.js
@@ -0,0 +1,105 @@
+
+function ChangeImage(Element, newImage) {
+ Element.src = newImage;
+}
+
+imageArray = new Array();
+imageArray[0] = new Image();
+imageArray[0].src = "images/nav_about_active.gif";
+imageArray[1] = new Image();
+imageArray[1].src = "images/nav_about_active_over.gif";
+imageArray[2] = new Image();
+imageArray[2].src = "images/nav_about.gif";
+imageArray[3] = new Image();
+imageArray[3].src = "images/nav_about_over.gif";
+imageArray[4] = new Image();
+imageArray[4].src = "images/nav_contact_active.gif";
+imageArray[5] = new Image();
+imageArray[5].src = "images/nav_contact_active_over.gif";
+imageArray[6] = new Image();
+imageArray[6].src = "images/nav_contact.gif";
+imageArray[7] = new Image();
+imageArray[7].src = "images/nav_contact_over.gif";
+imageArray[8] = new Image();
+imageArray[8].src = "images/nav_features_active.gif";
+imageArray[9] = new Image();
+imageArray[9].src = "images/nav_features_active_over.gif";
+imageArray[10] = new Image();
+imageArray[10].src = "images/nav_features.gif";
+imageArray[11] = new Image();
+imageArray[11].src = "images/nav_features_over.gif";
+imageArray[12] = new Image();
+imageArray[12].src = "images/nav_help_active.gif";
+imageArray[13] = new Image();
+imageArray[13].src = "images/nav_help_active_over.gif";
+imageArray[14] = new Image();
+imageArray[14].src = "images/nav_help.gif";
+imageArray[15] = new Image();
+imageArray[15].src = "images/nav_help_over.gif";
+imageArray[16] = new Image();
+imageArray[16].src = "images/nav_home_active.gif";
+imageArray[17] = new Image();
+imageArray[17].src = "images/nav_home_active_over.gif";
+imageArray[18] = new Image();
+imageArray[18].src = "images/nav_home.gif";
+imageArray[19] = new Image();
+imageArray[19].src = "images/nav_home_over.gif";
+imageArray[20] = new Image();
+imageArray[20].src = "images/nav_links_over.gif";
+imageArray[21] = new Image();
+imageArray[21].src = "images/nav_network_active.gif";
+imageArray[22] = new Image();
+imageArray[22].src = "images/nav_network_active_over.gif";
+imageArray[23] = new Image();
+imageArray[23].src = "images/nav_network.gif";
+imageArray[24] = new Image();
+imageArray[24].src = "images/nav_network_over.gif";
+imageArray[25] = new Image();
+imageArray[25].src = "images/nav_news_over.gif";
+imageArray[26] = new Image();
+imageArray[26].src = "images/nav_philosophy_active.gif";
+imageArray[27] = new Image();
+imageArray[27].src = "images/nav_philosophy_active_over.gif";
+imageArray[28] = new Image();
+imageArray[28].src = "images/nav_philosophy.gif";
+imageArray[29] = new Image();
+imageArray[29].src = "images/nav_philosophy_over.gif";
+imageArray[30] = new Image();
+imageArray[30].src = "images/nav_preferences_active.gif";
+imageArray[32] = new Image();
+imageArray[32].src = "images/nav_preferences_active_over.gif";
+imageArray[33] = new Image();
+imageArray[33].src = "images/nav_preferences.gif";
+imageArray[34] = new Image();
+imageArray[34].src = "images/nav_preferences_over.gif";
+imageArray[35] = new Image();
+imageArray[35].src = "images/nav_register_active.gif";
+imageArray[36] = new Image();
+imageArray[36].src = "images/nav_register_active_over.gif";
+imageArray[37] = new Image();
+imageArray[37].src = "images/nav_register.gif";
+imageArray[38] = new Image();
+imageArray[38].src = "images/nav_register_over.gif";
+imageArray[39] = new Image();
+imageArray[39].src = "images/nav_services_active.gif";
+imageArray[40] = new Image();
+imageArray[40].src = "images/nav_services_active_over.gif";
+imageArray[41] = new Image();
+imageArray[41].src = "images/nav_services.gif";
+imageArray[42] = new Image();
+imageArray[42].src = "images/nav_services_over.gif";
+imageArray[43] = new Image();
+imageArray[43].src = "images/nav_signup_over.gif";
+imageArray[44] = new Image();
+imageArray[44].src = "images/nav_testimonials_active.gif";
+imageArray[45] = new Image();
+imageArray[45].src = "images/nav_testimonials_active_over.gif";
+imageArray[46] = new Image();
+imageArray[46].src = "images/nav_testimonials.gif";
+imageArray[47] = new Image();
+imageArray[47].src = "images/nav_testimonials_over.gif";
+imageArray[48] = new Image();
+imageArray[48].src = "images/nav_webmail_active.gif";
+imageArray[49] = new Image();
+imageArray[49].src = "images/nav_webmail_active_over.gif";
+
diff --git a/res/pages/js/statistics.js b/res/pages/js/statistics.js
new file mode 100644
index 00000000..f15cf57f
--- /dev/null
+++ b/res/pages/js/statistics.js
@@ -0,0 +1,8 @@
+
+$(document).ready(function() {
+
+ if ($.browser.mozilla) {
+ $("div#statistics table").wrap("<div id='table_wrapper' style='padding: 0px; margin: 0px; width: 420px;'></div>");
+ $("div#statistics div#table_wrapper").corner("round");
+ }
+}); \ No newline at end of file
diff --git a/res/pages/json/requests/ad.json b/res/pages/json/requests/ad.json
new file mode 100644
index 00000000..85eeac51
--- /dev/null
+++ b/res/pages/json/requests/ad.json
@@ -0,0 +1,7 @@
+{
+ "id": 1,
+ "method": "ad",
+ "params": {
+ "context": "loading"
+ }
+}
diff --git a/res/pages/json/requests/alert.acknowledge.json b/res/pages/json/requests/alert.acknowledge.json
new file mode 100644
index 00000000..4bd73318
--- /dev/null
+++ b/res/pages/json/requests/alert.acknowledge.json
@@ -0,0 +1,7 @@
+{
+ "id": 2,
+ "method": "alert.acknowledge",
+ "params": {
+ "alertIDs": [1,2]
+ }
+}
diff --git a/res/pages/json/requests/alert.list.json b/res/pages/json/requests/alert.list.json
new file mode 100644
index 00000000..bd6aab1c
--- /dev/null
+++ b/res/pages/json/requests/alert.list.json
@@ -0,0 +1,5 @@
+{
+ "id": 3,
+ "method": "alert.list",
+ "params": {}
+}
diff --git a/res/pages/json/requests/aliases.json b/res/pages/json/requests/aliases.json
new file mode 100644
index 00000000..9c3d502b
--- /dev/null
+++ b/res/pages/json/requests/aliases.json
@@ -0,0 +1,5 @@
+{
+ "id": 4,
+ "method": "aliases",
+ "params": {}
+}
diff --git a/res/pages/json/requests/attachments.add.json b/res/pages/json/requests/attachments.add.json
new file mode 100644
index 00000000..41a3c343
--- /dev/null
+++ b/res/pages/json/requests/attachments.add.json
@@ -0,0 +1,8 @@
+{
+ "id": 5,
+ "method": "attachments.add",
+ "params": {
+ "composeID": 45,
+ "filename": "script.js"
+ }
+}
diff --git a/res/pages/json/requests/attachments.progress.json b/res/pages/json/requests/attachments.progress.json
new file mode 100644
index 00000000..322345be
--- /dev/null
+++ b/res/pages/json/requests/attachments.progress.json
@@ -0,0 +1,7 @@
+{
+ "id": 6,
+ "method": "attachments.progress",
+ "params": {
+ "attachmentID": 34
+ }
+}
diff --git a/res/pages/json/requests/attachments.remove.json b/res/pages/json/requests/attachments.remove.json
new file mode 100644
index 00000000..4edeec47
--- /dev/null
+++ b/res/pages/json/requests/attachments.remove.json
@@ -0,0 +1,8 @@
+{
+ "id": 7,
+ "method": "attachments.remove",
+ "params": {
+ "composeID": 1,
+ "attachmentID": 34
+ }
+}
diff --git a/res/pages/json/requests/auth.json b/res/pages/json/requests/auth.json
new file mode 100644
index 00000000..80603aee
--- /dev/null
+++ b/res/pages/json/requests/auth.json
@@ -0,0 +1,8 @@
+{
+ "id": 8,
+ "method": "auth",
+ "params": {
+ "username": "magma",
+ "password": "test"
+ }
+}
diff --git a/res/pages/json/requests/config.edit.json b/res/pages/json/requests/config.edit.json
new file mode 100644
index 00000000..386b511b
--- /dev/null
+++ b/res/pages/json/requests/config.edit.json
@@ -0,0 +1,8 @@
+{
+ "id":4,
+ "method":"config.edit",
+ "params":{
+ "phone": "2145551212",
+ "secure": "true"
+ }
+}
diff --git a/res/pages/json/requests/config.load.json b/res/pages/json/requests/config.load.json
new file mode 100644
index 00000000..1369c2e6
--- /dev/null
+++ b/res/pages/json/requests/config.load.json
@@ -0,0 +1,5 @@
+{
+ "id": 3,
+ "method": "config.load",
+ "params": {}
+}
diff --git a/res/pages/json/requests/contacts.add.json b/res/pages/json/requests/contacts.add.json
new file mode 100644
index 00000000..56fa01ec
--- /dev/null
+++ b/res/pages/json/requests/contacts.add.json
@@ -0,0 +1,11 @@
+{
+ "id":4,
+ "method":"contacts.add",
+ "params":{
+ "folderID":7,
+ "contact":{
+ "name":"Jenna",
+ "email":"jenna@jameson.com"
+ }
+ }
+}
diff --git a/res/pages/json/requests/contacts.edit.json b/res/pages/json/requests/contacts.edit.json
new file mode 100644
index 00000000..a055fe98
--- /dev/null
+++ b/res/pages/json/requests/contacts.edit.json
@@ -0,0 +1,11 @@
+{
+ "id":4,
+ "method":"contacts.edit",
+ "params":{
+ "folderID":7,
+ "contactID": 1,
+ "contact":{
+ "phone": "2145551212"
+ }
+ }
+}
diff --git a/res/pages/json/requests/contacts.list.json b/res/pages/json/requests/contacts.list.json
new file mode 100644
index 00000000..c7d13f6f
--- /dev/null
+++ b/res/pages/json/requests/contacts.list.json
@@ -0,0 +1,7 @@
+{
+ "id": 9,
+ "method": "contacts.list",
+ "params": {
+ "folderID": 3
+ }
+}
diff --git a/res/pages/json/requests/contacts.load.json b/res/pages/json/requests/contacts.load.json
new file mode 100644
index 00000000..0d5564bb
--- /dev/null
+++ b/res/pages/json/requests/contacts.load.json
@@ -0,0 +1,8 @@
+{
+ "id": 10,
+ "method": "contacts.load",
+ "params": {
+ "contactID": 88
+ "folderID": 26
+ }
+}
diff --git a/res/pages/json/requests/contacts.move.json b/res/pages/json/requests/contacts.move.json
new file mode 100644
index 00000000..7bb6c7fa
--- /dev/null
+++ b/res/pages/json/requests/contacts.move.json
@@ -0,0 +1,9 @@
+{
+ "id": 11,
+ "method": "contacts.move",
+ "params": {
+ "contactID": 100,
+ "sourceFolderID": 1,
+ "targetFolderID": 10
+ }
+}
diff --git a/res/pages/json/requests/contacts.remove.json b/res/pages/json/requests/contacts.remove.json
new file mode 100644
index 00000000..5b9dcb58
--- /dev/null
+++ b/res/pages/json/requests/contacts.remove.json
@@ -0,0 +1,8 @@
+{
+ "id": 12,
+ "method": "contacts.remove",
+ "params": {
+ "folderID": 1
+ "contactID": 88
+ }
+}
diff --git a/res/pages/json/requests/display-options.json b/res/pages/json/requests/display-options.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/display-options.json
diff --git a/res/pages/json/requests/folders.add.json b/res/pages/json/requests/folders.add.json
new file mode 100644
index 00000000..8cb81431
--- /dev/null
+++ b/res/pages/json/requests/folders.add.json
@@ -0,0 +1,8 @@
+{
+ "id": 13,
+ "method": "folders.add",
+ "params": {
+ "parentID": 9,
+ "name": "Bitbucket"
+ }
+}
diff --git a/res/pages/json/requests/folders.list.json b/res/pages/json/requests/folders.list.json
new file mode 100644
index 00000000..e70a044f
--- /dev/null
+++ b/res/pages/json/requests/folders.list.json
@@ -0,0 +1,7 @@
+{
+ "id": 14,
+ "method": "folders.list",
+ "params": {
+ "context": "mail"
+ }
+}
diff --git a/res/pages/json/requests/folders.move.json b/res/pages/json/requests/folders.move.json
new file mode 100644
index 00000000..5ce131fe
--- /dev/null
+++ b/res/pages/json/requests/folders.move.json
@@ -0,0 +1,8 @@
+{
+ "id": 15,
+ "method": "folders.move",
+ "params": {
+ "sourceFolderID": "5",
+ "targetFolderID": "12"
+ }
+}
diff --git a/res/pages/json/requests/folders.remove.json b/res/pages/json/requests/folders.remove.json
new file mode 100644
index 00000000..6114445b
--- /dev/null
+++ b/res/pages/json/requests/folders.remove.json
@@ -0,0 +1,7 @@
+{
+ "id": 16,
+ "method": "folders.remove",
+ "params": {
+ "folderID": 30
+ }
+}
diff --git a/res/pages/json/requests/folders.rename.json b/res/pages/json/requests/folders.rename.json
new file mode 100644
index 00000000..3de8eac0
--- /dev/null
+++ b/res/pages/json/requests/folders.rename.json
@@ -0,0 +1,8 @@
+{
+ "id": 17,
+ "method": "folders.rename",
+ "params": {
+ "folderID": 30,
+ "name": "New Name"
+ }
+}
diff --git a/res/pages/json/requests/folders.tag.json b/res/pages/json/requests/folders.tag.json
new file mode 100644
index 00000000..cd4b6b56
--- /dev/null
+++ b/res/pages/json/requests/folders.tag.json
@@ -0,0 +1,7 @@
+{
+ "id": 16,
+ "method": "folders.tag",
+ "params": {
+ "folderID": 30
+ }
+}
diff --git a/res/pages/json/requests/help.page.json b/res/pages/json/requests/help.page.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/help.page.json
diff --git a/res/pages/json/requests/help.topics.json b/res/pages/json/requests/help.topics.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/help.topics.json
diff --git a/res/pages/json/requests/logout.json b/res/pages/json/requests/logout.json
new file mode 100644
index 00000000..3dd924a4
--- /dev/null
+++ b/res/pages/json/requests/logout.json
@@ -0,0 +1,5 @@
+{
+ "id": 18,
+ "method": "logout",
+ "params": {}
+}
diff --git a/res/pages/json/requests/logs.mail.json b/res/pages/json/requests/logs.mail.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/logs.mail.json
diff --git a/res/pages/json/requests/logs.security.json b/res/pages/json/requests/logs.security.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/logs.security.json
diff --git a/res/pages/json/requests/logs.statistics.json b/res/pages/json/requests/logs.statistics.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/requests/logs.statistics.json
diff --git a/res/pages/json/requests/messages.compose.json b/res/pages/json/requests/messages.compose.json
new file mode 100644
index 00000000..1a456735
--- /dev/null
+++ b/res/pages/json/requests/messages.compose.json
@@ -0,0 +1,5 @@
+{
+ "id": 15,
+ "method": "messages.compose",
+ "params": {}
+}
diff --git a/res/pages/json/requests/messages.copy.json b/res/pages/json/requests/messages.copy.json
new file mode 100644
index 00000000..357d7a04
--- /dev/null
+++ b/res/pages/json/requests/messages.copy.json
@@ -0,0 +1,9 @@
+{
+ "id": 19,
+ "method": "messages.copy",
+ "params": {
+ "messageIDs": [100, 101, 102],
+ "sourceFolderID": 1,
+ "targetFolderID": 10
+ }
+}
diff --git a/res/pages/json/requests/messages.flag.json b/res/pages/json/requests/messages.flag.json
new file mode 100644
index 00000000..da4fcc79
--- /dev/null
+++ b/res/pages/json/requests/messages.flag.json
@@ -0,0 +1,36 @@
+[{
+ "id": 20,
+ "method": "messages.flag",
+ "params": {
+ "action": "add",
+ "flags": ["junk"],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.flag",
+ "params": {
+ "action": "replace",
+ "flags": [],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.flag",
+ "params": {
+ "action": "remove",
+ "flags": ["read"],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.flag",
+ "params": {
+ "action": "list",
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+}]
diff --git a/res/pages/json/requests/messages.list.json b/res/pages/json/requests/messages.list.json
new file mode 100644
index 00000000..5652a337
--- /dev/null
+++ b/res/pages/json/requests/messages.list.json
@@ -0,0 +1,7 @@
+{
+ "id": 21,
+ "method": "messages.list",
+ "params": {
+ "folderID": 1
+ }
+}
diff --git a/res/pages/json/requests/messages.load.json b/res/pages/json/requests/messages.load.json
new file mode 100644
index 00000000..09cddcc8
--- /dev/null
+++ b/res/pages/json/requests/messages.load.json
@@ -0,0 +1,9 @@
+{
+ "id": 22,
+ "method": "messages.load",
+ "params": {
+ "messageID": 1,
+ "folderID": 1,
+ "sections": ["header", "body", "attachments"]
+ }
+}
diff --git a/res/pages/json/requests/messages.move.json b/res/pages/json/requests/messages.move.json
new file mode 100644
index 00000000..c769e707
--- /dev/null
+++ b/res/pages/json/requests/messages.move.json
@@ -0,0 +1,9 @@
+{
+ "id": 23,
+ "method": "messages.move",
+ "params": {
+ "messageIDs": [100, 101, 102],
+ "sourceFolderID": 1,
+ "targetFolderID": 10
+ }
+}
diff --git a/res/pages/json/requests/messages.remove.json b/res/pages/json/requests/messages.remove.json
new file mode 100644
index 00000000..fd1c624a
--- /dev/null
+++ b/res/pages/json/requests/messages.remove.json
@@ -0,0 +1,8 @@
+{
+ "id": 24,
+ "method": "messages.remove",
+ "params": {
+ "folderID": 1,
+ "messageIDs": [100, 110, 120]
+ }
+}
diff --git a/res/pages/json/requests/messages.send.json b/res/pages/json/requests/messages.send.json
new file mode 100644
index 00000000..d51a9932
--- /dev/null
+++ b/res/pages/json/requests/messages.send.json
@@ -0,0 +1,18 @@
+{
+ "id": 25,
+ "method": "messages.send",
+ "params": {
+ "composeID": 0,
+ "from": "Douglas Crockford <dc@yahoo.com>",
+ "to": [ "Linus Torvalds <lt@kernel.org>" ],
+ "cc": [ ],
+ "bcc": [ ],
+ "subject": "Kernel cruft",
+ "priority": "normal",
+ "attachments": [0, 1, 3],
+ "body": {
+ "text": "Linux Cruft\nSplit Linux into specialized kernels to clean up some cruft!",
+ "html": "<h1>Linux Cruft</h1><p>Split Linux into specialized kernels to clean up some cruft!</p><p>DC</p>"
+ }
+ }
+}
diff --git a/res/pages/json/requests/messages.tag.json b/res/pages/json/requests/messages.tag.json
new file mode 100644
index 00000000..f199ade6
--- /dev/null
+++ b/res/pages/json/requests/messages.tag.json
@@ -0,0 +1,36 @@
+[{
+ "id": 20,
+ "method": "messages.tag",
+ "params": {
+ "action": "add",
+ "tags": ["javascript", "magma"],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.tag",
+ "params": {
+ "action": "replace",
+ "tags": [],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.tag",
+ "params": {
+ "action": "remove",
+ "tags": ["almost", "done"],
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+},{
+ "id": 20,
+ "method": "messages.tag",
+ "params": {
+ "action": "list",
+ "messageIDs": [100, 101, 102],
+ "folderID": 1
+ }
+}]
diff --git a/res/pages/json/requests/messages.tags.json b/res/pages/json/requests/messages.tags.json
new file mode 100644
index 00000000..762b29b3
--- /dev/null
+++ b/res/pages/json/requests/messages.tags.json
@@ -0,0 +1,6 @@
+{
+ "id": 31,
+ "method": "messages.tags",
+ "params": {}
+}
+
diff --git a/res/pages/json/requests/meta.json b/res/pages/json/requests/meta.json
new file mode 100644
index 00000000..edaf359c
--- /dev/null
+++ b/res/pages/json/requests/meta.json
@@ -0,0 +1,5 @@
+{
+ "id": 27,
+ "method": "meta",
+ "params": {}
+}
diff --git a/res/pages/json/requests/scrape.add.json b/res/pages/json/requests/scrape.add.json
new file mode 100644
index 00000000..0145b52c
--- /dev/null
+++ b/res/pages/json/requests/scrape.add.json
@@ -0,0 +1,10 @@
+{
+ "id": 27,
+ "method": "scrape.add",
+ "params": {
+ "messageID": 203,
+ "id": 59,
+ "name": "John Doe",
+ "email": "jdoe@example.com"
+ }
+}
diff --git a/res/pages/json/requests/scrape.json b/res/pages/json/requests/scrape.json
new file mode 100644
index 00000000..220d7d0a
--- /dev/null
+++ b/res/pages/json/requests/scrape.json
@@ -0,0 +1,7 @@
+{
+ "id": 28,
+ "method": "scrape",
+ "params": {
+ "messageID": 203
+ }
+}
diff --git a/res/pages/json/requests/search.json b/res/pages/json/requests/search.json
new file mode 100644
index 00000000..3f8e442f
--- /dev/null
+++ b/res/pages/json/requests/search.json
@@ -0,0 +1,18 @@
+{
+ "id": 29,
+ "method": "search",
+ "params": {
+ "searchin": 0,
+ "queries": [{
+ "field": "from",
+ "filter": "contains",
+ "query": "Paul Grahm"
+ }, {
+ "field": "date",
+ "range": {
+ "from": 23112342342,
+ "to": 2342342343
+ }
+ }]
+ }
+}
diff --git a/res/pages/json/requests/settings.identity.json b/res/pages/json/requests/settings.identity.json
new file mode 100644
index 00000000..ad9da139
--- /dev/null
+++ b/res/pages/json/requests/settings.identity.json
@@ -0,0 +1,5 @@
+{
+ "id": 30,
+ "method": "settings.identity",
+ "params": {}
+}
diff --git a/res/pages/json/responses/ad.json b/res/pages/json/responses/ad.json
new file mode 100644
index 00000000..100a545b
--- /dev/null
+++ b/res/pages/json/responses/ad.json
@@ -0,0 +1,38 @@
+[{
+ "result": {
+ "ad": {
+ "href": "/advertisement.html",
+ "title": "Loading Ad",
+ "img": {
+ "src": "http://placehold.it/300x250",
+ "alt": "Loading Ad"
+ }
+ },
+ "fact": {
+ "text": [
+ "Most elephants weigh less than the tongue of a blue whale."
+ ]
+ }
+ }
+}, {
+ "result": {
+ "ad": {
+ "href": "/advertisement.html",
+ "title": "Wide Advertisement",
+ "img": {
+ "src": "http://placehold.it/728x90",
+ "alt": "Wide Advertisement"
+ }
+ }
+ }
+}, {
+ "result": {
+ "ad": {
+ "href": "/text-ad.html",
+ "title": "Text Ad",
+ "text": "This is a text ad"
+ }
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/alert.acknowledge.json b/res/pages/json/responses/alert.acknowledge.json
new file mode 100644
index 00000000..68b6b8e3
--- /dev/null
+++ b/res/pages/json/responses/alert.acknowledge.json
@@ -0,0 +1,7 @@
+{
+ "jsonrpc": "2.0",
+ "result": {
+ "alert.acknowledge": "success"
+ },
+ "id": 3
+}
diff --git a/res/pages/json/responses/alert.list.json b/res/pages/json/responses/alert.list.json
new file mode 100644
index 00000000..b45bc17a
--- /dev/null
+++ b/res/pages/json/responses/alert.list.json
@@ -0,0 +1,17 @@
+[{
+ "result": [{
+ "alertID": 1,
+ "type": "alert",
+ "message": "It's a trap!",
+ "date": "1029381092830"
+ }, {
+ "alertID": 2,
+ "type": "warning",
+ "message": "Your subscription will expire soon.",
+ "date": "12093810293801"
+ }]
+}, {
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/aliases.json b/res/pages/json/responses/aliases.json
new file mode 100644
index 00000000..6f03541e
--- /dev/null
+++ b/res/pages/json/responses/aliases.json
@@ -0,0 +1,18 @@
+[{
+ "result": [{
+ "email": "peter@lavabit.com",
+ "name": "Peter"
+ }, {
+ "email": "work@lavabit.com",
+ "name": "Work",
+ "def": true
+ }, {
+ "email": "peter@pan.com",
+ "name": "Website"
+ }, {
+ "email": "email@example.com",
+ "name": "Test Email"
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/attachments.add.json b/res/pages/json/responses/attachments.add.json
new file mode 100644
index 00000000..8889b9b7
--- /dev/null
+++ b/res/pages/json/responses/attachments.add.json
@@ -0,0 +1,7 @@
+[{
+ "result": {
+ "attachmentID": 34
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/attachments.progress.json b/res/pages/json/responses/attachments.progress.json
new file mode 100644
index 00000000..06d6e167
--- /dev/null
+++ b/res/pages/json/responses/attachments.progress.json
@@ -0,0 +1,7 @@
+[{
+ "result": {
+ "progress": 54
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/attachments.remove.json b/res/pages/json/responses/attachments.remove.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/attachments.remove.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/auth.json b/res/pages/json/responses/auth.json
new file mode 100644
index 00000000..14679620
--- /dev/null
+++ b/res/pages/json/responses/auth.json
@@ -0,0 +1,16 @@
+[{
+ "result": {
+ "auth": "success",
+ "session": ""
+ }
+}, {
+ "result": {
+ "auth": "failed",
+ "message": "The username and password provided are incorrect, please try again."
+ }
+}, {
+ "result": {
+ "auth": "locked",
+ "message": "Your account is locked due to abuse."
+ }
+}]
diff --git a/res/pages/json/responses/contacts.add.json b/res/pages/json/responses/contacts.add.json
new file mode 100644
index 00000000..972cda20
--- /dev/null
+++ b/res/pages/json/responses/contacts.add.json
@@ -0,0 +1,7 @@
+[{
+ "result": {
+ "contactID": 88
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/contacts.edit.json b/res/pages/json/responses/contacts.edit.json
new file mode 100644
index 00000000..4b3480c3
--- /dev/null
+++ b/res/pages/json/responses/contacts.edit.json
@@ -0,0 +1,23 @@
+[{
+ "result": {
+ "name": "Peter Pan",
+ "email": {
+ "primary": "peter@lavabit.com",
+ "alternate-1": "peter@pan.com"
+ },
+ "chat": {
+ "yahoo": "pp@yahoo.com",
+ "live": "pp@hotmail.com",
+ "aim": "peanutbutter",
+ "google": "pp@gmail.com"
+ },
+ "phone": {
+ "home": "123-456-7890",
+ "work": "123-456-7890",
+ "mobile": "123-456-7890",
+ "address": ["1234 Lost Way", "Neverland Isl"]
+ }
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/contacts.list.json b/res/pages/json/responses/contacts.list.json
new file mode 100644
index 00000000..63b839c7
--- /dev/null
+++ b/res/pages/json/responses/contacts.list.json
@@ -0,0 +1,67 @@
+[{
+ "result": [{
+ "contactID": 45,
+ "name": "John Resig",
+ "email": "jr@mozilla.org",
+ "company": "Mozilla",
+ "img": {
+ "src": "http://www.gravatar.com/avatar/00000000000000000000000000000000?s=32&d=mm&f=y",
+ "alt": "Avatar"
+ }
+ }, {
+ "contactID": 48,
+ "name": "Linus Torvalds",
+ "email": "lt@kernel.org",
+ "company": "Linux"
+ }, {
+ "contactID": 53,
+ "name": "Douglas Crockford",
+ "email": "dc@yahoo.com",
+ "company": "Yahoo",
+ "img": {
+ "src": "http://www.gravatar.com/avatar/00000000000000000000000000000000?s=32&d=mm&f=y",
+ "alt": "Avatar"
+ }
+ }, {
+ "contactID": 58,
+ "name": "Paul Graham",
+ "email": "pg@ycombinator.com",
+ "company": "Y Combinator",
+ "img": {
+ "src": "http://www.gravatar.com/avatar/00000000000000000000000000000000?s=32&d=mm&f=y",
+ "alt": "Avatar"
+ }
+ }, {
+ "contactID": 64,
+ "name": "John Paul",
+ "email": "jp@morgan.com"
+ }]
+}, {
+ "result": [{
+ "contactID": 45,
+ "name": "John Resig",
+ "email": "jr@mozilla.org",
+ "company": "Mozilla",
+ }, {
+ "contactID": 48,
+ "name": "Linus Torvalds",
+ "email": "lt@kernel.org",
+ "company": "Linux"
+ }, {
+ "contactID": 53,
+ "name": "Douglas Crockford",
+ "email": "dc@yahoo.com",
+ "company": "Yahoo",
+ }, {
+ "contactID": 58,
+ "name": "Paul Graham",
+ "email": "pg@ycombinator.com",
+ "company": "Y Combinator",
+ }, {
+ "contactID": 64,
+ "name": "John Paul",
+ "email": "jp@morgan.com"
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/contacts.load.json b/res/pages/json/responses/contacts.load.json
new file mode 100644
index 00000000..13859651
--- /dev/null
+++ b/res/pages/json/responses/contacts.load.json
@@ -0,0 +1,22 @@
+[{
+ "result": {
+ "name": "Douglas Crockford",
+ "email": {
+ "primary": "dougcrock@lavabit.com",
+ "alternate1": "dc@crockford.com"
+ },
+ "chat": {
+ "yahoo": "crockford@yahoo.com",
+ "aim": "thejsguy",
+ "google": "crockford@gmail.com"
+ },
+ "contact": {
+ "home": "123-456-7890",
+ "work": "123-456-7890",
+ "mobile": "123-456-7890",
+ "address": "1234 Yahoo<br \/>Sunnyvale, CA 12345 "
+ }
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/contacts.move.json b/res/pages/json/responses/contacts.move.json
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/res/pages/json/responses/contacts.move.json
diff --git a/res/pages/json/responses/contacts.remove.json b/res/pages/json/responses/contacts.remove.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/contacts.remove.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/display-options.json b/res/pages/json/responses/display-options.json
new file mode 100644
index 00000000..1dd0bbec
--- /dev/null
+++ b/res/pages/json/responses/display-options.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "reading-pane": 2,
+ "fields": ["from", "to", "subject", "date", "size"],
+ "sort": {
+ "field": "from",
+ "order": 1
+ }
+ },
+ "error": true
+}
diff --git a/res/pages/json/responses/folders.add.json b/res/pages/json/responses/folders.add.json
new file mode 100644
index 00000000..9c6a7b58
--- /dev/null
+++ b/res/pages/json/responses/folders.add.json
@@ -0,0 +1,7 @@
+[{
+ "result": {
+ "folderID": 41
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/folders.list.json b/res/pages/json/responses/folders.list.json
new file mode 100644
index 00000000..4694ed42
--- /dev/null
+++ b/res/pages/json/responses/folders.list.json
@@ -0,0 +1,107 @@
+[{
+ "result": [{
+ "folderID": 1,
+ "name": "Inbox"
+ }, {
+ "folderID": 5,
+ "name": "Projects"
+ }, {
+ "folderID": 8,
+ "parentID": 12,
+ "name": "jQuery Plugins"
+ }, {
+ "folderID": 10,
+ "parentID": 12,
+ "name": "Tests"
+ }, {
+ "folderID": 11,
+ "parentID": 10,
+ "name": "Webmail"
+ }, {
+ "folderID": 15,
+ "parentID": 12,
+ "name": "Templates"
+ }, {
+ "folderID": 12,
+ "name": "Javascript"
+ }, {
+ "folderID": 9,
+ "parentID": 8,
+ "name": "Querios"
+ }]
+}, {
+ "result": [{
+ "folderID": 200,
+ "name": "All"
+ }, {
+ "folderID": 201,
+ "name": "People"
+ }, {
+ "folderID": 202,
+ "name": "Business"
+ }, {
+ "folderID": 203,
+ "name": "Collected"
+ }, {
+ "folderID": 204,
+ "name": "Work"
+ }, {
+ "folderID": 205,
+ "name": "Personal"
+ }, {
+ "folderID": 206,
+ "parentID": 205,
+ "name": "Family"
+ }, {
+ "folderID": 207,
+ "parentID": 205,
+ "name": "Friends"
+ }, {
+ "folderID": 999,
+ "name": "No Gravatars"
+ }]
+}, {
+ "result": [{
+ "folderID": 100,
+ "name": "Identity"
+ }, {
+ "folderID": 101,
+ "name": "Mail Settings"
+ }, {
+ "folderID": 102,
+ "name": "Portal Settings"
+ }, {
+ "folderID": 103,
+ "name": "Account Upgrades"
+ }, {
+ "folderID": 104,
+ "name": "Password"
+ }]
+}, {
+ "result": [{
+ "folderID": 400,
+ "name": "Statistics"
+ }, {
+ "folderID": 401,
+ "name": "Security"
+ }, {
+ "folderID": 402,
+ "name": "Contacts"
+ }, {
+ "folderID": 403,
+ "name": "Mail"
+ }]
+}, {
+ "result": [{
+ "folderID": 500,
+ "name": "Help Category"
+ }, {
+ "folderID": 501,
+ "name": "Help Category"
+ }, {
+ "folderID": 502,
+ "name": "Help Category"
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/folders.move.json b/res/pages/json/responses/folders.move.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/folders.move.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/folders.remove.json b/res/pages/json/responses/folders.remove.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/folders.remove.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/folders.rename.json b/res/pages/json/responses/folders.rename.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/folders.rename.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/folders.tag.json b/res/pages/json/responses/folders.tag.json
new file mode 100644
index 00000000..d1b50845
--- /dev/null
+++ b/res/pages/json/responses/folders.tag.json
@@ -0,0 +1,9 @@
+[{
+ "result": [{
+ "tag": "javascript",
+ "messageIDs": [1,2,3]
+ }, {
+ "tag": "victor",
+ "messageIDs": [128]
+ }]
+} \ No newline at end of file
diff --git a/res/pages/json/responses/help.page.json b/res/pages/json/responses/help.page.json
new file mode 100644
index 00000000..8258a404
--- /dev/null
+++ b/res/pages/json/responses/help.page.json
@@ -0,0 +1,7 @@
+[{
+ "jsonrpc": "2.0",
+ "result": "<h2>Help</h2><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ipsum orci, dictum quis vestibulum id, lobortis eget nibh. Cras diam ante, adipiscing vel elementum ut, fringilla non magna. Nunc eu metus libero, at condimentum dolor. Donec sit amet est quis eros condimentum lobortis et ut ipsum. Suspendisse diam nisl, blandit vitae lobortis vitae, fermentum ut est. Sed placerat nisl eu neque auctor non tempor urna mattis. Aenean convallis nibh eros. Nunc egestas scelerisque urna, non congue erat iaculis in. Donec consectetur luctus fringilla. In in ligula eu libero mattis ornare. Curabitur iaculis porta leo eu consectetur. Nulla lacus tortor, vehicula non pellentesque sed, tincidunt at ante. Mauris rhoncus rhoncus nisl eget rhoncus. Etiam consequat nunc ut enim bibendum vitae porta dui laoreet. Aenean placerat nisi ac urna dignissim dignissim in ut velit.</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ipsum orci, dictum quis vestibulum id, lobortis eget nibh. Cras diam ante, adipiscing vel elementum ut, fringilla non magna. Nunc eu metus libero, at condimentum dolor. Donec sit amet est quis eros condimentum lobortis et ut ipsum. Suspendisse diam nisl, blandit vitae lobortis vitae, fermentum ut est. Sed placerat nisl eu neque auctor non tempor urna mattis. Aenean convallis nibh eros. Nunc egestas scelerisque urna, non congue erat iaculis in. Donec consectetur luctus fringilla. In in ligula eu libero mattis ornare. Curabitur iaculis porta leo eu consectetur. Nulla lacus tortor, vehicula non pellentesque sed, tincidunt at ante. Mauris rhoncus rhoncus nisl eget rhoncus. Etiam consequat nunc ut enim bibendum vitae porta dui laoreet. Aenean placerat nisi ac urna dignissim dignissim in ut velit.</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ipsum orci, dictum quis vestibulum id, lobortis eget nibh. Cras diam ante, adipiscing vel elementum ut, fringilla non magna. Nunc eu metus libero, at condimentum dolor. Donec sit amet est quis eros condimentum lobortis et ut ipsum. Suspendisse diam nisl, blandit vitae lobortis vitae, fermentum ut est. Sed placerat nisl eu neque auctor non tempor urna mattis. Aenean convallis nibh eros. Nunc egestas scelerisque urna, non congue erat iaculis in. Donec consectetur luctus fringilla. In in ligula eu libero mattis ornare. Curabitur iaculis porta leo eu consectetur. Nulla lacus tortor, vehicula non pellentesque sed, tincidunt at ante. Mauris rhoncus rhoncus nisl eget rhoncus. Etiam consequat nunc ut enim bibendum vitae porta dui laoreet. Aenean placerat nisi ac urna dignissim dignissim in ut velit.</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ipsum orci, dictum quis vestibulum id, lobortis eget nibh. Cras diam ante, adipiscing vel elementum ut, fringilla non magna. Nunc eu metus libero, at condimentum dolor. Donec sit amet est quis eros condimentum lobortis et ut ipsum. Suspendisse diam nisl, blandit vitae lobortis vitae, fermentum ut est. Sed placerat nisl eu neque auctor non tempor urna mattis. Aenean convallis nibh eros. Nunc egestas scelerisque urna, non congue erat iaculis in. Donec consectetur luctus fringilla. In in ligula eu libero mattis ornare. Curabitur iaculis porta leo eu consectetur. Nulla lacus tortor, vehicula non pellentesque sed, tincidunt at ante. Mauris rhoncus rhoncus nisl eget rhoncus. Etiam consequat nunc ut enim bibendum vitae porta dui laoreet. Aenean placerat nisi ac urna dignissim dignissim in ut velit.</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ipsum orci, dictum quis vestibulum id, lobortis eget nibh. Cras diam ante, adipiscing vel elementum ut, fringilla non magna. Nunc eu metus libero, at condimentum dolor. Donec sit amet est quis eros condimentum lobortis et ut ipsum. Suspendisse diam nisl, blandit vitae lobortis vitae, fermentum ut est. Sed placerat nisl eu neque auctor non tempor urna mattis. Aenean convallis nibh eros. Nunc egestas scelerisque urna, non congue erat iaculis in. Donec consectetur luctus fringilla. In in ligula eu libero mattis ornare. Curabitur iaculis porta leo eu consectetur. Nulla lacus tortor, vehicula non pellentesque sed, tincidunt at ante. Mauris rhoncus rhoncus nisl eget rhoncus. Etiam consequat nunc ut enim bibendum vitae porta dui laoreet. Aenean placerat nisi ac urna dignissim dignissim in ut velit.</p>",
+ "id": 100
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/help.topics.json b/res/pages/json/responses/help.topics.json
new file mode 100644
index 00000000..0459ad96
--- /dev/null
+++ b/res/pages/json/responses/help.topics.json
@@ -0,0 +1,16 @@
+[{
+ "jsonrpc": "2.0",
+ "result": [{
+ "topicID": 600,
+ "name": "Help Topic"
+ }, {
+ "topicID": 601,
+ "name": "Help Topic"
+ }, {
+ "topicID": 602,
+ "name": "Help Topic"
+ }],
+ "id": 100
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/logout.json b/res/pages/json/responses/logout.json
new file mode 100644
index 00000000..2c19dfa1
--- /dev/null
+++ b/res/pages/json/responses/logout.json
@@ -0,0 +1,7 @@
+[{
+ "jsonrpc": "2.0",
+ "result": {
+ "logout": "success"
+ },
+ "id": 100
+}]
diff --git a/res/pages/json/responses/logs.mail.json b/res/pages/json/responses/logs.mail.json
new file mode 100644
index 00000000..dba67c38
--- /dev/null
+++ b/res/pages/json/responses/logs.mail.json
@@ -0,0 +1,59 @@
+[{
+ "result": [{
+ "messageID": 100,
+ "queue": 1011,
+ "type": "Incoming",
+ "from": "Bill Shakespeare",
+ "to": "Bill Gates",
+ "outcome": "Delivered",
+ "utc": 102032012310,
+ "bytes": 1024
+ }, {
+ "messageID": 101,
+ "queue": 1012,
+ "type": "Outgoing",
+ "from": "Bob Dole",
+ "to": "Bob Dylan",
+ "outcome": "Delayed",
+ "utc": 102033422369,
+ "bytes": 1232
+ }, {
+ "messageID": 102,
+ "queue": 1013,
+ "type": "Incoming",
+ "from": "Bill Shakespeare",
+ "to": "Bill Gates",
+ "outcome": "Delivered",
+ "utc": 102032012310,
+ "bytes": 123145
+ }, {
+ "messageID": 103,
+ "queue": 1014,
+ "type": "Outgoing",
+ "from": "Bob Dole",
+ "to": "Bob Dylan",
+ "outcome": "Delayed",
+ "utc": 102033422369,
+ "bytes": 10
+ }, {
+ "messageID": 104,
+ "queue": 1015,
+ "type": "Incoming",
+ "from": "Bill Shakespeare",
+ "to": "Bill Gates",
+ "outcome": "Delivered",
+ "utc": 102032012310,
+ "bytes": 999
+ }, {
+ "messageID": 105,
+ "queue": 1016,
+ "type": "Outgoing",
+ "from": "Bob Dole",
+ "to": "Bob Dylan",
+ "outcome": "Delayed",
+ "utc": 102033422369,
+ "bytes": 123
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/logs.security.json b/res/pages/json/responses/logs.security.json
new file mode 100644
index 00000000..af47d9e8
--- /dev/null
+++ b/res/pages/json/responses/logs.security.json
@@ -0,0 +1,23 @@
+[{
+ "result": [{
+ "utc": 10239834048,
+ "type": "Login",
+ "severity": "Zero",
+ "ip": "0.0.0.0",
+ "protocol": "Web"
+ }, {
+ "utc": 10238439074,
+ "type": "Logout",
+ "severity": "Zero",
+ "ip": "0.0.0.0",
+ "protocol": "Web"
+ }, {
+ "utc": 10239834157,
+ "type": "Login",
+ "severity": "Zero",
+ "ip": "0.0.0.0",
+ "protocol": "IMAP"
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/logs.statistics.json b/res/pages/json/responses/logs.statistics.json
new file mode 100644
index 00000000..cf35d85b
--- /dev/null
+++ b/res/pages/json/responses/logs.statistics.json
@@ -0,0 +1,42 @@
+[{
+ "result": {
+ "account": {
+ "username": "Mike",
+ "name": "Mike",
+ "number": 1,
+ "reputation": "100%",
+ "plan": "Free",
+ "date": "Nov 11th, 2011"
+ },
+ "storage": {
+ "space": 10203192038,
+ "folders": 12,
+ "stored": 1000303,
+ "archived": 12030123213,
+ "encrypted": 1200310320
+ },
+ "logins": {
+ "smtp": 1000,
+ "pop": 1000,
+ "imap": 1000,
+ "web": 1000
+ },
+ "messages": {
+ "received": 1000,
+ "sent": 1000
+ },
+ "email": {
+ "address": "mike@lavabit.com"
+ },
+ "transfer": {
+ "sent": 2,
+ "received": 3
+ },
+ "blocked": {
+ "spam": 0,
+ "bounced": 0
+ }
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.compose.json b/res/pages/json/responses/messages.compose.json
new file mode 100644
index 00000000..61a3f2fd
--- /dev/null
+++ b/res/pages/json/responses/messages.compose.json
@@ -0,0 +1,5 @@
+[{
+ "result": { "composeID": 1 }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.copy.json b/res/pages/json/responses/messages.copy.json
new file mode 100644
index 00000000..f954f51b
--- /dev/null
+++ b/res/pages/json/responses/messages.copy.json
@@ -0,0 +1,11 @@
+[{
+ "result": [{
+ "sourceMessageID": 1021,
+ "targetMessageID": 1035
+ }, {
+ "sourceMessageID": 1022,
+ "targetMessageID": 1036
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.flag.json b/res/pages/json/responses/messages.flag.json
new file mode 100644
index 00000000..ce0031d5
--- /dev/null
+++ b/res/pages/json/responses/messages.flag.json
@@ -0,0 +1,14 @@
+[{
+ "jsonrpc":"2.0",
+ "result":{
+ "maessages.flag" : "success"
+ },
+ "id":20
+}, {
+ "jsonrpc":"2.0",
+ "result": {
+ "messageID":1,
+ "flags": ["flagged"]
+ },
+ "id":20
+}]
diff --git a/res/pages/json/responses/messages.list.json b/res/pages/json/responses/messages.list.json
new file mode 100644
index 00000000..b63fad66
--- /dev/null
+++ b/res/pages/json/responses/messages.list.json
@@ -0,0 +1,61 @@
+[{
+ "result": []
+}, {
+ "result": [{
+ "messageID": 1024,
+ "flags": ["seen", "flagged"],
+ "from": "zWilliam Adama",
+ "to": "Apallo",
+ "addressedTo": "apallo@lavabit.com",
+ "replyTo": "bill@lavabit.com",
+ "returnPath": "bill@lavabit.com",
+ "subject": "Pegasus",
+ "utc": 1109749011,
+ "arrivalUtc": 1109749011,
+ "snippet": "Go the the gym softy",
+ "bytes": 487479772
+ }, {
+ "messageID": 1023,
+ "attachment": true,
+ "from": "Douglas Crockford",
+ "to": "John Resig",
+ "addressedTo": "jresig@lavabit.com",
+ "replyTo": "dcrock@lavabit.com",
+ "returnPath": "dcrock@lavabit.com",
+ "carbon": "mozilla@lavabit.com",
+ "subject": "jQuery cruft",
+ "utc": 1266415038183,
+ "arrivalUtc": 1020030,
+ "snippet": "jQuery is too crufty. YUI is much leaner.",
+ "bytes": 895625721,
+ "tags": ["Javascript"]
+ }, {
+ "messageID": 1022,
+ "from": "Paul Grahm",
+ "to": "Douglas Crockford",
+ "addressedTo": "dcrock@lavabit.com",
+ "replyTo": "paulg@lavabit.com",
+ "returnPath": "paulg@lavabit.com",
+ "subject": "Reply: Y Combinator cruft",
+ "utc": 1071564744567,
+ "arrivalUtc": 1023012302,
+ "snippet": "I have a startup idea. It's a startup starter...",
+ "bytes": 1014127863,
+ "tags": ["Yahoo", "Y Combinator", "cruft"]
+ }, {
+ "messageID": 1021,
+ "from": "Douglas Crockford",
+ "to": "David Flanagan",
+ "addressedTo": "dflan@lavabit.com",
+ "replyTo": "dcrock@lavabit.com",
+ "returnPath": "dcrock@lavabit.com",
+ "subject": "Javascript: The Definitive Guide cruft",
+ "utc": 1054397333677,
+ "arrivalUtc": 10023100321,
+ "snippet": "You should only talk about the good parts.",
+ "bytes": 839535031,
+ "tags": ["Javascript"]
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.load.json b/res/pages/json/responses/messages.load.json
new file mode 100644
index 00000000..02529f3f
--- /dev/null
+++ b/res/pages/json/responses/messages.load.json
@@ -0,0 +1,70 @@
+[
+ {
+ "result": {
+ "meta": {
+ "messageID": 1,
+ "folderID": 1,
+ "flags": [
+
+ ],
+ "tags": [
+ "thefuture"
+ ]
+ },
+ "header": {
+ "sender": "forwarder@example.org",
+ "from": "John Resig",
+ "to": "Mozilla",
+ "cc": "carbon@example.org",
+ "bcc": "blind@example.org",
+ "returnpath": "bounces@example.org",
+ "subject": "jQuery",
+ "date": 1003939300349,
+ "replyto": "John Resig",
+ "size": 87348
+ },
+ "body": {
+ "html": "<h1>Hi There!</h1><p>jQuery 1.5 just came out. You should use it!</p><p>John Resig</p>"
+ },
+ "attachments": [
+ {
+ "attachmentID": 59,
+ "name": "Photo1.jpg",
+ "size": 11039,
+ "type": "image/jpeg"
+ },
+ {
+ "attachmentID": 60,
+ "name": "Photo2.jpg",
+ "size": 12093,
+ "type": "image/jpeg"
+ },
+ {
+ "attachmentID": 60,
+ "name": "profits.xlsx",
+ "size": 59120,
+ "type": "application/octet-stream"
+ }
+ ]
+ }
+ }, {
+ "result": {
+ "info": {
+ "source": {
+ "ip": "1.23.45.678",
+ "dns": "dns1.email.com",
+ "reputation": "Good"
+ },
+ "security": {
+ "secure": true,
+ "spf": true,
+ "dkim": false
+ },
+ "server": {
+ "utc": 1003029300349,
+ "images": true,
+ "warnings": "The message is spoofed! The signature it carries did not validate! "
+ }
+ }
+ }
+}]
diff --git a/res/pages/json/responses/messages.move.json b/res/pages/json/responses/messages.move.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/messages.move.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.remove.json b/res/pages/json/responses/messages.remove.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/messages.remove.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.send.json b/res/pages/json/responses/messages.send.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/messages.send.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.tag.json b/res/pages/json/responses/messages.tag.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/messages.tag.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/messages.tags.json b/res/pages/json/responses/messages.tags.json
new file mode 100644
index 00000000..9ccda6be
--- /dev/null
+++ b/res/pages/json/responses/messages.tags.json
@@ -0,0 +1,5 @@
+[{
+ "result": ["Important", "Work", "Personal", "To Do"]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/meta.json b/res/pages/json/responses/meta.json
new file mode 100644
index 00000000..fbcaa6ea
--- /dev/null
+++ b/res/pages/json/responses/meta.json
@@ -0,0 +1,17 @@
+[{
+ "result": {
+ "userID": 32,
+ "clientIP": "127.0.0.1",
+ "location": "Dallas, TX",
+ "timezone": "Central",
+ "reputation": 88,
+ "plan": "Basic",
+ "version": "1.02",
+ "quota": "45%",
+ "javascript": true,
+ "stylesheets": true,
+ "connection": true
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/scrape.add.json b/res/pages/json/responses/scrape.add.json
new file mode 100644
index 00000000..5efc887a
--- /dev/null
+++ b/res/pages/json/responses/scrape.add.json
@@ -0,0 +1,5 @@
+[{
+ "result": true
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/scrape.json b/res/pages/json/responses/scrape.json
new file mode 100644
index 00000000..4c1aa4ed
--- /dev/null
+++ b/res/pages/json/responses/scrape.json
@@ -0,0 +1,25 @@
+[{
+ "result": [{
+ "contactID": 45,
+ "name": "John Resig",
+ "email": "jr@mozilla.org",
+ }, {
+ "contactID": 48,
+ "name": "Linus Torvalds",
+ "email": "lt@kernel.org",
+ }, {
+ "contactID": 53,
+ "name": "Douglas Crockford",
+ "email": "dc@yahoo.com",
+ }, {
+ "contactID": 58,
+ "name": "Paul Graham",
+ "email": "pg@ycombinator.com"
+ }, {
+ "contactID": 64,
+ "name": "John Paul",
+ "email": "jp@morgan.com"
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/search.json b/res/pages/json/responses/search.json
new file mode 100644
index 00000000..bb4e51c2
--- /dev/null
+++ b/res/pages/json/responses/search.json
@@ -0,0 +1,33 @@
+[{
+ "result": [{
+ "messageID": 1024,
+ "flags": ["seen", "flagged"],
+ "from": "William Adama",
+ "to": "Apallo",
+ "addressedTo": "apallo@lavabit.com",
+ "replyTo": "bill@lavabit.com",
+ "returnPath": "bill@lavabit.com",
+ "subject": "Pegasus",
+ "utc": 1109749011,
+ "arrivalUtc": 1109749011,
+ "snippet": "Go the the gym softy",
+ "bytes": 487479772
+ }, {
+ "messageID": 1023,
+ "attachment": true,
+ "from": "Douglas Crockford",
+ "to": "John Resig",
+ "addressedTo": "jresig@lavabit.com",
+ "replyTo": "dcrock@lavabit.com",
+ "returnPath": "dcrock@lavabit.com",
+ "carbon": "mozilla@lavabit.com",
+ "subject": "jQuery cruft",
+ "utc": 1266415038183,
+ "arrivalUtc": 1020030,
+ "snippet": "jQuery is too crufty. YUI is much leaner.",
+ "bytes": 895625721,
+ "tags": ["Javascript"]
+ }]
+}, {
+ "error": true
+}]
diff --git a/res/pages/json/responses/settings.identity.json b/res/pages/json/responses/settings.identity.json
new file mode 100644
index 00000000..61f137a5
--- /dev/null
+++ b/res/pages/json/responses/settings.identity.json
@@ -0,0 +1,10 @@
+[{
+ "result": {
+ "name": "Lava Bit",
+ "first": "Lava",
+ "last": "Bit",
+ "website": "lavabit.com"
+ }
+}, {
+ "error": true
+}]
diff --git a/res/pages/outlook.html b/res/pages/outlook.html
new file mode 100644
index 00000000..5dc7a1a3
--- /dev/null
+++ b/res/pages/outlook.html
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Outlook Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Outlook for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#outlook">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="outlook" class="content">
+ <h2>Outlook Tutorial</h2>
+ <p>This short tutorial will show you how to configure Outlook 2007 for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <img src="images/outlook_01.png" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add a new e-mail account in Outlook, select the ‘Tools’ menu and then the ‘Account Settings' option. If 'Account Settings' does not appear, make sure you do not have a previously created account selected. Click on 'All Mail Items' on the left then open the 'Tools' menu.</p>
+ <p>If you’re starting Outlook for the first time, you’ll be prompted to add an e-mail account. If this is the case, select ‘Yes’ and then press the ‘Next’ button. You’ll then skip to Step Three in this tutorial.</p>
+ <img src="images/outlook_02.png" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Select the 'E-mail' tab then click 'New'.</p>
+ <img src="images/outlook_03.png" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>Click the check box to manually configure the account then click 'Next'.</p>
+ <img src="images/outlook_04.png" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>Choose 'Internet E-mail' and click 'Next'.</p>
+ <img src="images/outlook_05.png" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>In the 'Your Name' field, enter the name you would like to have displayed to your recipients. You will most likely use your full name but you are free to enter anything you wish such as your company name or a short description of the account if it serves a special purpose. Enter your $Provider e-mail address in the next field.</p>
+ <p>Select IMAP for the account type. IMAP has the benefit of leaving your messages on the server intact so that you can sync your mail with multiple computers or devices. Next, enter '$Provider.com' for the incoming and outgoing mail servers.</p>
+ <p>Your 'User Name' is everything that precedes @$Provider.com in your email address. Be sure to use lower case letters and enter it exactly as it appears in your e-mail address. After you fill in your password, you can check the 'Remember password' box to have Outlook store your password. If you do, be sure to write your password down and store it in a safe place in case you lose your account information for any reason. If you leave it unchecked, Outlook will prompt you for the password everytime you send or receive email.</p>
+ <p>Leave the 'Secure Password Authentication' checkbox blank. This is a Microsoft implementation that only works with Microsoft related e-mail accounts. Instead of clicking 'Next', go ahead and choose 'More Settings' then move on to Step Six.</p>
+ <img src="images/outlook_06.png" alt="Step six image." />
+ <h3>Step Six</h3>
+ <p>First, click on the 'Outgoing Server' tab and check the box to require authentication for the outgoing server. Use the same settings as your incoming mail server. Then select the 'Advanced' tab.</p>
+ <img src="images/outlook_07.png" alt="Step seven image." />
+ <h3>Step Seven</h3>
+ <p>Choose TLS for both the incoming and outgoing encryption types. TLS offers the same security as SSL and works on the default ports of 143 for IMAP and 25 for SMTP. Click 'Ok' to close the settings menu and click 'Next' to continue the new e-mail wizard.</p>
+ <img src="images/outlook_08.png" alt="Step eight image." />
+ <h3>Step Eight</h3>
+ <p>You're all setup. Click 'Finish' to close the wizard. If you created the account from the 'Account Settings' menu, go ahead and close it.</p>
+ <img src="images/outlook_09.png" alt="Step nine image." />
+ <h3>Step Nine</h3>
+ <p>You should see a message from the $Provider Support Team in your Inbox. You can also try sending an e-mail to make sure your outgoing mail works as well.</p>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. Of course, if you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/outlook_express.html b/res/pages/outlook_express.html
new file mode 100644
index 00000000..b1ffd892
--- /dev/null
+++ b/res/pages/outlook_express.html
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Outlook Express Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Outlook Express for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#outlook_express">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="outlook_express" class="content">
+ <h2>Outlook Express Tutorial</h2>
+ <p>This short tutorial will show you how to configure Outlook Express 6 for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <img src="images/express_1.jpg" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add a new e-mail account in Outlook Express, select the ‘Tools’ menu and then the ‘Accounts’ option. If you’re starting Outlook Express for the first time, you can skip directly to Step Three.</p>
+ <img src="images/express_2.jpg" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Click the ‘Add’ button and then select the ‘Mail’ option.</p>
+ <img src="images/express_3.jpg" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>In the ‘Display name’ field enter the name you want e-mails to appear from. Typically this is your full name, such as ‘John Doe’ or the name of the business the account is used for, such as ‘ACME Widgets’. For this tutorial, we’ve chosen to use ‘Example User’. When you’re done, click the ‘Next’ button.</p>
+ <img src="images/express_4.jpg" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>In the ‘E-mail address’ field, enter your new $Provider e-mail address. This will be your account_name@$Provider.com. In this example we have used the account name ‘example’. If the e-mail address you provide here doesn’t match the account name entered in Step Six, you won’t be able to send e-mail. When you’re done, click the ‘Next’ button.</p>
+ <img src="images/express_5.jpg" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>Make sure ‘<acronym title="Post Office Protocol Version 3">POP3</acronym>’ is selected in the drop down menu and then enter ‘$Provider.com’ in both the outgoing and incoming e-mail server fields. When you’re done, click the ‘Next’ button.</p>
+ <img src="images/express_6.jpg" alt="Step six image." />
+ <h3>Step Six</h3>
+ <p>Enter the name of your new $Provider account in the field ‘Account Name’. If you’d like Outlook Express to store your password, enter it here and check the ‘Remember password’ option. If you don’t check this option, Outlook Express will prompt you for the password every time you send or receive e-mail. When you’re done, click the ‘Next’ button.</p>
+ <p>If you choose to have Outlook Express remember your password, please make sure you write it down and store it in a safe place. Because of the encryption methods we use at $Provider, our administrators can’t change your account password without knowing the existing password.</p>
+ <img src="images/express_7.jpg" alt="Step seven image." />
+ <h3>Step Seven</h3>
+ <p>At this point, you’ve successfully registered the account with Outlook Express and can download your e-mail. We still have one more change to make before you can send e-mail. Click the ‘Finish’ button, and we’ll be ready to make that last change.</p>
+ <img src="images/express_8.jpg" alt="Step eight image." />
+ <h3>Step Eight</h3>
+ <p>To send e-mail, we require you to enable SMTP authentication. To enable SMTP authentication, make sure your new $Provider account is selected and press the ‘Properties’ button.</p>
+ <p>If you are starting Outlook Express for the first time, you won’t be returned to this screen. To get here, follow the instructions for Step One: select the ‘Tools’ menu and then the ‘Accounts’ option. If necessary, navigate to the ‘Mail’ tab pictured above. Make sure your $Provider account is selected and then press the ‘Properties’ button.</p>
+ <img src="images/express_9.jpg" alt="Step nine image." />
+ <h3>Step Nine</h3>
+ <p>The option for enabling SMTP authentication is located on the ‘Servers’ tab. </p>
+ <img src="images/express_10.jpg" alt="Step ten image." />
+ <h3>Step Ten</h3>
+ <p>Make sure the ‘My server requires authentication’ option is checked and then press ‘OK’. </p>
+ <img src="images/express_11.jpg" alt="Step eleven image." />
+ <h3>Step Eleven</h3>
+ <p>Your account is now set up and ready to go. Press the ‘Close’ button, and we can test the account.</p>
+ <img src="images/express_12.jpg" alt="Step twelve image." />
+ <h3>Step Twelve</h3>
+ <p>To prompt Outlook Express to download your e-mail immediately, press the ‘Send/Recv’ button. </p>
+ <img src="images/express_13.jpg" alt="Step thirteen image." />
+ <h3>Step Thirteen</h3>
+ <p>You should be greeted briefly by the progress dialog box pictured above. If you choose not to enter your password during Step Six, you’ll be asked for it here. </p>
+ <img src="images/express_14.jpg" alt="Step fourteen image." />
+ <h3>Step Fourteen</h3>
+ <p>If everything worked correctly, the progress dialog box should disappear and a welcome message from the $Provider Support Team should be in your Inbox. You can also try sending an e-mail to make sure you outgoing mail works as well.</p>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our troubleshooting page for solutions to the most common problems. We also recommend you that you follow optional Step Fifteen, because enabling <acronym title="Secure Sockets Layer">SSL</acronym> and changing the port numbers will fix the most common problem people encounter (hover:Common Problem Hover). If you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ <img src="images/express_15.jpg" alt="Step fifteen image." />
+ <h3>Step Fifteen</h3>
+ <p>For most users, this step is optional but recommended. For added security, we highly recommend enabling <acronym title="Secure Sockets Layer">SSL</acronym>. To enable <acronym title="Secure Sockets Layer">SSL</acronym>, select the ‘Tools’ menu and then the ‘Accounts’ option. Make sure your $Provider account is selected and then press the ‘Properties’ button. Navigate to the ‘Advanced’ tab pictured above and then check both ‘This server requires a secure connection (SSL)’ options. You’ll need to manually change the outgoing e-mail port to 465. The incoming e-mail port should automatically change to 995. When you’re done, the screen should look like the one above. Press the ‘OK’ button to save your changes and test the ‘Send/Recv’ button again.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/personal_e_mail.html b/res/pages/personal_e_mail.html
new file mode 100644
index 00000000..f05bce2c
--- /dev/null
+++ b/res/pages/personal_e_mail.html
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Personal E-mail</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes the e-mail plans and features that make our service better than the rest." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ <script type="text/javascript" src="/js/jquery.js"></script>
+ <script type="text/javascript" src="/js/jquery.corner.js"></script>
+ <script type="text/javascript" src="/js/personal_e_mail.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#personal_e_mail">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="personal_e_mail" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="personal_e_mail.html" class="active">Personal E-mail</a></li>
+ <li><a href="corporate_e_mail.html">Corporate E-mail</a></li>
+ <!-- <li><a href="disposable_e_mail.html">Disposable E-mail</a></li> -->
+ <!-- <li><a href="advertising.html">Advertising</a></li> -->
+ </ul>
+ </div>
+ <h2 id="start">Personal E-mail Services</h2>
+ <p>You live on the Internet and depend on your e-mail. You want an e-mail service that delivers. $Provider delivers—more than half a million e-mails a day, and we’re just getting started.</p>
+ <p>We know you’re serious about your messages. So are we. We’ve built an e-mail system just to handle your e-mail. Unlike the rest, your $Provider inbox is completely customized. Only want messages from people with valid <acronym title="Sender Policy Framework">SPF</acronym> records? No problem. Don’t want to use realtime blackholes because your uncle’s e-mail always gets blocked? No problem. Have no idea what were talking about? No problem—just accept our default options. We give you the freedom that comes with running your own e-mail server—without the hassle or expense.</p>
+ <p>Choose the plan that works best for you. If you’re curious, just try one of our free plans. You can always upgrade later. If you need the features or the space, jump right into one of our value-priced plans.</p>
+ <p>All of our plans include access to our <acronym title="Post Office Protocol Version 3">POP3</acronym>, <acronym title="Internet Message Access Protocol">IMAP</acronym> and webmail servers for downloading your incoming e-mail and access to our <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> servers for sending your outgoing messages.</p>
+ <table class="standard">
+ <caption>The different $Provider account plans.</caption>
+ <thead>
+ <tr>
+ <td></td>
+ <th>Basic Account</th>
+ <th>Personal Account</th>
+ <th>Enhanced Account</th>
+ <th>Premium Account</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>Storage:</th>
+ <td>128 <acronym title="Megabytes">MB</acronym></td>
+ <td>1,024 <acronym title="Megabytes">MB</acronym></td>
+ <td>1,024 <acronym title="Megabytes">MB</acronym></td>
+ <td>8,192 <acronym title="Megabytes">MB</acronym></td>
+ </tr>
+ <tr>
+ <th>Advertising:</th>
+ <td>No</td>
+ <td>Yes</td>
+ <td>No</td>
+ <td>No</td>
+ </tr>
+ <tr>
+ <th>Virus Protection:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Personalized Statistical Spam Filter:</th>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Sender Policy Framework (SPF) Support:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Domainkeys Support:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Realtime Blacklist (RBL) Support:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Greylisting Support:</th>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Filter Support:</th>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Scatter Back Protection:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Message Forwarding:</th>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Automatic Replies:</th>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Secure Sockets Layer (SSL) Support:</th>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Secure Mail Storage:</th>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes</td>
+ </tr>
+ <tr>
+ <th>Incoming Message Limit:</th>
+ <td>1,024 <span>messages/day</span></td>
+ <td>1,024 <span>messages/day</span></td>
+ <td>1,024 <span>messages/day</span></td>
+ <td>8,192 <span>messages/day</span></td>
+ </tr>
+ <tr>
+ <th>Outgoing Message Limit:</th>
+ <td>256 <span>messages/day</span></td>
+ <td>256 <span>messages/day</span></td>
+ <td>512 <span>messages/day</span></td>
+ <td>768 <span>messages/day</span></td>
+ </tr>
+ <tr>
+ <th>Message Size Limit:</th>
+ <td>32 <acronym title="Megabytes">MB</acronym></td>
+ <td>64 <acronym title="Megabytes">MB</acronym></td>
+ <td>64 <acronym title="Megabytes">MB</acronym></td>
+ <td>128 <acronym title="Megabytes">MB</acronym></td>
+ </tr>
+ <tr>
+ <th>Price:</th>
+ <td>$amount year</td>
+ <td>$amount year</td>
+ <td>$amount year</td>
+ <td>$amount year</td>
+ </tr>
+ </tbody>
+ </table>
+ <p id="discount"><em>Receive a %5 discount when you pre-pay five or more years. Receive a %10 discount when you pre-pay ten years.</em></p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/pidgin.html b/res/pages/pidgin.html
new file mode 100644
index 00000000..c7083248
--- /dev/null
+++ b/res/pages/pidgin.html
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Pidgin Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Pidgin for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#pidgin">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="pidgin" class="content">
+ <h2>Pidgin Tutorial</h2>
+ <p>This short tutorial will show you how to configure Pidgin for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <p>Please note that the following screenshots were taken in version 2.7.</p>
+ <img src="images/pidgin_01.png" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add your $Provider account to Pidgin, open the 'Accounts' menu and click 'Manage Accounts'. If you are running Pidgin for the first time, you can click 'Add' in the welcome screen to skip to Step Three.</p>
+ <img src="images/pidgin_02.png" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Next, click the 'Add' button.</p>
+ <img src="images/pidgin_03.png" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>First, select the XMPP protocol.</p>
+ <img src="images/pidgin_04.png" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>Your 'Username' is everything that precedes @$Provider.com in your email address. Be sure to use lowercase letters and enter it exactly as it appears in your e-mail address. Next enter '$Provider.com' into the 'Domain' field.</p>
+ <p>The 'Resource' is used to identify the computer your using and is only significant if you intend to access your account from different computers. We reccomend using a familiar label like 'desktop' or 'laptop.' You could also use the computer's location for the Resource; examples based on the location are 'home' or 'office'.</p>
+ <p>Enter the same password you use to access your email in the 'Password' field. You can choose to have Pidgin remember your password by checking the 'Remember password' box. If you leave it unchecked, Pidgin will ask you to enter your password every time it connects.</p>
+ <p>Finally, enter the name you want to have displayed in other instant messenger clients in the 'Local alias' field. You will most likely use your full name but you are free to enter anything you wish such as your company name or a short description of the account if it serves a special purpose. Click 'Add' to create the account.</p>
+ <img src="images/pidgin_05.png" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>Your instant messaging account is ready to go. Click 'Close' to go to the main window.</p>
+ <img src="images/pidgin_06.png" alt="Step six image." />
+ <h3>Step Six</h3>
+ <p>Add a buddy to your list by clicking 'Buddies' then 'Add Buddy'.</p>
+ <img src="images/pidgin_07.png" alt="Step seven image." />
+ <h3>Step Seven</h3>
+ <p>Select your $Provider account then enter username of your buddy. The username can be any service that supports XMPP. For $Provider users, the username will be their email address. The alias will be the name that is shown in your buddy list. Choose a group to add your buddy to then click 'Add'. Once your 'Add' request is authorized you'll be able to see when your buddy is online and available to chat.</p>
+ <img src="images/pidgin_08.png" alt="Step eight image." />
+ <h3>Step Eight</h3>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. Of course, if you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/res/pages/questions.html b/res/pages/questions.html
new file mode 100644
index 00000000..980c2831
--- /dev/null
+++ b/res/pages/questions.html
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Questions</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page answers common questions about $Provider and our services." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#questions">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="questions" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="settings.html">Configuration Settings</a></li>
+ <li><a href="questions.html" class="active">Common Questions</a></li>
+ <li><a href="troubleshooting.html">Troubleshooting</a></li>
+ <li><a href="tutorials.html">Tutorials</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Common Questions</h2>
+ <h3>$Provider Questions</h3>
+ <h4><a href="#q3">Do you send spam or support spammers?</a></h4>
+ <h4><a href="#q4">Will I ever have to pay for this service?</a></h4>
+ <h4><a href="#q5">Will you ever advertise in my outgoing messages?</a></h4>
+ <h3>Service Questions</h3>
+ <h4><a href="#q6">Do you offer IMAP or webmail access?</a></h4>
+ <h4><a href="#q13">Should I use IMAP or POP?</a></h4>
+ <h4><a href="#q7">I’ve lost my password, how do I reset it?</a></h4>
+ <h4><a href="#q8">What are the size, bandwidth and quantity limits on incoming and outgoing mail?</a></h4>
+ <h4><a href="#q12">How do I close my account?</a></h4>
+ <h3>Technical Questions</h3>
+ <h4><a href="#q9">How are you connected to the Internet and through whom?</a></h4>
+ <h4><a href="#q10">Do you support encryption?</a></h4>
+ <h4><a href="#q11">What web standards do you support?</a></h4>
+ <h4 id="q3">How is your server configured?</h4>
+ <p>$Provider services use a custom built e-mail server. This custom server uses open source libraries and runs well on inexpensive hardware. Because of this, $Provider has managed to keep its costs very low. Low costs coupled with the revenue generated from premium accounts and advertising enable us to provide services for free. The profits haven’t bought any of the founders a tropical island or an Italian sports car, but they are enough to keep the servers connected to the Internet—and cover the cost of an occasional late night pizza.</p>
+ <h4 id="q3">Do you send spam, or support spammers?</h4>
+ <p>We don’t send any unsolicited commercial e-mail—AKA spam. People often think that because a spam message they receive mentions a $Provider.com address, we’re responsible. The truth is, our staff doesn’t send any e-mail unless replying to someone, or notifying our members of a critical outage.</p>
+ <p>It’s also important to note that we limit all of our accounts to 256 to 768 outgoing messages a day. These limits are designed to discourage spammers from relaying spam through our servers. The limits don’t always discourage spam, but they do limit the damage a handful of rogue users can do.</p>
+ <p>If you encounter a spam message that mentions $Provider, please use our <a href="report_abuse.html">contact page</a> to let us know. Please be sure to always include the e-mail headers. The $Provider Abuse Team can use those headers to determine if a message passed through our network (usually not the case), and if not, complain to the appropriate ISP.</p>
+ <h4 id="q6">Do you offer IMAP or webmail access?</h4>
+ <p>Yes we offer access to your account via POP3, IMAP and <a href="portal">Webmail</a>. Complete information is available on our <a href="settings.html">settings page</a>.</p>
+ <h4 id="q13">Should I use IMAP or POP?</h4>
+ <p>The main difference between IMAP and POP is that IMAP leaves a copy of your message on the server where as POP deletes the message after downloading it. Therefore, IMAP lets you sync multiple computers and devices with your $Provider account and POP you will only work well with one computer or device.</p>
+ <h4 id="q7">I’ve lost my password, how do I reset it?</h4>
+ <p>Unfortunately, because of the encryption methods we use at $Provider, it’s impossible for our administrators to change an account password without knowing the existing password. If you’re in the habit of forgetting passwords, take a minute to write it down and store it in a safe place.</p>
+ <h4 id="q8">What are the size, bandwidth and quantity limits on incoming and outgoing mail?</h4>
+ <p>We have no bandwidth restrictions on our accounts. Size and quantity limits vary according to a user&apos;s plan. Please visit <a href="personal_e_mail.html">our plans page</a> to see the specific limits that apply. If you require limits higher than what is currently available please <a href="contact.html">contact us</a>. While such requests are evaluated on a case-by-case basis, increases are generally available to users on paid plans or users who have been actively using $Provider for an extended period of time.</p>
+ <p>For outgoing messages, we limit accounts to between 256 and 768 messages in a rolling 24-hour period. We do this to prevent spammers from abusing our service and ruining things for the rest of our users. Our limits aren’t perfect though. If you have a legitimate need and have been an active user of $Provider for an extended period of time, you’re welcome to <a href="contact.html">contact us</a> to request a higher limit. We review and approve these requests on a case by case basis. $Provider’s free e-mail service isn’t intended for commercial use, and we won’t grant requests based on a commercial need. If your business needs to send more messages please consider one of our <a href="corporate_e_mail.html">corporate accounts</a>.</p>
+ <p>Message size limits are also based on the <a href="personal_e_mail.html">account plan</a>, and range from 32 MB for our Basic plan up to 128 MB for our Premium plan.</p>
+ <p>All of the limits mentioned are maximums. Most of these limits can also be changed by users in the preferences portal.</p>
+ <h4 id="q12">How do I close my account?</h4>
+ <p>To close your account visit the <a href="apps/locker">account locker</a> where you will need to provide your username and password. The system will also automatically lock any account that goes unchecked for 120 days.</p>
+ <h4 id="q10">Do you support encryption?</h4>
+ <p>Yes, we support encryption and encourage our users to enable encryption in their e-mail client. We support POP3 over SSL on port 995 and SMTP over SSL on port 465. We also support using the STARTTLS command. Our SSL certificate has been granted by the <a href="http://www.comodogroup.com">Comodo Group</a>.</p>
+ <h4 id="q11">What web standards do you support?</h4>
+ <p>We make an effort to support web standards. The Internet only functions because everyone agrees to play by the same rules. If you find an area of our site or functionality of our software that deviates from a documented standard, please <a href="contact.html">contact us</a>. We support web standards because standards give individuals using assistive software easier access to our content. See our <a href="wai_aaa.html">accessibility page</a> for details on the efforts we’ve made to accommodate assistive technologies. You can also look at our <acronym title="Extensible Hypertext Markup Language"><a href="xhtml_valid.html">XHTML</a></acronym> and <acronym title="Cascading Style Sheet"><a href="css_valid.html">CSS</a></acronym> validation pages to verify that our site has been built with standards in mind.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/report_abuse.html b/res/pages/report_abuse.html
new file mode 100644
index 00000000..ce7bd233
--- /dev/null
+++ b/res/pages/report_abuse.html
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Report Abuse</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page lets members of the Internet easily report abuse by our users." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ <script type="text/javascript" src="/js/jquery.js"></script>
+ <script type="text/javascript" src="/js/contact.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#report_abuse">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="report_abuse" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="contact">Contact $Provider</a></li>
+ <li><a href="report_abuse.html" class="active">Report Abuse</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Report Abuse</h2>
+ <p>We don’t like people abusing our system. If you see a $Provider account used for spam, or some other violation of our <a href="abuse_policy.html">abuse policy</a>, please let us know. If reporting e-mail abuse, please be sure to send us the message, including the header information so we can trace the message back to its source. We’ll do our best to investigate and respond to any abuse.</p>
+ <form id="contact" method="post" action="contact>
+ <table>
+ <caption>Contact the $Provider team through this form.</caption>
+ <tbody>
+ <tr>
+ <th><label for="your_name">Your Name:</label></th>
+ <td><input id="your_name" name="your_name" type="text" value="" /></td>
+ </tr>
+ <tr>
+ <th><label for="your_e_mail">Your E-mail:</label></th>
+ <td><input id="your_e_mail" name="your_e_mail" type="text" value="" /></td>
+ </tr>
+ <tr id="message">
+ <th><label for="your_message">Your Message:</label></th>
+ <td><textarea id="your_message" name="your_message" rows="10" cols="60"></textarea></td>
+ </tr>
+ </tbody>
+ </table>
+ <div>
+ <button id="abuse_sub" name="abuse_sub" type="submit" title="Use this button to submit the form."><strong>Submit &gt;&gt;</strong></button>
+ </div>
+ </form>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/secure.html b/res/pages/secure.html
new file mode 100644
index 00000000..d9a66d5d
--- /dev/null
+++ b/res/pages/secure.html
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Secure</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page contains a detailed description of the asymmetric encryption used by $Provider’s e-mail service." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#secure">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="/help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="secure" class="content">
+ <h2>Security Through Asymmetric Encryption</h2>
+ <h3>Why is secure mail storage important?</h3>
+ <p>In an era where Microsoft and Yahoo’s e-mail services sell access past their spam filters, Google profiles user’s inboxes for targeted advertising, and <acronym title="American Telephone and Telegraph">AT&amp;T</acronym> allows the government to tap phone calls without a court warrant; we decided to take a stand.</p>
+ <p>$Provider has developed a system so secure that it prevents everyone, including us, from reading the e-mail of the people that use it.</p>
+ <phIn safer times, a strict Privacy Policy would have been enough to protect the rights of honest Internet citizens. But everything changed when the United States Congress passed the Providing Appropriate Tools Required to Intercept and Obstruct Terrorism (<acronym title="Providing Appropriate Tools Required to Intercept and Obstruct Terrorism">PATRIOT</acronym>) Act in 2001. If you’re currently unaware of the PATRIOT Act, we highly recommend you visit the <a href="http://www.eff.org/">Electronic Frontier Foundation (EFF)</a> website.</p>
+ <p>The key element of the <acronym title="Providing Appropriate Tools Required to Intercept and Obstruct Terrorism">PATRIOT</acronym> Act is that it allows the FBI to issue National Security Letters (NSLs). NSLs are used to force an Internet Service Provider, like $Provider, to surrender all private information related to a particular user. The problem is that NSLs come without the oversight of a court and can be issued in secret. Issuing an NSL in secret effectively denies the accused an opportunity to defend himself in court. Fortunately, the courts ruled NSLs unconstitutional in 2005; but not before illustrating the need for a technological guarantee of privacy.</p>
+ <p>$Provider believes that a civil society depends on the open, free and private flow of ideas. The type of monitoring promoted by the <acronym title="Providing Appropriate Tools Required to Intercept and Obstruct Terrorism">PATRIOT</acronym> Act restricts that flow of ideas because it intimidates those afraid of retaliation. To counteract this chilling effect, $Provider developed its secure e-mail platform. We feel e-mail has evolved into a critical channel for the communication of ideas in a healthy democracy. It’s precisely because of e-mail’s importance that we strive so hard to protect private e-mails from eavesdropping.</p>
+ <h3>How does it work?</h3>
+ <p>How does asymmetric encryption protect your privacy? The short description is that for users of this feature, incoming e-mail messages are encrypted before they’re saved onto our servers. Once a message has been encrypted, only someone who has the account password can decrypt the message. Like all safety measures, encryption is only effective if it’s used. To ensure privacy, $Provider has developed a complex system that makes the entire encryption and decryption process transparent to the end user.</p>
+ <p>This process works by combining three different encryption schemes with Elliptical Curve Cryptography (ECC) as the cornerstone. When a user activates the asymmetric encryption feature, two ECC keys are generated with 521 bits of strength. The first key, or the <q>public</q> key, is stored in plain text on the server. This public key is used to encrypt incoming messages. Because of how ECC works, only someone with the second “private” key can decipher messages encrypted with the public key. To protect the private key from attackers, it is encrypted using the Advanced Encryption Standard (AES) (hover:AES Hover) with a 256 bit key. AES is a synchronous encryption scheme that uses a secret passphrase to encrypt/decrypt a ciphered message. In the case of $Provider’s secure e-mail system, the ciphered message is a user’s private key and the secret passphrase is a hashed version of the user’s password.</p>
+ <p>To ensure maximum security, passwords are hashed using the Secure Hash Algorithm (SHA). SHA takes the plaintext password as its input and produces a random 512 bit string as the output. With only the SHA output, it is cryptographically impossible to determine the original input. Effectively, hashing is a repeatable one-way process.</p>
+ <p>To increase the randomness of our hash outputs and the difficulty of reversing the process, $Provider.combines the password with the account name and a cryptographic salt (hover:Salt Hover). This combined string is then hashed three consecutive times, with the former iteration’s output being used as the input value of the next iteration. The output of the first hash iteration is used as the secret passphrase for AES mentioned above. The third iteration is stored in our password database and is used to verify that users entered their password correctly.</p>
+ <p>The product of this encryption process is a message that is cryptographically impossible to read without the password. We say <q>cryptographically impossible</q> because, in theory, an attacker with unlimited computing resources could use brute force to decipher the original message. However in practice, the key lengths $Provider has chosen equal enough possible inputs that a brute-force attack shouldn’t be feasible for a long time to come.</p>
+ <p>We should note that this encryption process is only secure if you select a strong password. If your password is weak, an attacker would only need to brute force the password to crack our encryption. We should also note that this feature only protects messages on the $Provider servers. Messages can always be intercepted before they reach $Provider or between $Provider’s servers and your personal computer, if SSL is not used. Finally, messages can be retrieved from your local hard drive if encryption software isn’t used on your computer to protect the files. These vulnerabilities are intentional. Our goal was to make invading a user’s privacy difficult, by protecting messages at their most vulnerable point. That doesn’t mean a dedicated attacker, like the United States government, couldn’t intercept the message in transit or once it reaches your computer.</p>
+ <p>Our hope is the difficulty associated with those strategies means they will only be used by governments on terrorists and scammers, not on honest citizens. If you’re intent on hiding your communications from the government, we recommend you investigate systems that secure messages throughout the entire e-mail system and not just at one particular point along that journey.</p>
+ <h3>How do you take advantage of this feature?</h3>
+ <p>We’ve chosen to offer this feature only to our paid users. We made this decision for two reasons. The first is a belief that with paying customers, there is a money trail. If the account is used for illegal purposes that money trail can be used to track down the account owner. The second reason is that the broader encryption process requires a significant amount of computing power. We can only justify the added expense for premium accounts.</p>
+ <p>Like insurance, we hope our secure e-mail platform is something you’ll never need. However, should the issue ever arise, like insurance, you’ll be glad you have it.</p>
+ <p>If you’re a paid account holder, you can activate this feature by logging into the preferences portal and setting the feature <q>Secure</q> to <q>Enabled.</q></p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/services.html b/res/pages/services.html
new file mode 100644
index 00000000..bb02b4fa
--- /dev/null
+++ b/res/pages/services.html
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Services</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes our e-mail and advertising services." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#services">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="/index.html">Home</a></li>
+ <li><a id="nav_about" href="/about.html">About</a></li>
+ <li><a id="nav_features" href="/features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="/help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="services" class="content">
+ <h2>Services</h2>
+ <h3><a href="personal_e_mail.html">Personal E-mail</a></h3>
+ <p>$Provider gives you a variety of e-mail options. Use this page to learn about our plans—and why we’re the only choice for people who are serious about their e-mail.</p>
+ <h3><a href="corporate_e_mail.html">Corporate E-mail</a></h3>
+ <p>Do you run an organization with demanding e-mail users but don’t want the hassle of providing high-quality e-mail services? Let our experts handle the heavy lifting. Whether you need e-mail service for 10 or 1,000, you’ll get our robust platform, competitive pricing—and the same advanced features as our personal accounts.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/settings.html b/res/pages/settings.html
new file mode 100644
index 00000000..62934f1c
--- /dev/null
+++ b/res/pages/settings.html
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Settings</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes the settings used to access your $Provider e-mail account." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#settings">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="/index.html">Home</a></li>
+ <li><a id="nav_about" href="/about.html">About</a></li>
+ <li><a id="nav_features" href="/features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="/help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="/contact">Contact Information</a></li>
+ <li><a id="nav_webmail" href="/portal">Webmail</a></li>
+ <li><a id="nav_register" href="/register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="settings" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="settings.html" class="active">Configuration Settings</a></li>
+ <li><a href="questions.html">Common Questions</a></li>
+ <li><a href="troubleshooting.html">Troubleshooting</a></li>
+ <li><a href="tutorials.html">Tutorials</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Email Settings</h2>
+ <table>
+ <caption>Webmail information.</caption>
+ <tbody>
+ <tr>
+ <th>Webmail Address:</th>
+ <td><a href="webmail">/webmail</a></td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <caption>Incoming mail Settings.</caption>
+ <tbody>
+ <tr>
+ <th>Incoming Mail Server:</th>
+ <td>$Provider.com</td>
+ </tr>
+ <tr>
+ <th>POP3 Port:</th>
+ <td>110</td>
+ </tr>
+ <tr>
+ <th>POP3 over SSL Port:</th>
+ <td>995</td>
+ </tr>
+ <tr>
+ <th>IMAP4 Port:</th>
+ <td>143</td>
+ </tr>
+ <tr>
+ <th>IMAP4 over SSL Port:</th>
+ <td>993</td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <caption>Outgoing mail Settings.</caption>
+ <tbody>
+ <tr>
+ <th>Outgoing Mail Server:</th>
+ <td>$Provider.com</td>
+ </tr>
+ <tr>
+ <th>SMTP Ports:</th>
+ <td>25, 587, 2525, or 3535</td>
+ </tr>
+ <tr>
+ <th>SMTP over SSL Port:</th>
+ <td>465</td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Instant Messenger Settings</h2>
+ <table>
+ <caption>XMPP Server Settings</caption>
+ <tbody>
+ <tr>
+ <th>XMPP Server:</th>
+ <td>$Provider.com</td>
+ </tr>
+ <tr>
+ <th>XMPP Port:</th>
+ <td>5222</td>
+ </tr>
+ <tr>
+ <th>XMPP over SSL Port:</th>
+ <td>5223</td>
+ </tr>
+ </tbody>
+ </table>
+ <p>For our servers to relay outbound e-mail you must enable authentication. Please see our <a href="tutorials.html">tutorials</a> for help configuring your client. Our servers support the PLAIN and LOGIN authentication methods.</p>
+ <p>All outbound e-mails must be relayed through our servers. If you do not use our servers for outbound e-mail you risk having your messages rejected by the recipient’s e-mail server. This is because $Provider publishes <acronym title="Sender Policy Framework">SPF</acronym> and domainkey information that some e-mail servers use to verify that messages claiming to be from $Provider.com originated on a server authorized to represent $Provider.com.</p>
+ <p>If you need help configuring your e-mail or instant messaging client to use these settings, please try one of our <a href="tutorials.html">tutorials</a>. If your client is configured correctly and you’re still having problems, please visit our <a href="troubleshooting.html">troubleshooting page</a> for hints. You can also use our <a href="contact.html">contact page</a> to ask the $Provider Support Team for help.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/site_map.html b/res/pages/site_map.html
new file mode 100644
index 00000000..c6c4c884
--- /dev/null
+++ b/res/pages/site_map.html
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Sitemap</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a link to every $Provider page that doesn’t require authentication." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#site_map">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="site_map" class="content">
+ <h2>Site Map</h2>
+ <table>
+ <caption>$Provider site map.</caption>
+ <thead>
+ <tr>
+ <th>Description</th>
+ <th>Link</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>About</td>
+ <td><a href="about.html">/about.html</a></td>
+ </tr>
+ <tr>
+ <td>Contact</td>
+ <td><a href="contact">/contact</a></td>
+ </tr>
+ <tr>
+ <td>Corporate E-Mail</td>
+ <td><a href="corporate_e_mail.html">/corporate_e_mail.html</a></td>
+ </tr>
+ <tr>
+ <td>Credits</td>
+ <td><a href="credits.html">/credits.html</a></td>
+ </tr>
+ <tr>
+ <td>CSS Valid</td>
+ <td><a href="css_valid.html">/css_valid.html</a></td>
+ </tr>
+ <tr>
+ <td>Features</td>
+ <td><a href="features.html">/features.html</a></td>
+ </tr>
+ <tr>
+ <td>Help</td>
+ <td><a href="help.html">/help.html</a></td>
+ </tr>
+ <tr>
+ <td>History</td>
+ <td><a href="history.html">/history.html</a></td>
+ </tr>
+ <tr>
+ <td>Home</td>
+ <td><a href="index.html">/index.html</a></td>
+ </tr>
+ <tr>
+ <td>News</td>
+ <td><a href="news.html">/news.html</a></td>
+ </tr>
+ <tr>
+ <td>Outlook Tutorial</td>
+ <td><a href="outlook.html">/outlook.html</a></td>
+ </tr>
+ <tr>
+ <td>Outlook Express Tutorial</td>
+ <td><a href="outlook_express.html">/outlook_express.html</a></td>
+ </tr>
+ <tr>
+ <td>Personal E-Mail</td>
+ <td><a href="personal_e_mail.html">/personal_e_mail.html</a></td>
+ </tr>
+ <tr>
+ <td>Philosophy</td>
+ <td><a href="philosophy.html">/philosophy.html</a></td>
+ </tr>
+ <tr>
+ <td>Pidgin Tutorial</td>
+ <td><a href="pidgin.html">/pidgin.html</a></td>
+ </tr>
+ <tr>
+ <td>Questions</td>
+ <td><a href="questions.html">/questions.html</a></td>
+ </tr>
+ <tr>
+ <td>Report Abuse</td>
+ <td><a href="report_abuse.html">/report_abuse.html</a></td>
+ </tr>
+ <tr>
+ <td>Secure</td>
+ <td><a href="secure.html">/secure.html</a></td>
+ </tr>
+ <tr>
+ <td>Services</td>
+ <td><a href="services.html">/services.html</a></td>
+ </tr>
+ <tr>
+ <td>Settings</td>
+ <td><a href="settings.html">/settings.html</a></td>
+ </tr>
+ <tr>
+ <td>Sign Up</td>
+ <td><a href="register">/register</a></td>
+ </tr>
+ <tr>
+ <td>Site Map</td>
+ <td><a href="site_map.html">/site_map.html</a></td>
+ </tr>
+ <tr>
+ <td>Testimonials</td>
+ <td><a href="testimonials.html">/testimonials.html</a></td>
+ </tr>
+ <tr>
+ <td>Thunderbird Tutorial</td>
+ <td><a href="thunderbird.html">/thunderbird.html</a></td>
+ </tr>
+ <tr>
+ <td>Trillian Tutorial</td>
+ <td><a href="trillian.html">/trillian.html</a></td>
+ </tr>
+ <tr>
+ <td>Troubleshooting</td>
+ <td><a href="troubleshooting.html">/troubleshooting.html</a></td>
+ </tr>
+ <tr>
+ <td>Tutorials</td>
+ <td><a href="tutorials.html">/tutorials.html</a></td>
+ </tr>
+ <tr>
+ <td>WAI AAA</td>
+ <td><a href="wai_aaa.html">/wai_aaa.html</a></td>
+ </tr>
+ <tr>
+ <td>Webmail</td>
+ <td><a href="webmail/">/webmail/</a></td>
+ </tr>
+ <tr>
+ <td>Windows Live Mail Tutorial</td>
+ <td><a href="windows_live_mail.html">/windows_live_mail.html</a></td>
+ </tr>
+ <tr>
+ <td>XHTML Valid</td>
+ <td><a href="xhtml_valid.html">/xhtml_valid.html</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/sitemap.xml b/res/pages/sitemap.xml
new file mode 100644
index 00000000..ac750ecb
--- /dev/null
+++ b/res/pages/sitemap.xml
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+ <url>
+ <loc>http://$Provider.com/</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>1.0</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/about.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/features.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/services.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/philosophy.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/testimonials.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/network.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/help.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/contact.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/apps/preferences</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.8</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/apps/webmail/src/login.php</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>1.0</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/apps/register</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.9</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/secure.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>1.0</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/statistics.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/terms_of_use.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/privacy_policy.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/advertising_policy.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/abuse_policy.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/report_abuse.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/site_map.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/credits.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/xhtml_valid.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/css_valid.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/wai_aaa.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/news.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/history.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/personal_e_mail.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/corporate_e_mail.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/hardware_software.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/graphs.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/health.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/settings.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.9</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/questions.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.6</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/troubleshooting.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/tutorials.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.6</priority>
+ </url>
+ <url>
+ <loc>https://$Provider.com/apps/locker</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/outlook.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/outlook_express.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/pidgin.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/thunderbird.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/trillian.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/windows_live_mail.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ <url>
+ <loc>http://$Provider.com/disposable_e_mail.html</loc>
+ <lastmod>2011-01-26</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+ </urlset>
diff --git a/res/pages/thunderbird.html b/res/pages/thunderbird.html
new file mode 100644
index 00000000..824bbdb5
--- /dev/null
+++ b/res/pages/thunderbird.html
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Thunderbird Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Thunderbird for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#thunderbird">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="thunderbird" class="content">
+ <h2>Thunderbird Tutorial</h2>
+ <p>This short tutorial will show you how to configure Thunderbird for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <p>Please note that the following screenshots were taken in version 3.1.</p>
+ <img src="images/thunderbird_01.png" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add a new account to an existing Thunderbird installation, select the ‘Tools’ menu and then the ‘Account Settings’ option.</p>
+ <p>If you’re starting Thunderbird for the first time, it will automatically display the Mail Account Setup prompt and you can skip to Step Three.</p>
+ <img src="images/thunderbird_02.png" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Click on the 'Account Actions' drop down and select 'Add Mail Account'.</p>
+ <img src="images/thunderbird_03.png" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>In the 'Your name' field, enter the name you would like to have displayed to your recipients. You will most likely use your full name but you are free to enter anything you wish such as your company name or a short description of the account if it serves a special purpose.</p>
+ <p>Enter your $Provider e-mail address and password in the next two fields. You can choose to have Thunderbird remember your password by checking the 'Remember password' box. If you do, be sure to write your password down and store it in a safe place in case you lose your account information for any reason. If you leave it unchecked, Thunderbird will prompt you for the password everytime you send or receive email. Click 'Continue' to move to Step Four.</p>
+ <img src="images/thunderbird_04.png" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>At this time, Thunderbird will detect the optimal settings for your account and automatically configure everything for you.</p>
+ <p>By default, Thunderbird chooses IMAP over the alternative, POP3, for incoming mail. IMAP has the benefit of leaving your messages on the server intact so that you can sync your mail with multiple computers or devices. Thunderbird will also choose the default ports for both IMAP and SMTP with STARTTLS for security. STARTTLS offers the same security as SSL so it is perfectly fine to use the default settings.</p>
+ <p>If you are happy with these settings, click 'Create Account' to finish. Otherwise, you can click the 'Edit' button above 'Create Account' and configure everything to meet your needs. Take a look at the <a href="settings.html">configuration settings</a> to see all of the available ports for each protocol.</p>
+ <img src="images/thunderbird_05.png" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>If you created the account from the 'Account Settings' menu, press 'Ok' to close it. You will see your new email address listed in the frame on the left. Click on your account then choose 'Read messages' to view your mail.</p>
+ <img src="images/thunderbird_06.png" alt="Step six image." />
+ <h3>Step Six</h3>
+ <p>You should see a message from the $Provider Support Team in your Inbox. You can also try sending an e-mail to make sure your outgoing mail works as well.</p>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. Of course, if you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/trillian.html b/res/pages/trillian.html
new file mode 100644
index 00000000..0dbd1e6a
--- /dev/null
+++ b/res/pages/trillian.html
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Trillian Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Pidgin for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#trillian">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="trillian" class="content">
+ <h2>Trillian Tutorial</h2>
+ <p>This short tutorial will show you how to configure Trillian for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <img src="images/trillian_01.png" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add your $Provider account to Trillian, open the 'Trillian' menu and click 'Manage Accounts'.</p>
+ <img src="images/trillian_02.png" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Next, click the 'Add a new account' button then select 'Jabber/XMPP' in the popup menu.</p>
+ <img src="images/trillian_03.png" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>Enter your $Provider email address and password then click 'Connect'. That's all there is to it! You can go ahead and close the preferences window.</p>
+ <img src="images/trillian_04.png" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>To add a contact, click the 'Trillian' menu then select 'Add Contact'.</p>
+ <img src="images/trillian_05.png" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>Choose your $Provider account in the 'Account' drop down menu.</p>
+ <img src="images/trillian_06.png" alt="Step six image." />
+ <h3>Step Six</h3>
+ <p>Enter your contact's Jabber ID (<acronym>JID</acronym>). If you are adding another $Provider user, you would enter their $Provider email address. The 'Display Name' is the name that will appear in your contact list. Choose a group to add your new contact to then click the 'Add' button.</p>
+ <img src="images/trillian_07.png" alt="Step seven image." />
+ <h3>Step Seven</h3>
+ <p>Once your buddy accepts your request, you will be able to instant message each other using your $Provider and other XMPP accounts.</p>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. Of course, if you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
+
diff --git a/res/pages/troubleshooting.html b/res/pages/troubleshooting.html
new file mode 100644
index 00000000..3c9f3486
--- /dev/null
+++ b/res/pages/troubleshooting.html
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Troubleshooting</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides troubleshooting tips for people having problems connecting to our server." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#troubleshooting">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="troubleshooting" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="settings.html">Configuration Settings</a></li>
+ <li><a href="questions.html">Common Questions</a></li>
+ <li><a href="troubleshooting.html" class="active">Troubleshooting</a></li>
+ <li><a href="tutorials.html">Tutorials</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Troubleshooting</h2>
+ <h3>Generic Errors</h3>
+ <h4><a href="#t1">Could not connect to $Provider.com.</a></h4>
+ <h3>Webmail Errors</h3>
+ <h4><a href="#t2">Cannot login with the correct name and password.</a></h4>
+ <h3>Outlook Errors</h3>
+ <h4><a href="#t3">Could not connect to $Provider.com.</a></h4>
+ <h3>Windows Live Mail Errors</h3>
+ <h4><a href="#t4">General authentication failed.</a></h4>
+ <h4 id="t1">Could not connect to $Provider.com.</h4>
+ <p>Many ISPs have chosen to block SMTP port 25 to prevent their customers from sending spam. To work around this problem, please configure your e-mail client to use port 587 for SMTP. You can also configure your client to use SMTP over SSL on port 465.</p>
+ <h4 id="t2">Cannot login with the correct username and password.</h4>
+ <p>In order to login into the webmail system you must have cookies enabled. With cookies disabled the server will refuse your username and password even if they are correct.</p>
+ <h4 id="t3">Could not connect to $Provider.com.</h4>
+ <p>Make sure you have Secure Password Authentication (<acronym>SPA</acronym>) disabled. <acronym>SPA</acronym> is a Microsoft security implementation that only works with Microsoft related e-mail accounts. To disable <acronym>SPA</acronym>, open the 'Tools' menu then choose 'Account Settings'. Select your $Provider account from the list then click the 'Change' button above. Uncheck the secure password authentication box then click 'Next' to continue. Click 'Finish' to apply your settings.</p>
+ <h4 id="t4">General Authentication Failed.</h4>
+ <p>Make sure you have Secure Password Authentication (<acronym>SPA</acronym>) disabled. <acronym>SPA</acronym> is a Microsoft security implementation that only works with Microsoft related e-mail accounts. To disable <acronym>SPA</acronym>, select your $Provider account in the left frame of the main window then click the 'Properties' button in the upper left. Select the 'Servers' tab and choose to 'Log on using clear text authentication'. If you followed the <a href="windows_live_mail.html">Windows Live Mail Tutorial</a> or you created your account with SSL required, clear text authentication is not a security lapse since you are sending your Information over a secure connection.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/tutorials.html b/res/pages/tutorials.html
new file mode 100644
index 00000000..df785bc3
--- /dev/null
+++ b/res/pages/tutorials.html
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Tutorials</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides access to our configuration tutorials." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#tutorials">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="tutorials" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="settings.html">Configuration Settings</a></li>
+ <li><a href="questions.html">Common Questions</a></li>
+ <li><a href="troubleshooting.html">Troubleshooting</a></li>
+ <li><a href="tutorials.html" class="active">Tutorials</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Tutorials</h2>
+ <p>Sometimes all it takes is a clear example to follow. Here are step-by-step guides on how to configure different e-mail and instant messanger clients for use with $Provider. With the help of these tutorials, you should be accessing your $Provider e-mail and sending instant messages in just a few minutes.</p>
+ <h3>E-mail Tutorials</h3>
+ <h4><a href="thunderbird.html">Thunderbird Tutorial</a></h4>
+ <h4><a href="outlook.html">Outlook Tutorial</a></h4>
+ <h4><a href="outlook_express.html">Outlook Express Tutorial</a></h4>
+ <h4><a href="windows_live_mail.html">Windows Live Mail Tutorial</a></h4>
+ <h3>Instant Messenger Tutorials</h3>
+ <h4><a href="pidgin.html">Pidgin Tutorial</a></h4>
+ <h4><a href="trillian.html">Trillian Tutorial</a></h4>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/wai_aaa.html b/res/pages/wai_aaa.html
new file mode 100644
index 00000000..c7d198a4
--- /dev/null
+++ b/res/pages/wai_aaa.html
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Web Accessibility</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page describes the $Provider efforts at becoming web accessible." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#wai_aaa">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="wai_aaa" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="xhtml_valid.html">XHTML Valid</a></li>
+ <li><a href="css_valid.html">CSS Valid</a></li>
+ <li><a href="wai_aaa.html" class="active">Web Accessibility</a></li>
+ </ul>
+ </div>
+ <h2 id="start">Web Accessibility</h2>
+ <p>Internet users with disabilities sometimes find web sites difficult or even impossible to use because of the way they were designed or implemented. In contrast, $Provider works hard to make sure that our website complies with known guidelines for web accessibility.</p>
+ <p>To make our site accessible, we have strictly followed the <acronym title="Extensible Hypertext Markup Language">XHTML</acronym> and <acronym title="Cascading Style Sheets">CSS</acronym> specifications so that assistive technologies can easily parse our web pages. We try to keep text clear, concise and easily resizable. We have avoided using color exclusively to convey important information. We’ve tried to keep navigation usable with just a keyboard. We’ve even checked to make sure our pages render acceptably with <acronym title="Cascading Style Sheets">CSS</acronym> disabled.</p>
+ <p>We’ve taken these steps in accordance with the Content Accessibility Guidelines put forth by the <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym>. Wherever possible, we’ve sought to achieve a <acronym title="Triple A">AAA</acronym> level of conformance. If you come across part of our site that deviates from this standard or have an idea on how we can improve, please use our <a href="contact.html">contact page</a> to let us know. Like all human endeavours our website has flaws—but with your help, we’ll keep striving for perfection.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/windows_live_mail.html b/res/pages/windows_live_mail.html
new file mode 100644
index 00000000..cefb9b39
--- /dev/null
+++ b/res/pages/windows_live_mail.html
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. Windows Live Mail Tutorial</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides a step-by-step guide on how to configure Windows Live Mail for use with the $Provider network." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#windows_live_mail">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="windows_live_mail" class="content">
+ <h2>Windows Live Mail Tutorial</h2>
+ <p>This short tutorial will show you how to configure Windows Live Mail 2011 for use with $Provider. If you continue having difficulty after following this tutorial, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. And if all else fails, please use our <a href="contact.html">contact page</a> to get in touch with the $Provider Support Team.</p>
+ <img src="images/windows_live_mail_01.png" alt="Step one image." />
+ <h3>Step One</h3>
+ <p>To add a new e-mail account in Windows Live Mail, select the 'Accounts' tab then click the 'Email' button.</p>
+ <img src="images/windows_live_mail_02.png" alt="Step two image." />
+ <h3>Step Two</h3>
+ <p>Enter your $Provider e-mail address and password in the 'Email address' and 'Password:' fields. You can choose to have Windows Live Mail remember your password by checking the 'Remember this password' box. If you leave it unchecked, Windows Live Mail will prompt you for the password everytime you send or receive email.</p>
+ <p>The display name field lets you choose the name you would like to have displayed to your recipients. You will most likely use your full name but you are free to enter anything you wish such as your company name or a short description of the account if it serves a special purpose.</p>
+ <p>Leave the manual configuration check box unchecked then click 'Next' to move on to Step Three.</p>
+ <img src="images/windows_live_mail_03.png" alt="Step three image." />
+ <h3>Step Three</h3>
+ <p>For the incoming server, choose IMAP as the server type. IMAP has the benefit of leaving your messages on the server intact so that you can sync your mail with multiple computers or devices. Enter '$Provider.com' in the 'Server address' field and check the box to require a secure SSL connection. This will automatically change the port to the correct number, 993. Do not change the authentication method to Secure Password Authentication. Instead we reccomend the use of the SSL which protects all of the data sent over the network, not just the password.</p>
+ <p>Your 'Logon user name' is everything that precedes @$Provider.com in your email address. Enter '$Provider.com' in the outgoing server field and check the box next to 'Require a secure connection (SSL).' Be sure to manually enter 465 in the port number box. Click 'Next' to continue.</p>
+ <img src="images/windows_live_mail_04.png" alt="Step four image." />
+ <h3>Step Four</h3>
+ <p>You're all setup. Click 'Finish' to close the wizard.</p>
+ <img src="images/windows_live_mail_05.png" alt="Step five image." />
+ <h3>Step Five</h3>
+ <p>At this point, if you have any messages available on the $Provider servers they should be downloaded to your computer. We also reccomend emailing a test message to verify your client's ability to send messages.</p>
+ <p>For most users, this is the happy ending to the story. If you hit a snag, please check our <a href="troubleshooting.html">troubleshooting page</a> for solutions to the most common problems. Of course, if you’re still stuck, please use our <a href="contact.html">contact page</a> to get help from the $Provider Support Team.</p>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/pages/xhtml_valid.html b/res/pages/xhtml_valid.html
new file mode 100644
index 00000000..0441577e
--- /dev/null
+++ b/res/pages/xhtml_valid.html
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+ <title>$Provider ..::.. XHTML Validation</title>
+ <meta name="author" content="Ladar Levison"/>
+ <meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page provides links to validate all $Provider pages as XHTML strict using the W3C validation engine." />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <link rel="stylesheet" type="text/css" href="css/site.css" />
+ <script type="text/javascript" src="/js/site.js"></script>
+ </head>
+ <body>
+ <div id="page">
+ <div id="header">
+ <h1>$Provider</h1>
+ <div>
+ <ul id="nav_col_1">
+ <li class="skip"><a href="#xhtml_valid">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="index.html">Home</a></li>
+ <li><a id="nav_about" href="about.html">About</a></li>
+ <li><a id="nav_features" href="features.html">Features</a></li>
+ </ul>
+ <ul id="nav_col_2">
+ <li><a id="nav_help" href="help.html">Help</a></li>
+ </ul>
+ <ul id="nav_col_3">
+ <li><a id="nav_contact" href="contact.html">Contact Information</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" href="register">Sign&nbsp;Up</a></li>
+ </ul>
+ </div>
+ </div>
+ <div id="xhtml_valid" class="content">
+ <div id="secondary">
+ <ul>
+ <li class="skip"><a href="#start">Skip Secondary Navigation</a></li>
+ <li><a href="xhtml_valid.html" class="active">XHTML Valid</a></li>
+ <li><a href="css_valid.html">CSS Valid</a></li>
+ <li><a href="wai_aaa.html">Web Accessibility</a></li>
+ </ul>
+ </div>
+ <h2 id="start">XHTML Validation</h2>
+ <p>$Provider supports web standards. All of the pages across the $Provider website have been created with a strict adherence to the <acronym title="Extensible Hypertext Markup Language">XHTML</acronym> specification. The <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> is responsible for creating and maintaining the <acronym title="Extensible Hypertext Markup Language">XHTML</acronym> standard. Along with the specification the <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> provides set of <acronym title="Document Type Definitions">DTD</acronym> that developers can use to verify that their pages precisely meet the <acronym title="World Wide Web Consortium"><a href="http://www.w3.org">W3C</a></acronym> standards.</p>
+ <p>Here are links you can be use to validate all of the $Provider web pages against the <acronym title="Extensible Hypertext Markup Language">XHTML</acronym> Strict <acronym title="Document Type Definition">DTD</acronym>. $Provider is proud of its support for web standards and encourages developers to validate their web pages against this specification.</p>
+ <table>
+ <caption>$Provider validation links.</caption>
+ <thead>
+ <tr>
+ <th>Description</th>
+ <th>File Name</th>
+ <th>Validation Link</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>About</td>
+ <td><a href="about.html">/about.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fabout.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Account Locker</td>
+ <td><a href="apps/locker">apps/locker</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=https%3A%2F%2F$Provider.com%2Fapps%2Flocker">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Advertising Policy</td>
+ <td><a href="advertising_policy.html">/advertising_policy.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fadvertising_policy.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Contact</td>
+ <td><a href="contact">contact</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=https%3A%2F%2F$Provider.com%2Fcontact.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Corporate E-Mail</td>
+ <td><a href="corporate_e_mail.html">/corporate_e_mail.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fcorporate_e_mail.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Credits</td>
+ <td><a href="credits.html">/credits.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fcredits.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>CSS Valid</td>
+ <td><a href="css_valid.html">/css_valid.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fcss_valid.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Features</td>
+ <td><a href="features.html">/features.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Ffeatures.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Help</td>
+ <td><a href="help.html">/help.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fhelp.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>History</td>
+ <td><a href="history.html">/history.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fhistory.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Home</td>
+ <td><a href="index.html">/index.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Findex.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>News</td>
+ <td><a href="news.html">/news.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fnews.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Outlook Tutorial</td>
+ <td><a href="outlook.html">/outlook.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Foutlook.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Outlook Express Tutorial</td>
+ <td><a href="outlook_express.html">/outlook_express.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Foutlook_express.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Personal E-Mail</td>
+ <td><a href="personal_e_mail.html">/personal_e_mail.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fpersonal_e_mail.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Philosophy</td>
+ <td><a href="philosophy.html">/philosophy.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fphilosophy.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Pidgin Tutorial</td>
+ <td><a href="pidgin.html">/pidgin.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fpidgin.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Questions</td>
+ <td><a href="questions.html">/questions.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fquestions.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Report Abuse</td>
+ <td><a href="report_abuse.html">report_abuse.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=https%3A%2F%2F$Provider.com%2Freport_abuse.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Secure</td>
+ <td><a href="secure.html">/secure.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fsecure.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Services</td>
+ <td><a href="services.html">/services.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fservices.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Settings</td>
+ <td><a href="settings.html">/settings.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fsettings.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Sign Up</td>
+ <td><a href="register">register</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=https%3A%2F%2F$Provider.com%2Fapps%2Fregister">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Site Map</td>
+ <td><a href="site_map.html">/site_map.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fsite_map.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Testimonials</td>
+ <td><a href="testimonials.html">/testimonials.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Ftestimonials.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Thunderbird Tutorial</td>
+ <td><a href="thunderbird.html">/thunderbird.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fthunderbird.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Trillian Tutorial</td>
+ <td><a href="trillian.html">/trillian.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Ftrillian.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Troubleshooting</td>
+ <td><a href="troubleshooting.html">/troubleshooting.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Ftroubleshooting.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Tutorials</td>
+ <td><a href="tutorials.html">/tutorials.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Ftutorials.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>WAI AAA</td>
+ <td><a href="wai_aaa.html">/wai_aaa.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fwai_aaa.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>Windows Live Mail Tutorial</td>
+ <td><a href="windows_live_mail.html">/windows_live_mail.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fwindows_live_mail.html">Validate</a></td>
+ </tr>
+ <tr>
+ <td>XHTML Valid</td>
+ <td><a href="xhtml_valid.html">/xhtml_valid.html</a></td>
+ <td><a href="http://validator.w3.org/check?verbose=1&amp;uri=http%3A%2F%2F$Provider.com%2Fxhtml_valid.html">Validate</a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div id="footer">
+ <ul>
+ <li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
+ <li><a href="report_abuse.html">Report Abuse</a></li>
+ <li id="last"><a href="site_map.html">Site Map</a></li>
+ <li><a href="credits.html">Credits</a></li>
+ </ul>
+ <p id="copyright">&copy; Lavabit LLC. All rights reserved.</p>
+ <p id="badges"><a href="xhtml_valid.html"><img src="images/xhtml_strict_grey.gif" onmouseover="ChangeImage(this, '/images/xhtml_strict_red.gif')" onmouseout="ChangeImage(this, '/images/xhtml_strict_grey.gif')" alt="XHTML Strict" /></a> <a href="css_valid.html"><img src="images/css_valid_grey.gif" onmouseover="ChangeImage(this, '/images/css_valid_red.gif')" onmouseout="ChangeImage(this, '/images/css_valid_grey.gif')" alt="CSS Valid" /></a> <a href="wai_aaa.html"><img src="images/wai_aaa_grey.gif" onmouseover="ChangeImage(this, '/images/wai_aaa_red.gif')" onmouseout="ChangeImage(this, '/images/wai_aaa_grey.gif')" alt="WAI AAA Conformance" /></a></p>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/res/templates/register/message.template b/res/templates/register/message.template
index 8002148f..810961fd 100644
--- a/res/templates/register/message.template
+++ b/res/templates/register/message.template
@@ -2,10 +2,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <title>Lavabit ..::.. Sign Up</title>
+ <title>$Provider ..::.. Sign Up</title>
<meta name="author" content="Ladar Levison" />
<meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
- <meta name="description" lang="en" content="Lavabit is a premier POP3 e-mail provider with free and premium accounts. This page is step three of three in the Lavabit registration process. This page indicates that your registration is complete." />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page is step three of three in the $Provider registration process. This page indicates that your registration is complete." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-store" />
@@ -15,7 +15,7 @@
<body>
<div id="page">
<div id="header">
- <h1>Lavabit</h1>
+ <h1>$Provider</h1>
<div>
<ul id="nav_col_1">
<li class="skip"><a href="#index">Skip Primary Navigation</a></li>
@@ -31,10 +31,10 @@
<li><a id="nav_help" href="/help.html">Help</a></li>
</ul>
<ul id="nav_col_3">
- <li><a id="nav_contact" href="https://lavabit.com/contact">Contact Information</a></li>
- <li><a id="nav_preferences" href="https://lavabit.com/apps/preferences">Preferences</a></li>
- <li><a id="nav_webmail" href="https://lavabit.com/portal">Webmail</a></li>
- <li><a id="nav_register" class="active" href="https://lavabit.com/register">Sign&nbsp;Up</a></li>
+ <li><a id="nav_contact" href="https://$Provider.com/contact">Contact Information</a></li>
+ <li><a id="nav_preferences" href="https://$Provider.com/apps/preferences">Preferences</a></li>
+ <li><a id="nav_webmail" href="https://$Provider.com/portal">Webmail</a></li>
+ <li><a id="nav_register" class="active" href="https://$Provider.com/register">Sign&nbsp;Up</a></li>
</ul>
</div>
</div>
@@ -48,7 +48,7 @@
<li><a href="/privacy_policy.html">Privacy Policy</a></li>
<li><a href="/advertising_policy.html">Advertising Policy</a></li>
<li><a href="/abuse_policy.html">Abuse Policy</a></li>
- <li><a href="https://lavabit.com/report_abuse.html">Report Abuse</a></li>
+ <li><a href="https://$Provider.com/report_abuse.html">Report Abuse</a></li>
<li id="last"><a href="/site_map.html">Site Map</a></li>
<li><a href="/credits.html">Credits</a></li>
</ul>
diff --git a/res/templates/register/step1.template b/res/templates/register/step1.template
index d0676be5..bf3acbd6 100644
--- a/res/templates/register/step1.template
+++ b/res/templates/register/step1.template
@@ -2,10 +2,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <title>Lavabit ..::.. Sign Up - Step One</title>
+ <title>$Provider ..::.. Sign Up - Step One</title>
<meta name="Author" content="Ladar Levison" />
<meta name="Keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
- <meta name="Description" lang="en" content="Lavabit is a premier POP3 e-mail provider with free and premium accounts. This page is step one of three in the Lavabit registration process. Use this page to select basic account information such as your username and password." />
+ <meta name="Description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page is step one of three in the $Provider registration process. Use this page to select basic account information such as your username and password." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-store" />
@@ -17,37 +17,33 @@
<body>
<div id="page">
<div id="header">
- <h1>Lavabit</h1>
+ <h1>$Provider</h1>
<div>
<ul id="nav_col_1">
- <li class="skip"><a href="#index">Skip Primary Navigation</a></li>
- <li><a id="nav_home" class="active" href="/index.html">Home</a></li>
+ <li class="skip"><a href="#reg_step1">Skip Primary Navigation</a></li>
+ <li><a id="nav_home" href="/index.html">Home</a></li>
<li><a id="nav_about" href="/about.html">About</a></li>
<li><a id="nav_features" href="/features.html">Features</a></li>
- <li><a id="nav_services" href="/services.html">Services</a></li>
+
</ul>
<ul id="nav_col_2">
- <li><a id="nav_philosophy" href="/philosophy.html">Philosophy</a></li>
- <li><a id="nav_testimonials" href="/testimonials.html">Testimonials</a></li>
- <li><a id="nav_network" href="/network.html">Network</a></li>
- <li><a id="nav_help" href="/help.html">Help</a></li>
+ <li><a id="nav_help" href="help.html">Help</a></li>
</ul>
<ul id="nav_col_3">
- <li><a id="nav_contact" href="https://lavabit.com/contact">Contact Information</a></li>
- <li><a id="nav_preferences" href="https://lavabit.com/apps/preferences">Preferences</a></li>
- <li><a id="nav_webmail" href="https://lavabit.com/portal">Webmail</a></li>
- <li><a id="nav_register" class="active" href="https://lavabit.com/register">Sign&nbsp;Up</a></li>
+ <li><a id="nav_contact" href="contact">Contact</a></li>
+ <li><a id="nav_webmail" href="webmail/">Webmail</a></li>
+ <li><a id="nav_register" class="active" href="register">Sign&nbsp;Up</a></li>
</ul>
</div>
</div>
<div id="reg_step1" class="content">
<div id="reg_col_1">
<h2>Sign Up</h2>
- <p>Welcome to the Lavabit sign up page. With a few mouse clicks, you&rsquo;ll be enjoying your new Lavabit e-mail account. Please start by filling out the form and pressing the <q>Next</q> button.</p>$ERROR$
+ <p>Welcome to the $Provider sign up page. With a few mouse clicks, you&rsquo;ll be enjoying your new $Provider e-mail account. Please start by filling out the form and pressing the <q>Next</q> button.</p>$ERROR$
</div>
<form id="step1" method="post" action="/register">
<table>
- <caption>Step one of three in the Lavabit registration process.</caption>
+ <caption>Step one of three in the $Provider registration process.</caption>
<tbody>
<tr>
<th><label for="username">Username:</label></th>
@@ -69,8 +65,8 @@
<td colspan="2"><img src="/register?captcha=$SESSION$" alt="To prevent automated programs from registering accounts we make our new users enter the characters they see in this image. If you have trouble deciphering the image or have a disability, please use our contact form to request a new account." /></td>
</tr>
</tbody>
- </table>
- <p>By submitting this form you acknowledge that you have read and agree to the Lavabit <a href="/terms_of_use.html">Terms of Use</a>, <a href="/privacy_policy.html">Privacy Policy</a> and <a href="/abuse_policy.html">Abuse Policy</a>.</p>
+ </table>
+ <p>By submitting this form you acknowledge that you have read and agree to the $Provider Terms of Use, Privacy Policy and Abuse Policy.</p>
<div id="button">
<input id="session" name="session" type="hidden" value="$SESSION$" />
<button id="next1" name="next1" type="submit" title="Use this button to submit the form."><strong>Next &gt;&gt;</strong></button>
@@ -85,7 +81,7 @@
<li><a href="/privacy_policy.html">Privacy Policy</a></li>
<li><a href="/advertising_policy.html">Advertising Policy</a></li>
<li><a href="/abuse_policy.html">Abuse Policy</a></li>
- <li><a href="https://lavabit.com/report_abuse.html">Report Abuse</a></li>
+ <li><a href="https://$Provider.com/report_abuse.html">Report Abuse</a></li>
<li id="last"><a href="/site_map.html">Site Map</a></li>
<li><a href="/credits.html">Credits</a></li>
</ul>
diff --git a/res/templates/register/step2.template b/res/templates/register/step2.template
index b8c0938f..a018c49f 100644
--- a/res/templates/register/step2.template
+++ b/res/templates/register/step2.template
@@ -2,10 +2,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <title>Lavabit ..::.. Sign Up - Step Two</title>
+ <title>$Provider ..::.. Sign Up - Step Two</title>
<meta name="author" content="Ladar Levison" />
<meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
- <meta name="description" lang="en" content="Lavabit is a premier POP3 e-mail provider with free and premium accounts. This page is step two of three in the Lavabit registration process. Use this page to select your Lavabit account plan." />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page is step two of three in the $Provider registration process. Use this page to select your $Provider account plan." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-store" />
@@ -18,7 +18,7 @@
<body>
<div id="page">
<div id="header">
- <h1>Lavabit</h1>
+ <h1>$Provider</h1>
<div>
<ul id="nav_col_1">
<li class="skip"><a href="#index">Skip Primary Navigation</a></li>
@@ -34,10 +34,10 @@
<li><a id="nav_help" href="/help.html">Help</a></li>
</ul>
<ul id="nav_col_3">
- <li><a id="nav_contact" href="https://lavabit.com/contact">Contact Information</a></li>
- <li><a id="nav_preferences" href="https://lavabit.com/apps/preferences">Preferences</a></li>
- <li><a id="nav_webmail" href="https://lavabit.com/portal">Webmail</a></li>
- <li><a id="nav_register" class="active" href="https://lavabit.com/register">Sign&nbsp;Up</a></li>
+ <li><a id="nav_contact" href="https://$Provider.com/contact">Contact Information</a></li>
+ <li><a id="nav_preferences" href="https://$Provider.com/apps/preferences">Preferences</a></li>
+ <li><a id="nav_webmail" href="https://$Provider.com/portal">Webmail</a></li>
+ <li><a id="nav_register" class="active" href="https://$Provider.com/register">Sign&nbsp;Up</a></li>
</ul>
</div>
</div>
@@ -56,7 +56,7 @@
<ul>
<li class="skip"><a href="#copyright">Skip Footer Navigation</a></li>
<li><a href="/terms_of_use.html">Terms of Use</a></li>
- <li><a href="https://lavabit.com/report_abuse.html">Report Abuse</a></li>
+ <li><a href="https://$Provider.com/report_abuse.html">Report Abuse</a></li>
<li id="last"><a href="/site_map.html">Site Map</a></li>
<li><a href="/credits.html">Credits</a></li>
</ul>
diff --git a/res/templates/register/step3.template b/res/templates/register/step3.template
index dfc1cfa8..5becd40d 100644
--- a/res/templates/register/step3.template
+++ b/res/templates/register/step3.template
@@ -2,10 +2,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
- <title>Lavabit ..::.. Sign Up - Complete</title>
+ <title>$Provider ..::.. Sign Up - Complete</title>
<meta name="author" content="Ladar Levison" />
<meta name="keywords" lang="en" content="email free pop imap webmail spam secure private privacy" />
- <meta name="description" lang="en" content="Lavabit is a premier POP3 e-mail provider with free and premium accounts. This page is step three of three in the Lavabit registration process. This page indicates that your registration is complete." />
+ <meta name="description" lang="en" content="$Provider is a premier POP3 e-mail provider with free and premium accounts. This page is step three of three in the $Provider registration process. This page indicates that your registration is complete." />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-store" />
@@ -15,7 +15,7 @@
<body>
<div id="page">
<div id="header">
- <h1>Lavabit</h1>
+ <h1>$Provider</h1>
<div>
<ul id="nav_col_1">
<li class="skip"><a href="#index">Skip Primary Navigation</a></li>
@@ -31,19 +31,19 @@
<li><a id="nav_help" href="/help.html">Help</a></li>
</ul>
<ul id="nav_col_3">
- <li><a id="nav_contact" href="https://lavabit.com/contact">Contact Information</a></li>
- <li><a id="nav_preferences" href="https://lavabit.com/apps/preferences">Preferences</a></li>
- <li><a id="nav_webmail" href="https://lavabit.com/portal">Webmail</a></li>
- <li><a id="nav_register" class="active" href="https://lavabit.com/register">Sign&nbsp;Up</a></li>
+ <li><a id="nav_contact" href="https://$Provider.com/contact">Contact Information</a></li>
+ <li><a id="nav_preferences" href="https://$Provider.com/apps/preferences">Preferences</a></li>
+ <li><a id="nav_webmail" href="https://$Provider.com/portal">Webmail</a></li>
+ <li><a id="nav_register" class="active" href="https://$Provider.com/register">Sign&nbsp;Up</a></li>
</ul>
</div>
</div>
<div id="reg_step3" class="content">
<p>Your account has been created successfully! You can access your account using any standards-compliant <acronym title="Post Office Protocol Version 3">POP3</acronym> e-mail client. We recommend Mozilla Thunderbird as a free alternative. Other options include Microsoft&rsquo;s Outlook Express and Outlook.</p>
- <p>Configure your e-mail client to use <q><strong>lavabit.com</strong></q> as both your incoming and outgoing e-mail server. Don&rsquo;t forget to <strong>enable authentication</strong> for both incoming and outgoing messages. Our servers don&rsquo;t allow unauthenticated relaying. We accept <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> connections on ports 25, 2525 and 3535. We support <acronym title="Post Office Protocol Version 3">POP3</acronym> connections on port 110. We also support <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> over <acronym title="Secure Sockets Layer">SSL</acronym> on port 465 and <acronym title="Post Office Protocol Version 3">POP3</acronym> over <acronym title="Secure Sockets Layer">SSL</acronym> using port 995.</p>
- <p>If you have difficulty configuring your e-mail client please consult the <a href="/help.html">help</a> section of our website. If you&rsquo;re still having difficulty, please use our <a href="https://lavabit.com/contact">contact page</a> for assistance.</p>
+ <p>Configure your e-mail client to use <q><strong>$Provider.com</strong></q> as both your incoming and outgoing e-mail server. Don&rsquo;t forget to <strong>enable authentication</strong> for both incoming and outgoing messages. Our servers don&rsquo;t allow unauthenticated relaying. We accept <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> connections on ports 25, 2525 and 3535. We support <acronym title="Post Office Protocol Version 3">POP3</acronym> connections on port 110. We also support <acronym title="Simple Mail Transfer Protocol">SMTP</acronym> over <acronym title="Secure Sockets Layer">SSL</acronym> on port 465 and <acronym title="Post Office Protocol Version 3">POP3</acronym> over <acronym title="Secure Sockets Layer">SSL</acronym> using port 995.</p>
+ <p>If you have difficulty configuring your e-mail client please consult the <a href="/help.html">help</a> section of our website. If you&rsquo;re still having difficulty, please use our <a href="https://$Provider.com/contact">contact page</a> for assistance.</p>
<p>Please be sure to remember your password. <strong>If you forget your password, we can&rsquo;t change it for you.</strong> Also be sure to check your e-mail every 120 days, or your account may be locked and eventually deleted.</p>
- <p>Welcome to Lavabit! Please use our services responsibly.</p>
+ <p>Welcome to $Provider! Please use our services responsibly.</p>
</div>
<div id="footer">
<ul>
@@ -52,7 +52,7 @@
<li><a href="/privacy_policy.html">Privacy Policy</a></li>
<li><a href="/advertising_policy.html">Advertising Policy</a></li>
<li><a href="/abuse_policy.html">Abuse Policy</a></li>
- <li><a href="https://lavabit.com/report_abuse.html">Report Abuse</a></li>
+ <li><a href="https://$Provider.com/report_abuse.html">Report Abuse</a></li>
<li id="last"><a href="/site_map.html">Site Map</a></li>
<li><a href="/credits.html">Credits</a></li>
</ul>