From e107ad4ea932d963aea5dcbd2fac901c920f8ed2 Mon Sep 17 00:00:00 2001 From: "jhawkins@chromium.org" Date: Sat, 18 Aug 2012 23:29:35 +0000 Subject: Options: Rename chrome/browser/resources/options2 -> chrome/browser/resources/options. BUG=none TEST=none R=csilv Review URL: https://chromiumcodereview.appspot.com/10809005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152267 0039d316-1c4b-4281-b951-d872f2087c98 --- .../browser/resources/chromeos/proxy_settings.html | 16 +- chrome/browser/resources/options/OWNERS | 4 + chrome/browser/resources/options/alert_overlay.css | 7 + .../browser/resources/options/alert_overlay.html | 13 + chrome/browser/resources/options/alert_overlay.js | 152 +++ .../options/autofill_edit_address_overlay.html | 100 ++ .../options/autofill_edit_address_overlay.js | 325 +++++ .../options/autofill_edit_creditcard_overlay.html | 31 + .../options/autofill_edit_creditcard_overlay.js | 205 +++ .../resources/options/autofill_edit_overlay.css | 101 ++ .../browser/resources/options/autofill_options.css | 52 + .../resources/options/autofill_options.html | 39 + .../browser/resources/options/autofill_options.js | 233 ++++ .../resources/options/autofill_options_list.js | 506 ++++++++ .../browser/resources/options/browser_options.css | 425 ++++++ .../browser/resources/options/browser_options.html | 541 ++++++++ .../browser/resources/options/browser_options.js | 1350 ++++++++++++++++++++ .../options/browser_options_profile_list.js | 105 ++ .../options/browser_options_startup_page_list.js | 310 +++++ .../options/certificate_backup_overlay.html | 39 + .../options/certificate_backup_overlay.js | 117 ++ .../options/certificate_edit_ca_trust_overlay.html | 38 + .../options/certificate_edit_ca_trust_overlay.js | 164 +++ .../options/certificate_import_error_overlay.html | 14 + .../options/certificate_import_error_overlay.js | 68 + .../resources/options/certificate_manager.css | 32 + .../resources/options/certificate_manager.html | 142 ++ .../resources/options/certificate_manager.js | 255 ++++ .../options/certificate_restore_overlay.html | 18 + .../options/certificate_restore_overlay.js | 101 ++ .../browser/resources/options/certificate_tree.css | 12 + .../browser/resources/options/certificate_tree.js | 154 +++ chrome/browser/resources/options/chromeos/OWNERS | 6 + .../options/chromeos/accounts_options.html | 67 + .../resources/options/chromeos/accounts_options.js | 156 +++ .../options/chromeos/accounts_options_page.css | 97 ++ .../options/chromeos/accounts_user_list.js | 194 +++ .../options/chromeos/accounts_user_name_edit.js | 130 ++ .../resources/options/chromeos/bluetooth.css | 152 +++ .../chromeos/bluetooth_add_device_overlay.html | 23 + .../chromeos/bluetooth_add_device_overlay.js | 91 ++ .../options/chromeos/bluetooth_device_list.js | 338 +++++ .../chromeos/bluetooth_pair_device_overlay.html | 28 + .../chromeos/bluetooth_pair_device_overlay.js | 367 ++++++ .../options/chromeos/cellular_plan_element.js | 132 ++ .../options/chromeos/change_picture_options.css | 193 +++ .../options/chromeos/change_picture_options.html | 32 + .../options/chromeos/change_picture_options.js | 506 ++++++++ .../resources/options/chromeos/display_options.css | 103 ++ .../options/chromeos/display_options.html | 33 + .../resources/options/chromeos/display_options.js | 454 +++++++ .../resources/options/chromeos/internet_detail.css | 83 ++ .../options/chromeos/internet_detail.html | 667 ++++++++++ .../resources/options/chromeos/internet_detail.js | 1018 +++++++++++++++ .../chromeos/internet_detail_ip_address_field.js | 111 ++ .../options/chromeos/keyboard_overlay.html | 52 + .../resources/options/chromeos/keyboard_overlay.js | 28 + .../options/chromeos/language_chewing_options.html | 147 +++ .../options/chromeos/language_hangul_options.html | 24 + .../options/chromeos/language_mozc_options.html | 141 ++ .../options/chromeos/language_pinyin_options.html | 154 +++ .../resources/options/chromeos/network_list.js | 1098 ++++++++++++++++ .../resources/options/chromeos/pointer_overlay.css | 7 + .../options/chromeos/pointer_overlay.html | 41 + .../resources/options/chromeos/pointer_overlay.js | 81 ++ .../options/chromeos/preferred_networks.html | 16 + .../options/chromeos/preferred_networks.js | 165 +++ .../resources/options/chromeos/proxy_rules_list.js | 139 ++ .../options/chromeos/set_wallpaper_options.css | 51 + .../options/chromeos/set_wallpaper_options.html | 33 + .../options/chromeos/set_wallpaper_options.js | 324 +++++ .../options/clear_browser_data_overlay.css | 22 + .../options/clear_browser_data_overlay.html | 92 ++ .../options/clear_browser_data_overlay.js | 116 ++ .../browser/resources/options/content_settings.css | 84 ++ .../resources/options/content_settings.html | 356 ++++++ .../browser/resources/options/content_settings.js | 179 +++ .../options/content_settings_exceptions_area.html | 99 ++ .../options/content_settings_exceptions_area.js | 590 +++++++++ .../resources/options/content_settings_ui.js | 67 + .../resources/options/controlled_setting.js | 138 ++ chrome/browser/resources/options/cookies_list.js | 903 +++++++++++++ chrome/browser/resources/options/cookies_view.css | 212 +++ chrome/browser/resources/options/cookies_view.html | 26 + chrome/browser/resources/options/cookies_view.js | 144 +++ .../resources/options/cookies_view_app.html | 26 + .../browser/resources/options/cookies_view_app.js | 48 + .../resources/options/deletable_item_list.js | 193 +++ .../resources/options/editable_text_field.js | 375 ++++++ chrome/browser/resources/options/font_settings.css | 49 + .../browser/resources/options/font_settings.html | 106 ++ chrome/browser/resources/options/font_settings.js | 236 ++++ .../browser/resources/options/handler_options.css | 55 + .../browser/resources/options/handler_options.html | 36 + .../browser/resources/options/handler_options.js | 80 ++ .../resources/options/handler_options_list.js | 227 ++++ .../resources/options/home_page_overlay.css | 9 + .../resources/options/home_page_overlay.html | 32 + .../browser/resources/options/home_page_overlay.js | 157 +++ .../resources/options/import_data_overlay.css | 27 + .../resources/options/import_data_overlay.html | 71 + .../resources/options/import_data_overlay.js | 247 ++++ .../resources/options/inline_editable_list.js | 415 ++++++ .../resources/options/instant_confirm_overlay.css | 16 + .../resources/options/instant_confirm_overlay.html | 17 + .../resources/options/instant_confirm_overlay.js | 50 + .../options/language_add_language_overlay.html | 26 + .../options/language_add_language_overlay.js | 73 ++ chrome/browser/resources/options/language_list.js | 468 +++++++ .../browser/resources/options/language_options.css | 215 ++++ .../resources/options/language_options.html | 103 ++ .../browser/resources/options/language_options.js | 860 +++++++++++++ .../resources/options/manage_profile_overlay.css | 147 +++ .../resources/options/manage_profile_overlay.html | 92 ++ .../resources/options/manage_profile_overlay.js | 317 +++++ .../resources/options/media_galleries_list.js | 59 + .../options/media_galleries_manager_overlay.html | 17 + .../options/media_galleries_manager_overlay.js | 82 ++ chrome/browser/resources/options/options.html | 164 +++ chrome/browser/resources/options/options.js | 236 ++++ chrome/browser/resources/options/options_bundle.js | 100 ++ .../resources/options/options_focus_manager.js | 34 + chrome/browser/resources/options/options_page.css | 584 +++++++++ chrome/browser/resources/options/options_page.js | 908 +++++++++++++ .../browser/resources/options/password_manager.css | 28 + .../resources/options/password_manager.html | 35 + .../browser/resources/options/password_manager.js | 230 ++++ .../resources/options/password_manager_list.css | 58 + .../resources/options/password_manager_list.js | 297 +++++ chrome/browser/resources/options/pref_ui.js | 846 ++++++++++++ chrome/browser/resources/options/preferences.js | 185 +++ .../resources/options/profiles_icon_grid.js | 68 + chrome/browser/resources/options/search_box.html | 10 + .../resources/options/search_engine_manager.css | 78 ++ .../resources/options/search_engine_manager.html | 25 + .../resources/options/search_engine_manager.js | 129 ++ .../options/search_engine_manager_engine_list.js | 315 +++++ chrome/browser/resources/options/search_page.css | 74 ++ chrome/browser/resources/options/search_page.html | 12 + chrome/browser/resources/options/search_page.js | 572 +++++++++ .../browser/resources/options/settings_dialog.js | 70 + .../resources/options/spelling_confirm_overlay.css | 8 + .../options/spelling_confirm_overlay.html | 18 + .../resources/options/spelling_confirm_overlay.js | 50 + .../browser/resources/options/startup_overlay.css | 41 + .../browser/resources/options/startup_overlay.html | 23 + .../browser/resources/options/startup_overlay.js | 174 +++ .../browser/resources/options/startup_section.html | 36 + .../resources/options/subpages_tab_controls.css | 74 ++ chrome/browser/resources/options/sync_section.html | 62 + chrome/browser/resources/options2/OWNERS | 4 - .../browser/resources/options2/alert_overlay.css | 7 - .../browser/resources/options2/alert_overlay.html | 13 - chrome/browser/resources/options2/alert_overlay.js | 152 --- .../options2/autofill_edit_address_overlay.html | 100 -- .../options2/autofill_edit_address_overlay.js | 325 ----- .../options2/autofill_edit_creditcard_overlay.html | 31 - .../options2/autofill_edit_creditcard_overlay.js | 205 --- .../resources/options2/autofill_edit_overlay.css | 101 -- .../resources/options2/autofill_options.css | 52 - .../resources/options2/autofill_options.html | 39 - .../browser/resources/options2/autofill_options.js | 233 ---- .../resources/options2/autofill_options_list.js | 506 -------- .../browser/resources/options2/browser_options.css | 425 ------ .../resources/options2/browser_options.html | 541 -------- .../browser/resources/options2/browser_options.js | 1350 -------------------- .../options2/browser_options_profile_list.js | 105 -- .../options2/browser_options_startup_page_list.js | 310 ----- .../options2/certificate_backup_overlay.html | 39 - .../options2/certificate_backup_overlay.js | 117 -- .../certificate_edit_ca_trust_overlay.html | 38 - .../options2/certificate_edit_ca_trust_overlay.js | 164 --- .../options2/certificate_import_error_overlay.html | 14 - .../options2/certificate_import_error_overlay.js | 68 - .../resources/options2/certificate_manager.css | 32 - .../resources/options2/certificate_manager.html | 142 -- .../resources/options2/certificate_manager.js | 255 ---- .../options2/certificate_restore_overlay.html | 18 - .../options2/certificate_restore_overlay.js | 101 -- .../resources/options2/certificate_tree.css | 12 - .../browser/resources/options2/certificate_tree.js | 154 --- chrome/browser/resources/options2/chromeos/OWNERS | 6 - .../options2/chromeos/accounts_options.html | 67 - .../options2/chromeos/accounts_options.js | 156 --- .../options2/chromeos/accounts_options_page.css | 97 -- .../options2/chromeos/accounts_user_list.js | 194 --- .../options2/chromeos/accounts_user_name_edit.js | 130 -- .../resources/options2/chromeos/bluetooth.css | 152 --- .../chromeos/bluetooth_add_device_overlay.html | 23 - .../chromeos/bluetooth_add_device_overlay.js | 91 -- .../options2/chromeos/bluetooth_device_list.js | 338 ----- .../chromeos/bluetooth_pair_device_overlay.html | 28 - .../chromeos/bluetooth_pair_device_overlay.js | 367 ------ .../options2/chromeos/cellular_plan_element.js | 132 -- .../options2/chromeos/change_picture_options.css | 193 --- .../options2/chromeos/change_picture_options.html | 32 - .../options2/chromeos/change_picture_options.js | 506 -------- .../options2/chromeos/display_options.css | 103 -- .../options2/chromeos/display_options.html | 33 - .../resources/options2/chromeos/display_options.js | 454 ------- .../options2/chromeos/internet_detail.css | 83 -- .../options2/chromeos/internet_detail.html | 667 ---------- .../resources/options2/chromeos/internet_detail.js | 1018 --------------- .../chromeos/internet_detail_ip_address_field.js | 111 -- .../options2/chromeos/keyboard_overlay.html | 52 - .../options2/chromeos/keyboard_overlay.js | 28 - .../chromeos/language_chewing_options.html | 147 --- .../options2/chromeos/language_hangul_options.html | 24 - .../options2/chromeos/language_mozc_options.html | 141 -- .../options2/chromeos/language_pinyin_options.html | 154 --- .../resources/options2/chromeos/network_list.js | 1098 ---------------- .../options2/chromeos/pointer_overlay.css | 7 - .../options2/chromeos/pointer_overlay.html | 41 - .../resources/options2/chromeos/pointer_overlay.js | 81 -- .../options2/chromeos/preferred_networks.html | 16 - .../options2/chromeos/preferred_networks.js | 165 --- .../options2/chromeos/proxy_rules_list.js | 139 -- .../options2/chromeos/set_wallpaper_options.css | 51 - .../options2/chromeos/set_wallpaper_options.html | 33 - .../options2/chromeos/set_wallpaper_options.js | 324 ----- .../options2/clear_browser_data_overlay.css | 22 - .../options2/clear_browser_data_overlay.html | 92 -- .../options2/clear_browser_data_overlay.js | 116 -- .../resources/options2/content_settings.css | 84 -- .../resources/options2/content_settings.html | 356 ------ .../browser/resources/options2/content_settings.js | 179 --- .../options2/content_settings_exceptions_area.html | 99 -- .../options2/content_settings_exceptions_area.js | 590 --------- .../resources/options2/content_settings_ui.js | 67 - .../resources/options2/controlled_setting.js | 138 -- chrome/browser/resources/options2/cookies_list.js | 903 ------------- chrome/browser/resources/options2/cookies_view.css | 212 --- .../browser/resources/options2/cookies_view.html | 26 - chrome/browser/resources/options2/cookies_view.js | 144 --- .../resources/options2/cookies_view_app.html | 26 - .../browser/resources/options2/cookies_view_app.js | 48 - .../resources/options2/deletable_item_list.js | 193 --- .../resources/options2/editable_text_field.js | 375 ------ .../browser/resources/options2/font_settings.css | 49 - .../browser/resources/options2/font_settings.html | 106 -- chrome/browser/resources/options2/font_settings.js | 236 ---- .../browser/resources/options2/handler_options.css | 55 - .../resources/options2/handler_options.html | 36 - .../browser/resources/options2/handler_options.js | 80 -- .../resources/options2/handler_options_list.js | 227 ---- .../resources/options2/home_page_overlay.css | 9 - .../resources/options2/home_page_overlay.html | 32 - .../resources/options2/home_page_overlay.js | 157 --- .../resources/options2/import_data_overlay.css | 27 - .../resources/options2/import_data_overlay.html | 71 - .../resources/options2/import_data_overlay.js | 247 ---- .../resources/options2/inline_editable_list.js | 415 ------ .../resources/options2/instant_confirm_overlay.css | 16 - .../options2/instant_confirm_overlay.html | 17 - .../resources/options2/instant_confirm_overlay.js | 50 - .../options2/language_add_language_overlay.html | 26 - .../options2/language_add_language_overlay.js | 73 -- chrome/browser/resources/options2/language_list.js | 468 ------- .../resources/options2/language_options.css | 215 ---- .../resources/options2/language_options.html | 103 -- .../browser/resources/options2/language_options.js | 860 ------------- .../resources/options2/manage_profile_overlay.css | 147 --- .../resources/options2/manage_profile_overlay.html | 92 -- .../resources/options2/manage_profile_overlay.js | 317 ----- .../resources/options2/media_galleries_list.js | 59 - .../options2/media_galleries_manager_overlay.html | 17 - .../options2/media_galleries_manager_overlay.js | 82 -- chrome/browser/resources/options2/options.html | 164 --- chrome/browser/resources/options2/options.js | 236 ---- .../browser/resources/options2/options_bundle.js | 100 -- .../resources/options2/options_focus_manager.js | 34 - chrome/browser/resources/options2/options_page.css | 584 --------- chrome/browser/resources/options2/options_page.js | 908 ------------- .../resources/options2/password_manager.css | 28 - .../resources/options2/password_manager.html | 35 - .../browser/resources/options2/password_manager.js | 230 ---- .../resources/options2/password_manager_list.css | 58 - .../resources/options2/password_manager_list.js | 297 ----- chrome/browser/resources/options2/pref_ui.js | 846 ------------ chrome/browser/resources/options2/preferences.js | 185 --- .../resources/options2/profiles_icon_grid.js | 68 - chrome/browser/resources/options2/search_box.html | 10 - .../resources/options2/search_engine_manager.css | 78 -- .../resources/options2/search_engine_manager.html | 25 - .../resources/options2/search_engine_manager.js | 129 -- .../options2/search_engine_manager_engine_list.js | 315 ----- chrome/browser/resources/options2/search_page.css | 74 -- chrome/browser/resources/options2/search_page.html | 12 - chrome/browser/resources/options2/search_page.js | 572 --------- .../browser/resources/options2/settings_dialog.js | 70 - .../options2/spelling_confirm_overlay.css | 8 - .../options2/spelling_confirm_overlay.html | 18 - .../resources/options2/spelling_confirm_overlay.js | 50 - .../browser/resources/options2/startup_overlay.css | 41 - .../resources/options2/startup_overlay.html | 23 - .../browser/resources/options2/startup_overlay.js | 174 --- .../resources/options2/startup_section.html | 36 - .../resources/options2/subpages_tab_controls.css | 74 -- .../browser/resources/options2/sync_section.html | 62 - chrome/browser/resources/options2_resources.grd | 15 - chrome/browser/resources/options_resources.grd | 15 + chrome/browser/resources/sync_promo/sync_promo.js | 2 +- .../browser/resources/web_dev_style/js_checker.py | 2 +- chrome/browser/ui/webui/options2/options_ui.cc | 6 +- chrome/chrome_resources.gyp | 4 +- tools/gritsettings/resource_ids | 2 +- 306 files changed, 26669 insertions(+), 26669 deletions(-) create mode 100644 chrome/browser/resources/options/OWNERS create mode 100644 chrome/browser/resources/options/alert_overlay.css create mode 100644 chrome/browser/resources/options/alert_overlay.html create mode 100644 chrome/browser/resources/options/alert_overlay.js create mode 100644 chrome/browser/resources/options/autofill_edit_address_overlay.html create mode 100644 chrome/browser/resources/options/autofill_edit_address_overlay.js create mode 100644 chrome/browser/resources/options/autofill_edit_creditcard_overlay.html create mode 100644 chrome/browser/resources/options/autofill_edit_creditcard_overlay.js create mode 100644 chrome/browser/resources/options/autofill_edit_overlay.css create mode 100644 chrome/browser/resources/options/autofill_options.css create mode 100644 chrome/browser/resources/options/autofill_options.html create mode 100644 chrome/browser/resources/options/autofill_options.js create mode 100644 chrome/browser/resources/options/autofill_options_list.js create mode 100644 chrome/browser/resources/options/browser_options.css create mode 100644 chrome/browser/resources/options/browser_options.html create mode 100644 chrome/browser/resources/options/browser_options.js create mode 100644 chrome/browser/resources/options/browser_options_profile_list.js create mode 100644 chrome/browser/resources/options/browser_options_startup_page_list.js create mode 100644 chrome/browser/resources/options/certificate_backup_overlay.html create mode 100644 chrome/browser/resources/options/certificate_backup_overlay.js create mode 100644 chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html create mode 100644 chrome/browser/resources/options/certificate_edit_ca_trust_overlay.js create mode 100644 chrome/browser/resources/options/certificate_import_error_overlay.html create mode 100644 chrome/browser/resources/options/certificate_import_error_overlay.js create mode 100644 chrome/browser/resources/options/certificate_manager.css create mode 100644 chrome/browser/resources/options/certificate_manager.html create mode 100644 chrome/browser/resources/options/certificate_manager.js create mode 100644 chrome/browser/resources/options/certificate_restore_overlay.html create mode 100644 chrome/browser/resources/options/certificate_restore_overlay.js create mode 100644 chrome/browser/resources/options/certificate_tree.css create mode 100644 chrome/browser/resources/options/certificate_tree.js create mode 100644 chrome/browser/resources/options/chromeos/OWNERS create mode 100644 chrome/browser/resources/options/chromeos/accounts_options.html create mode 100644 chrome/browser/resources/options/chromeos/accounts_options.js create mode 100644 chrome/browser/resources/options/chromeos/accounts_options_page.css create mode 100644 chrome/browser/resources/options/chromeos/accounts_user_list.js create mode 100644 chrome/browser/resources/options/chromeos/accounts_user_name_edit.js create mode 100644 chrome/browser/resources/options/chromeos/bluetooth.css create mode 100644 chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.html create mode 100644 chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.js create mode 100644 chrome/browser/resources/options/chromeos/bluetooth_device_list.js create mode 100644 chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.html create mode 100644 chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.js create mode 100644 chrome/browser/resources/options/chromeos/cellular_plan_element.js create mode 100644 chrome/browser/resources/options/chromeos/change_picture_options.css create mode 100644 chrome/browser/resources/options/chromeos/change_picture_options.html create mode 100644 chrome/browser/resources/options/chromeos/change_picture_options.js create mode 100644 chrome/browser/resources/options/chromeos/display_options.css create mode 100644 chrome/browser/resources/options/chromeos/display_options.html create mode 100644 chrome/browser/resources/options/chromeos/display_options.js create mode 100644 chrome/browser/resources/options/chromeos/internet_detail.css create mode 100644 chrome/browser/resources/options/chromeos/internet_detail.html create mode 100644 chrome/browser/resources/options/chromeos/internet_detail.js create mode 100644 chrome/browser/resources/options/chromeos/internet_detail_ip_address_field.js create mode 100644 chrome/browser/resources/options/chromeos/keyboard_overlay.html create mode 100644 chrome/browser/resources/options/chromeos/keyboard_overlay.js create mode 100644 chrome/browser/resources/options/chromeos/language_chewing_options.html create mode 100644 chrome/browser/resources/options/chromeos/language_hangul_options.html create mode 100644 chrome/browser/resources/options/chromeos/language_mozc_options.html create mode 100644 chrome/browser/resources/options/chromeos/language_pinyin_options.html create mode 100644 chrome/browser/resources/options/chromeos/network_list.js create mode 100644 chrome/browser/resources/options/chromeos/pointer_overlay.css create mode 100644 chrome/browser/resources/options/chromeos/pointer_overlay.html create mode 100644 chrome/browser/resources/options/chromeos/pointer_overlay.js create mode 100644 chrome/browser/resources/options/chromeos/preferred_networks.html create mode 100644 chrome/browser/resources/options/chromeos/preferred_networks.js create mode 100644 chrome/browser/resources/options/chromeos/proxy_rules_list.js create mode 100644 chrome/browser/resources/options/chromeos/set_wallpaper_options.css create mode 100644 chrome/browser/resources/options/chromeos/set_wallpaper_options.html create mode 100644 chrome/browser/resources/options/chromeos/set_wallpaper_options.js create mode 100644 chrome/browser/resources/options/clear_browser_data_overlay.css create mode 100644 chrome/browser/resources/options/clear_browser_data_overlay.html create mode 100644 chrome/browser/resources/options/clear_browser_data_overlay.js create mode 100644 chrome/browser/resources/options/content_settings.css create mode 100644 chrome/browser/resources/options/content_settings.html create mode 100644 chrome/browser/resources/options/content_settings.js create mode 100644 chrome/browser/resources/options/content_settings_exceptions_area.html create mode 100644 chrome/browser/resources/options/content_settings_exceptions_area.js create mode 100644 chrome/browser/resources/options/content_settings_ui.js create mode 100644 chrome/browser/resources/options/controlled_setting.js create mode 100644 chrome/browser/resources/options/cookies_list.js create mode 100644 chrome/browser/resources/options/cookies_view.css create mode 100644 chrome/browser/resources/options/cookies_view.html create mode 100644 chrome/browser/resources/options/cookies_view.js create mode 100644 chrome/browser/resources/options/cookies_view_app.html create mode 100644 chrome/browser/resources/options/cookies_view_app.js create mode 100644 chrome/browser/resources/options/deletable_item_list.js create mode 100644 chrome/browser/resources/options/editable_text_field.js create mode 100644 chrome/browser/resources/options/font_settings.css create mode 100644 chrome/browser/resources/options/font_settings.html create mode 100644 chrome/browser/resources/options/font_settings.js create mode 100644 chrome/browser/resources/options/handler_options.css create mode 100644 chrome/browser/resources/options/handler_options.html create mode 100644 chrome/browser/resources/options/handler_options.js create mode 100644 chrome/browser/resources/options/handler_options_list.js create mode 100644 chrome/browser/resources/options/home_page_overlay.css create mode 100644 chrome/browser/resources/options/home_page_overlay.html create mode 100644 chrome/browser/resources/options/home_page_overlay.js create mode 100644 chrome/browser/resources/options/import_data_overlay.css create mode 100644 chrome/browser/resources/options/import_data_overlay.html create mode 100644 chrome/browser/resources/options/import_data_overlay.js create mode 100644 chrome/browser/resources/options/inline_editable_list.js create mode 100644 chrome/browser/resources/options/instant_confirm_overlay.css create mode 100644 chrome/browser/resources/options/instant_confirm_overlay.html create mode 100644 chrome/browser/resources/options/instant_confirm_overlay.js create mode 100644 chrome/browser/resources/options/language_add_language_overlay.html create mode 100644 chrome/browser/resources/options/language_add_language_overlay.js create mode 100644 chrome/browser/resources/options/language_list.js create mode 100644 chrome/browser/resources/options/language_options.css create mode 100644 chrome/browser/resources/options/language_options.html create mode 100644 chrome/browser/resources/options/language_options.js create mode 100644 chrome/browser/resources/options/manage_profile_overlay.css create mode 100644 chrome/browser/resources/options/manage_profile_overlay.html create mode 100644 chrome/browser/resources/options/manage_profile_overlay.js create mode 100644 chrome/browser/resources/options/media_galleries_list.js create mode 100644 chrome/browser/resources/options/media_galleries_manager_overlay.html create mode 100644 chrome/browser/resources/options/media_galleries_manager_overlay.js create mode 100644 chrome/browser/resources/options/options.html create mode 100644 chrome/browser/resources/options/options.js create mode 100644 chrome/browser/resources/options/options_bundle.js create mode 100644 chrome/browser/resources/options/options_focus_manager.js create mode 100644 chrome/browser/resources/options/options_page.css create mode 100644 chrome/browser/resources/options/options_page.js create mode 100644 chrome/browser/resources/options/password_manager.css create mode 100644 chrome/browser/resources/options/password_manager.html create mode 100644 chrome/browser/resources/options/password_manager.js create mode 100644 chrome/browser/resources/options/password_manager_list.css create mode 100644 chrome/browser/resources/options/password_manager_list.js create mode 100644 chrome/browser/resources/options/pref_ui.js create mode 100644 chrome/browser/resources/options/preferences.js create mode 100644 chrome/browser/resources/options/profiles_icon_grid.js create mode 100644 chrome/browser/resources/options/search_box.html create mode 100644 chrome/browser/resources/options/search_engine_manager.css create mode 100644 chrome/browser/resources/options/search_engine_manager.html create mode 100644 chrome/browser/resources/options/search_engine_manager.js create mode 100644 chrome/browser/resources/options/search_engine_manager_engine_list.js create mode 100644 chrome/browser/resources/options/search_page.css create mode 100644 chrome/browser/resources/options/search_page.html create mode 100644 chrome/browser/resources/options/search_page.js create mode 100644 chrome/browser/resources/options/settings_dialog.js create mode 100644 chrome/browser/resources/options/spelling_confirm_overlay.css create mode 100644 chrome/browser/resources/options/spelling_confirm_overlay.html create mode 100644 chrome/browser/resources/options/spelling_confirm_overlay.js create mode 100644 chrome/browser/resources/options/startup_overlay.css create mode 100644 chrome/browser/resources/options/startup_overlay.html create mode 100644 chrome/browser/resources/options/startup_overlay.js create mode 100644 chrome/browser/resources/options/startup_section.html create mode 100644 chrome/browser/resources/options/subpages_tab_controls.css create mode 100644 chrome/browser/resources/options/sync_section.html delete mode 100644 chrome/browser/resources/options2/OWNERS delete mode 100644 chrome/browser/resources/options2/alert_overlay.css delete mode 100644 chrome/browser/resources/options2/alert_overlay.html delete mode 100644 chrome/browser/resources/options2/alert_overlay.js delete mode 100644 chrome/browser/resources/options2/autofill_edit_address_overlay.html delete mode 100644 chrome/browser/resources/options2/autofill_edit_address_overlay.js delete mode 100644 chrome/browser/resources/options2/autofill_edit_creditcard_overlay.html delete mode 100644 chrome/browser/resources/options2/autofill_edit_creditcard_overlay.js delete mode 100644 chrome/browser/resources/options2/autofill_edit_overlay.css delete mode 100644 chrome/browser/resources/options2/autofill_options.css delete mode 100644 chrome/browser/resources/options2/autofill_options.html delete mode 100644 chrome/browser/resources/options2/autofill_options.js delete mode 100644 chrome/browser/resources/options2/autofill_options_list.js delete mode 100644 chrome/browser/resources/options2/browser_options.css delete mode 100644 chrome/browser/resources/options2/browser_options.html delete mode 100644 chrome/browser/resources/options2/browser_options.js delete mode 100644 chrome/browser/resources/options2/browser_options_profile_list.js delete mode 100644 chrome/browser/resources/options2/browser_options_startup_page_list.js delete mode 100644 chrome/browser/resources/options2/certificate_backup_overlay.html delete mode 100644 chrome/browser/resources/options2/certificate_backup_overlay.js delete mode 100644 chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.html delete mode 100644 chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.js delete mode 100644 chrome/browser/resources/options2/certificate_import_error_overlay.html delete mode 100644 chrome/browser/resources/options2/certificate_import_error_overlay.js delete mode 100644 chrome/browser/resources/options2/certificate_manager.css delete mode 100644 chrome/browser/resources/options2/certificate_manager.html delete mode 100644 chrome/browser/resources/options2/certificate_manager.js delete mode 100644 chrome/browser/resources/options2/certificate_restore_overlay.html delete mode 100644 chrome/browser/resources/options2/certificate_restore_overlay.js delete mode 100644 chrome/browser/resources/options2/certificate_tree.css delete mode 100644 chrome/browser/resources/options2/certificate_tree.js delete mode 100644 chrome/browser/resources/options2/chromeos/OWNERS delete mode 100644 chrome/browser/resources/options2/chromeos/accounts_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/accounts_options.js delete mode 100644 chrome/browser/resources/options2/chromeos/accounts_options_page.css delete mode 100644 chrome/browser/resources/options2/chromeos/accounts_user_list.js delete mode 100644 chrome/browser/resources/options2/chromeos/accounts_user_name_edit.js delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth.css delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.html delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.js delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth_device_list.js delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.html delete mode 100644 chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.js delete mode 100644 chrome/browser/resources/options2/chromeos/cellular_plan_element.js delete mode 100644 chrome/browser/resources/options2/chromeos/change_picture_options.css delete mode 100644 chrome/browser/resources/options2/chromeos/change_picture_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/change_picture_options.js delete mode 100644 chrome/browser/resources/options2/chromeos/display_options.css delete mode 100644 chrome/browser/resources/options2/chromeos/display_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/display_options.js delete mode 100644 chrome/browser/resources/options2/chromeos/internet_detail.css delete mode 100644 chrome/browser/resources/options2/chromeos/internet_detail.html delete mode 100644 chrome/browser/resources/options2/chromeos/internet_detail.js delete mode 100644 chrome/browser/resources/options2/chromeos/internet_detail_ip_address_field.js delete mode 100644 chrome/browser/resources/options2/chromeos/keyboard_overlay.html delete mode 100644 chrome/browser/resources/options2/chromeos/keyboard_overlay.js delete mode 100644 chrome/browser/resources/options2/chromeos/language_chewing_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/language_hangul_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/language_mozc_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/language_pinyin_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/network_list.js delete mode 100644 chrome/browser/resources/options2/chromeos/pointer_overlay.css delete mode 100644 chrome/browser/resources/options2/chromeos/pointer_overlay.html delete mode 100644 chrome/browser/resources/options2/chromeos/pointer_overlay.js delete mode 100644 chrome/browser/resources/options2/chromeos/preferred_networks.html delete mode 100644 chrome/browser/resources/options2/chromeos/preferred_networks.js delete mode 100644 chrome/browser/resources/options2/chromeos/proxy_rules_list.js delete mode 100644 chrome/browser/resources/options2/chromeos/set_wallpaper_options.css delete mode 100644 chrome/browser/resources/options2/chromeos/set_wallpaper_options.html delete mode 100644 chrome/browser/resources/options2/chromeos/set_wallpaper_options.js delete mode 100644 chrome/browser/resources/options2/clear_browser_data_overlay.css delete mode 100644 chrome/browser/resources/options2/clear_browser_data_overlay.html delete mode 100644 chrome/browser/resources/options2/clear_browser_data_overlay.js delete mode 100644 chrome/browser/resources/options2/content_settings.css delete mode 100644 chrome/browser/resources/options2/content_settings.html delete mode 100644 chrome/browser/resources/options2/content_settings.js delete mode 100644 chrome/browser/resources/options2/content_settings_exceptions_area.html delete mode 100644 chrome/browser/resources/options2/content_settings_exceptions_area.js delete mode 100644 chrome/browser/resources/options2/content_settings_ui.js delete mode 100644 chrome/browser/resources/options2/controlled_setting.js delete mode 100644 chrome/browser/resources/options2/cookies_list.js delete mode 100644 chrome/browser/resources/options2/cookies_view.css delete mode 100644 chrome/browser/resources/options2/cookies_view.html delete mode 100644 chrome/browser/resources/options2/cookies_view.js delete mode 100644 chrome/browser/resources/options2/cookies_view_app.html delete mode 100644 chrome/browser/resources/options2/cookies_view_app.js delete mode 100644 chrome/browser/resources/options2/deletable_item_list.js delete mode 100644 chrome/browser/resources/options2/editable_text_field.js delete mode 100644 chrome/browser/resources/options2/font_settings.css delete mode 100644 chrome/browser/resources/options2/font_settings.html delete mode 100644 chrome/browser/resources/options2/font_settings.js delete mode 100644 chrome/browser/resources/options2/handler_options.css delete mode 100644 chrome/browser/resources/options2/handler_options.html delete mode 100644 chrome/browser/resources/options2/handler_options.js delete mode 100644 chrome/browser/resources/options2/handler_options_list.js delete mode 100644 chrome/browser/resources/options2/home_page_overlay.css delete mode 100644 chrome/browser/resources/options2/home_page_overlay.html delete mode 100644 chrome/browser/resources/options2/home_page_overlay.js delete mode 100644 chrome/browser/resources/options2/import_data_overlay.css delete mode 100644 chrome/browser/resources/options2/import_data_overlay.html delete mode 100644 chrome/browser/resources/options2/import_data_overlay.js delete mode 100644 chrome/browser/resources/options2/inline_editable_list.js delete mode 100644 chrome/browser/resources/options2/instant_confirm_overlay.css delete mode 100644 chrome/browser/resources/options2/instant_confirm_overlay.html delete mode 100644 chrome/browser/resources/options2/instant_confirm_overlay.js delete mode 100644 chrome/browser/resources/options2/language_add_language_overlay.html delete mode 100644 chrome/browser/resources/options2/language_add_language_overlay.js delete mode 100644 chrome/browser/resources/options2/language_list.js delete mode 100644 chrome/browser/resources/options2/language_options.css delete mode 100644 chrome/browser/resources/options2/language_options.html delete mode 100644 chrome/browser/resources/options2/language_options.js delete mode 100644 chrome/browser/resources/options2/manage_profile_overlay.css delete mode 100644 chrome/browser/resources/options2/manage_profile_overlay.html delete mode 100644 chrome/browser/resources/options2/manage_profile_overlay.js delete mode 100644 chrome/browser/resources/options2/media_galleries_list.js delete mode 100644 chrome/browser/resources/options2/media_galleries_manager_overlay.html delete mode 100644 chrome/browser/resources/options2/media_galleries_manager_overlay.js delete mode 100644 chrome/browser/resources/options2/options.html delete mode 100644 chrome/browser/resources/options2/options.js delete mode 100644 chrome/browser/resources/options2/options_bundle.js delete mode 100644 chrome/browser/resources/options2/options_focus_manager.js delete mode 100644 chrome/browser/resources/options2/options_page.css delete mode 100644 chrome/browser/resources/options2/options_page.js delete mode 100644 chrome/browser/resources/options2/password_manager.css delete mode 100644 chrome/browser/resources/options2/password_manager.html delete mode 100644 chrome/browser/resources/options2/password_manager.js delete mode 100644 chrome/browser/resources/options2/password_manager_list.css delete mode 100644 chrome/browser/resources/options2/password_manager_list.js delete mode 100644 chrome/browser/resources/options2/pref_ui.js delete mode 100644 chrome/browser/resources/options2/preferences.js delete mode 100644 chrome/browser/resources/options2/profiles_icon_grid.js delete mode 100644 chrome/browser/resources/options2/search_box.html delete mode 100644 chrome/browser/resources/options2/search_engine_manager.css delete mode 100644 chrome/browser/resources/options2/search_engine_manager.html delete mode 100644 chrome/browser/resources/options2/search_engine_manager.js delete mode 100644 chrome/browser/resources/options2/search_engine_manager_engine_list.js delete mode 100644 chrome/browser/resources/options2/search_page.css delete mode 100644 chrome/browser/resources/options2/search_page.html delete mode 100644 chrome/browser/resources/options2/search_page.js delete mode 100644 chrome/browser/resources/options2/settings_dialog.js delete mode 100644 chrome/browser/resources/options2/spelling_confirm_overlay.css delete mode 100644 chrome/browser/resources/options2/spelling_confirm_overlay.html delete mode 100644 chrome/browser/resources/options2/spelling_confirm_overlay.js delete mode 100644 chrome/browser/resources/options2/startup_overlay.css delete mode 100644 chrome/browser/resources/options2/startup_overlay.html delete mode 100644 chrome/browser/resources/options2/startup_overlay.js delete mode 100644 chrome/browser/resources/options2/startup_section.html delete mode 100644 chrome/browser/resources/options2/subpages_tab_controls.css delete mode 100644 chrome/browser/resources/options2/sync_section.html delete mode 100644 chrome/browser/resources/options2_resources.grd create mode 100644 chrome/browser/resources/options_resources.grd diff --git a/chrome/browser/resources/chromeos/proxy_settings.html b/chrome/browser/resources/chromeos/proxy_settings.html index 3194ca6..1579556 100644 --- a/chrome/browser/resources/chromeos/proxy_settings.html +++ b/chrome/browser/resources/chromeos/proxy_settings.html @@ -5,8 +5,8 @@ - - + + @@ -22,16 +22,16 @@ - - - - - + + + + + - + diff --git a/chrome/browser/resources/options/OWNERS b/chrome/browser/resources/options/OWNERS new file mode 100644 index 0000000..8d7d56d --- /dev/null +++ b/chrome/browser/resources/options/OWNERS @@ -0,0 +1,4 @@ +csilv@chromium.org +dbeam@chromium.org +estade@chromium.org +jhawkins@chromium.org diff --git a/chrome/browser/resources/options/alert_overlay.css b/chrome/browser/resources/options/alert_overlay.css new file mode 100644 index 0000000..b2d9960 --- /dev/null +++ b/chrome/browser/resources/options/alert_overlay.css @@ -0,0 +1,7 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#alertOverlayMessage { + width: 400px; +} diff --git a/chrome/browser/resources/options/alert_overlay.html b/chrome/browser/resources/options/alert_overlay.html new file mode 100644 index 0000000..94be15c --- /dev/null +++ b/chrome/browser/resources/options/alert_overlay.html @@ -0,0 +1,13 @@ + diff --git a/chrome/browser/resources/options/alert_overlay.js b/chrome/browser/resources/options/alert_overlay.js new file mode 100644 index 0000000..dbfd409 --- /dev/null +++ b/chrome/browser/resources/options/alert_overlay.js @@ -0,0 +1,152 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + /** + * AlertOverlay class + * Encapsulated handling of a generic alert. + * @class + */ + function AlertOverlay() { + OptionsPage.call(this, 'alertOverlay', '', 'alertOverlay'); + } + + cr.addSingletonGetter(AlertOverlay); + + AlertOverlay.prototype = { + // Inherit AlertOverlay from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Whether the page can be shown. Used to make sure the page is only + * shown via AlertOverlay.Show(), and not via the address bar. + * @private + */ + canShow_: false, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('alertOverlayOk').onclick = function(event) { + self.handleOK_(); + }; + + $('alertOverlayCancel').onclick = function(event) { + self.handleCancel_(); + }; + }, + + /** @inheritDoc */ + get nestingLevel() { + // AlertOverlay is special in that it is not tied to one page or overlay. + // Set the nesting level arbitrarily high so as to always be recognized as + // the top-most visible page. + return 99; + }, + + /** + * Handle the 'ok' button. Clear the overlay and call the ok callback if + * available. + * @private + */ + handleOK_: function() { + OptionsPage.closeOverlay(); + if (this.okCallback != undefined) { + this.okCallback.call(); + } + }, + + /** + * Handle the 'cancel' button. Clear the overlay and call the cancel + * callback if available. + * @private + */ + handleCancel_: function() { + OptionsPage.closeOverlay(); + if (this.cancelCallback != undefined) { + this.cancelCallback.call(); + } + }, + + /** + * The page is getting hidden. Don't let it be shown again. + */ + willHidePage: function() { + canShow_ = false; + }, + + /** @inheritDoc */ + canShowPage: function() { + return this.canShow_; + }, + }; + + /** + * Show an alert overlay with the given message, button titles, and + * callbacks. + * @param {string} title The alert title to display to the user. + * @param {string} message The alert message to display to the user. + * @param {string} okTitle The title of the OK button. If undefined or empty, + * no button is shown. + * @param {string} cancelTitle The title of the cancel button. If undefined or + * empty, no button is shown. + * @param {function} okCallback A function to be called when the user presses + * the ok button. The alert window will be closed automatically. Can be + * undefined. + * @param {function} cancelCallback A function to be called when the user + * presses the cancel button. The alert window will be closed + * automatically. Can be undefined. + */ + AlertOverlay.show = function( + title, message, okTitle, cancelTitle, okCallback, cancelCallback) { + if (title != undefined) { + $('alertOverlayTitle').textContent = title; + $('alertOverlayTitle').style.display = 'block'; + } else { + $('alertOverlayTitle').style.display = 'none'; + } + + if (message != undefined) { + $('alertOverlayMessage').textContent = message; + $('alertOverlayMessage').style.display = 'block'; + } else { + $('alertOverlayMessage').style.display = 'none'; + } + + if (okTitle != undefined && okTitle != '') { + $('alertOverlayOk').textContent = okTitle; + $('alertOverlayOk').style.display = 'block'; + } else { + $('alertOverlayOk').style.display = 'none'; + } + + if (cancelTitle != undefined && cancelTitle != '') { + $('alertOverlayCancel').textContent = cancelTitle; + $('alertOverlayCancel').style.display = 'inline'; + } else { + $('alertOverlayCancel').style.display = 'none'; + } + + var alertOverlay = AlertOverlay.getInstance(); + alertOverlay.okCallback = okCallback; + alertOverlay.cancelCallback = cancelCallback; + alertOverlay.canShow_ = true; + + // Intentionally don't show the URL in the location bar as we don't want + // people trying to navigate here by hand. + OptionsPage.showPageByName('alertOverlay', false); + }; + + // Export + return { + AlertOverlay: AlertOverlay + }; +}); diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.html b/chrome/browser/resources/options/autofill_edit_address_overlay.html new file mode 100644 index 0000000..361445e --- /dev/null +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.html @@ -0,0 +1,100 @@ + diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.js b/chrome/browser/resources/options/autofill_edit_address_overlay.js new file mode 100644 index 0000000..8d0059c --- /dev/null +++ b/chrome/browser/resources/options/autofill_edit_address_overlay.js @@ -0,0 +1,325 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + // The GUID of the loaded address. + var guid; + + /** + * AutofillEditAddressOverlay class + * Encapsulated handling of the 'Add Page' overlay page. + * @class + */ + function AutofillEditAddressOverlay() { + OptionsPage.call(this, 'autofillEditAddress', + loadTimeData.getString('autofillEditAddressTitle'), + 'autofill-edit-address-overlay'); + } + + cr.addSingletonGetter(AutofillEditAddressOverlay); + + AutofillEditAddressOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.createMultiValueLists_(); + + var self = this; + $('autofill-edit-address-cancel-button').onclick = function(event) { + self.dismissOverlay_(); + }; + + // TODO(jhawkins): Investigate other possible solutions. + $('autofill-edit-address-apply-button').onclick = function(event) { + // Blur active element to ensure that pending changes are committed. + if (document.activeElement) + document.activeElement.blur(); + // Blurring is delayed for list elements. Queue save and close to + // ensure that pending changes have been applied. + setTimeout(function() { + self.saveAddress_(); + self.dismissOverlay_(); + }, 0); + }; + + // Prevent 'blur' events on the OK and cancel buttons, which can trigger + // insertion of new placeholder elements. The addition of placeholders + // affects layout, which interferes with being able to click on the + // buttons. + $('autofill-edit-address-apply-button').onmousedown = function(event) { + event.preventDefault(); + }; + $('autofill-edit-address-cancel-button').onmousedown = function(event) { + event.preventDefault(); + }; + + self.guid = ''; + self.populateCountryList_(); + self.clearInputFields_(); + self.connectInputEvents_(); + }, + + /** + * Creates, decorates and initializes the multi-value lists for full name, + * phone, and email. + * @private + */ + createMultiValueLists_: function() { + var list = $('full-name-list'); + options.autofillOptions.AutofillNameValuesList.decorate(list); + list.autoExpands = true; + + list = $('phone-list'); + options.autofillOptions.AutofillPhoneValuesList.decorate(list); + list.autoExpands = true; + + list = $('email-list'); + options.autofillOptions.AutofillValuesList.decorate(list); + list.autoExpands = true; + }, + + /** + * Updates the data model for the list named |listName| with the values from + * |entries|. + * @param {String} listName The id of the list. + * @param {Array} entries The list of items to be added to the list. + */ + setMultiValueList_: function(listName, entries) { + // Add data entries. + var list = $(listName); + + // Add special entry for adding new values. + var augmentedList = entries.slice(); + augmentedList.push(null); + list.dataModel = new ArrayDataModel(augmentedList); + + // Update the status of the 'OK' button. + this.inputFieldChanged_(); + + list.dataModel.addEventListener('splice', + this.inputFieldChanged_.bind(this)); + list.dataModel.addEventListener('change', + this.inputFieldChanged_.bind(this)); + }, + + /** + * Clears any uncommitted input, resets the stored GUID and dismisses the + * overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + this.guid = ''; + OptionsPage.closeOverlay(); + }, + + /** + * Aggregates the values in the input fields into an array and sends the + * array to the Autofill handler. + * @private + */ + saveAddress_: function() { + var address = new Array(); + address[0] = this.guid; + var list = $('full-name-list'); + address[1] = list.dataModel.slice(0, list.dataModel.length - 1); + address[2] = $('company-name').value; + address[3] = $('addr-line-1').value; + address[4] = $('addr-line-2').value; + address[5] = $('city').value; + address[6] = $('state').value; + address[7] = $('postal-code').value; + address[8] = $('country').value; + list = $('phone-list'); + address[9] = list.dataModel.slice(0, list.dataModel.length - 1); + list = $('email-list'); + address[10] = list.dataModel.slice(0, list.dataModel.length - 1); + + chrome.send('setAddress', address); + }, + + /** + * Connects each input field to the inputFieldChanged_() method that enables + * or disables the 'Ok' button based on whether all the fields are empty or + * not. + * @private + */ + connectInputEvents_: function() { + var self = this; + $('company-name').oninput = $('addr-line-1').oninput = + $('addr-line-2').oninput = $('city').oninput = $('state').oninput = + $('postal-code').oninput = function(event) { + self.inputFieldChanged_(); + }; + + $('country').onchange = function(event) { + self.countryChanged_(); + }; + }, + + /** + * Checks the values of each of the input fields and disables the 'Ok' + * button if all of the fields are empty. + * @private + */ + inputFieldChanged_: function() { + // Length of lists are tested for <= 1 due to the "add" placeholder item + // in the list. + var disabled = + $('full-name-list').items.length <= 1 && + !$('company-name').value && + !$('addr-line-1').value && !$('addr-line-2').value && + !$('city').value && !$('state').value && !$('postal-code').value && + !$('country').value && $('phone-list').items.length <= 1 && + $('email-list').items.length <= 1; + $('autofill-edit-address-apply-button').disabled = disabled; + }, + + /** + * Updates the postal code and state field labels appropriately for the + * selected country. + * @private + */ + countryChanged_: function() { + var countryCode = $('country').value || + loadTimeData.getString('defaultCountryCode'); + + var details = loadTimeData.getValue('autofillCountryData')[countryCode]; + var postal = $('postal-code-label'); + postal.textContent = details['postalCodeLabel']; + $('state-label').textContent = details['stateLabel']; + + // Also update the 'Ok' button as needed. + this.inputFieldChanged_(); + }, + + /** + * Populates the country list. + var countryList = $('country'); + for (var i = 0; i < countries.length; i++) { + var country = new Option(countries[i].name, countries[i].countryCode); + country.disabled = countries[i].disabled; + countryList.appendChild(country); + } + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + this.setMultiValueList_('full-name-list', []); + $('company-name').value = ''; + $('addr-line-1').value = ''; + $('addr-line-2').value = ''; + $('city').value = ''; + $('state').value = ''; + $('postal-code').value = ''; + $('country').value = ''; + this.setMultiValueList_('phone-list', []); + this.setMultiValueList_('email-list', []); + + this.countryChanged_(); + }, + + /** + * Loads the address data from |address|, sets the input fields based on + * this data and stores the GUID of the address. + * @private + */ + loadAddress_: function(address) { + this.setInputFields_(address); + this.inputFieldChanged_(); + this.guid = address['guid']; + }, + + /** + * Sets the value of each input field according to |address| + * @private + */ + setInputFields_: function(address) { + this.setMultiValueList_('full-name-list', address['fullName']); + $('company-name').value = address['companyName']; + $('addr-line-1').value = address['addrLine1']; + $('addr-line-2').value = address['addrLine2']; + $('city').value = address['city']; + $('state').value = address['state']; + $('postal-code').value = address['postalCode']; + $('country').value = address['country']; + this.setMultiValueList_('phone-list', address['phone']); + this.setMultiValueList_('email-list', address['email']); + + this.countryChanged_(); + }, + }; + + AutofillEditAddressOverlay.clearInputFields = function() { + AutofillEditAddressOverlay.getInstance().clearInputFields_(); + }; + + AutofillEditAddressOverlay.loadAddress = function(address) { + AutofillEditAddressOverlay.getInstance().loadAddress_(address); + }; + + AutofillEditAddressOverlay.setTitle = function(title) { + $('autofill-address-title').textContent = title; + }; + + AutofillEditAddressOverlay.setValidatedPhoneNumbers = function(numbers) { + AutofillEditAddressOverlay.getInstance().setMultiValueList_('phone-list', + numbers); + }; + + // Export + return { + AutofillEditAddressOverlay: AutofillEditAddressOverlay + }; +}); diff --git a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html new file mode 100644 index 0000000..bf3278b --- /dev/null +++ b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.html @@ -0,0 +1,31 @@ + diff --git a/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js new file mode 100644 index 0000000..c9a0b0e --- /dev/null +++ b/chrome/browser/resources/options/autofill_edit_creditcard_overlay.js @@ -0,0 +1,205 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + // The GUID of the loaded credit card. + var guid_; + + /** + * AutofillEditCreditCardOverlay class + * Encapsulated handling of the 'Add Page' overlay page. + * @class + */ + function AutofillEditCreditCardOverlay() { + OptionsPage.call(this, 'autofillEditCreditCard', + loadTimeData.getString('autofillEditCreditCardTitle'), + 'autofill-edit-credit-card-overlay'); + } + + cr.addSingletonGetter(AutofillEditCreditCardOverlay); + + AutofillEditCreditCardOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('autofill-edit-credit-card-cancel-button').onclick = function(event) { + self.dismissOverlay_(); + }; + $('autofill-edit-credit-card-apply-button').onclick = function(event) { + self.saveCreditCard_(); + self.dismissOverlay_(); + }; + + self.guid_ = ''; + self.hasEditedNumber_ = false; + self.clearInputFields_(); + self.connectInputEvents_(); + self.setDefaultSelectOptions_(); + }, + + /** + * Clears any uncommitted input, and dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + this.guid_ = ''; + this.hasEditedNumber_ = false; + OptionsPage.closeOverlay(); + }, + + /** + * Aggregates the values in the input fields into an array and sends the + * array to the Autofill handler. + * @private + */ + saveCreditCard_: function() { + var creditCard = new Array(5); + creditCard[0] = this.guid_; + creditCard[1] = $('name-on-card').value; + creditCard[2] = $('credit-card-number').value; + creditCard[3] = $('expiration-month').value; + creditCard[4] = $('expiration-year').value; + chrome.send('setCreditCard', creditCard); + }, + + /** + * Connects each input field to the inputFieldChanged_() method that enables + * or disables the 'Ok' button based on whether all the fields are empty or + * not. + * @private + */ + connectInputEvents_: function() { + var ccNumber = $('credit-card-number'); + $('name-on-card').oninput = ccNumber.oninput = + $('expiration-month').onchange = $('expiration-year').onchange = + this.inputFieldChanged_.bind(this); + }, + + /** + * Checks the values of each of the input fields and disables the 'Ok' + * button if all of the fields are empty. + * @param {Event} opt_event Optional data for the 'input' event. + * @private + */ + inputFieldChanged_: function(opt_event) { + var disabled = !$('name-on-card').value && !$('credit-card-number').value; + $('autofill-edit-credit-card-apply-button').disabled = disabled; + }, + + /** + * Sets the default values of the options in the 'Expiration date' select + * controls. + * @private + */ + setDefaultSelectOptions_: function() { + // Set the 'Expiration month' default options. + var expirationMonth = $('expiration-month'); + expirationMonth.options.length = 0; + for (var i = 1; i <= 12; ++i) { + var text; + if (i < 10) + text = '0' + i; + else + text = i; + + var option = document.createElement('option'); + option.text = text; + option.value = text; + expirationMonth.add(option, null); + } + + // Set the 'Expiration year' default options. + var expirationYear = $('expiration-year'); + expirationYear.options.length = 0; + + var date = new Date(); + var year = parseInt(date.getFullYear()); + for (var i = 0; i < 10; ++i) { + var text = year + i; + var option = document.createElement('option'); + option.text = text; + option.value = text; + expirationYear.add(option, null); + } + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + $('name-on-card').value = ''; + $('credit-card-number').value = ''; + $('expiration-month').selectedIndex = 0; + $('expiration-year').selectedIndex = 0; + + // Reset the enabled status of the 'Ok' button. + this.inputFieldChanged_(); + }, + + /** + * Sets the value of each input field according to |creditCard| + * @private + */ + setInputFields_: function(creditCard) { + $('name-on-card').value = creditCard['nameOnCard']; + $('credit-card-number').value = creditCard['creditCardNumber']; + + // The options for the year select control may be out-dated at this point, + // e.g. the user opened the options page before midnight on New Year's Eve + // and then loaded a credit card profile to edit in the new year, so + // reload the select options just to be safe. + this.setDefaultSelectOptions_(); + + var idx = parseInt(creditCard['expirationMonth'], 10); + $('expiration-month').selectedIndex = idx - 1; + + expYear = creditCard['expirationYear']; + var date = new Date(); + var year = parseInt(date.getFullYear()); + for (var i = 0; i < 10; ++i) { + var text = year + i; + if (expYear == String(text)) + $('expiration-year').selectedIndex = i; + } + }, + + /** + * Loads the credit card data from |creditCard|, sets the input fields based + * on this data and stores the GUID of the credit card. + * @private + */ + loadCreditCard_: function(creditCard) { + this.setInputFields_(creditCard); + this.inputFieldChanged_(); + this.guid_ = creditCard['guid']; + }, + }; + + AutofillEditCreditCardOverlay.clearInputFields = function(title) { + AutofillEditCreditCardOverlay.getInstance().clearInputFields_(); + }; + + AutofillEditCreditCardOverlay.loadCreditCard = function(creditCard) { + AutofillEditCreditCardOverlay.getInstance().loadCreditCard_(creditCard); + }; + + AutofillEditCreditCardOverlay.setTitle = function(title) { + $('autofill-credit-card-title').textContent = title; + }; + + // Export + return { + AutofillEditCreditCardOverlay: AutofillEditCreditCardOverlay + }; +}); diff --git a/chrome/browser/resources/options/autofill_edit_overlay.css b/chrome/browser/resources/options/autofill_edit_overlay.css new file mode 100644 index 0000000..76f56a6 --- /dev/null +++ b/chrome/browser/resources/options/autofill_edit_overlay.css @@ -0,0 +1,101 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#autofill-edit-address-overlay { + min-width: 510px; +} + +#autofill-edit-credit-card-overlay { + min-width: 500px; +} + +div.table { + display: table; +} + +div.cell { + display: table-cell; +} + +div.row { + display: table-row; +} + +div.input { + padding: 2px; +} + +/* Size to match large name fields. */ +#company-name, +#addr-line-1, +#addr-line-2 { + width: 206px; +} + +#country { + max-width: 450px; +} + +#autofill-edit-address-overlay list { + /* Min height is a multiple of the list item height (32) */ + min-height: 32px; + width: 176px; +} + +#autofill-edit-address-overlay list div.static-text { + -webkit-border-radius: 2px; + -webkit-box-flex: 1; + -webkit-padding-end: 4px; + -webkit-padding-start: 4px; + border: 1px solid darkGray; + /* Set the line-height and min-height to match the height of an input element, + * so that even empty cells renderer with the correct height. + */ + line-height: 1.75em; + min-height: 1.75em; + width: 141px; +} + +#autofill-edit-address-overlay list input { + width: 151px; +} + +#autofill-name-labels { + -webkit-box-orient: horizontal; + /* Set the margin to compensate for each list item's close button and + * padding. + */ + -webkit-margin-end: 25px; + display: -webkit-box; +} + +#autofill-name-labels label { + -webkit-box-flex: 1; + display: block; + /* Set the minimum width to the size of an input element, so that all boxes + * have an equal amount of flex space to work with. + */ + min-width: 141px; +} + +#autofill-edit-address-overlay list#full-name-list div.static-text { + width: 131px; +} + +#autofill-edit-address-overlay list#full-name-list input { + width: 141px; +} + +#autofill-edit-address-overlay list#full-name-list { + width: 100%; +} + +#full-name-list div[role='listitem'] > div { + -webkit-box-orient: horizontal; + display: -webkit-box; +} + +#full-name-list div[role='listitem'] > div > div { + -webkit-box-flex: 1; +} diff --git a/chrome/browser/resources/options/autofill_options.css b/chrome/browser/resources/options/autofill_options.css new file mode 100644 index 0000000..488197c --- /dev/null +++ b/chrome/browser/resources/options/autofill_options.css @@ -0,0 +1,52 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#autofill-options { + min-width: 550px; +} + +#autofill-help { + bottom: 18px; + position: absolute; +} + +#autofill-options list { + min-height: 172px; +} + +.autofill-list-item { + -webkit-box-flex: 1; + -webkit-padding-start: 8px; + overflow: hidden; + text-overflow: ellipsis; +} + +.autofill-list-item + img { + -webkit-padding-end: 20px; + vertical-align: top; +} + +#autofill-options > div:last-child { + margin-top: 15px; +} + +#autofill-options > div.settings-list > div:last-child { + border-top: 1px solid #d9d9d9; + padding: 5px 10px; +} + +#autofill-add-address, +#autofill-add-creditcard { + margin: 5px 5px; +} + +#autofill-options .list-inline-button { + margin-top: 0; + vertical-align: top; +} + +#autofill-options div[role='listitem']:not(:hover):not([selected]) + .list-inline-button { + display: none; +} diff --git a/chrome/browser/resources/options/autofill_options.html b/chrome/browser/resources/options/autofill_options.html new file mode 100644 index 0000000..a1c4481 --- /dev/null +++ b/chrome/browser/resources/options/autofill_options.html @@ -0,0 +1,39 @@ + diff --git a/chrome/browser/resources/options/autofill_options.js b/chrome/browser/resources/options/autofill_options.js new file mode 100644 index 0000000..82d8c24 --- /dev/null +++ b/chrome/browser/resources/options/autofill_options.js @@ -0,0 +1,233 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + var ArrayDataModel = cr.ui.ArrayDataModel; + + ///////////////////////////////////////////////////////////////////////////// + // AutofillOptions class: + + /** + * Encapsulated handling of Autofill options page. + * @constructor + */ + function AutofillOptions() { + OptionsPage.call(this, + 'autofill', + loadTimeData.getString('autofillOptionsPageTabTitle'), + 'autofill-options'); + } + + cr.addSingletonGetter(AutofillOptions); + + AutofillOptions.prototype = { + __proto__: OptionsPage.prototype, + + /** + * The address list. + * @type {DeletableItemList} + * @private + */ + addressList_: null, + + /** + * The credit card list. + * @type {DeletableItemList} + * @private + */ + creditCardList_: null, + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.createAddressList_(); + this.createCreditCardList_(); + + var self = this; + $('autofill-add-address').onclick = function(event) { + self.showAddAddressOverlay_(); + }; + $('autofill-add-creditcard').onclick = function(event) { + self.showAddCreditCardOverlay_(); + }; + $('autofill-options-confirm').onclick = function(event) { + OptionsPage.closeOverlay(); + }; + + // TODO(jhawkins): What happens when Autofill is disabled whilst on the + // Autofill options page? + }, + + /** + * Creates, decorates and initializes the address list. + * @private + */ + createAddressList_: function() { + this.addressList_ = $('address-list'); + options.autofillOptions.AutofillAddressList.decorate(this.addressList_); + this.addressList_.autoExpands = true; + }, + + /** + * Creates, decorates and initializes the credit card list. + * @private + */ + createCreditCardList_: function() { + this.creditCardList_ = $('creditcard-list'); + options.autofillOptions.AutofillCreditCardList.decorate( + this.creditCardList_); + this.creditCardList_.autoExpands = true; + }, + + /** + * Shows the 'Add address' overlay, specifically by loading the + * 'Edit address' overlay, emptying the input fields and modifying the + * overlay title. + * @private + */ + showAddAddressOverlay_: function() { + var title = loadTimeData.getString('addAddressTitle'); + AutofillEditAddressOverlay.setTitle(title); + AutofillEditAddressOverlay.clearInputFields(); + OptionsPage.navigateToPage('autofillEditAddress'); + }, + + /** + * Shows the 'Add credit card' overlay, specifically by loading the + * 'Edit credit card' overlay, emptying the input fields and modifying the + * overlay title. + * @private + */ + showAddCreditCardOverlay_: function() { + var title = loadTimeData.getString('addCreditCardTitle'); + AutofillEditCreditCardOverlay.setTitle(title); + AutofillEditCreditCardOverlay.clearInputFields(); + OptionsPage.navigateToPage('autofillEditCreditCard'); + }, + + /** + * Updates the data model for the address list with the values from + * |entries|. + * @param {Array} entries The list of addresses. + */ + setAddressList_: function(entries) { + this.addressList_.dataModel = new ArrayDataModel(entries); + }, + + /** + * Updates the data model for the credit card list with the values from + * |entries|. + * @param {Array} entries The list of credit cards. + */ + setCreditCardList_: function(entries) { + this.creditCardList_.dataModel = new ArrayDataModel(entries); + }, + + /** + * Removes the Autofill address represented by |guid|. + * @param {String} guid The GUID of the address to remove. + * @private + */ + removeAddress_: function(guid) { + chrome.send('removeAddress', [guid]); + }, + + /** + * Removes the Autofill credit card represented by |guid|. + * @param {String} guid The GUID of the credit card to remove. + * @private + */ + removeCreditCard_: function(guid) { + chrome.send('removeCreditCard', [guid]); + }, + + /** + * Requests profile data for the address represented by |guid| from the + * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler + * calls showEditAddressOverlay(). + * @param {String} guid The GUID of the address to edit. + * @private + */ + loadAddressEditor_: function(guid) { + chrome.send('loadAddressEditor', [guid]); + }, + + /** + * Requests profile data for the credit card represented by |guid| from the + * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler + * calls showEditCreditCardOverlay(). + * @param {String} guid The GUID of the credit card to edit. + * @private + */ + loadCreditCardEditor_: function(guid) { + chrome.send('loadCreditCardEditor', [guid]); + }, + + /** + * Shows the 'Edit address' overlay, using the data in |address| to fill the + * input fields. |address| is a list with one item, an associative array + * that contains the address data. + * @private + */ + showEditAddressOverlay_: function(address) { + var title = loadTimeData.getString('editAddressTitle'); + AutofillEditAddressOverlay.setTitle(title); + AutofillEditAddressOverlay.loadAddress(address); + OptionsPage.navigateToPage('autofillEditAddress'); + }, + + /** + * Shows the 'Edit credit card' overlay, using the data in |credit_card| to + * fill the input fields. |address| is a list with one item, an associative + * array that contains the credit card data. + * @private + */ + showEditCreditCardOverlay_: function(creditCard) { + var title = loadTimeData.getString('editCreditCardTitle'); + AutofillEditCreditCardOverlay.setTitle(title); + AutofillEditCreditCardOverlay.loadCreditCard(creditCard); + OptionsPage.navigateToPage('autofillEditCreditCard'); + }, + }; + + AutofillOptions.setAddressList = function(entries) { + AutofillOptions.getInstance().setAddressList_(entries); + }; + + AutofillOptions.setCreditCardList = function(entries) { + AutofillOptions.getInstance().setCreditCardList_(entries); + }; + + AutofillOptions.removeAddress = function(guid) { + AutofillOptions.getInstance().removeAddress_(guid); + }; + + AutofillOptions.removeCreditCard = function(guid) { + AutofillOptions.getInstance().removeCreditCard_(guid); + }; + + AutofillOptions.loadAddressEditor = function(guid) { + AutofillOptions.getInstance().loadAddressEditor_(guid); + }; + + AutofillOptions.loadCreditCardEditor = function(guid) { + AutofillOptions.getInstance().loadCreditCardEditor_(guid); + }; + + AutofillOptions.editAddress = function(address) { + AutofillOptions.getInstance().showEditAddressOverlay_(address); + }; + + AutofillOptions.editCreditCard = function(creditCard) { + AutofillOptions.getInstance().showEditCreditCardOverlay_(creditCard); + }; + + // Export + return { + AutofillOptions: AutofillOptions + }; + +}); + diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js new file mode 100644 index 0000000..7bb9c7b --- /dev/null +++ b/chrome/browser/resources/options/autofill_options_list.js @@ -0,0 +1,506 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.autofillOptions', function() { + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + /** @const */ var InlineEditableItem = options.InlineEditableItem; + /** @const */ var InlineEditableItemList = options.InlineEditableItemList; + + function AutofillEditProfileButton(guid, edit) { + var editButtonEl = document.createElement('button'); + editButtonEl.className = 'list-inline-button custom-appearance'; + editButtonEl.textContent = + loadTimeData.getString('autofillEditProfileButton'); + editButtonEl.onclick = function(e) { edit(guid); }; + + // Don't select the row when clicking the button. + editButtonEl.onmousedown = function(e) { + e.stopPropagation(); + }; + + return editButtonEl; + } + + /** + * Creates a new address list item. + * @param {Array} entry An array of the form [guid, label]. + * @constructor + * @extends {options.DeletableItem} + */ + function AddressListItem(entry) { + var el = cr.doc.createElement('div'); + el.guid = entry[0]; + el.label = entry[1]; + el.__proto__ = AddressListItem.prototype; + el.decorate(); + + return el; + } + + AddressListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + // The stored label. + var label = this.ownerDocument.createElement('div'); + label.className = 'autofill-list-item'; + label.textContent = this.label; + this.contentElement.appendChild(label); + + // The 'Edit' button. + var editButtonEl = new AutofillEditProfileButton( + this.guid, + AutofillOptions.loadAddressEditor); + this.contentElement.appendChild(editButtonEl); + }, + }; + + /** + * Creates a new credit card list item. + * @param {Array} entry An array of the form [guid, label, icon]. + * @constructor + * @extends {options.DeletableItem} + */ + function CreditCardListItem(entry) { + var el = cr.doc.createElement('div'); + el.guid = entry[0]; + el.label = entry[1]; + el.icon = entry[2]; + el.description = entry[3]; + el.__proto__ = CreditCardListItem.prototype; + el.decorate(); + + return el; + } + + CreditCardListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + // The stored label. + var label = this.ownerDocument.createElement('div'); + label.className = 'autofill-list-item'; + label.textContent = this.label; + this.contentElement.appendChild(label); + + // The credit card icon. + var icon = this.ownerDocument.createElement('image'); + icon.src = this.icon; + icon.alt = this.description; + this.contentElement.appendChild(icon); + + // The 'Edit' button. + var editButtonEl = new AutofillEditProfileButton( + this.guid, + AutofillOptions.loadCreditCardEditor); + this.contentElement.appendChild(editButtonEl); + }, + }; + + /** + * Creates a new value list item. + * @param {AutofillValuesList} list The parent list of this item. + * @param {String} entry A string value. + * @constructor + * @extends {options.InlineEditableItem} + */ + function ValuesListItem(list, entry) { + var el = cr.doc.createElement('div'); + el.list = list; + el.value = entry ? entry : ''; + el.__proto__ = ValuesListItem.prototype; + el.decorate(); + + return el; + } + + ValuesListItem.prototype = { + __proto__: InlineEditableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + // Note: This must be set prior to calling |createEditableTextCell|. + this.isPlaceholder = !this.value; + + // The stored value. + var cell = this.createEditableTextCell(this.value); + this.contentElement.appendChild(cell); + this.input = cell.querySelector('input'); + + if (this.isPlaceholder) { + this.input.placeholder = this.list.getAttribute('placeholder'); + this.deletable = false; + } + + this.addEventListener('commitedit', this.onEditCommitted_); + }, + + /** + * @return {string} This item's value. + * @protected + */ + value_: function() { + return this.input.value; + }, + + /** + * @param {Object} value The value to test. + * @return {boolean} True if the given value is non-empty. + * @protected + */ + valueIsNonEmpty_: function(value) { + return !!value; + }, + + /** + * @return {boolean} True if value1 is logically equal to value2. + */ + valuesAreEqual_: function(value1, value2) { + return value1 === value2; + }, + + /** + * Clears the item's value. + * @protected + */ + clearValue_: function() { + this.input.value = ''; + }, + + /** + * Called when committing an edit. + * If this is an "Add ..." item, committing a non-empty value adds that + * value to the end of the values list, but also leaves this "Add ..." item + * in place. + * @param {Event} e The end event. + * @private + */ + onEditCommitted_: function(e) { + var value = this.value_(); + var i = this.list.items.indexOf(this); + if (i < this.list.dataModel.length && + this.valuesAreEqual_(value, this.list.dataModel.item(i))) { + return; + } + + var entries = this.list.dataModel.slice(); + if (this.valueIsNonEmpty_(value) && + !entries.some(this.valuesAreEqual_.bind(this, value))) { + // Update with new value. + if (this.isPlaceholder) { + // It is important that updateIndex is done before validateAndSave. + // Otherwise we can not be sure about AddRow index. + this.list.dataModel.updateIndex(i); + this.list.validateAndSave(i, 0, value); + } else { + this.list.validateAndSave(i, 1, value); + } + } else { + // Reject empty values and duplicates. + if (!this.isPlaceholder) + this.list.dataModel.splice(i, 1); + else + this.clearValue_(); + } + }, + }; + + /** + * Creates a new name value list item. + * @param {AutofillNameValuesList} list The parent list of this item. + * @param {array} entry An array of [first, middle, last] names. + * @constructor + * @extends {options.ValuesListItem} + */ + function NameListItem(list, entry) { + var el = cr.doc.createElement('div'); + el.list = list; + el.first = entry ? entry[0] : ''; + el.middle = entry ? entry[1] : ''; + el.last = entry ? entry[2] : ''; + el.__proto__ = NameListItem.prototype; + el.decorate(); + + return el; + } + + NameListItem.prototype = { + __proto__: ValuesListItem.prototype, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + // Note: This must be set prior to calling |createEditableTextCell|. + this.isPlaceholder = !this.first && !this.middle && !this.last; + + // The stored value. + // For the simulated static "input element" to display correctly, the + // value must not be empty. We use a space to force the UI to render + // correctly when the value is logically empty. + var cell = this.createEditableTextCell(this.first); + this.contentElement.appendChild(cell); + this.firstNameInput = cell.querySelector('input'); + + cell = this.createEditableTextCell(this.middle); + this.contentElement.appendChild(cell); + this.middleNameInput = cell.querySelector('input'); + + cell = this.createEditableTextCell(this.last); + this.contentElement.appendChild(cell); + this.lastNameInput = cell.querySelector('input'); + + if (this.isPlaceholder) { + this.firstNameInput.placeholder = + loadTimeData.getString('autofillAddFirstNamePlaceholder'); + this.middleNameInput.placeholder = + loadTimeData.getString('autofillAddMiddleNamePlaceholder'); + this.lastNameInput.placeholder = + loadTimeData.getString('autofillAddLastNamePlaceholder'); + this.deletable = false; + } + + this.addEventListener('commitedit', this.onEditCommitted_); + }, + + /** @inheritDoc */ + value_: function() { + return [this.firstNameInput.value, + this.middleNameInput.value, + this.lastNameInput.value]; + }, + + /** @inheritDoc */ + valueIsNonEmpty_: function(value) { + return value[0] || value[1] || value[2]; + }, + + /** @inheritDoc */ + valuesAreEqual_: function(value1, value2) { + // First, check for null values. + if (!value1 || !value2) + return value1 == value2; + + return value1[0] === value2[0] && + value1[1] === value2[1] && + value1[2] === value2[2]; + }, + + /** @inheritDoc */ + clearValue_: function() { + this.firstNameInput.value = ''; + this.middleNameInput.value = ''; + this.lastNameInput.value = ''; + }, + }; + + /** + * Base class for shared implementation between address and credit card lists. + * @constructor + * @extends {options.DeletableItemList} + */ + var AutofillProfileList = cr.ui.define('list'); + + AutofillProfileList.prototype = { + __proto__: DeletableItemList.prototype, + + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + + this.addEventListener('blur', this.onBlur_); + }, + + /** + * When the list loses focus, unselect all items in the list. + * @private + */ + onBlur_: function() { + this.selectionModel.unselectAll(); + }, + }; + + /** + * Create a new address list. + * @constructor + * @extends {options.AutofillProfileList} + */ + var AutofillAddressList = cr.ui.define('list'); + + AutofillAddressList.prototype = { + __proto__: AutofillProfileList.prototype, + + decorate: function() { + AutofillProfileList.prototype.decorate.call(this); + }, + + /** @inheritDoc */ + activateItemAtIndex: function(index) { + AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]); + }, + + /** @inheritDoc */ + createItem: function(entry) { + return new AddressListItem(entry); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + AutofillOptions.removeAddress(this.dataModel.item(index)[0]); + }, + }; + + /** + * Create a new credit card list. + * @constructor + * @extends {options.DeletableItemList} + */ + var AutofillCreditCardList = cr.ui.define('list'); + + AutofillCreditCardList.prototype = { + __proto__: AutofillProfileList.prototype, + + decorate: function() { + AutofillProfileList.prototype.decorate.call(this); + }, + + /** @inheritDoc */ + activateItemAtIndex: function(index) { + AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]); + }, + + /** @inheritDoc */ + createItem: function(entry) { + return new CreditCardListItem(entry); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]); + }, + }; + + /** + * Create a new value list. + * @constructor + * @extends {options.InlineEditableItemList} + */ + var AutofillValuesList = cr.ui.define('list'); + + AutofillValuesList.prototype = { + __proto__: InlineEditableItemList.prototype, + + /** @inheritDoc */ + createItem: function(entry) { + return new ValuesListItem(this, entry); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + this.dataModel.splice(index, 1); + }, + + /** @inheritDoc */ + shouldFocusPlaceholder: function() { + return false; + }, + + /** + * Called when the list hierarchy as a whole loses or gains focus. + * If the list was focused in response to a mouse click, call into the + * superclass's implementation. If the list was focused in response to a + * keyboard navigation, focus the first item. + * If the list loses focus, unselect all the elements. + * @param {Event} e The change event. + * @private + */ + handleListFocusChange_: function(e) { + // We check to see whether there is a selected item as a proxy for + // distinguishing between mouse- and keyboard-originated focus events. + var selectedItem = this.selectedItem; + if (selectedItem) + InlineEditableItemList.prototype.handleListFocusChange_.call(this, e); + + if (!e.newValue) { + // When the list loses focus, unselect all the elements. + this.selectionModel.unselectAll(); + } else { + // When the list gains focus, select the first item if nothing else is + // selected. + var firstItem = this.getListItemByIndex(0); + if (!selectedItem && firstItem && e.newValue) + firstItem.handleFocus_(); + } + }, + + /** + * Called when a new list item should be validated; subclasses are + * responsible for implementing if validation is required. + * @param {number} index The index of the item that was inserted or changed. + * @param {number} remove The number items to remove. + * @param {string} value The value of the item to insert. + */ + validateAndSave: function(index, remove, value) { + this.dataModel.splice(index, remove, value); + }, + }; + + /** + * Create a new value list for phone number validation. + * @constructor + * @extends {options.AutofillValuesList} + */ + var AutofillNameValuesList = cr.ui.define('list'); + + AutofillNameValuesList.prototype = { + __proto__: AutofillValuesList.prototype, + + /** @inheritDoc */ + createItem: function(entry) { + return new NameListItem(this, entry); + }, + }; + + /** + * Create a new value list for phone number validation. + * @constructor + * @extends {options.AutofillValuesList} + */ + var AutofillPhoneValuesList = cr.ui.define('list'); + + AutofillPhoneValuesList.prototype = { + __proto__: AutofillValuesList.prototype, + + /** @inheritDoc */ + validateAndSave: function(index, remove, value) { + var numbers = this.dataModel.slice(0, this.dataModel.length - 1); + numbers.splice(index, remove, value); + var info = new Array(); + info[0] = index; + info[1] = numbers; + info[2] = $('country').value; + chrome.send('validatePhoneNumbers', info); + }, + }; + + return { + AddressListItem: AddressListItem, + CreditCardListItem: CreditCardListItem, + ValuesListItem: ValuesListItem, + NameListItem: NameListItem, + AutofillAddressList: AutofillAddressList, + AutofillCreditCardList: AutofillCreditCardList, + AutofillValuesList: AutofillValuesList, + AutofillNameValuesList: AutofillNameValuesList, + AutofillPhoneValuesList: AutofillPhoneValuesList, + }; +}); diff --git a/chrome/browser/resources/options/browser_options.css b/chrome/browser/resources/options/browser_options.css new file mode 100644 index 0000000..7e20964 --- /dev/null +++ b/chrome/browser/resources/options/browser_options.css @@ -0,0 +1,425 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#change-home-page-section { + margin-left: 30px; +} + +#home-page-url { + display: inline-block; + max-width: 400px; + overflow: hidden; + text-overflow: ellipsis; +} + +#default-browser-state { + margin-top: 6px; +} + +#sync-overview p { + display: inline; +} + +#account-picture-wrapper { + border: 1px solid rgba(0, 0, 0, 0.3); + border-radius: 4px; + cursor: pointer; + display: inline-block; + float: left; + margin: 0 2px 10px 0; + padding: 3px; + position: relative; +} + +html[dir=rtl] #account-picture-wrapper { + float: right; +} + +#account-picture { + height: 56px; + vertical-align: middle; + width: 56px; +} + +#change-picture-caption { + background: rgba(0, 0, 0, 0.5); + bottom: 0; + color: white; + font-size: small; + margin: 3px 0; + position: absolute; + text-align: center; + /* Width of #account-picture. */ + width: 56px; +} + +#account-picture-wrapper:not(:hover) #change-picture-caption { + visibility: hidden; +} + +#sync-general { + -webkit-margin-start: 76px; + margin-bottom: 10px; +} + +#sync-buttons { + clear: both; +} + +#profiles-list { + margin-bottom: 10px; + min-height: 0; +} + +#profiles-list > * { + height: 40px; +} + +#profiles-list:focus { + border-color: rgb(77, 144, 254); +} + +.profile-img { + height: 31px; + padding: 3px; + vertical-align: middle; + width: 38px; +} + +.profile-item-current { + font-weight: bold; +} + +#profiles-buttons { + white-space: nowrap; +} + +.sync-error { + background: rgb(255, 219, 219); + border: 1px solid rgb(206, 76, 76); + border-radius: 2px; + padding: 10px; +} + +.sync-error .link-button { + margin: 0 1ex; + padding: 0; +} + +#mac-passwords-warning { + margin-top: 10px; +} + +input[type='range'] { + vertical-align: middle; +} + +/* Internet settings */ + +#network-settings { + position: relative; +} + +#network-list { + min-height: 0; + width: 320px; +} + +.network-group { + -webkit-box-orient: horizontal; + display: -webkit-box; + height: 42px; + vertical-align: middle; +} + +list:not([disabled]) > .network-group:hover, +list:not([disabled]) > .network-group[selected] { + background-color: #f8f8f8 !important; + background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), + rgba(255, 255, 255, 0)) !important; + box-shadow: inset 0 0 1px 1px #f0f0f0; +} + +.network-group-labels { + -webkit-box-flex: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + padding-top: 3px; +} + +.network-icon { + -webkit-margin-end: 8px; + background-position: left top; + background-size: 25px; + display: -webkit-box; + height: 25px; + width: 25px; +} + +@-webkit-keyframes connecting-animation { + 0% { + background-position: 0 25%; + } + 12.5% { + background-position: 0 50%; + } + 25% { + background-position: 0 75%; + } + 37.5% { + background-position: 0 100%; + } + 50% { + background-position: 0 100%; + } + 62.5% { + background-position: 0 75%; + } + 75% { + background-position: 0 50%; + } + 87.5% { + background-position: 0 25%; + } +} + +.network-wifi { + background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_ARCS_DARK'); + background-size: 25px; +} + +.network-wimax { + background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_BARS_DARK'); + background-size: 25px; +} + +.network-cellular { + background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_BARS_DARK'); + background-size: 25px; +} + +.network-vpn { + background-image: url('chrome://theme/IDR_STATUSBAR_VPN'); + background-size: 25px; +} + +.network-add-connection, +.network-control-active, +.network-control-inactive { + background-position: center center !important; + background-repeat: no-repeat; +} + +.network-add-connection { + background-image: url('chrome://theme/IDR_SIDETABS_NEW_TAB'); + background-size: 16px; +} + +.network-control-inactive { + background-image: none; +} + +.network-control-active { + background-image: url('chrome://theme/IDR_PROFILE_SELECTED'); + background-size: 16px; +} + +.network-options-button { + -webkit-box-flex: 0; + -webkit-transform: scale(0.6); + background-image: none; + background-position: center center; + display: block; + opacity: 0.5; + vertical-align: middle; + width: 19px; +} + +.network-group > .controlled-setting-indicator, +.network-menu-item > .controlled-setting-indicator { + -webkit-box-flex: 0; + -webkit-margin-end: 5px; + display: block; + height: 16px; + margin-top: 9px; + width: 16px; +} + +.network-group > .controlled-setting-indicator { + margin-top: 0; +} + +.network-options-button:hover { + opacity: 1; +} + +@-webkit-keyframes vpn-connecting-animation { + from { + opacity: 1; + } + to { + opacity: 0.2; + } +} + +.network-connecting { + -webkit-animation: connecting-animation 1s step-end infinite; +} + +.network-vpn.network-connecting { + -webkit-animation: vpn-connecting-animation 500ms alternate infinite; +} + +.network-title { + font-weight: 600; + line-height: 120%; +} + +.network-subtitle { + color: #333; + display: inline-block; + line-height: 100%; + max-width: 260px; + opacity: 0.4; + overflow: hidden; + padding-bottom: 3px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.network-selector { + background: right center no-repeat; + background-image: url('../shared/images/select.png'); + padding-right: 20px; +} + +.network-menu { + -webkit-box-shadow: + 0 0 0 1px rgba(0,0,0,0.1), + 0 5px 1px 1px rgba(0,0,0,0.1), + 0 5px 2px 1px rgba(0,0,0,0.1), + 0 5px 12px 1px rgba(0,0,0,0.5); + background: #fff; + display: block; + position: absolute; + width: 320px; + z-index: 1; +} + +.network-menu-item { + -webkit-padding-start: 32px; + background: left center no-repeat; + background-size: 25px; + display: -webkit-box; + height: 32px; + margin-left: 4px; + margin-right: 4px; + vertical-align: middle; +} + +@media (pointer:coarse) { + network-menu-item { + height: 40px; + } +} + +.network-menu-item-label { + -webkit-box-flex: 1; + color: #555; + display: block; + overflow-x: hidden; + padding-top: 8px; + text-overflow: ellipsis; + vertical-align: middle; + white-space: nowrap; +} + +.active-network { + color: black; + font-weight: bold; +} + +@media (pointer:coarse) { + .network-menu-item-label { + padding-top: 14px; + } +} + +.network-disabled-control { + color: #999; +} + +/* Restrict the size of the networks menu, by limiting the number of + visible networks. */ +.network-menu-group { + max-height: 192px; + overflow-x: hidden; + overflow-y: auto; + text-overflow: ellipsis; +} + +@media (pointer:coarse) { + .network-menu-group { + max-height: 200px; + } +} + +html[dir=rtl] .network-menu-item { + background: right center no-repeat; +} + +.network-menu-item:hover { + background-color: #eee; +} + +.network-menu > hr { + opacity: 0.4; +} + +#shared-proxies { + margin-top: 12px; +} + +#web-content-section select, +.web-content-select-label { + min-width: 145px; +} + +.web-content-select-label > span:only-of-type { + display: inline-block; + min-width: 100px; +} + +#timezone-value { + display: inline-block; + vertical-align: baseline; +} + +#privacy-explanation { + line-height: 1.8em; +} + +#advanced-settings { + height: 0; + margin-top: 8px; + overflow: hidden; +} + +#auto-open-file-types-label { + padding: 0.45em 0 +} + +.sliding { + -webkit-transition: height 200ms; + overflow-y: hidden; +} + +#keyboard-overlay .option-value > select { + width: 100%; +} + +#keyboard-overlay table { + /* Same as .settings-row {margin}. */ + -webkit-border-vertical-spacing: 0.65em; +} diff --git a/chrome/browser/resources/options/browser_options.html b/chrome/browser/resources/options/browser_options.html new file mode 100644 index 0000000..77c4215 --- /dev/null +++ b/chrome/browser/resources/options/browser_options.html @@ -0,0 +1,541 @@ + diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js new file mode 100644 index 0000000..e0acdb7 --- /dev/null +++ b/chrome/browser/resources/options/browser_options.js @@ -0,0 +1,1350 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + var ArrayDataModel = cr.ui.ArrayDataModel; + var RepeatingButton = cr.ui.RepeatingButton; + + // + // BrowserOptions class + // Encapsulated handling of browser options page. + // + function BrowserOptions() { + OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'), + 'settings'); + } + + cr.addSingletonGetter(BrowserOptions); + + BrowserOptions.prototype = { + __proto__: options.OptionsPage.prototype, + + // State variables. + syncSetupCompleted: false, + + /** + * The cached value of the instant.confirm_dialog_shown preference. + * @type {bool} + * @private + */ + instantConfirmDialogShown_: false, + + /** + * The cached value of the spellcheck.confirm_dialog_shown preference. + * @type {bool} + * @private + */ + spellcheckConfirmDialogShown_: false, + + /** + * Keeps track of whether |onShowHomeButtonChanged_| has been called. See + * |onShowHomeButtonChanged_|. + * @type {bool} + * @private + */ + onShowHomeButtonChangedCalled_: false, + + /** + * @inheritDoc + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + var self = this; + + // Ensure that navigation events are unblocked on uber page. A reload of + // the settings page while an overlay is open would otherwise leave uber + // page in a blocked state, where tab switching is not possible. + uber.invokeMethodOnParent('stopInterceptingEvents'); + + window.addEventListener('message', this.handleWindowMessage_.bind(this)); + + $('advanced-settings-expander').onclick = function() { + self.toggleSectionWithAnimation_( + $('advanced-settings'), + $('advanced-settings-container')); + + // If the link was focused (i.e., it was activated using the keyboard) + // and it was used to show the section (rather than hiding it), focus + // the first element in the container. + if (document.activeElement === $('advanced-settings-expander') && + $('advanced-settings').style.height === '') { + var focusElement = $('advanced-settings-container').querySelector( + 'button, input, list, select, a[href]'); + if (focusElement) + focusElement.focus(); + } + } + + $('advanced-settings').addEventListener('webkitTransitionEnd', + this.updateAdvancedSettingsExpander_.bind(this)); + + if (cr.isChromeOS) + UIAccountTweaks.applyGuestModeVisibility(document); + + // Sync (Sign in) section. + this.updateSyncState_(loadTimeData.getValue('syncData')); + + $('sync-action-link').onclick = function(event) { + if (cr.isChromeOS) { + // On Chrome OS, sign out the user and sign in again to get fresh + // credentials on auth errors. + SyncSetupOverlay.doSignOutOnAuthError(); + } else { + SyncSetupOverlay.showErrorUI(); + } + }; + $('start-stop-sync').onclick = function(event) { + if (self.syncSetupCompleted) + SyncSetupOverlay.showStopSyncingUI(); + else if (cr.isChromeOS) + SyncSetupOverlay.showSetupUIWithoutLogin(); + else + SyncSetupOverlay.showSetupUI(); + }; + $('customize-sync').onclick = function(event) { + if (cr.isChromeOS) + SyncSetupOverlay.showSetupUIWithoutLogin(); + else + SyncSetupOverlay.showSetupUI(); + }; + + // Internet connection section (ChromeOS only). + if (cr.isChromeOS) { + options.network.NetworkList.decorate($('network-list')); + options.network.NetworkList.refreshNetworkData( + loadTimeData.getValue('networkData')); + } + + // On Startup section. + Preferences.getInstance().addEventListener('session.restore_on_startup', + this.onRestoreOnStartupChanged_.bind(this)); + + $('startup-set-pages').onclick = function() { + OptionsPage.navigateToPage('startup'); + }; + + // Appearance section. + Preferences.getInstance().addEventListener('browser.show_home_button', + this.onShowHomeButtonChanged_.bind(this)); + + Preferences.getInstance().addEventListener('homepage', + this.onHomePageChanged_.bind(this)); + Preferences.getInstance().addEventListener('homepage_is_newtabpage', + this.onHomePageIsNtpChanged_.bind(this)); + + $('change-home-page').onclick = function(event) { + OptionsPage.navigateToPage('homePageOverlay'); + }; + + if ($('set-wallpaper')) { + $('set-wallpaper').onclick = function(event) { + chrome.send('openWallpaperManager'); + }; + } + + $('themes-gallery').onclick = function(event) { + window.open(loadTimeData.getString('themesGalleryURL')); + }; + $('themes-reset').onclick = function(event) { + chrome.send('themesReset'); + }; + + // Device section (ChromeOS only). + if (cr.isChromeOS) { + $('keyboard-settings-button').onclick = function(evt) { + OptionsPage.navigateToPage('keyboard-overlay'); + }; + $('pointer-settings-button').onclick = function(evt) { + OptionsPage.navigateToPage('pointer-overlay'); + }; + } + + // Search section. + $('manage-default-search-engines').onclick = function(event) { + OptionsPage.navigateToPage('searchEngines'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ManageSearchEngines']); + }; + $('default-search-engine').addEventListener('change', + this.setDefaultSearchEngine_); + $('instant-enabled-control').customChangeHandler = function(event) { + if (this.checked && !self.instantConfirmDialogShown_) { + OptionsPage.showPageByName('instantConfirm', false); + return true; // Stop default preference processing. + } + return false; // Allow default preference processing. + }; + Preferences.getInstance().addEventListener('instant.confirm_dialog_shown', + this.onInstantConfirmDialogShownChanged_.bind(this)); + + // Users section. + if (loadTimeData.valueExists('profilesInfo')) { + $('profiles-section').hidden = false; + + var profilesList = $('profiles-list'); + options.browser_options.ProfileList.decorate(profilesList); + profilesList.autoExpands = true; + + this.setProfilesInfo_(loadTimeData.getValue('profilesInfo')); + + profilesList.addEventListener('change', + this.setProfileViewButtonsStatus_); + $('profiles-create').onclick = function(event) { + chrome.send('createProfileInfo'); + }; + $('profiles-manage').onclick = function(event) { + ManageProfileOverlay.showManageDialog(); + }; + $('profiles-delete').onclick = function(event) { + var selectedProfile = self.getSelectedProfileItem_(); + if (selectedProfile) + ManageProfileOverlay.showDeleteDialog(selectedProfile); + }; + } + + if (cr.isChromeOS) { + if (!UIAccountTweaks.loggedInAsGuest()) { + $('account-picture-wrapper').onclick = function(event) { + OptionsPage.navigateToPage('changePicture'); + }; + } + + // Username (canonical email) of the currently logged in user or + // |kGuestUser| if a guest session is active. + this.username_ = loadTimeData.getString('username'); + + this.updateAccountPicture_(); + + $('manage-accounts-button').onclick = function(event) { + OptionsPage.navigateToPage('accounts'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ManageAccounts']); + }; + } else { + $('import-data').onclick = function(event) { + ImportDataOverlay.show(); + chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']); + }; + + if ($('themes-GTK-button')) { + $('themes-GTK-button').onclick = function(event) { + chrome.send('themesSetGTK'); + }; + } + } + + // Default browser section. + if (!cr.isChromeOS) { + $('set-as-default-browser').onclick = function(event) { + chrome.send('becomeDefaultBrowser'); + }; + + $('auto-launch').onclick = this.handleAutoLaunchChanged_; + } + + // Privacy section. + $('privacyContentSettingsButton').onclick = function(event) { + OptionsPage.navigateToPage('content'); + OptionsPage.showTab($('cookies-nav-tab')); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ContentSettings']); + }; + $('privacyClearDataButton').onclick = function(event) { + OptionsPage.navigateToPage('clearBrowserData'); + chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']); + }; + // 'spelling-enabled-control' element is only present on Chrome branded + // builds. + if ($('spelling-enabled-control')) { + $('spelling-enabled-control').customChangeHandler = function(event) { + if (this.checked && !self.spellcheckConfirmDialogShown_) { + OptionsPage.showPageByName('spellingConfirm', false); + return true; + } + return false; + }; + Preferences.getInstance().addEventListener( + 'spellcheck.confirm_dialog_shown', + this.onSpellcheckConfirmDialogShownChanged_.bind(this)); + } + // 'metricsReportingEnabled' element is only present on Chrome branded + // builds. + if ($('metricsReportingEnabled')) { + $('metricsReportingEnabled').onclick = function(event) { + chrome.send('metricsReportingCheckboxAction', + [String(event.target.checked)]); + }; + } + + // Bluetooth (CrOS only). + if (cr.isChromeOS) { + options.system.bluetooth.BluetoothDeviceList.decorate( + $('bluetooth-paired-devices-list')); + + $('bluetooth-add-device').onclick = + this.handleAddBluetoothDevice_.bind(this); + + $('enable-bluetooth').onchange = function(event) { + var state = $('enable-bluetooth').checked; + chrome.send('bluetoothEnableChange', [Boolean(state)]); + }; + + $('bluetooth-reconnect-device').onclick = function(event) { + var device = $('bluetooth-paired-devices-list').selectedItem; + var address = device.address; + chrome.send('updateBluetoothDevice', [address, 'connect']); + OptionsPage.closeOverlay(); + }; + + $('bluetooth-reconnect-device').onmousedown = function(event) { + // Prevent 'blur' event, which would reset the list selection, + // thereby disabling the apply button. + event.preventDefault(); + }; + + $('bluetooth-paired-devices-list').addEventListener('change', + function() { + var item = $('bluetooth-paired-devices-list').selectedItem; + var disabled = !item || item.connected; + $('bluetooth-reconnect-device').disabled = disabled; + }); + } + + // Passwords and Forms section. + $('autofill-settings').onclick = function(event) { + OptionsPage.navigateToPage('autofill'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ShowAutofillSettings']); + }; + $('manage-passwords').onclick = function(event) { + OptionsPage.navigateToPage('passwords'); + OptionsPage.showTab($('passwords-nav-tab')); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ShowPasswordManager']); + }; + if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { + // Disable and turn off Autofill in guest mode. + var autofillEnabled = $('autofill-enabled'); + autofillEnabled.disabled = true; + autofillEnabled.checked = false; + cr.dispatchSimpleEvent(autofillEnabled, 'change'); + $('autofill-settings').disabled = true; + + // Disable and turn off Password Manager in guest mode. + var passwordManagerEnabled = $('password-manager-enabled'); + passwordManagerEnabled.disabled = true; + passwordManagerEnabled.checked = false; + cr.dispatchSimpleEvent(passwordManagerEnabled, 'change'); + $('manage-passwords').disabled = true; + } + + if (cr.isMac) { + $('mac-passwords-warning').hidden = + !loadTimeData.getBoolean('multiple_profiles'); + } + + // Network section. + if (!cr.isChromeOS) { + $('proxiesConfigureButton').onclick = function(event) { + chrome.send('showNetworkProxySettings'); + }; + } + + // Web Content section. + $('fontSettingsCustomizeFontsButton').onclick = function(event) { + OptionsPage.navigateToPage('fonts'); + chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']); + }; + $('defaultFontSize').onchange = function(event) { + var value = event.target.options[event.target.selectedIndex].value; + Preferences.setIntegerPref( + 'webkit.webprefs.default_fixed_font_size', + value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, ''); + chrome.send('defaultFontSizeAction', [String(value)]); + }; + $('defaultZoomFactor').onchange = function(event) { + chrome.send('defaultZoomFactorAction', + [String(event.target.options[event.target.selectedIndex].value)]); + }; + + // Languages section. + $('language-button').onclick = function(event) { + OptionsPage.navigateToPage('languages'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_LanuageAndSpellCheckSettings']); + }; + + // Downloads section. + Preferences.getInstance().addEventListener('download.default_directory', + this.onDefaultDownloadDirectoryChanged_.bind(this)); + $('downloadLocationChangeButton').onclick = function(event) { + chrome.send('selectDownloadLocation'); + }; + if (!cr.isChromeOS) { + $('autoOpenFileTypesResetToDefault').onclick = function(event) { + chrome.send('autoOpenFileTypesAction'); + }; + } + + // HTTPS/SSL section. + if (cr.isWindows || cr.isMac) { + $('certificatesManageButton').onclick = function(event) { + chrome.send('showManageSSLCertificates'); + }; + } else { + $('certificatesManageButton').onclick = function(event) { + OptionsPage.navigateToPage('certificates'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_ManageSSLCertificates']); + }; + } + $('sslCheckRevocation').onclick = function(event) { + chrome.send('checkRevocationCheckboxAction', + [String($('sslCheckRevocation').checked)]); + }; + + // Cloud Print section. + // 'cloudPrintProxyEnabled' is true for Chrome branded builds on + // certain platforms, or could be enabled by a lab. + if (!cr.isChromeOS) { + $('cloudPrintConnectorSetupButton').onclick = function(event) { + if ($('cloudPrintManageButton').style.display == 'none') { + // Disable the button, set its text to the intermediate state. + $('cloudPrintConnectorSetupButton').textContent = + loadTimeData.getString('cloudPrintConnectorEnablingButton'); + $('cloudPrintConnectorSetupButton').disabled = true; + chrome.send('showCloudPrintSetupDialog'); + } else { + chrome.send('disableCloudPrintConnector'); + } + }; + } + $('cloudPrintManageButton').onclick = function(event) { + chrome.send('showCloudPrintManagePage'); + }; + + // Accessibility section (CrOS only). + if (cr.isChromeOS) { + $('accessibility-spoken-feedback-check').onchange = function(event) { + chrome.send('spokenFeedbackChange', + [$('accessibility-spoken-feedback-check').checked]); + }; + + $('accessibility-high-contrast-check').onchange = function(event) { + chrome.send('highContrastChange', + [$('accessibility-high-contrast-check').checked]); + }; + + $('accessibility-screen-magnifier-check').onchange = function(event) { + chrome.send('screenMagnifierChange', + [$('accessibility-screen-magnifier-check').checked]); + }; + } + + // Display management section (CrOS only). + if (cr.isChromeOS) { + $('display-options-button').onclick = function(event) { + OptionsPage.navigateToPage('display'); + chrome.send('coreOptionsUserMetricsAction', + ['Options_Display']); + } + } + + // Background mode section. + if ($('backgroundModeCheckbox')) { + cr.defineProperty($('backgroundModeCheckbox'), + 'controlledBy', + cr.PropertyKind.ATTR); + $('backgroundModeCheckbox').onclick = function(event) { + chrome.send('backgroundModeAction', + [String($('backgroundModeCheckbox').checked)]); + }; + } + }, + + /** + * @inheritDoc + */ + didShowPage: function() { + $('search-field').focus(); + }, + + /** + * Event listener for the 'session.restore_on_startup' pref. + * @param {Event} event The preference change event. + * @private + */ + onRestoreOnStartupChanged_: function(event) { + /** @const */ var showPagesValue = Number($('startup-show-pages').value); + /** @const */ var showHomePageValue = 0; + + $('startup-set-pages').disabled = event.value.disabled && + event.value.value != showPagesValue; + + if (event.value.value == showHomePageValue) { + // If the user previously selected "Show the homepage", the + // preference will already be migrated to "Open a specific page". So + // the only way to reach this code is if the 'restore on startup' + // preference is managed. + assert(event.value.controlledBy); + + // Select "open the following pages" and lock down the list of URLs + // to reflect the intention of the policy. + $('startup-show-pages').checked = true; + StartupOverlay.getInstance().setControlsDisabled(true); + } else { + // Re-enable the controls in the startup overlay if necessary. + StartupOverlay.getInstance().updateControlStates(); + } + }, + + /** + * Handler for messages sent from the main uber page. + * @param {Event} e The 'message' event from the uber page. + * @private + */ + handleWindowMessage_: function(e) { + if (e.data.method == 'frameSelected') + $('search-field').focus(); + }, + + /** + * Shows the given section, with animation. + * @param {HTMLElement} section The section to be shown. + * @param {HTMLElement} container The container for the section. Must be + * inside of |section|. + * @private + */ + showSectionWithAnimation_: function(section, container) { + this.addTransitionEndListener_(section); + + // Unhide + section.hidden = false; + + // Delay starting the transition so that hidden change will be + // processed. + setTimeout(function() { + // Reveal the section using a WebKit transition. + section.classList.add('sliding'); + section.style.height = + container.offsetHeight + 'px'; + }, 0); + }, + + /** + * See showSectionWithAnimation_. + */ + hideSectionWithAnimation_: function(section, container) { + this.addTransitionEndListener_(section); + + // Before we start hiding the section, we need to set + // the height to a pixel value. + section.style.height = container.offsetHeight + 'px'; + + // Delay starting the transition so that the height change will be + // processed. + setTimeout(function() { + // Hide the section using a WebKit transition. + section.classList.add('sliding'); + section.style.height = ''; + }, 0); + }, + + /** + * See showSectionWithAnimation_. + */ + toggleSectionWithAnimation_: function(section, container) { + if (section.style.height == '') + this.showSectionWithAnimation_(section, container); + else + this.hideSectionWithAnimation_(section, container); + }, + + /** + * Adds a |webkitTransitionEnd| listener to the given section so that + * it can be animated. The listener will only be added to a given section + * once, so this can be called as multiple times. + * @param {HTMLElement} section The section to be animated. + * @private + */ + addTransitionEndListener_: function(section) { + if (section.hasTransitionEndListener_) + return; + + section.addEventListener('webkitTransitionEnd', + this.onTransitionEnd_.bind(this)); + section.hasTransitionEndListener_ = true; + }, + + /** + * Called after an animation transition has ended. + * @private + */ + onTransitionEnd_: function(event) { + if (event.propertyName != 'height') + return; + + var section = event.target; + + // Disable WebKit transitions. + section.classList.remove('sliding'); + + if (section.style.height == '') { + // Hide the content so it can't get tab focus. + section.hidden = true; + } else { + // Set the section height to 'auto' to allow for size changes + // (due to font change or dynamic content). + section.style.height = 'auto'; + } + }, + + updateAdvancedSettingsExpander_: function() { + var expander = $('advanced-settings-expander'); + if ($('advanced-settings').style.height == '') + expander.textContent = loadTimeData.getString('showAdvancedSettings'); + else + expander.textContent = loadTimeData.getString('hideAdvancedSettings'); + }, + + /** + * Updates the sync section with the given state. + * @param {Object} syncData A bunch of data records that describe the status + * of the sync system. + * @private + */ + updateSyncState_: function(syncData) { + if (!syncData.syncSystemEnabled) { + $('sync-section').hidden = true; + return; + } + + $('sync-section').hidden = false; + this.syncSetupCompleted = syncData.setupCompleted; + $('customize-sync').hidden = !syncData.setupCompleted; + + var startStopButton = $('start-stop-sync'); + startStopButton.disabled = syncData.managed || + syncData.setupInProgress; + startStopButton.hidden = + syncData.setupCompleted && cr.isChromeOS; + startStopButton.textContent = + syncData.setupCompleted ? + loadTimeData.getString('syncButtonTextStop') : + syncData.setupInProgress ? + loadTimeData.getString('syncButtonTextInProgress') : + loadTimeData.getString('syncButtonTextStart'); + + + // TODO(estade): can this just be textContent? + $('sync-status-text').innerHTML = syncData.statusText; + var statusSet = syncData.statusText.length != 0; + $('sync-overview').hidden = statusSet; + $('sync-status').hidden = !statusSet; + + $('sync-action-link').textContent = syncData.actionLinkText; + $('sync-action-link').hidden = syncData.actionLinkText.length == 0; + $('sync-action-link').disabled = syncData.managed; + + if (syncData.hasError) + $('sync-status').classList.add('sync-error'); + else + $('sync-status').classList.remove('sync-error'); + + $('customize-sync').disabled = syncData.hasUnrecoverableError; + // Move #enable-auto-login-checkbox to a different location on CrOS. + if (cr.isChromeOs) { + $('sync-general').insertBefore($('sync-status').nextSibling, + $('enable-auto-login-checkbox')); + } + $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible; + }, + + /** + * Get the start/stop sync button DOM element. Used for testing. + * @return {DOMElement} The start/stop sync button. + * @private + */ + getStartStopSyncButton_: function() { + return $('start-stop-sync'); + }, + + /** + * Event listener for the 'show home button' preference. Shows/hides the + * UI for changing the home page with animation, unless this is the first + * time this function is called, in which case there is no animation. + * @param {Event} event The preference change event. + */ + onShowHomeButtonChanged_: function(event) { + var section = $('change-home-page-section'); + if (this.onShowHomeButtonChangedCalled_) { + var container = $('change-home-page-section-container'); + if (event.value.value) + this.showSectionWithAnimation_(section, container); + else + this.hideSectionWithAnimation_(section, container); + } else { + section.hidden = !event.value.value; + this.onShowHomeButtonChangedCalled_ = true; + } + }, + + /** + * Event listener for the 'homepage is NTP' preference. Updates the label + * next to the 'Change' button. + * @param {Event} event The preference change event. + */ + onHomePageIsNtpChanged_: function(event) { + $('home-page-url').hidden = event.value.value; + $('home-page-ntp').hidden = !event.value.value; + }, + + /** + * Event listener for changes to the homepage preference. Updates the label + * next to the 'Change' button. + * @param {Event} event The preference change event. + */ + onHomePageChanged_: function(event) { + $('home-page-url').textContent = this.stripHttp_(event.value.value); + }, + + /** + * Removes the 'http://' from a URL, like the omnibox does. If the string + * doesn't start with 'http://' it is returned unchanged. + * @param {string} url The url to be processed + * @return {string} The url with the 'http://' removed. + */ + stripHttp_: function(url) { + return url.replace(/^http:\/\//, ''); + }, + + /** + * Shows the autoLaunch preference and initializes its checkbox value. + * @param {bool} enabled Whether autolaunch is enabled or or not. + * @private + */ + updateAutoLaunchState_: function(enabled) { + $('auto-launch-option').hidden = false; + $('auto-launch').checked = enabled; + }, + + /** + * Called when the value of the instant.confirm_dialog_shown preference + * changes. Cache this value. + * @param {Event} event Change event. + * @private + */ + onInstantConfirmDialogShownChanged_: function(event) { + this.instantConfirmDialogShown_ = event.value.value; + }, + + /** + * Called when the value of the spellcheck.confirm_dialog_shown preference + * changes. Cache this value. + * @param {Event} event Change event. + * @private + */ + onSpellcheckConfirmDialogShownChanged_: function(event) { + this.spellcheckConfirmDialogShown_ = event.value.value; + }, + + /** + * Called when the value of the download.default_directory preference + * changes. + * @param {Event} event Change event. + * @private + */ + onDefaultDownloadDirectoryChanged_: function(event) { + $('downloadLocationPath').value = event.value.value; + if (cr.isChromeOS) { + // On ChromeOS, strip out /special for drive paths, and + // /home/chronos/user for local files. + $('downloadLocationPath').value = $('downloadLocationPath').value. + replace(/^\/(special|home\/chronos\/user)/, ''); + } + }, + + /** + * Update the Default Browsers section based on the current state. + * @param {string} statusString Description of the current default state. + * @param {boolean} isDefault Whether or not the browser is currently + * default. + * @param {boolean} canBeDefault Whether or not the browser can be default. + * @private + */ + updateDefaultBrowserState_: function(statusString, isDefault, + canBeDefault) { + if (!cr.isChromeOS) { + var label = $('default-browser-state'); + label.textContent = statusString; + + $('set-as-default-browser').hidden = !canBeDefault || isDefault; + } + }, + + /** + * Clears the search engine popup. + * @private + */ + clearSearchEngines_: function() { + $('default-search-engine').textContent = ''; + }, + + /** + * Updates the search engine popup with the given entries. + * @param {Array} engines List of available search engines. + * @param {number} defaultValue The value of the current default engine. + * @param {boolean} defaultManaged Whether the default search provider is + * managed. If true, the default search provider can't be changed. + * @private + */ + updateSearchEngines_: function(engines, defaultValue, defaultManaged) { + this.clearSearchEngines_(); + engineSelect = $('default-search-engine'); + engineSelect.disabled = defaultManaged; + engineCount = engines.length; + var defaultIndex = -1; + for (var i = 0; i < engineCount; i++) { + var engine = engines[i]; + var option = new Option(engine['name'], engine['index']); + if (defaultValue == option.value) + defaultIndex = i; + engineSelect.appendChild(option); + } + if (defaultIndex >= 0) + engineSelect.selectedIndex = defaultIndex; + }, + + /** + * Set the default search engine based on the popup selection. + * @private + */ + setDefaultSearchEngine_: function() { + var engineSelect = $('default-search-engine'); + var selectedIndex = engineSelect.selectedIndex; + if (selectedIndex >= 0) { + var selection = engineSelect.options[selectedIndex]; + chrome.send('setDefaultSearchEngine', [String(selection.value)]); + } + }, + + /** + * Sets or clear whether Chrome should Auto-launch on computer startup. + * @private + */ + handleAutoLaunchChanged_: function() { + chrome.send('toggleAutoLaunch', [$('auto-launch').checked]); + }, + + /** + * Get the selected profile item from the profile list. This also works + * correctly if the list is not displayed. + * @return {Object} the profile item object, or null if nothing is selected. + * @private + */ + getSelectedProfileItem_: function() { + var profilesList = $('profiles-list'); + if (profilesList.hidden) { + if (profilesList.dataModel.length > 0) + return profilesList.dataModel.item(0); + } else { + return profilesList.selectedItem; + } + return null; + }, + + /** + * Helper function to set the status of profile view buttons to disabled or + * enabled, depending on the number of profiles and selection status of the + * profiles list. + * @private + */ + setProfileViewButtonsStatus_: function() { + var profilesList = $('profiles-list'); + var selectedProfile = profilesList.selectedItem; + var hasSelection = selectedProfile != null; + var hasSingleProfile = profilesList.dataModel.length == 1; + $('profiles-manage').disabled = !hasSelection || + !selectedProfile.isCurrentProfile; + if (hasSelection && !selectedProfile.isCurrentProfile) + $('profiles-manage').title = loadTimeData.getString('currentUserOnly'); + else + $('profiles-manage').title = ''; + $('profiles-delete').disabled = !hasSelection && !hasSingleProfile; + var importData = $('import-data'); + if (importData) { + importData.disabled = $('import-data').disabled = hasSelection && + !selectedProfile.isCurrentProfile; + } + }, + + /** + * Display the correct dialog layout, depending on how many profiles are + * available. + * @param {number} numProfiles The number of profiles to display. + * @private + */ + setProfileViewSingle_: function(numProfiles) { + var hasSingleProfile = numProfiles == 1; + $('profiles-list').hidden = hasSingleProfile; + $('profiles-single-message').hidden = !hasSingleProfile; + $('profiles-manage').hidden = hasSingleProfile; + $('profiles-delete').textContent = hasSingleProfile ? + loadTimeData.getString('profilesDeleteSingle') : + loadTimeData.getString('profilesDelete'); + }, + + /** + * Adds all |profiles| to the list. + * @param {Array.} profiles An array of profile info objects. + * each object is of the form: + * profileInfo = { + * name: "Profile Name", + * iconURL: "chrome://path/to/icon/image", + * filePath: "/path/to/profile/data/on/disk", + * isCurrentProfile: false + * }; + * @private + */ + setProfilesInfo_: function(profiles) { + this.setProfileViewSingle_(profiles.length); + // add it to the list, even if the list is hidden so we can access it + // later. + $('profiles-list').dataModel = new ArrayDataModel(profiles); + + // Received new data. If showing the "manage" overlay, keep it up to + // date. If showing the "delete" overlay, close it. + if (ManageProfileOverlay.getInstance().visible && + !$('manage-profile-overlay-manage').hidden) { + ManageProfileOverlay.showManageDialog(); + } else { + ManageProfileOverlay.getInstance().visible = false; + } + + this.setProfileViewButtonsStatus_(); + }, + + /** + * Returns the currently active profile for this browser window. + * @return {Object} A profile info object. + * @private + */ + getCurrentProfile_: function() { + for (var i = 0; i < $('profiles-list').dataModel.length; i++) { + var profile = $('profiles-list').dataModel.item(i); + if (profile.isCurrentProfile) + return profile; + } + + assert(false, + 'There should always be a current profile, but none found.'); + }, + + setGtkThemeButtonEnabled_: function(enabled) { + if (!cr.isChromeOS && navigator.platform.match(/linux|BSD/i)) + $('themes-GTK-button').disabled = !enabled; + }, + + setThemesResetButtonEnabled_: function(enabled) { + $('themes-reset').disabled = !enabled; + }, + + /** + * (Re)loads IMG element with current user account picture. + * @private + */ + updateAccountPicture_: function() { + var picture = $('account-picture'); + if (picture) { + picture.src = 'chrome://userimage/' + this.username_ + '?id=' + + Date.now(); + } + }, + + /** + * Handle the 'add device' button click. + * @private + */ + handleAddBluetoothDevice_: function() { + $('bluetooth-unpaired-devices-list').clear(); + chrome.send('findBluetoothDevices'); + OptionsPage.showPageByName('bluetooth', false); + }, + + /** + * Set the checked state of the metrics reporting checkbox. + * @private + */ + setMetricsReportingCheckboxState_: function(checked, disabled) { + $('metricsReportingEnabled').checked = checked; + $('metricsReportingEnabled').disabled = disabled; + }, + + /** + * @private + */ + setMetricsReportingSettingVisibility_: function(visible) { + if (visible) + $('metricsReportingSetting').style.display = 'block'; + else + $('metricsReportingSetting').style.display = 'none'; + }, + + /** + * Set the visibility of the password generation checkbox. + * @private + */ + setPasswordGenerationSettingVisibility_: function(visible) { + if (visible) + $('password-generation-checkbox').style.display = 'block'; + else + $('password-generation-checkbox').style.display = 'none'; + }, + + /** + * Set the font size selected item. + * @private + */ + setFontSize_: function(font_size_value) { + var selectCtl = $('defaultFontSize'); + for (var i = 0; i < selectCtl.options.length; i++) { + if (selectCtl.options[i].value == font_size_value) { + selectCtl.selectedIndex = i; + if ($('Custom')) + selectCtl.remove($('Custom').index); + return; + } + } + + // Add/Select Custom Option in the font size label list. + if (!$('Custom')) { + var option = new Option(loadTimeData.getString('fontSizeLabelCustom'), + -1, false, true); + option.setAttribute('id', 'Custom'); + selectCtl.add(option); + } + $('Custom').selected = true; + }, + + /** + * Populate the page zoom selector with values received from the caller. + * @param {Array} items An array of items to populate the selector. + * each object is an array with three elements as follows: + * 0: The title of the item (string). + * 1: The value of the item (number). + * 2: Whether the item should be selected (boolean). + * @private + */ + setupPageZoomSelector_: function(items) { + var element = $('defaultZoomFactor'); + + // Remove any existing content. + element.textContent = ''; + + // Insert new child nodes into select element. + var value, title, selected; + for (var i = 0; i < items.length; i++) { + title = items[i][0]; + value = items[i][1]; + selected = items[i][2]; + element.appendChild(new Option(title, value, false, selected)); + } + }, + + /** + * Shows/hides the autoOpenFileTypesResetToDefault button and label, with + * animation. + * @param {boolean} display Whether to show the button and label or not. + * @private + */ + setAutoOpenFileTypesDisplayed_: function(display) { + if (cr.isChromeOS) + return; + + if ($('advanced-settings').hidden) { + // If the Advanced section is hidden, don't animate the transition. + $('auto-open-file-types-section').hidden = !display; + } else { + if (display) { + this.showSectionWithAnimation_( + $('auto-open-file-types-section'), + $('auto-open-file-types-container')); + } else { + this.hideSectionWithAnimation_( + $('auto-open-file-types-section'), + $('auto-open-file-types-container')); + } + } + }, + + /** + * Set the enabled state for the proxy settings button. + * @private + */ + setupProxySettingsSection_: function(disabled, label) { + if (!cr.isChromeOS) { + $('proxiesConfigureButton').disabled = disabled; + $('proxiesLabel').textContent = label; + } + }, + + /** + * Set the checked state for the sslCheckRevocation checkbox. + * @private + */ + setCheckRevocationCheckboxState_: function(checked, disabled) { + $('sslCheckRevocation').checked = checked; + $('sslCheckRevocation').disabled = disabled; + }, + + /** + * Set the checked state for the backgroundModeCheckbox element. + * @private + */ + setBackgroundModeCheckboxState_: function( + checked, disabled, controlled_by) { + $('backgroundModeCheckbox').checked = checked; + $('backgroundModeCheckbox').disabled = disabled; + $('backgroundModeCheckbox').controlledBy = controlled_by; + OptionsPage.updateManagedBannerVisibility(); + }, + + /** + * Set the Cloud Print proxy UI to enabled, disabled, or processing. + * @private + */ + setupCloudPrintConnectorSection_: function(disabled, label, allowed) { + if (!cr.isChromeOS) { + $('cloudPrintConnectorLabel').textContent = label; + if (disabled || !allowed) { + $('cloudPrintConnectorSetupButton').textContent = + loadTimeData.getString('cloudPrintConnectorDisabledButton'); + $('cloudPrintManageButton').style.display = 'none'; + } else { + $('cloudPrintConnectorSetupButton').textContent = + loadTimeData.getString('cloudPrintConnectorEnabledButton'); + $('cloudPrintManageButton').style.display = 'inline'; + } + $('cloudPrintConnectorSetupButton').disabled = !allowed; + } + }, + + /** + * @private + */ + removeCloudPrintConnectorSection_: function() { + if (!cr.isChromeOS) { + var connectorSectionElm = $('cloud-print-connector-section'); + if (connectorSectionElm) + connectorSectionElm.parentNode.removeChild(connectorSectionElm); + } + }, + + /** + * Set the initial state of the spoken feedback checkbox. + * @private + */ + setSpokenFeedbackCheckboxState_: function(checked) { + $('accessibility-spoken-feedback-check').checked = checked; + }, + + /** + * Set the initial state of the high contrast checkbox. + * @private + */ + setHighContrastCheckboxState_: function(checked) { + $('accessibility-high-contrast-check').checked = checked; + }, + + /** + * Set the initial state of the screen magnifier checkbox. + * @private + */ + setScreenMagnifierCheckboxState_: function(checked) { + $('accessibility-screen-magnifier-check').checked = checked; + }, + + /** + * Set the initial state of the virtual keyboard checkbox. + * @private + */ + setVirtualKeyboardCheckboxState_: function(checked) { + // TODO(zork): Update UI + }, + + /** + * Show/hide mouse settings slider. + * @private + */ + showMouseControls_: function(show) { + $('mouse-settings').hidden = !show; + }, + + /** + * Show/hide touchpad settings slider. + * @private + */ + showTouchpadControls_: function(show) { + $('touchpad-settings').hidden = !show; + }, + + /** + * Show/hide the display options button on the System settings page. + * @private + */ + showDisplayOptions_: function(show) { + $('display-options-section').hidden = !show; + }, + + /** + * Activate the bluetooth settings section on the System settings page. + * @private + */ + showBluetoothSettings_: function() { + $('bluetooth-devices').hidden = false; + }, + + /** + * Dectivates the bluetooth settings section from the System settings page. + * @private + */ + hideBluetoothSettings_: function() { + $('bluetooth-devices').hidden = true; + }, + + /** + * Sets the state of the checkbox indicating if bluetooth is turned on. The + * state of the "Find devices" button and the list of discovered devices may + * also be affected by a change to the state. + * @param {boolean} checked Flag Indicating if Bluetooth is turned on. + * @private + */ + setBluetoothState_: function(checked) { + $('enable-bluetooth').checked = checked; + $('bluetooth-paired-devices-list').parentNode.hidden = !checked; + $('bluetooth-add-device').hidden = !checked; + $('bluetooth-reconnect-device').hidden = !checked; + // Flush list of previously discovered devices if bluetooth is turned off. + if (!checked) { + $('bluetooth-paired-devices-list').clear(); + $('bluetooth-unpaired-devices-list').clear(); + } else { + chrome.send('getPairedBluetoothDevices'); + } + }, + + /** + * Adds an element to the list of available bluetooth devices. If an element + * with a matching address is found, the existing element is updated. + * @param {{name: string, + * address: string, + * paired: boolean, + * bonded: boolean, + * connected: boolean}} device + * Decription of the bluetooth device. + * @private + */ + addBluetoothDevice_: function(device) { + var list = $('bluetooth-unpaired-devices-list'); + if (device.paired) { + // Test to see if the device is currently in the unpaired list, in which + // case it should be removed from that list. + var index = $('bluetooth-unpaired-devices-list').find(device.address); + if (index != undefined) + $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index); + list = $('bluetooth-paired-devices-list'); + } else { + // Test to see if the device is currently in the paired list, in which + // case it should be removed from that list. + var index = $('bluetooth-paired-devices-list').find(device.address); + if (index != undefined) + $('bluetooth-paired-devices-list').deleteItemAtIndex(index); + } + list.appendDevice(device); + + // One device can be in the process of pairing. If found, display + // the Bluetooth pairing overlay. + if (device.pairing) + BluetoothPairing.showDialog(device); + }, + + /** + * Removes an element from the list of available devices. + * @param {string} address Unique address of the device. + * @private + */ + removeBluetoothDevice_: function(address) { + var index = $('bluetooth-unpaired-devices-list').find(address); + if (index != undefined) { + $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index); + } else { + index = $('bluetooth-paired-devices-list').find(address); + if (index != undefined) + $('bluetooth-paired-devices-list').deleteItemAtIndex(index); + } + } + + }; + + //Forward public APIs to private implementations. + [ + 'addBluetoothDevice', + 'getCurrentProfile', + 'getStartStopSyncButton', + 'hideBluetoothSettings', + 'removeCloudPrintConnectorSection', + 'removeBluetoothDevice', + 'setAutoOpenFileTypesDisplayed', + 'setBackgroundModeCheckboxState', + 'setBluetoothState', + 'setCheckRevocationCheckboxState', + 'setFontSize', + 'setGtkThemeButtonEnabled', + 'setHighContrastCheckboxState', + 'setMetricsReportingCheckboxState', + 'setMetricsReportingSettingVisibility', + 'setPasswordGenerationSettingVisibility', + 'setProfilesInfo', + 'setScreenMagnifierCheckboxState', + 'setSpokenFeedbackCheckboxState', + 'setThemesResetButtonEnabled', + 'setupCloudPrintConnectorSection', + 'setupPageZoomSelector', + 'setupProxySettingsSection', + 'setVirtualKeyboardCheckboxState', + 'showBluetoothSettings', + 'showDisplayOptions', + 'showMouseControls', + 'showTouchpadControls', + 'updateAccountPicture', + 'updateAutoLaunchState', + 'updateDefaultBrowserState', + 'updateManagedBannerVisibility', + 'updateSearchEngines', + 'updateSyncState', + 'updateStartupPages', + ].forEach(function(name) { + BrowserOptions[name] = function() { + var instance = BrowserOptions.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + if (cr.isChromeOS) { + /** + * Returns username (canonical email) of the user logged in (ChromeOS only). + * @return {string} user email. + */ + // TODO(jhawkins): Investigate the use case for this method. + BrowserOptions.getLoggedInUsername = function() { + return BrowserOptions.getInstance().username_; + }; + } + + // Export + return { + BrowserOptions: BrowserOptions + }; +}); diff --git a/chrome/browser/resources/options/browser_options_profile_list.js b/chrome/browser/resources/options/browser_options_profile_list.js new file mode 100644 index 0000000..822a379 --- /dev/null +++ b/chrome/browser/resources/options/browser_options_profile_list.js @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.browser_options', function() { + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; + + /** + * Creates a new profile list item. + * @param {Object} profileInfo The profile this item respresents. + * @constructor + * @extends {cr.ui.DeletableItem} + */ + function ProfileListItem(profileInfo) { + var el = cr.doc.createElement('div'); + el.profileInfo_ = profileInfo; + ProfileListItem.decorate(el); + return el; + } + + /** + * Decorates an element as a profile list item. + * @param {!HTMLElement} el The element to decorate. + */ + ProfileListItem.decorate = function(el) { + el.__proto__ = ProfileListItem.prototype; + el.decorate(); + }; + + ProfileListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * @type {string} the file path of this profile list item. + */ + get profilePath() { + return this.profileInfo_.filePath; + }, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + var profileInfo = this.profileInfo_; + + var iconEl = this.ownerDocument.createElement('img'); + iconEl.className = 'profile-img'; + iconEl.src = profileInfo.iconURL; + this.contentElement.appendChild(iconEl); + + var nameEl = this.ownerDocument.createElement('div'); + if (profileInfo.isCurrentProfile) + nameEl.classList.add('profile-item-current'); + this.contentElement.appendChild(nameEl); + + var displayName = profileInfo.name; + if (profileInfo.isCurrentProfile) { + displayName = loadTimeData.getStringF('profilesListItemCurrent', + profileInfo.name); + } + nameEl.textContent = displayName; + + // Ensure that the button cannot be tabbed to for accessibility reasons. + this.closeButtonElement.tabIndex = -1; + }, + }; + + var ProfileList = cr.ui.define('list'); + + ProfileList.prototype = { + __proto__: DeletableItemList.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.selectionModel = new ListSingleSelectionModel(); + }, + + /** @inheritDoc */ + createItem: function(pageInfo) { + var item = new ProfileListItem(pageInfo); + return item; + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + ManageProfileOverlay.showDeleteDialog(this.dataModel.item(index)); + }, + + /** @inheritDoc */ + activateItemAtIndex: function(index) { + // Don't allow the user to edit a profile that is not current. + var profileInfo = this.dataModel.item(index); + if (profileInfo.isCurrentProfile) + ManageProfileOverlay.showManageDialog(profileInfo); + }, + }; + + return { + ProfileList: ProfileList + }; +}); + diff --git a/chrome/browser/resources/options/browser_options_startup_page_list.js b/chrome/browser/resources/options/browser_options_startup_page_list.js new file mode 100644 index 0000000..f88e5d4 --- /dev/null +++ b/chrome/browser/resources/options/browser_options_startup_page_list.js @@ -0,0 +1,310 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.browser_options', function() { + /** @const */ var AutocompleteList = cr.ui.AutocompleteList; + /** @const */ var InlineEditableItem = options.InlineEditableItem; + /** @const */ var InlineEditableItemList = options.InlineEditableItemList; + + /** + * Creates a new startup page list item. + * @param {Object} pageInfo The page this item represents. + * @constructor + * @extends {cr.ui.ListItem} + */ + function StartupPageListItem(pageInfo) { + var el = cr.doc.createElement('div'); + el.pageInfo_ = pageInfo; + StartupPageListItem.decorate(el); + return el; + } + + /** + * Decorates an element as a startup page list item. + * @param {!HTMLElement} el The element to decorate. + */ + StartupPageListItem.decorate = function(el) { + el.__proto__ = StartupPageListItem.prototype; + el.decorate(); + }; + + StartupPageListItem.prototype = { + __proto__: InlineEditableItem.prototype, + + /** + * Input field for editing the page url. + * @type {HTMLElement} + * @private + */ + urlField_: null, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + var pageInfo = this.pageInfo_; + + if (pageInfo['modelIndex'] == '-1') { + this.isPlaceholder = true; + pageInfo['title'] = loadTimeData.getString('startupAddLabel'); + pageInfo['url'] = ''; + } + + var titleEl = this.ownerDocument.createElement('div'); + titleEl.className = 'title'; + titleEl.classList.add('favicon-cell'); + titleEl.classList.add('weakrtl'); + titleEl.textContent = pageInfo['title']; + if (!this.isPlaceholder) { + titleEl.style.backgroundImage = url('chrome://favicon/' + + pageInfo['url']); + titleEl.title = pageInfo['tooltip']; + } + + this.contentElement.appendChild(titleEl); + + var urlEl = this.createEditableTextCell(pageInfo['url']); + urlEl.className = 'url'; + urlEl.classList.add('weakrtl'); + this.contentElement.appendChild(urlEl); + + var urlField = urlEl.querySelector('input'); + urlField.className = 'weakrtl'; + urlField.placeholder = loadTimeData.getString('startupPagesPlaceholder'); + this.urlField_ = urlField; + + this.addEventListener('commitedit', this.onEditCommitted_); + + var self = this; + urlField.addEventListener('focus', function(event) { + self.parentNode.autocompleteList.attachToInput(urlField); + }); + urlField.addEventListener('blur', function(event) { + self.parentNode.autocompleteList.detach(); + }); + + if (!this.isPlaceholder) + this.draggable = true; + }, + + /** @inheritDoc */ + get currentInputIsValid() { + return this.urlField_.validity.valid; + }, + + /** @inheritDoc */ + get hasBeenEdited() { + return this.urlField_.value != this.pageInfo_['url']; + }, + + /** + * Called when committing an edit; updates the model. + * @param {Event} e The end event. + * @private + */ + onEditCommitted_: function(e) { + var url = this.urlField_.value; + if (this.isPlaceholder) + chrome.send('addStartupPage', [url]); + else + chrome.send('editStartupPage', [this.pageInfo_['modelIndex'], url]); + }, + }; + + var StartupPageList = cr.ui.define('list'); + + StartupPageList.prototype = { + __proto__: InlineEditableItemList.prototype, + + /** + * An autocomplete suggestion list for URL editing. + * @type {AutocompleteList} + */ + autocompleteList: null, + + /** + * The drop position information: "below" or "above". + */ + dropPos: null, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItemList.prototype.decorate.call(this); + + // Listen to drag and drop events. + this.addEventListener('dragstart', this.handleDragStart_.bind(this)); + this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); + this.addEventListener('dragover', this.handleDragOver_.bind(this)); + this.addEventListener('drop', this.handleDrop_.bind(this)); + this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); + this.addEventListener('dragend', this.handleDragEnd_.bind(this)); + }, + + /** @inheritDoc */ + createItem: function(pageInfo) { + var item = new StartupPageListItem(pageInfo); + item.urlField_.disabled = this.disabled; + return item; + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + chrome.send('removeStartupPages', [String(index)]); + }, + + /** + * Computes the target item of drop event. + * @param {Event} e The drop or dragover event. + * @private + */ + getTargetFromDropEvent_: function(e) { + var target = e.target; + // e.target may be an inner element of the list item + while (target != null && !(target instanceof StartupPageListItem)) { + target = target.parentNode; + } + return target; + }, + + /** + * Handles the dragstart event. + * @param {Event} e The dragstart event. + * @private + */ + handleDragStart_: function(e) { + // Prevent dragging if the list is disabled. + if (this.disabled) { + e.preventDefault(); + return false; + } + + var target = e.target; + // StartupPageListItem should be the only draggable element type in the + // page but let's make sure. + if (target instanceof StartupPageListItem) { + this.draggedItem = target; + this.draggedItem.editable = false; + e.dataTransfer.effectAllowed = 'move'; + // We need to put some kind of data in the drag or it will be + // ignored. Use the URL in case the user drags to a text field or the + // desktop. + e.dataTransfer.setData('text/plain', target.urlField_.value); + } + }, + + /* + * Handles the dragenter event. + * @param {Event} e The dragenter event. + * @private + */ + handleDragEnter_: function(e) { + e.preventDefault(); + }, + + /* + * Handles the dragover event. + * @param {Event} e The dragover event. + * @private + */ + handleDragOver_: function(e) { + var dropTarget = this.getTargetFromDropEvent_(e); + // Determines whether the drop target is to accept the drop. + // The drop is only successful on another StartupPageListItem. + if (!(dropTarget instanceof StartupPageListItem) || + dropTarget == this.draggedItem || dropTarget.isPlaceholder) { + this.hideDropMarker_(); + return; + } + // Compute the drop postion. Should we move the dragged item to + // below or above the drop target? + var rect = dropTarget.getBoundingClientRect(); + var dy = e.clientY - rect.top; + var yRatio = dy / rect.height; + var dropPos = yRatio <= .5 ? 'above' : 'below'; + this.dropPos = dropPos; + this.showDropMarker_(dropTarget, dropPos); + e.preventDefault(); + }, + + /* + * Handles the drop event. + * @param {Event} e The drop event. + * @private + */ + handleDrop_: function(e) { + var dropTarget = this.getTargetFromDropEvent_(e); + this.hideDropMarker_(); + + // Insert the selection at the new position. + var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_); + if (this.dropPos == 'below') + newIndex += 1; + + var selected = this.selectionModel.selectedIndexes; + var stringized_selected = []; + for (var j = 0; j < selected.length; j++) + stringized_selected.push(String(selected[j])); + + chrome.send('dragDropStartupPage', + [String(newIndex), stringized_selected]); + }, + + /** + * Handles the dragleave event. + * @param {Event} e The dragleave event. + * @private + */ + handleDragLeave_: function(e) { + this.hideDropMarker_(); + }, + + /** + * Handles the dragend event. + * @param {Event} e The dragend event. + * @private + */ + handleDragEnd_: function(e) { + this.draggedItem.editable = true; + this.draggedItem.updateEditState(); + }, + + /** + * Shows and positions the marker to indicate the drop target. + * @param {HTMLElement} target The current target list item of drop. + * @param {string} pos 'below' or 'above'. + * @private + */ + showDropMarker_: function(target, pos) { + window.clearTimeout(this.hideDropMarkerTimer_); + var marker = $('startupPagesListDropmarker'); + var rect = target.getBoundingClientRect(); + var markerHeight = 6; + if (pos == 'above') { + marker.style.top = (rect.top - markerHeight / 2) + 'px'; + } else { + marker.style.top = (rect.bottom - markerHeight / 2) + 'px'; + } + marker.style.width = rect.width + 'px'; + marker.style.left = rect.left + 'px'; + marker.style.display = 'block'; + }, + + /** + * Hides the drop marker. + * @private + */ + hideDropMarker_: function() { + // Hide the marker in a timeout to reduce flickering as we move between + // valid drop targets. + window.clearTimeout(this.hideDropMarkerTimer_); + this.hideDropMarkerTimer_ = window.setTimeout(function() { + $('startupPagesListDropmarker').style.display = ''; + }, 100); + }, + }; + + return { + StartupPageList: StartupPageList + }; +}); diff --git a/chrome/browser/resources/options/certificate_backup_overlay.html b/chrome/browser/resources/options/certificate_backup_overlay.html new file mode 100644 index 0000000..146b38f --- /dev/null +++ b/chrome/browser/resources/options/certificate_backup_overlay.html @@ -0,0 +1,39 @@ + diff --git a/chrome/browser/resources/options/certificate_backup_overlay.js b/chrome/browser/resources/options/certificate_backup_overlay.js new file mode 100644 index 0000000..c904a47 --- /dev/null +++ b/chrome/browser/resources/options/certificate_backup_overlay.js @@ -0,0 +1,117 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * CertificateBackupOverlay class + * Encapsulated handling of the 'enter backup password' overlay page. + * @class + */ + function CertificateBackupOverlay() { + OptionsPage.call(this, 'certificateBackupOverlay', + '', + 'certificateBackupOverlay'); + } + + cr.addSingletonGetter(CertificateBackupOverlay); + + CertificateBackupOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('certificateBackupCancelButton').onclick = function(event) { + self.cancelBackup_(); + }; + $('certificateBackupOkButton').onclick = function(event) { + self.finishBackup_(); + }; + var onBackupPasswordInput = function(event) { + self.comparePasswords_(); + }; + $('certificateBackupPassword').oninput = onBackupPasswordInput; + $('certificateBackupPassword2').oninput = onBackupPasswordInput; + + self.clearInputFields_(); + }, + + /** + * Clears any uncommitted input, and dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + OptionsPage.closeOverlay(); + }, + + /** + * Attempt the Backup operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishBackup_: function() { + chrome.send('exportPersonalCertificatePasswordSelected', + [$('certificateBackupPassword').value]); + $('certificateBackupCancelButton').disabled = true; + $('certificateBackupOkButton').disabled = true; + $('certificateBackupPassword').disabled = true; + $('certificateBackupPassword2').disabled = true; + }, + + /** + * Cancel the Backup operation. + * @private + */ + cancelBackup_: function() { + chrome.send('cancelImportExportCertificate'); + this.dismissOverlay_(); + }, + + /** + * Compares the password fields and sets the button state appropriately. + * @private + */ + comparePasswords_: function() { + var password1 = $('certificateBackupPassword').value; + var password2 = $('certificateBackupPassword2').value; + $('certificateBackupOkButton').disabled = + !password1 || password1 != password2; + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + $('certificateBackupPassword').value = ''; + $('certificateBackupPassword2').value = ''; + $('certificateBackupPassword').disabled = false; + $('certificateBackupPassword2').disabled = false; + $('certificateBackupCancelButton').disabled = false; + $('certificateBackupOkButton').disabled = true; + }, + }; + + CertificateBackupOverlay.show = function() { + CertificateBackupOverlay.getInstance().clearInputFields_(); + OptionsPage.navigateToPage('certificateBackupOverlay'); + }; + + CertificateBackupOverlay.dismiss = function() { + CertificateBackupOverlay.getInstance().dismissOverlay_(); + }; + + // Export + return { + CertificateBackupOverlay: CertificateBackupOverlay + }; +}); diff --git a/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html new file mode 100644 index 0000000..d875c27 --- /dev/null +++ b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.html @@ -0,0 +1,38 @@ + diff --git a/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.js b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.js new file mode 100644 index 0000000..5f017c8 --- /dev/null +++ b/chrome/browser/resources/options/certificate_edit_ca_trust_overlay.js @@ -0,0 +1,164 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * CertificateEditCaTrustOverlay class + * Encapsulated handling of the 'edit ca trust' and 'import ca' overlay pages. + * @class + */ + function CertificateEditCaTrustOverlay() { + OptionsPage.call(this, 'certificateEditCaTrustOverlay', + '', + 'certificateEditCaTrustOverlay'); + } + + cr.addSingletonGetter(CertificateEditCaTrustOverlay); + + CertificateEditCaTrustOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + OptionsPage.closeOverlay(); + }, + + /** + * Enables or disables input fields. + * @private + */ + enableInputs_: function(enabled) { + $('certificateCaTrustSSLCheckbox').disabled = + $('certificateCaTrustEmailCheckbox').disabled = + $('certificateCaTrustObjSignCheckbox').disabled = + $('certificateEditCaTrustCancelButton').disabled = + $('certificateEditCaTrustOkButton').disabled = !enabled; + }, + + /** + * Attempt the Edit operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishEdit_: function() { + // TODO(mattm): Send checked values as booleans. For now send them as + // strings, since WebUIBindings::send does not support any other types :( + chrome.send('editCaCertificateTrust', + [this.certId, + $('certificateCaTrustSSLCheckbox').checked.toString(), + $('certificateCaTrustEmailCheckbox').checked.toString(), + $('certificateCaTrustObjSignCheckbox').checked.toString()]); + this.enableInputs_(false); + }, + + /** + * Cancel the Edit operation. + * @private + */ + cancelEdit_: function() { + this.dismissOverlay_(); + }, + + /** + * Attempt the Import operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishImport_: function() { + // TODO(mattm): Send checked values as booleans. For now send them as + // strings, since WebUIBindings::send does not support any other types :( + chrome.send('importCaCertificateTrustSelected', + [$('certificateCaTrustSSLCheckbox').checked.toString(), + $('certificateCaTrustEmailCheckbox').checked.toString(), + $('certificateCaTrustObjSignCheckbox').checked.toString()]); + this.enableInputs_(false); + }, + + /** + * Cancel the Import operation. + * @private + */ + cancelImport_: function() { + chrome.send('cancelImportExportCertificate'); + this.dismissOverlay_(); + }, + }; + + /** + * Callback from CertificateManagerHandler with the trust values. + * @param {boolean} trustSSL The initial value of SSL trust checkbox. + * @param {boolean} trustEmail The initial value of Email trust checkbox. + * @param {boolean} trustObjSign The initial value of Object Signing trust. + */ + CertificateEditCaTrustOverlay.populateTrust = function( + trustSSL, trustEmail, trustObjSign) { + $('certificateCaTrustSSLCheckbox').checked = trustSSL; + $('certificateCaTrustEmailCheckbox').checked = trustEmail; + $('certificateCaTrustObjSignCheckbox').checked = trustObjSign; + CertificateEditCaTrustOverlay.getInstance().enableInputs_(true); + } + + /** + * Show the Edit CA Trust overlay. + * @param {string} certId The id of the certificate to be passed to the + * certificate manager model. + * @param {string} certName The display name of the certificate. + * checkbox. + */ + CertificateEditCaTrustOverlay.show = function(certId, certName) { + var self = CertificateEditCaTrustOverlay.getInstance(); + self.certId = certId; + $('certificateEditCaTrustCancelButton').onclick = function(event) { + self.cancelEdit_(); + } + $('certificateEditCaTrustOkButton').onclick = function(event) { + self.finishEdit_(); + } + $('certificateEditCaTrustDescription').textContent = + loadTimeData.getStringF('certificateEditCaTrustDescriptionFormat', + certName); + self.enableInputs_(false); + OptionsPage.navigateToPage('certificateEditCaTrustOverlay'); + chrome.send('getCaCertificateTrust', [certId]); + } + + /** + * Show the Import CA overlay. + * @param {string} certId The id of the certificate to be passed to the + * certificate manager model. + * @param {string} certName The display name of the certificate. + * checkbox. + */ + CertificateEditCaTrustOverlay.showImport = function(certName) { + var self = CertificateEditCaTrustOverlay.getInstance(); + // TODO(mattm): do we want a view certificate button here like firefox has? + $('certificateEditCaTrustCancelButton').onclick = function(event) { + self.cancelImport_(); + } + $('certificateEditCaTrustOkButton').onclick = function(event) { + self.finishImport_(); + } + $('certificateEditCaTrustDescription').textContent = + loadTimeData.getStringF('certificateImportCaDescriptionFormat', + certName); + CertificateEditCaTrustOverlay.populateTrust(false, false, false); + OptionsPage.navigateToPage('certificateEditCaTrustOverlay'); + } + + CertificateEditCaTrustOverlay.dismiss = function() { + CertificateEditCaTrustOverlay.getInstance().dismissOverlay_(); + }; + + // Export + return { + CertificateEditCaTrustOverlay: CertificateEditCaTrustOverlay + }; +}); diff --git a/chrome/browser/resources/options/certificate_import_error_overlay.html b/chrome/browser/resources/options/certificate_import_error_overlay.html new file mode 100644 index 0000000..d3946b4 --- /dev/null +++ b/chrome/browser/resources/options/certificate_import_error_overlay.html @@ -0,0 +1,14 @@ + diff --git a/chrome/browser/resources/options/certificate_import_error_overlay.js b/chrome/browser/resources/options/certificate_import_error_overlay.js new file mode 100644 index 0000000..4eed1d2 --- /dev/null +++ b/chrome/browser/resources/options/certificate_import_error_overlay.js @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + + /** + * CertificateImportErrorOverlay class + * Displays a list of certificates and errors. + * @class + */ + function CertificateImportErrorOverlay() { + OptionsPage.call(this, 'certificateImportErrorOverlay', '', + 'certificateImportErrorOverlay'); + } + + cr.addSingletonGetter(CertificateImportErrorOverlay); + + CertificateImportErrorOverlay.prototype = { + // Inherit CertificateImportErrorOverlay from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + OptionsPage.prototype.initializePage.call(this); + + $('certificateImportErrorOverlayOk').onclick = function(event) { + OptionsPage.closeOverlay(); + }; + }, + }; + + /** + * Show an alert overlay with the given message, button titles, and + * callbacks. + * @param {string} title The alert title to display to the user. + * @param {string} message The alert message to display to the user. + * @param {Array} certErrors The list of cert errors. Each error should have + * a .name and .error attribute. + */ + CertificateImportErrorOverlay.show = function(title, message, certErrors) { + $('certificateImportErrorOverlayTitle').textContent = title; + $('certificateImportErrorOverlayMessage').textContent = message; + + ul = $('certificateImportErrorOverlayCertErrors'); + ul.innerHTML = ''; + for (var i = 0; i < certErrors.length; ++i) { + li = document.createElement('li'); + li.textContent = loadTimeData.getStringF('certificateImportErrorFormat', + certErrors[i].name, + certErrors[i].error); + ul.appendChild(li); + } + + OptionsPage.navigateToPage('certificateImportErrorOverlay'); + } + + // Export + return { + CertificateImportErrorOverlay: CertificateImportErrorOverlay + }; + +}); diff --git a/chrome/browser/resources/options/certificate_manager.css b/chrome/browser/resources/options/certificate_manager.css new file mode 100644 index 0000000..0516e5b --- /dev/null +++ b/chrome/browser/resources/options/certificate_manager.css @@ -0,0 +1,32 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#certificateManagerPage { + min-width: 700px; +} + +#certificateRestoreOverlay h1 { + /* Leave enough space for the close-button. */ + /* TODO(KGR): make dialogs to use flex-boxes instead of relying on padding. */ + padding-right: 35px; +} + +/* Force tab strip to extend to the left and right edges of the window. */ +#certificate-manager-content-area { + padding: 6px 0 6px 0; +} + +#certificate-manager-content-area .subpages-tab-contents { + padding-left: 28px; + padding-right: 14px; +} + +.certificate-tree-table { + width: 100%; +} + +.certificate-tree { + /* TODO(mattm): BLAH. Make this not statically sized. */ + height: 300px; +} diff --git a/chrome/browser/resources/options/certificate_manager.html b/chrome/browser/resources/options/certificate_manager.html new file mode 100644 index 0000000..47d59d0 --- /dev/null +++ b/chrome/browser/resources/options/certificate_manager.html @@ -0,0 +1,142 @@ + diff --git a/chrome/browser/resources/options/certificate_manager.js b/chrome/browser/resources/options/certificate_manager.js new file mode 100644 index 0000000..480b8e1 --- /dev/null +++ b/chrome/browser/resources/options/certificate_manager.js @@ -0,0 +1,255 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + ///////////////////////////////////////////////////////////////////////////// + // CertificateManagerTab class: + + /** + * blah + * @param {!string} id The id of this tab. + */ + function CertificateManagerTab(id) { + this.tree = $(id + '-tree'); + + options.CertificatesTree.decorate(this.tree); + this.tree.addEventListener('change', + this.handleCertificatesTreeChange_.bind(this)); + + var tree = this.tree; + + this.viewButton = $(id + '-view'); + this.viewButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('viewCertificate', [selected.data.id]); + } + + this.editButton = $(id + '-edit'); + if (this.editButton !== null) { + if (id == 'serverCertsTab') { + this.editButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('editServerCertificate', [selected.data.id]); + } + } else if (id == 'caCertsTab') { + this.editButton.onclick = function(e) { + var data = tree.selectedItem.data; + CertificateEditCaTrustOverlay.show(data.id, data.name); + } + } + } + + this.backupButton = $(id + '-backup'); + if (this.backupButton !== null) { + this.backupButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('exportPersonalCertificate', [selected.data.id]); + } + } + + this.backupAllButton = $(id + '-backup-all'); + if (this.backupAllButton !== null) { + this.backupAllButton.onclick = function(e) { + chrome.send('exportAllPersonalCertificates'); + } + } + + this.importButton = $(id + '-import'); + if (this.importButton !== null) { + if (id == 'personalCertsTab') { + this.importButton.onclick = function(e) { + chrome.send('importPersonalCertificate', [false]); + } + } else if (id == 'serverCertsTab') { + this.importButton.onclick = function(e) { + chrome.send('importServerCertificate'); + } + } else if (id == 'caCertsTab') { + this.importButton.onclick = function(e) { + chrome.send('importCaCertificate'); + } + } + } + + this.importAndBindButton = $(id + '-import-and-bind'); + if (this.importAndBindButton !== null) { + if (id == 'personalCertsTab') { + this.importAndBindButton.onclick = function(e) { + chrome.send('importPersonalCertificate', [true]); + } + } + } + + this.exportButton = $(id + '-export'); + if (this.exportButton !== null) { + this.exportButton.onclick = function(e) { + var selected = tree.selectedItem; + chrome.send('exportCertificate', [selected.data.id]); + } + } + + this.deleteButton = $(id + '-delete'); + this.deleteButton.onclick = function(e) { + var data = tree.selectedItem.data; + AlertOverlay.show( + loadTimeData.getStringF(id + 'DeleteConfirm', data.name), + loadTimeData.getString(id + 'DeleteImpact'), + loadTimeData.getString('ok'), + loadTimeData.getString('cancel'), + function() { + tree.selectedItem = null; + chrome.send('deleteCertificate', [data.id]); + }); + } + } + + CertificateManagerTab.prototype = { + + /** + * Update button state. + * @private + * @param {!Object} data The data of the selected item. + */ + updateButtonState: function(data) { + var isCert = !!data && data.isCert; + var readOnly = !!data && data.readonly; + var extractable = !!data && data.extractable; + var hasChildren = this.tree.items.length > 0; + this.viewButton.disabled = !isCert; + if (this.editButton !== null) + this.editButton.disabled = !isCert; + if (this.backupButton !== null) + this.backupButton.disabled = !isCert || !extractable; + if (this.backupAllButton !== null) + this.backupAllButton.disabled = !hasChildren; + if (this.exportButton !== null) + this.exportButton.disabled = !isCert; + this.deleteButton.disabled = !isCert || readOnly; + }, + + /** + * Handles certificate tree selection change. + * @private + * @param {!Event} e The change event object. + */ + handleCertificatesTreeChange_: function(e) { + var data = null; + if (this.tree.selectedItem) { + data = this.tree.selectedItem.data; + } + + this.updateButtonState(data); + }, + }; + + // TODO(xiyuan): Use notification from backend instead of polling. + // TPM token check polling timer. + var tpmPollingTimer; + + // Initiate tpm token check if needed. + function checkTpmToken() { + var importAndBindButton = $('personalCertsTab-import-and-bind'); + + if (importAndBindButton && importAndBindButton.disabled) + chrome.send('checkTpmTokenReady'); + } + + // Stop tpm polling timer. + function stopTpmTokenCheckPolling() { + if (tpmPollingTimer) { + window.clearTimeout(tpmPollingTimer); + tpmPollingTimer = undefined; + } + } + + ///////////////////////////////////////////////////////////////////////////// + // CertificateManager class: + + /** + * Encapsulated handling of ChromeOS accounts options page. + * @constructor + */ + function CertificateManager(model) { + OptionsPage.call(this, 'certificates', + loadTimeData.getString('certificateManagerPageTabTitle'), + 'certificateManagerPage'); + } + + cr.addSingletonGetter(CertificateManager); + + CertificateManager.prototype = { + __proto__: OptionsPage.prototype, + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.personalTab = new CertificateManagerTab('personalCertsTab'); + this.serverTab = new CertificateManagerTab('serverCertsTab'); + this.caTab = new CertificateManagerTab('caCertsTab'); + this.otherTab = new CertificateManagerTab('otherCertsTab'); + + this.addEventListener('visibleChange', this.handleVisibleChange_); + + $('certificate-confirm').onclick = function() { + OptionsPage.closeOverlay(); + }; + }, + + initalized_: false, + + /** + * Handler for OptionsPage's visible property change event. + * @private + * @param {Event} e Property change event. + */ + handleVisibleChange_: function(e) { + if (!this.initalized_ && this.visible) { + this.initalized_ = true; + OptionsPage.showTab($('personal-certs-nav-tab')); + chrome.send('populateCertificateManager'); + } + + if (cr.isChromeOS) { + // Ensure TPM token check on visible and stop polling when hidden. + if (this.visible) + checkTpmToken(); + else + stopTpmTokenCheckPolling(); + } + } + }; + + // CertificateManagerHandler callbacks. + CertificateManager.onPopulateTree = function(args) { + $(args[0]).populate(args[1]); + }; + + CertificateManager.exportPersonalAskPassword = function(args) { + CertificateBackupOverlay.show(); + }; + + CertificateManager.importPersonalAskPassword = function(args) { + CertificateRestoreOverlay.show(); + }; + + CertificateManager.onCheckTpmTokenReady = function(ready) { + var importAndBindButton = $('personalCertsTab-import-and-bind'); + if (importAndBindButton) { + importAndBindButton.disabled = !ready; + + // Check again after 5 seconds if Tpm is not ready and certificate manager + // is still visible. + if (!ready && CertificateManager.getInstance().visible) + tpmPollingTimer = window.setTimeout(checkTpmToken, 5000); + } + }; + + // Export + return { + CertificateManagerTab: CertificateManagerTab, + CertificateManager: CertificateManager + }; +}); diff --git a/chrome/browser/resources/options/certificate_restore_overlay.html b/chrome/browser/resources/options/certificate_restore_overlay.html new file mode 100644 index 0000000..82cc202 --- /dev/null +++ b/chrome/browser/resources/options/certificate_restore_overlay.html @@ -0,0 +1,18 @@ + diff --git a/chrome/browser/resources/options/certificate_restore_overlay.js b/chrome/browser/resources/options/certificate_restore_overlay.js new file mode 100644 index 0000000..2e16fbf --- /dev/null +++ b/chrome/browser/resources/options/certificate_restore_overlay.js @@ -0,0 +1,101 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * CertificateRestoreOverlay class + * Encapsulated handling of the 'enter restore password' overlay page. + * @class + */ + function CertificateRestoreOverlay() { + OptionsPage.call(this, 'certificateRestore', '', + 'certificateRestoreOverlay'); + } + + cr.addSingletonGetter(CertificateRestoreOverlay); + + CertificateRestoreOverlay.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var self = this; + $('certificateRestoreCancelButton').onclick = function(event) { + self.cancelRestore_(); + }; + $('certificateRestoreOkButton').onclick = function(event) { + self.finishRestore_(); + }; + + self.clearInputFields_(); + }, + + /** @inheritDoc */ + didShowPage: function() { + $('certificateRestorePassword').focus(); + }, + + /** + * Clears any uncommitted input, and dismisses the overlay. + * @private + */ + dismissOverlay_: function() { + this.clearInputFields_(); + OptionsPage.closeOverlay(); + }, + + /** + * Attempt the restore operation. + * The overlay will be left up with inputs disabled until the backend + * finishes and dismisses it. + * @private + */ + finishRestore_: function() { + chrome.send('importPersonalCertificatePasswordSelected', + [$('certificateRestorePassword').value]); + $('certificateRestoreCancelButton').disabled = true; + $('certificateRestoreOkButton').disabled = true; + }, + + /** + * Cancel the restore operation. + * @private + */ + cancelRestore_: function() { + chrome.send('cancelImportExportCertificate'); + this.dismissOverlay_(); + }, + + /** + * Clears the value of each input field. + * @private + */ + clearInputFields_: function() { + $('certificateRestorePassword').value = ''; + $('certificateRestoreCancelButton').disabled = false; + $('certificateRestoreOkButton').disabled = false; + }, + }; + + CertificateRestoreOverlay.show = function() { + CertificateRestoreOverlay.getInstance().clearInputFields_(); + OptionsPage.navigateToPage('certificateRestore'); + }; + + CertificateRestoreOverlay.dismiss = function() { + CertificateRestoreOverlay.getInstance().dismissOverlay_(); + }; + + // Export + return { + CertificateRestoreOverlay: CertificateRestoreOverlay + }; + +}); diff --git a/chrome/browser/resources/options/certificate_tree.css b/chrome/browser/resources/options/certificate_tree.css new file mode 100644 index 0000000..d8f5350 --- /dev/null +++ b/chrome/browser/resources/options/certificate_tree.css @@ -0,0 +1,12 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +span.cert-untrusted { + background-color: pink; + border: 1px solid red; + border-radius: 3px; + margin-right: 3px; + padding-left: 1px; + padding-right: 1px; +} diff --git a/chrome/browser/resources/options/certificate_tree.js b/chrome/browser/resources/options/certificate_tree.js new file mode 100644 index 0000000..4e44bc1 --- /dev/null +++ b/chrome/browser/resources/options/certificate_tree.js @@ -0,0 +1,154 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var Tree = cr.ui.Tree; + /** @const */ var TreeItem = cr.ui.TreeItem; + + /** + * Creates a new tree folder for certificate data. + * @param {Object=} data Data used to create a certificate tree folder. + * @constructor + * @extends {TreeItem} + */ + function CertificateTreeFolder(data) { + data.isCert = false; + var treeFolder = new TreeItem({ + label: data.name, + data: data + }); + treeFolder.__proto__ = CertificateTreeFolder.prototype; + + if (data.icon) + treeFolder.icon = data.icon; + + return treeFolder; + } + + CertificateTreeFolder.prototype = { + __proto__: TreeItem.prototype, + + /** + * The tree path id/. + * @type {string} + */ + get pathId() { + return this.data.id; + } + }; + + /** + * Creates a new tree item for certificate data. + * @param {Object=} data Data used to create a certificate tree item. + * @constructor + * @extends {TreeItem} + */ + function CertificateTreeItem(data) { + data.isCert = true; + // TODO(mattm): other columns + var treeItem = new TreeItem({ + label: data.name, + data: data + }); + treeItem.__proto__ = CertificateTreeItem.prototype; + + if (data.icon) + treeItem.icon = data.icon; + + if (data.untrusted) { + var badge = document.createElement('span'); + badge.classList.add('cert-untrusted'); + badge.textContent = loadTimeData.getString('badgeCertUntrusted'); + treeItem.labelElement.insertBefore( + badge, treeItem.labelElement.firstChild); + } + + return treeItem; + } + + CertificateTreeItem.prototype = { + __proto__: TreeItem.prototype, + + /** + * The tree path id/. + * @type {string} + */ + get pathId() { + return this.parentItem.pathId + ',' + this.data.id; + } + }; + + /** + * Creates a new cookies tree. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {Tree} + */ + var CertificatesTree = cr.ui.define('tree'); + + CertificatesTree.prototype = { + __proto__: Tree.prototype, + + /** @inheritDoc */ + decorate: function() { + Tree.prototype.decorate.call(this); + this.treeLookup_ = {}; + }, + + /** @inheritDoc */ + addAt: function(child, index) { + Tree.prototype.addAt.call(this, child, index); + if (child.data && child.data.id) + this.treeLookup_[child.data.id] = child; + }, + + /** @inheritDoc */ + remove: function(child) { + Tree.prototype.remove.call(this, child); + if (child.data && child.data.id) + delete this.treeLookup_[child.data.id]; + }, + + /** + * Clears the tree. + */ + clear: function() { + // Remove all fields without recreating the object since other code + // references it. + for (var id in this.treeLookup_) + delete this.treeLookup_[id]; + this.textContent = ''; + }, + + /** + * Populate the tree. + * @param {Array} nodesData Nodes data array. + */ + populate: function(nodesData) { + this.clear(); + + for (var i = 0; i < nodesData.length; ++i) { + var subnodes = nodesData[i]['subnodes']; + delete nodesData[i]['subnodes']; + + var item = new CertificateTreeFolder(nodesData[i]); + this.addAt(item, i); + + for (var j = 0; j < subnodes.length; ++j) { + var subitem = new CertificateTreeItem(subnodes[j]); + item.addAt(subitem, j); + } + // Make tree expanded by default. + item.expanded = true; + } + + cr.dispatchSimpleEvent(this, 'change'); + }, + }; + + return { + CertificatesTree: CertificatesTree + }; +}); + diff --git a/chrome/browser/resources/options/chromeos/OWNERS b/chrome/browser/resources/options/chromeos/OWNERS new file mode 100644 index 0000000..1bb1ea6 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/OWNERS @@ -0,0 +1,6 @@ +dpolukhin@chromium.org +glotov@chromium.org +nkostylev@chromium.org + +# Change Picture and Accounts pages. +ivankr@chromium.org diff --git a/chrome/browser/resources/options/chromeos/accounts_options.html b/chrome/browser/resources/options/chromeos/accounts_options.html new file mode 100644 index 0000000..adb451a --- /dev/null +++ b/chrome/browser/resources/options/chromeos/accounts_options.html @@ -0,0 +1,67 @@ + diff --git a/chrome/browser/resources/options/chromeos/accounts_options.js b/chrome/browser/resources/options/chromeos/accounts_options.js new file mode 100644 index 0000000..a4faaa6 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/accounts_options.js @@ -0,0 +1,156 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + ///////////////////////////////////////////////////////////////////////////// + // AccountsOptions class: + + /** + * Encapsulated handling of ChromeOS accounts options page. + * @constructor + */ + function AccountsOptions(model) { + OptionsPage.call(this, 'accounts', + loadTimeData.getString('accountsPageTabTitle'), + 'accountsPage'); + // Whether to show the whitelist. + this.showWhitelist_ = false; + } + + cr.addSingletonGetter(AccountsOptions); + + AccountsOptions.prototype = { + // Inherit AccountsOptions from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initializes AccountsOptions page. + */ + initializePage: function() { + // Call base class implementation to starts preference initialization. + OptionsPage.prototype.initializePage.call(this); + + // Set up accounts page. + var userList = $('userList'); + userList.addEventListener('remove', this.handleRemoveUser_); + + var userNameEdit = $('userNameEdit'); + options.accounts.UserNameEdit.decorate(userNameEdit); + userNameEdit.addEventListener('add', this.handleAddUser_); + + // If the current user is not the owner, show some warning, + // and do not show the user list. + this.showWhitelist_ = UIAccountTweaks.currentUserIsOwner(); + if (this.showWhitelist_) { + options.accounts.UserList.decorate(userList); + } else { + if (!AccountsOptions.whitelistIsManaged()) { + $('ownerOnlyWarning').hidden = false; + } else { + this.managed = true; + } + } + + this.addEventListener('visibleChange', this.handleVisibleChange_); + + $('useWhitelistCheck').addEventListener('change', + this.handleUseWhitelistCheckChange_.bind(this)); + + Preferences.getInstance().addEventListener( + $('useWhitelistCheck').pref, + this.handleUseWhitelistPrefChange_.bind(this)); + + $('accounts-options-overlay-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + /** + * Update user list control state. + * @private + */ + updateControls_: function() { + $('userList').disabled = + $('userNameEdit').disabled = !this.showWhitelist_ || + AccountsOptions.whitelistIsManaged() || + !$('useWhitelistCheck').checked; + }, + + /** + * Handler for OptionsPage's visible property change event. + * @private + * @param {Event} e Property change event. + */ + handleVisibleChange_: function(e) { + if (this.visible) { + this.updateControls_(); + if (this.showWhitelist_) + $('userList').redraw(); + } + }, + + /** + * Handler for allow guest check change. + * @private + */ + handleUseWhitelistCheckChange_: function(e) { + // Whitelist existing users when guest login is being disabled. + if ($('useWhitelistCheck').checked) { + chrome.send('whitelistExistingUsers'); + } + + this.updateControls_(); + }, + + /** + * handler for allow guest pref change. + * @private + */ + handleUseWhitelistPrefChange_: function(e) { + this.updateControls_(); + }, + + /** + * Handler for "add" event fired from userNameEdit. + * @private + * @param {Event} e Add event fired from userNameEdit. + */ + handleAddUser_: function(e) { + chrome.send('whitelistUser', [e.user.email, e.user.name]); + }, + + /** + * Handler for "remove" event fired from userList. + * @private + * @param {Event} e Remove event fired from userList. + */ + handleRemoveUser_: function(e) { + chrome.send('unwhitelistUser', [e.user.username]); + } + }; + + + /** + * Returns whether the whitelist is managed by policy or not. + */ + AccountsOptions.whitelistIsManaged = function() { + return loadTimeData.getBoolean('whitelist_is_managed'); + }; + + /** + * Update account picture. + * @param {string} username User for which to update the image. + */ + AccountsOptions.updateAccountPicture = function(username) { + if (this.showWhitelist_) + $('userList').updateAccountPicture(username); + }; + + // Export + return { + AccountsOptions: AccountsOptions + }; + +}); diff --git a/chrome/browser/resources/options/chromeos/accounts_options_page.css b/chrome/browser/resources/options/chromeos/accounts_options_page.css new file mode 100644 index 0000000..ec7971e --- /dev/null +++ b/chrome/browser/resources/options/chromeos/accounts_options_page.css @@ -0,0 +1,97 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.user-list-table { + border: 1px solid lightgrey; + border-collapse: collapse; + border-spacing: 0; +} + +.user-name-edit-row { + background-color: rgb(235, 239, 250); + border: 1px solid lightgrey; + padding: 5px; +} + +.user-list-item { + padding: 2px; +} + +.user-icon { + border: 1px solid black; + height: 26px; + width: 26px; +} + +.user-email-label { + -webkit-margin-start: 10px; +} + +.user-name-label { + -webkit-margin-start: 10px; + color: darkgray; +} + +.user-email-name-block { + -webkit-box-flex: 1; + max-width: 318px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.remove-user-button { + background-image: -webkit-image-set( + url('../../../../../ui/resources/default_100_percent/close_bar.png') 1x, + url('../../../../../ui/resources/default_200_percent/close_bar.png') 2x); + height: 16px; + width: 16px; +} + +.remove-user-button:hover { + background-image: -webkit-image-set( + url('../../../../../ui/resources/default_100_percent/close_bar_hover.png') + 1x, + url('../../../../../ui/resources/default_200_percent/close_bar_hover.png') + 2x); +} + +#userList { + height: 166px; + padding: 5px; + width: 366px; +} + +#userList[disabled], +#userList[disabled] > [selected], +#userList[disabled] > :hover { + border-color: hsl(0, 0%, 85%); +} + +#userList[disabled] > [selected], +#userList[disabled] > :hover { + background-color: hsl(0, 0%, 90%); +} + +#userList[disabled] .remove-user-button { + visibility: hidden; +} + +#userNameEdit { + border: 1px solid lightgrey; + width: 366px; +} + +#ownerOnlyWarning { + -webkit-padding-start: 20px; + background-image: url('warning.png'); + background-repeat: no-repeat; + margin-bottom: 10px; + margin-top: 10px; + padding-bottom: 1px; +} + +input#userNameEdit:invalid { + background-color: rgb(255, 102, 102); +} diff --git a/chrome/browser/resources/options/chromeos/accounts_user_list.js b/chrome/browser/resources/options/chromeos/accounts_user_list.js new file mode 100644 index 0000000..7a2eda5 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/accounts_user_list.js @@ -0,0 +1,194 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.accounts', function() { + /** @const */ var List = cr.ui.List; + /** @const */ var ListItem = cr.ui.ListItem; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + /** + * Creates a new user list. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {cr.ui.List} + */ + var UserList = cr.ui.define('list'); + + UserList.prototype = { + __proto__: List.prototype, + + pref: 'cros.accounts.users', + + /** @inheritDoc */ + decorate: function() { + List.prototype.decorate.call(this); + + // HACK(arv): http://crbug.com/40902 + window.addEventListener('resize', this.redraw.bind(this)); + + var self = this; + + // Listens to pref changes. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + self.load_(event.value.value); + }); + }, + + createItem: function(user) { + return new UserListItem(user); + }, + + /** + * Finds the index of user by given username (canonicalized email). + * @private + * @param {string} username The username to look for. + * @return {number} The index of the found user or -1 if not found. + */ + indexOf_: function(username) { + var dataModel = this.dataModel; + if (!dataModel) + return -1; + + var length = dataModel.length; + for (var i = 0; i < length; ++i) { + var user = dataModel.item(i); + if (user.username == username) { + return i; + } + } + + return -1; + }, + + /** + * Update given user's account picture. + * @param {string} username User for which to update the image. + */ + updateAccountPicture: function(username) { + var index = this.indexOf_(username); + if (index >= 0) { + var item = this.getListItemByIndex(index); + if (item) + item.updatePicture(); + } + }, + + /** + * Loads given user list. + * @param {Array.} users An array of user info objects. + * @private + */ + load_: function(users) { + this.dataModel = new ArrayDataModel(users); + }, + + /** + * Removes given user from the list. + * @param {Object} user User info object to be removed from user list. + * @private + */ + removeUser_: function(user) { + var e = new Event('remove'); + e.user = user; + this.dispatchEvent(e); + } + }; + + /** + * Whether the user list is disabled. Only used for display purpose. + * @type {boolean} + */ + cr.defineProperty(UserList, 'disabled', cr.PropertyKind.BOOL_ATTR); + + /** + * Creates a new user list item. + * @param {Object} user The user account this represents. + * @constructor + * @extends {cr.ui.ListItem} + */ + function UserListItem(user) { + var el = cr.doc.createElement('div'); + el.user = user; + UserListItem.decorate(el); + return el; + } + + /** + * Decorates an element as a user account item. + * @param {!HTMLElement} el The element to decorate. + */ + UserListItem.decorate = function(el) { + el.__proto__ = UserListItem.prototype; + el.decorate(); + }; + + UserListItem.prototype = { + __proto__: ListItem.prototype, + + /** @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + + this.className = 'user-list-item'; + + this.icon_ = this.ownerDocument.createElement('img'); + this.icon_.className = 'user-icon'; + this.updatePicture(); + + var labelEmail = this.ownerDocument.createElement('span'); + labelEmail.className = 'user-email-label'; + labelEmail.textContent = this.user.email; + + var labelName = this.ownerDocument.createElement('span'); + labelName.className = 'user-name-label'; + labelName.textContent = this.user.owner ? + loadTimeData.getStringF('username_format', this.user.name) : + this.user.name; + + var emailNameBlock = this.ownerDocument.createElement('div'); + emailNameBlock.className = 'user-email-name-block'; + emailNameBlock.appendChild(labelEmail); + emailNameBlock.appendChild(labelName); + emailNameBlock.title = this.user.owner ? + loadTimeData.getStringF('username_format', this.user.email) : + this.user.email; + + this.appendChild(this.icon_); + this.appendChild(emailNameBlock); + + if (!this.user.owner) { + var removeButton = this.ownerDocument.createElement('button'); + removeButton.className = + 'raw-button remove-user-button custom-appearance'; + removeButton.addEventListener( + 'click', this.handleRemoveButtonClick_.bind(this)); + this.appendChild(removeButton); + } + }, + + /** + * Handles click on the remove button. + * @param {Event} e Click event. + * @private + */ + handleRemoveButtonClick_: function(e) { + // Handle left button click + if (e.button == 0) + this.parentNode.removeUser_(this.user); + }, + + /** + * Reloads user picture. + */ + updatePicture: function() { + this.icon_.src = 'chrome://userimage/' + this.user.username + + '?id=' + (new Date()).getTime(); + } + }; + + return { + UserList: UserList + }; +}); diff --git a/chrome/browser/resources/options/chromeos/accounts_user_name_edit.js b/chrome/browser/resources/options/chromeos/accounts_user_name_edit.js new file mode 100644 index 0000000..e6e5276 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/accounts_user_name_edit.js @@ -0,0 +1,130 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.accounts', function() { + /** @const */ var Event = cr.Event; + + /** + * Email alias only, assuming it's a gmail address. + * e.g. 'john' + * {name: 'john', email: 'john@gmail.com'} + * @const + */ + var format1String = + '^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)\\s*$'; + /** + * Email address only. + * e.g. 'john@chromium.org' + * {name: 'john', email: 'john@chromium.org'} + * @const + */ + var format2String = + '^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)@' + + '([A-Za-z0-9\-]{2,63}\\..+)\\s*$'; + /** + * Full format. + * e.g. '"John Doe" ' + * {name: 'John doe', email: 'john@chromium.org'} + * @const + */ + var format3String = + '^\\s*"{0,1}([^"]+)"{0,1}\\s*' + + '<([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+@' + + '[A-Za-z0-9\-]{2,63}\\..+)>\\s*$'; + + /** + * Creates a new user name edit element. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {HTMLInputElement} + */ + var UserNameEdit = cr.ui.define('input'); + + UserNameEdit.prototype = { + __proto__: HTMLInputElement.prototype, + + /** + * Called when an element is decorated as a user name edit. + */ + decorate: function() { + this.pattern = format1String + '|' + format2String + '|' + + format3String; + + this.onkeypress = this.handleKeyPress_.bind(this); + }, + + + /** + * Parses given str for user info. + * + * Note that the email parsing is based on RFC 5322 and does not support + * IMA (Internationalized Email Address). We take only the following chars + * as valid for an email alias (aka local-part): + * - Letters: a–z, A–Z + * - Digits: 0-9 + * - Characters: ! # $ % & ' * + - / = ? ^ _ ` { | } ~ + * - Dot: . (Note that we did not cover the cases that dot should not + * appear as first or last character and should not appear two or + * more times in a row.) + * + * @param {string} str A string to parse. + * @return {{name: string, email: string}} User info parsed from the string. + */ + parse: function(str) { + /** @const */ var format1 = new RegExp(format1String); + /** @const */ var format2 = new RegExp(format2String); + /** @const */ var format3 = new RegExp(format3String); + + var matches = format1.exec(str); + if (matches) { + return { + name: matches[1], + email: matches[1] + '@gmail.com' + }; + } + + matches = format2.exec(str); + if (matches) { + return { + name: matches[1], + email: matches[1] + '@' + matches[2] + }; + } + + matches = format3.exec(str); + if (matches) { + return { + name: matches[1], + email: matches[2] + }; + } + + return null; + }, + + /** + * Handler for key press event. + * @private + * @param {!Event} e The keypress event object. + */ + handleKeyPress_: function(e) { + // Enter + if (e.keyCode == 13) { + var user = this.parse(this.value); + if (user) { + var e = new Event('add'); + e.user = user; + this.dispatchEvent(e); + } + + this.select(); + } + } + }; + + return { + UserNameEdit: UserNameEdit + }; +}); + diff --git a/chrome/browser/resources/options/chromeos/bluetooth.css b/chrome/browser/resources/options/chromeos/bluetooth.css new file mode 100644 index 0000000..f8b1fd0 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth.css @@ -0,0 +1,152 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.bluetooth-device-list { + margin: 10px; + padding: 5px 10px; +} + +.bluetooth-device[paired] { + color: gray; +} + +#bluetooth-scanning { + -webkit-box-orient: horizontal; + display: -webkit-inline-box; + margin-bottom: 5px; +} + +#bluetooth-scanning-label { + -webkit-margin-start: 5px; + color: #999; +} + +#bluetooth-scanning-icon { + -webkit-margin-start: 10px; + height: 20px; + opacity: 0.66; + vertical-align: middle; + width: 20px; +} + +#bluetooth-paired-devices-list { + min-height: 96px !important; +} + +#bluetooth-paired-devices-list, +#bluetooth-unpaired-devices-list { + /* Prevent dialog from expanding if many devices are found. */ + max-height: 192px; + overflow-x: hidden; + overflow-y: auto; +} + +.bluetooth-empty-list-label { + box-sizing: border-box; + color: #999; + padding-top: 32px; + text-align: center; +} + +#bluetooth-paired-devices-list-empty-placeholder { + height: 96px; +} + +#bluetooth-unpaired-devices-list-empty-placeholder { + height: 192px; +} + +/* Fix the dimensions of the message area so that the dialog does not change + change size during the pairing process as the message changes. Sized + generously to accomodate the longest of the messages. */ +#bluetooth-pairing-message-area { + display: table; + height: 160px; + padding: 6px 0 !important; + width: 420px; +} + +/* Force the message to be vertical centered so that a shorter message does not + look out of place when there is room for a much longer message. */ +#bluetooth-pairing-message-contents { + display: table-cell; + vertical-align: middle; +} + +#bluetooth-pairing-instructions, +#bluetooth-pairing-passkey-display, +#bluetooth-pairing-passkey-entry, +#bluetooth-pairing-pincode-entry, +#bluetooth-passkey, +#bluetooth-pincode { + text-align: center; +} + +#bluetooth-pairing-instructions { + margin: 10px; +} + +#bluetooth-pairing-passkey-display, +#bluetooth-pairing-passkey-entry, +#bluetooth-pairing-pincode-entry { + margin: 40px 0; +} + +.bluetooth-keyboard-button { + -webkit-padding-end: 15px; + -webkit-padding-start: 15px; + background-image: -webkit-gradient(linear, + left top, + left bottom, + color-stop(0, #e9e9e9), + color-stop(1, #f5f5f5)); + border: 1px solid #d4d4d4; + border-radius: 4px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.07), + inset 0 1px 1px 1px #fff, + inset 0 -1px 1px 1px #ddd; + color: #666; + display: inline-block; + font-size: 14px; + font-weight: 600; + height: 38px; + line-height: 38px; + margin: 0 10px 0 0; + position: relative; + text-align: center; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + vertical-align: baseline; +} + +.bluetooth-keyboard-button:last-child { + margin: 0; +} + +#bluetooth-enter-key { + min-width: 54px; +} + +.bluetooth-passkey-char { + -webkit-margin-start: 45px; + color: #999; + font-size: 20px; + font-weight: 600; /* semibold */ + padding-bottom: 5px; +} + +.bluetooth-passkey-char:first-child { + -webkit-margin-start: 0; +} + +.bluetooth-keyboard-button.key-typed { + border: 1px solid #ccc; + box-shadow: 0 0 0 1px #888, + inset 0 1px 1px 1px #fff, + inset 0 -1px 1px 1px #eee; + color: #222; +} + +.bluetooth-keyboard-button.key-pin { + color: #222; +} diff --git a/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.html b/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.html new file mode 100644 index 0000000..a09222f --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.html @@ -0,0 +1,23 @@ + diff --git a/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.js b/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.js new file mode 100644 index 0000000..e5713001 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth_add_device_overlay.js @@ -0,0 +1,91 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * Encapsulated handling of the Bluetooth options page. + * @constructor + */ + function BluetoothOptions() { + OptionsPage.call(this, + 'bluetooth', + loadTimeData.getString('bluetoothOptionsPageTabTitle'), + 'bluetooth-options'); + } + + cr.addSingletonGetter(BluetoothOptions); + + BluetoothOptions.prototype = { + __proto__: OptionsPage.prototype, + + /** + * The list of available (unpaired) bluetooth devices. + * @type {DeletableItemList} + * @private + */ + deviceList_: null, + + /** @inheritDoc */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + this.createDeviceList_(); + + $('bluetooth-add-device-cancel-button').onclick = function(event) { + chrome.send('stopBluetoothDeviceDiscovery'); + OptionsPage.closeOverlay(); + }; + + var self = this; + $('bluetooth-add-device-apply-button').onclick = function(event) { + var device = self.deviceList_.selectedItem; + var address = device.address; + chrome.send('stopBluetoothDeviceDiscovery'); + OptionsPage.closeOverlay(); + device.pairing = 'bluetoothStartConnecting'; + options.BluetoothPairing.showDialog(device); + chrome.send('updateBluetoothDevice', [address, 'connect']); + }; + + $('bluetooth-add-device-apply-button').onmousedown = function(event) { + // Prevent 'blur' event, which would reset the list selection, + // thereby disabling the apply button. + event.preventDefault(); + }; + + $('bluetooth-unpaired-devices-list').addEventListener('change', + function() { + var item = $('bluetooth-unpaired-devices-list').selectedItem; + var disabled = !item || item.paired || item.connected; + $('bluetooth-add-device-apply-button').disabled = disabled; + }); + }, + + /** + * Creates, decorates and initializes the bluetooth device list. + * @private + */ + createDeviceList_: function() { + this.deviceList_ = $('bluetooth-unpaired-devices-list'); + options.system.bluetooth.BluetoothDeviceList.decorate(this.deviceList_); + this.deviceList_.autoExpands = true; + } + }; + + /** + * Automatically start the device discovery process if the + * "Add device" dialog is visible. + */ + BluetoothOptions.updateDiscovery = function() { + var page = BluetoothOptions.getInstance(); + if (page && page.visible) + chrome.send('findBluetoothDevices'); + } + + // Export + return { + BluetoothOptions: BluetoothOptions + }; +}); diff --git a/chrome/browser/resources/options/chromeos/bluetooth_device_list.js b/chrome/browser/resources/options/chromeos/bluetooth_device_list.js new file mode 100644 index 0000000..eb715d7 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth_device_list.js @@ -0,0 +1,338 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.system.bluetooth', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + + /** + * Bluetooth settings constants. + */ + function Constants() {} + + /** + * Creates a new bluetooth list item. + * @param {{name: string, + * address: string, + * paired: boolean, + * bonded: boolean, + * connected: boolean, + * pairing: string|undefined, + * passkey: number|undefined, + * entered: number|undefined}} device + * Description of the Bluetooth device. + * @constructor + * @extends {options.DeletableItem} + */ + function BluetoothListItem(device) { + var el = cr.doc.createElement('div'); + el.__proto__ = BluetoothListItem.prototype; + el.data = {}; + for (var key in device) + el.data[key] = device[key]; + el.decorate(); + // Only show the close button for paired devices. + el.deletable = device.paired; + return el; + } + + BluetoothListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * Description of the Bluetooth device. + * @type {{name: string, + * address: string, + * paired: boolean, + * bonded: boolean, + * connected: boolean, + * pairing: string|undefined, + * passkey: number|undefined, + * entered: number|undefined}} + */ + data: null, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + var label = this.ownerDocument.createElement('div'); + label.className = 'bluetooth-device-label'; + this.classList.add('bluetooth-device'); + this.connected = this.data.connected; + // Though strictly speaking, a connected device will also be paired, we + // are interested in tracking paired devices that are not connected. + this.paired = this.data.paired && !this.data.connected; + this.connecting = !!this.data.pairing; + var content = this.data.name; + // Update label for devices that are paired but not connected. + if (this.paired) { + content = content + ' (' + + loadTimeData.getString('bluetoothDeviceNotConnected') + ')'; + } + label.textContent = content; + this.contentElement.appendChild(label); + }, + }; + + /** + * Class for displaying a list of Bluetooth devices. + * @constructor + * @extends {options.DeletableItemList} + */ + var BluetoothDeviceList = cr.ui.define('list'); + + BluetoothDeviceList.prototype = { + __proto__: DeletableItemList.prototype, + + /** + * Height of a list entry in px. + * @type {number} + * @private + */ + itemHeight_: 32, + + /** + * Width of a list entry in px. + * @type {number} + * @private. + */ + itemWidth_: 400, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + // Force layout of all items even if not in the viewport to address + // calculation errors when the list is hidden. The impact on performance + // should be minimal given that the list is not expected to grow very + // large. + this.autoExpand = true; + this.addEventListener('blur', this.onBlur_); + this.clear(); + }, + + /** + * When the list loses focus, unselect all items in the list. + * @private + */ + onBlur_: function() { + // TODO(kevers): Should this be pushed up to the list class? + this.selectionModel.unselectAll(); + }, + + /** + * Adds a bluetooth device to the list of available devices. A check is + * made to see if the device is already in the list, in which case the + * existing device is updated. + * @param {{name: string, + * address: string, + * paired: boolean, + * bonded: boolean, + * connected: boolean, + * pairing: string|undefined, + * passkey: number|undefined, + * entered: number|undefined}} device + * Description of the bluetooth device. + * @return {boolean} True if the devies was successfully added or updated. + */ + appendDevice: function(device) { + var selectedDevice = this.getSelectedDevice_(); + var index = this.find(device.address); + if (index == undefined) { + this.dataModel.push(device); + this.redraw(); + } else { + this.dataModel.splice(index, 1, device); + this.redrawItem(index); + } + this.updateListVisibility_(); + if (selectedDevice) + this.setSelectedDevice_(selectedDevice); + return true; + }, + + /** + * Forces a revailidation of the list content. Content added while the list + * is hidden is not properly rendered when the list becomes visible. In + * addition, deleting a single item from the list results in a stale cache + * requiring an invalidation. + * @param {String=} opt_selection Optional address of device to select + * after refreshing the list. + */ + refresh: function(opt_selection) { + // TODO(kevers): Investigate if the root source of the problems can be + // fixed in cr.ui.list. + var selectedDevice = opt_selection ? opt_selection : + this.getSelectedDevice_(); + this.invalidate(); + this.redraw(); + if (selectedDevice) + this.setSelectedDevice_(selectedDevice); + }, + + /** + * Retrieves the address of the selected device, or null if no device is + * selected. + * @return {?String} Address of selected device or null. + * @private + */ + getSelectedDevice_: function() { + var selection = this.selectedItem; + if (selection) + return selection.address; + return null; + }, + + /** + * Selects the device with the matching address. + * @param {String} address The unique address of the device. + * @private + */ + setSelectedDevice_: function(address) { + var index = this.find(address); + if (index != undefined) + this.selectionModel.selectRange(index, index); + }, + + /** + * Perges all devices from the list. + */ + clear: function() { + this.dataModel = new ArrayDataModel([]); + this.redraw(); + this.updateListVisibility_(); + }, + + /** + * Returns the index of the list entry with the matching address. + * @param {string} address Unique address of the Bluetooth device. + * @return {number|undefined} Index of the matching entry or + * undefined if no match found. + */ + find: function(address) { + var size = this.dataModel.length; + for (var i = 0; i < size; i++) { + var entry = this.dataModel.item(i); + if (entry.address == address) + return i; + } + }, + + /** @inheritDoc */ + createItem: function(entry) { + return new BluetoothListItem(entry); + }, + + /** + * Overrides the default implementation, which is used to compute the + * size of an element in the list. The default implementation relies + * on adding a placeholder item to the list and fetching its size and + * position. This strategy does not work if an item is added to the list + * while it is hidden, as the computed metrics will all be zero in that + * case. + * @return {{height: number, marginTop: number, marginBottom: number, + * width: number, marginLeft: number, marginRight: number}} + * The height and width of the item, taking margins into account, + * and the margins themselves. + */ + measureItem: function() { + return { + height: this.itemHeight_, + marginTop: 0, + marginBotton: 0, + width: this.itemWidth_, + marginLeft: 0, + marginRight: 0 + }; + }, + + /** + * Override the default implementation to return a predetermined size, + * which in turns allows proper layout of items even if the list is hidden. + * @return {height: number, width: number} Dimensions of a single item in + * the list of bluetooth device. + * @private. + */ + getDefaultItemSize_: function() { + return { + height: this.itemHeight_, + width: this.itemWidth_ + }; + }, + + /** + * Override base implementation of handleClick_, which unconditionally + * removes the item. In this case, removal of the element is deferred + * pending confirmation from the Bluetooth adapter. + * @param {Event} e The click event object. + * @private + */ + handleClick_: function(e) { + if (this.disabled) + return; + + var target = e.target; + if (!target.classList.contains('row-delete-button')) + return; + + var listItem = this.getListItemAncestor(target); + var selected = this.selectionModel.selectedIndexes; + var index = this.getIndexOfListItem(listItem); + if (selected.indexOf(index) == -1) + selected = [index]; + for (var j = selected.length - 1; j >= 0; j--) { + var index = selected[j]; + var item = this.getListItemByIndex(index); + if (item && item.deletable) { + // Device is busy until we hear back from the Bluetooth adapter. + // Prevent double removal request. + item.deletable = false; + // TODO(kevers): Provide visual feedback that the device is busy. + + // Inform the bluetooth adapter that we are disconnecting or + // forgetting the device. + chrome.send('updateBluetoothDevice', + [item.data.address, item.connected ? 'disconnect' : 'forget']); + } + } + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var selectedDevice = this.getSelectedDevice_(); + this.dataModel.splice(index, 1); + this.refresh(selectedDevice); + this.updateListVisibility_(); + }, + + /** + * If the list has an associated empty list placholder then update the + * visibility of the list and placeholder. + * @private + */ + updateListVisibility_: function() { + var empty = this.dataModel.length == 0; + var listPlaceHolderID = this.id + '-empty-placeholder'; + if ($(listPlaceHolderID)) { + if (this.hidden != empty) { + this.hidden = empty; + $(listPlaceHolderID).hidden = !empty; + this.refresh(); + } + } + }, + }; + + cr.defineProperty(BluetoothListItem, 'connected', cr.PropertyKind.BOOL_ATTR); + + cr.defineProperty(BluetoothListItem, 'paired', cr.PropertyKind.BOOL_ATTR); + + cr.defineProperty(BluetoothListItem, 'connecting', cr.PropertyKind.BOOL_ATTR); + + return { + BluetoothListItem: BluetoothListItem, + BluetoothDeviceList: BluetoothDeviceList, + Constants: Constants + }; +}); diff --git a/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.html b/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.html new file mode 100644 index 0000000..95b9c03 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.html @@ -0,0 +1,28 @@ + diff --git a/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.js b/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.js new file mode 100644 index 0000000..be86dfc --- /dev/null +++ b/chrome/browser/resources/options/chromeos/bluetooth_pair_device_overlay.js @@ -0,0 +1,367 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * Enumeration of possible states during pairing. The value associated with + * each state maps to a localized string in the global variable + * |loadTimeData|. + * @enum {string} + */ + var PAIRING = { + STARTUP: 'bluetoothStartConnecting', + ENTER_PIN_CODE: 'bluetoothEnterPinCode', + ENTER_PASSKEY: 'bluetoothEnterPasskey', + REMOTE_PIN_CODE: 'bluetoothRemotePinCode', + REMOTE_PASSKEY: 'bluetoothRemotePasskey', + CONFIRM_PASSKEY: 'bluetoothConfirmPasskey', + }; + + /** + * List of IDs for conditionally visible elements in the dialog. + * @type {Array.} + * @const + */ + var ELEMENTS = ['bluetooth-pairing-passkey-display', + 'bluetooth-pairing-passkey-entry', + 'bluetooth-pairing-pincode-entry', + 'bluetooth-pair-device-connect-button', + 'bluetooth-pair-device-cancel-button', + 'bluetooth-pair-device-accept-button', + 'bluetooth-pair-device-reject-button', + 'bluetooth-pair-device-dismiss-button']; + + /** + * Encapsulated handling of the Bluetooth device pairing page. + * @constructor + */ + function BluetoothPairing() { + OptionsPage.call(this, + 'bluetoothPairing', + loadTimeData.getString('bluetoothOptionsPageTabTitle'), + 'bluetooth-pairing'); + } + + cr.addSingletonGetter(BluetoothPairing); + + BluetoothPairing.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Description of the bluetooth device. + * @type {{name: string, + * address: string, + * icon: Constants.DEVICE_TYPE, + * paired: boolean, + * bonded: boolean, + * connected: boolean, + * pairing: string|undefined, + * passkey: number|undefined, + * pincode: string|undefined, + * entered: number|undefined}} + * @private. + */ + device_: null, + + /** + * Can the dialog be programmatically dismissed. + * @type {boolean} + */ + dismissible_: true, + + /** @inheritDoc */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + var self = this; + $('bluetooth-pair-device-cancel-button').onclick = function() { + chrome.send('updateBluetoothDevice', + [self.device_.address, 'cancel']); + OptionsPage.closeOverlay(); + }; + $('bluetooth-pair-device-reject-button').onclick = function() { + chrome.send('updateBluetoothDevice', + [self.device_.address, 'reject']); + OptionsPage.closeOverlay(); + }; + $('bluetooth-pair-device-connect-button').onclick = function() { + var args = [self.device_.address, 'connect']; + var passkey = self.device_.passkey; + if (passkey) + args.push(String(passkey)); + else if (!$('bluetooth-pairing-passkey-entry').hidden) + args.push($('bluetooth-passkey').value); + else if (!$('bluetooth-pairing-pincode-entry').hidden) + args.push($('bluetooth-pincode').value); + chrome.send('updateBluetoothDevice', args); + OptionsPage.closeOverlay(); + }; + $('bluetooth-pair-device-accept-button').onclick = function() { + chrome.send('updateBluetoothDevice', + [self.device_.address, 'accept']); + OptionsPage.closeOverlay(); + }; + $('bluetooth-pair-device-dismiss-button').onclick = function() { + OptionsPage.closeOverlay(); + }; + $('bluetooth-passkey').oninput = function() { + var inputField = $('bluetooth-passkey'); + var value = inputField.value; + // Note that using is insufficient to restrict + // the input as it allows negative numbers and does not limit the + // number of charactes typed even if a range is set. Furthermore, + // it sometimes produces strange repaint artifacts. + var filtered = value.replace(/[^0-9]/g, ''); + if (filtered != value) + inputField.value = filtered; + $('bluetooth-pair-device-connect-button').disabled = + inputField.value.length == 0; + } + $('bluetooth-pincode').oninput = function() { + $('bluetooth-pair-device-connect-button').disabled = + $('bluetooth-pincode').value.length == 0; + } + $('bluetooth-passkey').addEventListener('keydown', + this.keyDownEventHandler_.bind(this)); + $('bluetooth-pincode').addEventListener('keydown', + this.keyDownEventHandler_.bind(this)); + }, + + /** + * Override to prevent showing the overlay if the Bluetooth device details + * have not been specified. Prevents showing an empty dialog if the user + * quits and restarts Chrome while in the process of pairing with a device. + * @return {boolean} True if the overlay can be displayed. + */ + canShowPage: function() { + return this.device_ && this.device_.address && this.device_.pairing; + }, + + /** + * Sets input focus on the passkey or pincode field if appropriate. + */ + didShowPage: function() { + if (!$('bluetooth-pincode').hidden) + $('bluetooth-pincode').focus(); + else if (!$('bluetooth-passkey').hidden) + $('bluetooth-passkey').focus(); + }, + + /** + * Configures the overlay for pairing a device. + * @param {Object} device Description of the bluetooth device. + */ + update: function(device) { + this.device_ = {}; + for (key in device) + this.device_[key] = device[key]; + // Update the pairing instructions. + var instructionsEl = $('bluetooth-pairing-instructions'); + this.clearElement_(instructionsEl); + this.dismissible_ = ('dismissible' in device) ? + device.dismissible : true; + + var message = loadTimeData.getString(device.pairing); + message = message.replace('%1', this.device_.name); + instructionsEl.textContent = message; + + // Update visibility of dialog elements. + if (this.device_.passkey) { + this.updatePasskey_(); + if (this.device_.pairing == PAIRING.CONFIRM_PASSKEY) { + // Confirming a match between displayed passkeys. + this.displayElements_(['bluetooth-pairing-passkey-display', + 'bluetooth-pair-device-accept-button', + 'bluetooth-pair-device-reject-button']); + } else { + // Remote entering a passkey. + this.displayElements_(['bluetooth-pairing-passkey-display', + 'bluetooth-pair-device-cancel-button']); + } + } else if (this.device_.pincode) { + this.updatePinCode_(); + this.displayElements_(['bluetooth-pairing-passkey-display', + 'bluetooth-pair-device-cancel-button']); + } else if (this.device_.pairing == PAIRING.ENTER_PIN_CODE) { + // Prompting the user to enter a PIN code. + this.displayElements_(['bluetooth-pairing-pincode-entry', + 'bluetooth-pair-device-connect-button', + 'bluetooth-pair-device-cancel-button']); + $('bluetooth-pincode').value = ''; + } else if (this.device_.pairing == PAIRING.ENTER_PASSKEY) { + // Prompting the user to enter a passkey. + this.displayElements_(['bluetooth-pairing-passkey-entry', + 'bluetooth-pair-device-connect-button', + 'bluetooth-pair-device-cancel-button']); + $('bluetooth-passkey').value = ''; + } else if (this.device_.pairing == PAIRING.STARTUP) { + // Starting the pairing process. + this.displayElements_(['bluetooth-pair-device-cancel-button']); + } else { + // Displaying an error message. + this.displayElements_(['bluetooth-pair-device-dismiss-button']); + } + // User is required to enter a passkey or pincode before the connect + // button can be enabled. The 'oninput' methods for the input fields + // determine when the connect button becomes active. + $('bluetooth-pair-device-connect-button').disabled = true; + }, + + /** + * Handles the ENTER key for the passkey or pincode entry field. + * @return {Event} a keydown event. + * @private + */ + keyDownEventHandler_: function(event) { + /** @const */ var ENTER_KEY_CODE = 13; + if (event.keyCode == ENTER_KEY_CODE) { + var button = $('bluetooth-pair-device-connect-button'); + if (!button.hidden) + button.click(); + } + }, + + /** + * Updates the visibility of elements in the dialog. + * @param {Array.} list List of conditionally visible elements that + * are to be made visible. + * @private + */ + displayElements_: function(list) { + var enabled = {}; + for (var i = 0; i < list.length; i++) { + var key = list[i]; + enabled[key] = true; + } + for (var i = 0; i < ELEMENTS.length; i++) { + var key = ELEMENTS[i]; + $(key).hidden = !enabled[key]; + } + }, + + /** + * Removes all children from an element. + * @param {!Element} element Target element to clear. + */ + clearElement_: function(element) { + var child = element.firstChild; + while (child) { + element.removeChild(child); + child = element.firstChild; + } + }, + + /** + * Formats an element for displaying the passkey. + */ + updatePasskey_: function() { + var passkeyEl = $('bluetooth-pairing-passkey-display'); + var keyClass = this.device_.pairing == PAIRING.REMOTE_PASSKEY ? + 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; + this.clearElement_(passkeyEl); + var key = String(this.device_.passkey); + var progress = this.device_.entered | 0; + for (var i = 0; i < key.length; i++) { + var keyEl = document.createElement('span'); + keyEl.textContent = key.charAt(i); + keyEl.className = keyClass; + if (i < progress) + keyEl.classList.add('key-typed'); + passkeyEl.appendChild(keyEl); + } + if (this.device_.pairing == PAIRING.REMOTE_PASSKEY) { + // Add enter key. + var label = loadTimeData.getString('bluetoothEnterKey'); + var keyEl = document.createElement('span'); + keyEl.textContent = label; + keyEl.className = keyClass; + keyEl.id = 'bluetooth-enter-key'; + passkeyEl.appendChild(keyEl); + } + passkeyEl.hidden = false; + }, + + /** + * Formats an element for displaying the PIN code. + */ + updatePinCode_: function() { + var passkeyEl = $('bluetooth-pairing-passkey-display'); + var keyClass = this.device_.pairing == PAIRING.REMOTE_PIN_CODE ? + 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; + this.clearElement_(passkeyEl); + var key = String(this.device_.pincode); + for (var i = 0; i < key.length; i++) { + var keyEl = document.createElement('span'); + keyEl.textContent = key.charAt(i); + keyEl.className = keyClass; + keyEl.classList.add('key-pin'); + passkeyEl.appendChild(keyEl); + } + if (this.device_.pairing == PAIRING.REMOTE_PIN_CODE) { + // Add enter key. + var label = loadTimeData.getString('bluetoothEnterKey'); + var keyEl = document.createElement('span'); + keyEl.textContent = label; + keyEl.className = keyClass; + keyEl.classList.add('key-pin'); + keyEl.id = 'bluetooth-enter-key'; + passkeyEl.appendChild(keyEl); + } + passkeyEl.hidden = false; + }, + }; + + /** + * Configures the device pairing instructions and displays the pairing + * overlay. + * @param {Object} device Description of the Bluetooth device. + */ + BluetoothPairing.showDialog = function(device) { + BluetoothPairing.getInstance().update(device); + OptionsPage.showPageByName('bluetoothPairing', false); + }; + + /** + * Displays a message from the Bluetooth adapter. + * @param {{string: label, + * string: address} data Data for constructing the message. + */ + BluetoothPairing.showMessage = function(data) { + var name = ''; + if (data.address.length > 0) { + name = data.address; + var list = $('bluetooth-paired-devices-list'); + var index = list.find(name); + if (index == undefined) { + list = $('bluetooth-unpaired-devices-list'); + index = list.find(name); + } + if (index != undefined) { + var entry = list.dataModel.item(index); + if (entry && entry.name) + name = entry.name; + } + } + BluetoothPairing.showDialog({name: name, + address: data.address, + pairing: data.label, + dismissible: false}); + }; + + /** + * Closes the Bluetooth pairing dialog. + */ + BluetoothPairing.dismissDialog = function() { + var overlay = OptionsPage.getTopmostVisiblePage(); + var dialog = BluetoothPairing.getInstance(); + if (overlay == dialog && dialog.dismissible_) + OptionsPage.closeOverlay(); + }; + + // Export + return { + BluetoothPairing: BluetoothPairing + }; +}); diff --git a/chrome/browser/resources/options/chromeos/cellular_plan_element.js b/chrome/browser/resources/options/chromeos/cellular_plan_element.js new file mode 100644 index 0000000..2ec59f0 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/cellular_plan_element.js @@ -0,0 +1,132 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.internet', function() { + /** + * Creates a new network list div. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {HTMLDivElement} + */ + var CellularPlanElement = cr.ui.define('div'); + + CellularPlanElement.prototype = { + __proto__: HTMLDivElement.prototype, + + /** @inheritDoc */ + decorate: function() { + }, + + /** + * Loads given network list. + * @param {Array} networks An array of network object. + */ + load: function(plans) { + this.textContent = ''; + if (!plans || !plans.length) { + var noplansDiv = this.ownerDocument.createElement('div'); + noplansDiv.textContent = loadTimeData.getString('noPlansFound'); + this.appendChild(detailsTable); + } else { + for (var i = 0; i < plans.length; ++i) { + this.appendChild(new CellularPlanItem(i, plans[i])); + } + } + } + }; + + /** + * Creates a new network item. + * @param {Object} network The network this represents. + * @constructor + * @extends {HTMLDivElement} + */ + function CellularPlanItem(idx, plan) { + var el = cr.doc.createElement('div'); + el.data = { + idx: idx, + planType: plan.planType, + name: plan.name, + planSummary: plan.planSummary, + dataRemaining: plan.dataRemaining, + planExpires: plan.planExpires, + warning: plan.warning + }; + CellularPlanItem.decorate(el); + return el; + } + + + /** + * Decorates an element as a network item. + * @param {!HTMLElement} el The element to decorate. + */ + CellularPlanItem.decorate = function(el) { + el.__proto__ = CellularPlanItem.prototype; + el.decorate(); + }; + + CellularPlanItem.prototype = { + __proto__: HTMLDivElement.prototype, + + /** @inheritDoc */ + decorate: function() { + this.className = 'cellular-plan'; + var detailsTable = this.createTable_('details-plan-table', + 'option-control-table'); + this.addRow_(detailsTable, 'plan-details-info', + 'option-name', 'planSummary', this.data.planSummary); + this.addRow_(detailsTable, 'plan-details-info', + 'option-name', null, loadTimeData.getString('planName'), + 'option-value', 'planName', this.data.name); + this.addRow_(detailsTable, 'plan-details-info', + 'option-name', null, loadTimeData.getString('dataRemaining'), + 'option-value', 'dataRemaining', this.data.dataRemaining); + this.addRow_(detailsTable, 'plan-details-info', + 'option-name', null, loadTimeData.getString('planExpires'), + 'option-value', 'dataRemaining', this.data.planExpires); + if (this.data.warning && this.data.warning != '') { + this.addRow_(detailsTable, 'plan-details-info', + 'option-name', 'planWarning', this.data.warning); + } + this.appendChild(detailsTable); + this.appendChild(this.ownerDocument.createElement('hr')); + }, + + createTable_: function(tableId, tableClass) { + var table = this.ownerDocument.createElement('table'); + table.id = tableId; + table.className = tableClass; + return table; + }, + + addRow_: function(table, rowClass, col1Class, col1Id, col1Value, + col2Class, col2Id, col2Value) { + var row = this.ownerDocument.createElement('tr'); + if (rowClass) + row.className = rowClass; + var col1 = this.ownerDocument.createElement('td'); + col1.className = col1Class; + if (col1Id) + col1.id = col1Id; + col1.textContent = col1Value; + if (!col2Class) + col1.setAttribute('colspan', '2'); + row.appendChild(col1); + if (col2Class) { + var col2 = this.ownerDocument.createElement('td'); + col2.className = col2Class; + if (col2Id) + col2.id = col2Id; + col2.textContent = col2Value; + row.appendChild(col2); + } + table.appendChild(row); + } + }; + + return { + CellularPlanElement: CellularPlanElement + }; +}); diff --git a/chrome/browser/resources/options/chromeos/change_picture_options.css b/chrome/browser/resources/options/chromeos/change_picture_options.css new file mode 100644 index 0000000..098b3ab --- /dev/null +++ b/chrome/browser/resources/options/chromeos/change_picture_options.css @@ -0,0 +1,193 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#change-picture-page .content-area { + min-width: 800px; +} + +#user-image-grid { + -webkit-user-drag: none; + -webkit-user-select: none; + display: inline-block; + margin: 10px; + outline: none; + /* Necessary for correct metrics calculation by grid.js. */ + overflow: hidden; + padding: 0; + width: 538px; +} + +#user-image-grid * { + margin: 0; + padding: 0; +} + +#user-image-grid img { + background-color: white; + height: 64px; + vertical-align: middle; + width: 64px; +} + +#user-image-grid > li { + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + display: inline-block; + margin: 8px; + padding: 3px; +} + +#user-image-grid [selected] { + border: 2px solid rgb(0, 102, 204); + padding: 2px; +} + +/** + * #user-image-preview can have the following classes: + * .default-image: one of the default images is selected (including the grey + * silhouette); + * .profile-image: profile image is selected; + * .online: camera is streaming video; + * .camera: camera (live or photo) is selected; + * .live: camera is in live mode (no photo taken yet/last photo removed). + */ + +#user-image-preview { + display: inline-block; + margin-right: 10px; + margin-top: 20px; + max-width: 220px; + vertical-align: top; +} + +html[dir=rtl] #user-image-preview { + float: left; +} + +#user-image-preview .perspective-box { + -webkit-perspective: 600px; +} + +.user-image-preview-img { + display: block; + max-height: 220px; + max-width: 220px; +} + +[camera=webrtc] .camera.live .user-image-preview-img { + display: none; +} + +.default-image .user-image-preview-img { + background: white; + border: solid 1px #cacaca; + border-radius: 4px; + padding: 2px; +} + +.user-image-stream-area { + display: none; + position: relative; +} + +[camera=webrtc] .camera.live .user-image-stream-area { + display: block; +} + +#user-image-stream-crop { + /* TODO(ivankr): temporary workaround for crbug.com/142347. */ + -webkit-transform: rotateY(360deg); + -webkit-transition: -webkit-transform 200ms linear; + height: 220px; + overflow: hidden; + position: relative; + width: 220px; +} + +.flip-x #user-image-stream-crop { + -webkit-transform: rotateY(180deg); +} + +/* TODO(ivankr): specify dimensions from real capture size. */ +.user-image-stream { + border: solid 1px #cacaca; + height: 220px; + /* Center image for 4:3 aspect ratio. */ + left: -16.6%; + position: absolute; + visibility: hidden; +} + +.online .user-image-stream { + visibility: visible; +} + +.user-image-stream-area .spinner { + display: none; + left: 14px; + position: absolute; + top: 14px; +} + +html[dir=rtl] .user-image-stream-area .spinner { + left: auto; + right: 14px; +} + +.camera.live:not(.online) .user-image-stream-area .spinner { + display: block; +} + +#flip-photo { + -webkit-transition: opacity 75ms linear; + background: url('chrome://theme/IDR_MIRROR_FLIP') no-repeat; + border: none; + bottom: 8px; + display: block; + height: 32px; + opacity: 0; + position: absolute; + right: 8px; + width: 32px; +} + +html[dir=rtl] #flip-photo { + left: 8px; + right: auto; +} + +/* "Flip photo" button is hidden during flip animation. */ +.online:not(.animation) .user-image-stream-area:hover #flip-photo { + opacity: 0.75; +} + +#discard-photo, +#take-photo { + display: none; + height: 25px; + margin: 4px 1px; + padding: 0; + width: 220px; +} + +[camera=webrtc] .camera:not(.live) #discard-photo { + background: url('chrome://theme/IDR_USER_IMAGE_RECYCLE') + no-repeat center 0; + display: block; +} + +[camera=webrtc] .camera.live.online #take-photo { + background: url('chrome://theme/IDR_USER_IMAGE_CAPTURE') + no-repeat center -1px; + display: block; +} + +#user-image-attribution { + -webkit-padding-start: 34px; + line-height: 26px; +} + +#user-image-author-website { + -webkit-padding-start: 5px; +} diff --git a/chrome/browser/resources/options/chromeos/change_picture_options.html b/chrome/browser/resources/options/chromeos/change_picture_options.html new file mode 100644 index 0000000..0bfa12c --- /dev/null +++ b/chrome/browser/resources/options/chromeos/change_picture_options.html @@ -0,0 +1,32 @@ + diff --git a/chrome/browser/resources/options/chromeos/change_picture_options.js b/chrome/browser/resources/options/chromeos/change_picture_options.js new file mode 100644 index 0000000..2404337 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/change_picture_options.js @@ -0,0 +1,506 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + var UserImagesGrid = options.UserImagesGrid; + var ButtonImages = UserImagesGrid.ButtonImages; + + /** + * Array of button URLs used on this page. + * @type {Array.} + * @const + */ + var ButtonImageUrls = [ + ButtonImages.TAKE_PHOTO, + ButtonImages.CHOOSE_FILE + ]; + + ///////////////////////////////////////////////////////////////////////////// + // ChangePictureOptions class: + + /** + * Encapsulated handling of ChromeOS change picture options page. + * @constructor + */ + function ChangePictureOptions() { + var isWebRTC = $('change-picture-page').getAttribute('camera') == 'webrtc'; + ChangePictureOptions.prototype = isWebRTC ? + ChangePictureOptionsWebRTCProto : ChangePictureOptionsOldProto; + // |this| has been already created so it's |__proto__| has to be reset. + this.__proto__ = ChangePictureOptions.prototype; + OptionsPage.call( + this, + 'changePicture', + loadTimeData.getString('changePicturePage'), + 'change-picture-page'); + } + + cr.addSingletonGetter(ChangePictureOptions); + + var ChangePictureOptionsOldProto = { + // Inherit ChangePictureOptions from OptionsPage. + __proto__: options.OptionsPage.prototype, + + /** + * Initializes ChangePictureOptions page. + */ + initializePage: function() { + // Call base class implementation to start preferences initialization. + OptionsPage.prototype.initializePage.call(this); + + var imageGrid = $('user-image-grid'); + UserImagesGrid.decorate(imageGrid); + + imageGrid.previewElement = $('user-image-preview'); + + imageGrid.addEventListener('select', + this.handleImageSelected_.bind(this)); + imageGrid.addEventListener('activate', + this.handleImageActivated_.bind(this)); + + // Add the "Choose file" button. + imageGrid.addItem(ButtonImages.CHOOSE_FILE, + loadTimeData.getString('chooseFile'), + this.handleChooseFile_.bind(this)); + + // Profile image data. + this.profileImage_ = imageGrid.addItem( + ButtonImages.PROFILE_PICTURE, + loadTimeData.getString('profilePhotoLoading')); + + // Old user image data (if present). + this.oldImage_ = null; + + $('change-picture-overlay-confirm').onclick = this.closePage_; + + chrome.send('onChangePicturePageInitialized'); + }, + + /** + * Called right after the page has been shown to user. + */ + didShowPage: function() { + $('user-image-grid').updateAndFocus(); + chrome.send('onChangePicturePageShown'); + }, + + /** + * Called right before the page is hidden. + */ + willHidePage: function() { + var imageGrid = $('user-image-grid'); + imageGrid.blur(); // Make sure the image grid is not active. + if (this.oldImage_) { + imageGrid.removeItem(this.oldImage_); + this.oldImage_ = null; + } + }, + + /** + * Called right after the page has been hidden. + */ + // TODO(ivankr): both callbacks are required as only one of them is called + // depending on the way the page was closed, see http://crbug.com/118923. + didClosePage: function() { + this.willHidePage(); + }, + + /** + * Closes current page, returning back to Personal Stuff page. + * @private + */ + closePage_: function() { + OptionsPage.closeOverlay(); + }, + + /** + * Handles "Take photo" button activation. + * @private + */ + handleTakePhoto_: function() { + chrome.send('takePhoto'); + this.closePage_(); + }, + + /** + * Handles "Choose a file" button activation. + * @private + */ + handleChooseFile_: function() { + chrome.send('chooseFile'); + this.closePage_(); + }, + + /** + * Handles image selection change. + * @private + */ + handleImageSelected_: function() { + var imageGrid = $('user-image-grid'); + var url = imageGrid.selectedItemUrl; + // Ignore deselection, selection change caused by program itself and + // selection of one of the action buttons. + if (url && + !imageGrid.inProgramSelection && + ButtonImageUrls.indexOf(url) == -1) { + chrome.send('selectImage', [url]); + } + }, + + /** + * Handles image activation (by pressing Enter). + * @private + */ + handleImageActivated_: function() { + switch ($('user-image-grid').selectedItemUrl) { + case ButtonImages.TAKE_PHOTO: + this.handleTakePhoto_(); + break; + case ButtonImages.CHOOSE_FILE: + this.handleChooseFile_(); + break; + default: + this.closePage_(); + break; + } + }, + + /** + * URL of the current user image. + * @type {string} + */ + get currentUserImageUrl() { + return 'chrome://userimage/' + BrowserOptions.getLoggedInUsername() + + '?id=' + (new Date()).getTime() + '&animated'; + }, + + /** + * Notifies about camera presence change. + * @param {boolean} present Whether a camera is present or not. + * @private + */ + setCameraPresent_: function(present) { + var imageGrid = $('user-image-grid'); + var showTakePhotoButton = present; + if (showTakePhotoButton && !this.takePhotoButton_) { + this.takePhotoButton_ = imageGrid.addItem( + ButtonImages.TAKE_PHOTO, + loadTimeData.getString('takePhoto'), + this.handleTakePhoto_.bind(this), + 1); + } else if (!showTakePhotoButton && this.takePhotoButton_) { + imageGrid.removeItem(this.takePhotoButton_); + this.takePhotoButton_ = null; + } + }, + + /** + * Adds or updates old user image taken from file/camera (neither a profile + * image nor a default one). + * @private + */ + setOldImage_: function() { + var imageGrid = $('user-image-grid'); + var url = this.currentUserImageUrl; + if (this.oldImage_) { + this.oldImage_ = imageGrid.updateItem(this.oldImage_, url); + } else { + // Insert next to the profile image. + var pos = imageGrid.indexOf(this.profileImage_) + 1; + this.oldImage_ = imageGrid.addItem(url, undefined, undefined, pos); + imageGrid.selectedItem = this.oldImage_; + } + }, + + /** + * Updates user's profile image. + * @param {string} imageUrl Profile image, encoded as data URL. + * @param {boolean} select If true, profile image should be selected. + * @private + */ + setProfileImage_: function(imageUrl, select) { + var imageGrid = $('user-image-grid'); + this.profileImage_ = imageGrid.updateItem( + this.profileImage_, imageUrl, loadTimeData.getString('profilePhoto')); + if (select) + imageGrid.selectedItem = this.profileImage_; + }, + + /** + * Selects user image with the given URL. + * @param {string} url URL of the image to select. + * @private + */ + setSelectedImage_: function(url) { + $('user-image-grid').selectedItemUrl = url; + }, + + /** + * Appends default images to the image grid. Should only be called once. + * @param {Array.<{url: string, author: string, website: string}>} images + * An array of default images data, including URL, author and website. + * @private + */ + setDefaultImages_: function(images) { + var imageGrid = $('user-image-grid'); + for (var i = 0, data; data = imagesData[i]; i++) { + imageGrid.addItem(data.url); + } + }, + }; + + var ChangePictureOptionsWebRTCProto = { + // Inherit ChangePictureOptions from OptionsPage. + __proto__: options.OptionsPage.prototype, + + /** + * Initializes ChangePictureOptions page. + */ + initializePage: function() { + // Call base class implementation to start preferences initialization. + OptionsPage.prototype.initializePage.call(this); + + var imageGrid = $('user-image-grid'); + UserImagesGrid.decorate(imageGrid); + + // Preview image will track the selected item's URL. + var previewElement = $('user-image-preview'); + imageGrid.previewElement = previewElement; + imageGrid.selectionType = 'default'; + + imageGrid.addEventListener('select', + this.handleImageSelected_.bind(this)); + imageGrid.addEventListener('activate', + this.handleImageActivated_.bind(this)); + + // Set the title for "Take Photo" button. + imageGrid.cameraTitle = loadTimeData.getString('takePhoto'); + + // Add the "Choose file" button. + imageGrid.addItem(ButtonImages.CHOOSE_FILE, + loadTimeData.getString('chooseFile'), + this.handleChooseFile_.bind(this)).type = 'file'; + + // Profile image data. + this.profileImage_ = imageGrid.addItem( + ButtonImages.PROFILE_PICTURE, + loadTimeData.getString('profilePhotoLoading')); + this.profileImage_.type = 'profile'; + + $('take-photo').addEventListener( + 'click', this.handleTakePhoto_.bind(this)); + $('discard-photo').addEventListener( + 'click', imageGrid.discardPhoto.bind(imageGrid)); + + // Toggle 'animation' class for the duration of WebKit transition. + $('flip-photo').addEventListener( + 'click', function(e) { + previewElement.classList.add('animation'); + imageGrid.flipPhoto = !imageGrid.flipPhoto; + }); + $('user-image-stream-crop').addEventListener( + 'webkitTransitionEnd', function(e) { + previewElement.classList.remove('animation'); + }); + + // Old user image data (if present). + this.oldImage_ = null; + + $('change-picture-overlay-confirm').addEventListener( + 'click', this.closePage_.bind(this)); + + chrome.send('onChangePicturePageInitialized'); + }, + + /** + * Called right after the page has been shown to user. + */ + didShowPage: function() { + var imageGrid = $('user-image-grid'); + imageGrid.updateAndFocus(); + // Reset camera element. + imageGrid.cameraImage = null; + // Autoplay but do not preselect. + imageGrid.checkCameraPresence(true, false); + chrome.send('onChangePicturePageShown'); + }, + + /** + * Called right before the page is hidden. + */ + willHidePage: function() { + var imageGrid = $('user-image-grid'); + imageGrid.blur(); // Make sure the image grid is not active. + imageGrid.stopCamera(); + if (this.oldImage_) { + imageGrid.removeItem(this.oldImage_); + this.oldImage_ = null; + } + }, + + /** + * Called right after the page has been hidden. + */ + // TODO(ivankr): both callbacks are required as only one of them is called + // depending on the way the page was closed, see http://crbug.com/118923. + didClosePage: function() { + this.willHidePage(); + }, + + /** + * Closes current page, returning back to Personal Stuff page. + * @private + */ + closePage_: function() { + OptionsPage.closeOverlay(); + }, + + /** + * Handles "Take photo" button click. + * @private + */ + handleTakePhoto_: function() { + $('user-image-grid').takePhoto(function(photoURL) { + chrome.send('photoTaken', [photoURL]); + }); + }, + + /** + * Handles "Choose a file" button activation. + * @private + */ + handleChooseFile_: function() { + chrome.send('chooseFile'); + this.closePage_(); + }, + + /** + * Handles image selection change. + * @private + */ + handleImageSelected_: function() { + var imageGrid = $('user-image-grid'); + var url = imageGrid.selectedItemUrl; + // Ignore selection change caused by program itself and selection of one + // of the action buttons. + if (!imageGrid.inProgramSelection && + url != ButtonImages.TAKE_PHOTO && url != ButtonImages.CHOOSE_FILE) { + chrome.send('selectImage', [url]); + } + // Update image attribution text. + var image = imageGrid.selectedItem; + $('user-image-author-name').textContent = image.author; + $('user-image-author-website').textContent = image.website; + $('user-image-author-website').href = image.website; + $('user-image-attribution').style.visibility = + (image.author || image.website) ? 'visible' : 'hidden'; + }, + + /** + * Handles image activation (by pressing Enter). + * @private + */ + handleImageActivated_: function() { + switch ($('user-image-grid').selectedItemUrl) { + case ButtonImages.TAKE_PHOTO: + this.handleTakePhoto_(); + break; + case ButtonImages.CHOOSE_FILE: + this.handleChooseFile_(); + break; + default: + this.closePage_(); + break; + } + }, + + /** + * URL of the current user image. + * @type {string} + */ + get currentUserImageUrl() { + return 'chrome://userimage/' + BrowserOptions.getLoggedInUsername() + + '?id=' + new Date().getTime(); + }, + + /** + * Adds or updates old user image taken from file/camera (neither a profile + * image nor a default one). + * @private + */ + setOldImage_: function() { + var imageGrid = $('user-image-grid'); + var url = this.currentUserImageUrl; + if (this.oldImage_) { + this.oldImage_ = imageGrid.updateItem(this.oldImage_, url); + } else { + // Insert next to the profile image. + var pos = imageGrid.indexOf(this.profileImage_) + 1; + this.oldImage_ = imageGrid.addItem(url, undefined, undefined, pos); + imageGrid.selectedItem = this.oldImage_; + } + }, + + /** + * Updates user's profile image. + * @param {string} imageUrl Profile image, encoded as data URL. + * @param {boolean} select If true, profile image should be selected. + * @private + */ + setProfileImage_: function(imageUrl, select) { + var imageGrid = $('user-image-grid'); + this.profileImage_ = imageGrid.updateItem( + this.profileImage_, imageUrl, loadTimeData.getString('profilePhoto')); + if (select) + imageGrid.selectedItem = this.profileImage_; + }, + + /** + * Selects user image with the given URL. + * @param {string} url URL of the image to select. + * @private + */ + setSelectedImage_: function(url) { + $('user-image-grid').selectedItemUrl = url; + }, + + /** + * Appends default images to the image grid. Should only be called once. + * @param {Array.<{url: string, author: string, website: string}>} images + * An array of default images data, including URL, author and website. + * @private + */ + setDefaultImages_: function(imagesData) { + var imageGrid = $('user-image-grid'); + for (var i = 0, data; data = imagesData[i]; i++) { + var item = imageGrid.addItem(data.url); + item.type = 'default'; + item.author = data.author || ''; + item.website = data.website || ''; + } + }, + }; + + // Forward public APIs to private implementations. + [ + 'setCameraPresent', + 'setDefaultImages', + 'setOldImage', + 'setProfileImage', + 'setSelectedImage', + ].forEach(function(name) { + ChangePictureOptions[name] = function() { + var instance = ChangePictureOptions.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + // Export + return { + ChangePictureOptions: ChangePictureOptions + }; + +}); diff --git a/chrome/browser/resources/options/chromeos/display_options.css b/chrome/browser/resources/options/chromeos/display_options.css new file mode 100644 index 0000000..7d32475 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/display_options.css @@ -0,0 +1,103 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#display-options { + background-color: rgb(240, 240, 240); +} + +#display-options-content-area { + padding: 0; +} + +#display-options-displays-view-host { + padding: 20px 0 20px 0; +} + +#display-options-displays-view { + position: relative; + width: 100%; +} + +#display-options-displays-view-mirroring { + margin: 20px 0 20px 0; +} + +#display-configurations { + background-color: white; + border-top: 1px solid lightgrey; + height: 65px; + padding: 15px; +} + +/* The arrow at the border #display-configurations to point the focused display. + * This is achieved by a square rotated by 45-deg, and it has border at the + * upper-half, which were left/top before the rotation. */ +#display-configuration-arrow { + -webkit-transform: rotate(45deg); + background-color: white; + border-left: 1px solid lightgrey; + border-top: 1px solid lightgrey; + height: 20px; + position: absolute; + width: 20px; + z-index: 1; +} + +#display-options-buttons-container { + float: right; + z-index: 2; +} + +html[dir=rtl] #display-options-buttons-container { + float: left; +} + + +#selected-display-data-container { + float: left; + line-height: 200%; + z-index: 2; +} + +html[dir=rtl] #selected-display-data-container { + float: right; +} + +#selected-display-name { + font-weight: bold; +} + +#display-launcher { + background-color: lightgrey; + bottom: 0; + height: 10px; + position: absolute; +} + +.displays-display { + background: rgb(240, 240, 240); + border: solid 1px; + font-weight: bold; + position: absolute; + text-align: center; + vertical-align: middle; + z-index: 2; +} + +.display-mirrored { + border: dashed 1px; +} + +.displays-focused { + border: solid 2px rgb(0, 138, 255); + color: rgb(0, 138, 255); +} + +.display-options-single-button-container { + margin: 5px 0 5px 0; +} + +.display-options-button { + width: 120px; +} diff --git a/chrome/browser/resources/options/chromeos/display_options.html b/chrome/browser/resources/options/chromeos/display_options.html new file mode 100644 index 0000000..3b865c8 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/display_options.html @@ -0,0 +1,33 @@ + diff --git a/chrome/browser/resources/options/chromeos/display_options.js b/chrome/browser/resources/options/chromeos/display_options.js new file mode 100644 index 0000000..b663d94 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/display_options.js @@ -0,0 +1,454 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + // The scale ratio of the display rectangle to its original size. + /** @const */ var VISUAL_SCALE = 1 / 10; + + // The number of pixels to share the edges between displays. + /** @const */ var MIN_OFFSET_OVERLAP = 5; + + /** + * Enumeration of secondary display layout. The value has to be same as the + * values in ash/monitor/monitor_controller.cc. + * @enum {number} + */ + var SecondaryDisplayLayout = { + TOP: 0, + RIGHT: 1, + BOTTOM: 2, + LEFT: 3 + }; + + /** + * Encapsulated handling of the 'Display' page. + * @constructor + */ + function DisplayOptions() { + OptionsPage.call(this, 'display', + loadTimeData.getString('displayOptionsPageTabTitle'), + 'display-options'); + this.mirroring_ = false; + this.focusedIndex_ = null; + this.displays_ = []; + } + + cr.addSingletonGetter(DisplayOptions); + + DisplayOptions.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + $('display-options-toggle-mirroring').onclick = (function() { + this.mirroring_ = !this.mirroring_; + chrome.send('setMirroring', [this.mirroring_]); + }).bind(this); + + $('display-options-apply').onclick = this.applyResult_.bind(this); + chrome.send('getDisplayInfo'); + }, + + /** @override */ + onVisibilityChanged_: function() { + OptionsPage.prototype.onVisibilityChanged_(this); + if (this.visible) + chrome.send('getDisplayInfo'); + }, + + /** + * Collects the current data and sends it to Chrome. + * @private + */ + applyResult_: function() { + // Offset is calculated from top or left edge. + var primary = this.displays_[0]; + var secondary = this.displays_[1]; + var offset; + if (this.layout_ == SecondaryDisplayLayout.LEFT || + this.layout_ == SecondaryDisplayLayout.RIGHT) { + offset = secondary.div.offsetTop - primary.div.offsetTop; + } else { + offset = secondary.div.offsetLeft - primary.div.offsetLeft; + } + chrome.send('setDisplayLayout', [this.layout_, offset / VISUAL_SCALE]); + }, + + /** + * Mouse move handler for dragging display rectangle. + * @private + * @param {Event} e The mouse move event. + */ + onMouseMove_: function(e) { + if (!this.dragging_) + return true; + + var index = -1; + for (var i = 0; i < this.displays_.length; i++) { + if (this.displays_[i] == this.dragging_.display) { + index = i; + break; + } + } + if (index < 0) + return true; + + // Note that current code of moving display-rectangles doesn't work + // if there are >=3 displays. This is our assumption for M21. + // TODO(mukai): Fix the code to allow >=3 displays. + var mousePosition = { + x: e.pageX - this.dragging_.offset.x, + y: e.pageY - this.dragging_.offset.y + }; + var newPosition = { + x: mousePosition.x - this.dragging_.clickLocation.x, + y: mousePosition.y - this.dragging_.clickLocation.y + }; + + var primaryDiv = this.displays_[0].div; + var display = this.dragging_.display; + + // Separate the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of + // the primary display, and decide which area the display should reside. + var diagonalSlope = primaryDiv.offsetHeight / primaryDiv.offsetWidth; + var topDownIntercept = + primaryDiv.offsetTop - primaryDiv.offsetLeft * diagonalSlope; + var bottomUpIntercept = primaryDiv.offsetTop + + primaryDiv.offsetHeight + primaryDiv.offsetLeft * diagonalSlope; + + if (mousePosition.y > + topDownIntercept + mousePosition.x * diagonalSlope) { + if (mousePosition.y > + bottomUpIntercept - mousePosition.x * diagonalSlope) + this.layout_ = SecondaryDisplayLayout.BOTTOM; + else + this.layout_ = SecondaryDisplayLayout.LEFT; + } else { + if (mousePosition.y > + bottomUpIntercept - mousePosition.x * diagonalSlope) + this.layout_ = SecondaryDisplayLayout.RIGHT; + else + this.layout_ = SecondaryDisplayLayout.TOP; + } + + if (this.layout_ == SecondaryDisplayLayout.LEFT || + this.layout_ == SecondaryDisplayLayout.RIGHT) { + if (newPosition.y > primaryDiv.offsetTop + primaryDiv.offsetHeight) + this.layout_ = SecondaryDisplayLayout.BOTTOM; + else if (newPosition.y + display.div.offsetHeight < + primaryDiv.offsetTop) + this.layout_ = SecondaryDisplayLayout.TOP; + } else { + if (newPosition.y > primaryDiv.offsetLeft + primaryDiv.offsetWidth) + this.layout_ = SecondaryDisplayLayout.RIGHT; + else if (newPosition.y + display.div.offsetWidth < + primaryDiv.offstLeft) + this.layout_ = SecondaryDisplayLayout.LEFT; + } + + switch (this.layout_) { + case SecondaryDisplayLayout.RIGHT: + display.div.style.left = + primaryDiv.offsetLeft + primaryDiv.offsetWidth + 'px'; + display.div.style.top = newPosition.y + 'px'; + break; + case SecondaryDisplayLayout.LEFT: + display.div.style.left = + primaryDiv.offsetLeft - display.div.offsetWidth + 'px'; + display.div.style.top = newPosition.y + 'px'; + break; + case SecondaryDisplayLayout.TOP: + display.div.style.top = + primaryDiv.offsetTop - display.div.offsetHeight + 'px'; + display.div.style.left = newPosition.x + 'px'; + break; + case SecondaryDisplayLayout.BOTTOM: + display.div.style.top = + primaryDiv.offsetTop + primaryDiv.offsetHeight + 'px'; + display.div.style.left = newPosition.x + 'px'; + break; + } + + return false; + }, + + /** + * Mouse down handler for dragging display rectangle. + * @private + * @param {Event} e The mouse down event. + */ + onMouseDown_: function(e) { + if (this.mirroring_) + return true; + + if (e.button != 0) + return true; + + this.focusedIndex_ = null; + for (var i = 0; i < this.displays_.length; i++) { + var display = this.displays_[i]; + if (this.displays_[i].div == e.target || + (i == 0 && $('display-launcher') == e.target)) { + this.focusedIndex_ = i; + break; + } + } + + for (var i = 0; i < this.displays_.length; i++) { + var display = this.displays_[i]; + display.div.className = 'displays-display'; + if (i != this.focusedIndex_) + continue; + + display.div.classList.add('displays-focused'); + // Do not drag the primary monitor. + if (i == 0) + continue; + + this.dragging_ = { + display: display, + clickLocation: {x: e.offsetX, y: e.offsetY}, + offset: {x: e.pageX - e.offsetX - display.div.offsetLeft, + y: e.pageY - e.offsetY - display.div.offsetTop} + }; + } + this.updateSelectedDisplayDescription_(); + return false; + }, + + /** + * Mouse up handler for dragging display rectangle. + * @private + * @param {Event} e The mouse up event. + */ + onMouseUp_: function(e) { + if (this.dragging_) { + // Make sure the dragging location is connected. + var primaryDiv = this.displays_[0].div; + var draggingDiv = this.dragging_.display.div; + if (this.layout_ == SecondaryDisplayLayout.LEFT || + this.layout_ == SecondaryDisplayLayout.RIGHT) { + var top = Math.max(draggingDiv.offsetTop, + primaryDiv.offsetTop - draggingDiv.offsetHeight + + MIN_OFFSET_OVERLAP); + top = Math.min(top, + primaryDiv.offsetTop + primaryDiv.offsetHeight - + MIN_OFFSET_OVERLAP); + draggingDiv.style.top = top + 'px'; + } else { + var left = Math.max(draggingDiv.offsetLeft, + primaryDiv.offsetLeft - draggingDiv.offsetWidth + + MIN_OFFSET_OVERLAP); + left = Math.min(left, + primaryDiv.offsetLeft + primaryDiv.offsetWidth - + MIN_OFFSET_OVERLAP); + draggingDiv.style.left = left + 'px'; + } + this.dragging_ = null; + } + this.updateSelectedDisplayDescription_(); + return false; + }, + + /** + * Updates the description of the selected display section. + * @private + */ + updateSelectedDisplayDescription_: function() { + if (this.focusedIndex_ == null || + this.displays_[this.focusedIndex_] == null) { + $('selected-display-data-container').hidden = true; + $('display-configuration-arrow').hidden = true; + return; + } + + $('selected-display-data-container').hidden = false; + var display = this.displays_[this.focusedIndex_]; + var nameElement = $('selected-display-name'); + while (nameElement.childNodes.length > 0) + nameElement.removeChild(nameElement.firstChild); + nameElement.appendChild(document.createTextNode(display.name)); + + var resolutionData = display.width + 'x' + display.height; + var resolutionElement = $('selected-display-resolution'); + while (resolutionElement.childNodes.length > 0) + resolutionElement.removeChild(resolutionElement.firstChild); + resolutionElement.appendChild(document.createTextNode(resolutionData)); + + var arrow = $('display-configuration-arrow'); + arrow.hidden = false; + arrow.style.top = + $('display-configurations').offsetTop - arrow.offsetHeight / 2 + 'px'; + arrow.style.left = display.div.offsetLeft + display.div.offsetWidth / 2 - + arrow.offsetWidth / 2 + 'px'; + }, + + /** + * Clears the drawing area for display rectangles. + * @private + */ + resetDisplaysView_: function() { + var displaysViewHost = $('display-options-displays-view-host'); + displaysViewHost.removeChild(displaysViewHost.firstChild); + this.displaysView_ = document.createElement('div'); + this.displaysView_.id = 'display-options-displays-view'; + this.displaysView_.onmousemove = this.onMouseMove_.bind(this); + this.displaysView_.onmousedown = this.onMouseDown_.bind(this); + this.displaysView_.onmouseup = this.onMouseUp_.bind(this); + displaysViewHost.appendChild(this.displaysView_); + }, + + /** + * Lays out the display rectangles for mirroring. + * @private + */ + layoutMirroringDisplays_: function() { + // Offset pixels for secondary display rectangles. + /** @const */ var MIRRORING_OFFSET_PIXELS = 2; + // Always show two displays because there must be two displays when + // the display_options is enabled. Don't rely on displays_.length because + // there is only one display from chrome's perspective in mirror mode. + /** @const */ var MIN_NUM_DISPLAYS = 2; + /** @const */ var MIRRORING_VERTICAL_MARGIN = 20; + + // The width/height should be same as the primary display: + var width = this.displays_[0].width * VISUAL_SCALE; + var height = this.displays_[0].height * VISUAL_SCALE; + + var numDisplays = Math.max(MIN_NUM_DISPLAYS, this.displays_.length); + + var totalWidth = width + numDisplays * MIRRORING_OFFSET_PIXELS; + var totalHeight = height + numDisplays * MIRRORING_OFFSET_PIXELS; + + this.displaysView_.style.height = totalHeight + 'px'; + this.displaysView_.classList.add( + 'display-options-displays-view-mirroring'); + + // The displays should be centered. + var offsetX = + $('display-options-displays-view').offsetWidth / 2 - totalWidth / 2; + + for (var i = 0; i < numDisplays; i++) { + var div = document.createElement('div'); + div.className = 'displays-display'; + div.style.top = i * MIRRORING_OFFSET_PIXELS + 'px'; + div.style.left = i * MIRRORING_OFFSET_PIXELS + offsetX + 'px'; + div.style.width = width + 'px'; + div.style.height = height + 'px'; + div.style.zIndex = i; + // set 'display-mirrored' class for the background display rectangles. + if (i != numDisplays - 1) + div.classList.add('display-mirrored'); + this.displaysView_.appendChild(div); + } + }, + + /** + * Layouts the display rectangles according to the current layout_. + * @private + */ + layoutDisplays_: function() { + var totalHeight = 0; + var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; + for (var i = 0; i < this.displays_.length; i++) { + var display = this.displays_[i]; + totalHeight += display.height * VISUAL_SCALE; + boundingBox.left = Math.min(boundingBox.left, display.x * VISUAL_SCALE); + boundingBox.right = Math.max( + boundingBox.right, (display.x + display.width) * VISUAL_SCALE); + boundingBox.top = Math.min(boundingBox.top, display.y * VISUAL_SCALE); + boundingBox.bottom = Math.max( + boundingBox.bottom, (display.y + display.height) * VISUAL_SCALE); + } + + // Prepare enough area for thisplays_view by adding the maximum height. + this.displaysView_.style.height = totalHeight + 'px'; + + // Centering the bounding box of the display rectangles. + var offset = {x: $('display-options-displays-view').offsetWidth / 2 - + (boundingBox.left + boundingBox.right) / 2, + y: totalHeight / 2 - + (boundingBox.top + boundingBox.bottom) / 2}; + + + for (var i = 0; i < this.displays_.length; i++) { + var display = this.displays_[i]; + var div = document.createElement('div'); + display.div = div; + + div.className = 'displays-display'; + if (i == this.focusedIndex_) + div.classList.add('displays-focused'); + div.style.width = display.width * VISUAL_SCALE + 'px'; + div.style.height = display.height * VISUAL_SCALE + 'px'; + div.style.lineHeight = div.style.height; + if (i == 0) { + // Assumes that first display is primary and put a grey rectangle to + // denote launcher below. + var launcher = document.createElement('div'); + launcher.id = 'display-launcher'; + launcher.style.width = display.div.style.width; + div.appendChild(launcher); + } + div.style.left = display.x * VISUAL_SCALE + offset.x + 'px'; + div.style.top = display.y * VISUAL_SCALE + offset.y + 'px'; + + div.appendChild(document.createTextNode(display.name)); + + this.displaysView_.appendChild(div); + } + }, + + /** + * Called when the display arrangement has changed. + * @private + * @param {boolean} mirroring Whether current mode is mirroring or not. + * @param {Array} displays The list of the display information. + * @param {SecondaryDisplayLayout} layout The layout strategy. + * @param {number} offset The offset of the secondary display. + */ + onDisplayChanged_: function(mirroring, displays, layout, offset) { + this.mirroring_ = mirroring; + this.layout_ = layout; + this.offset_ = offset; + + $('display-options-toggle-mirroring').textContent = + loadTimeData.getString( + this.mirroring_ ? 'stopMirroring' : 'startMirroring'); + + // Focus to the first display next to the primary one when |displays| list + // is updated. + if (this.mirroring_) + this.focusedIndex_ = null; + else if (this.displays_.length != displays.length) + this.focusedIndex_ = 1; + + this.displays_ = displays; + + this.resetDisplaysView_(); + if (this.mirroring_) + this.layoutMirroringDisplays_(); + else + this.layoutDisplays_(); + this.updateSelectedDisplayDescription_(); + }, + }; + + DisplayOptions.setDisplayInfo = function( + mirroring, displays, layout, offset) { + DisplayOptions.getInstance().onDisplayChanged_( + mirroring, displays, layout, offset); + }; + + // Export + return { + DisplayOptions: DisplayOptions + }; +}); diff --git a/chrome/browser/resources/options/chromeos/internet_detail.css b/chrome/browser/resources/options/chromeos/internet_detail.css new file mode 100644 index 0000000..b92a4f5 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/internet_detail.css @@ -0,0 +1,83 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/* Force tab strip to extend to the left and right edges of the window. */ +#internet-details-content-area { + -webkit-box-orient: vertical; + display: -webkit-box; + padding: 6px 0 0 0; +} + +#network-details-header { + -webkit-padding-start: 20px; + margin: 0; + padding-bottom: 12px; + padding-top: 32px; +} + +#network-details-title { + font-size: 18px; +} + +#network-details-subtitle-status { + color: rgb(53, 174, 71); +} + + +/* Fix the height of the subpages so that the dialog does not resize when the + user switches tabs. */ +#internet-details-content-area > .subpages-tab-contents { + -webkit-box-flex: 1; + -webkit-box-sizing: border-box; + -webkit-padding-end: 10px; + height: 380px; + min-width: 480px; + overflow-y: auto; +} + +#ip-config-list { + min-height: 96px !important; +} + +/* Minimum and maximum height are integer multiples of the height of a list + entry. */ +#ignored-host-list { + -webkit-margin-start: 0; + border: solid 1px #999; + max-height: 128px; + min-height: 64px; + width: 400px; +} + +#new-host { + -webkit-margin-start: 0; + margin-top: 8px; +} + +#ipconfig-section { + border-top: 1px solid #eee; + margin-bottom: 10px; + padding-top: 10px; +} + +#ipconfig-dns-section { + border-top: 1px solid #eee; + padding-top: 10px; +} + +#user-dns-settings:not([selected]) { + display: none; +} + +.dns-display { + -webkit-margin-start: 24px; + -webkit-transition: opacity 150ms ease-in-out; + color: #bbb; + font-style: italic; +} + +.dns-display:not([selected]) { + -webkit-transition: opacity 150ms ease-in-out; + display: none; +} \ No newline at end of file diff --git a/chrome/browser/resources/options/chromeos/internet_detail.html b/chrome/browser/resources/options/chromeos/internet_detail.html new file mode 100644 index 0000000..7069d27 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/internet_detail.html @@ -0,0 +1,667 @@ + diff --git a/chrome/browser/resources/options/chromeos/internet_detail.js b/chrome/browser/resources/options/chromeos/internet_detail.js new file mode 100644 index 0000000..51c28e7 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/internet_detail.js @@ -0,0 +1,1018 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.internet', function() { + var OptionsPage = options.OptionsPage; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var IPAddressField = options.internet.IPAddressField; + + /** + * Network settings constants. These enums must match their C++ + * counterparts. + */ + function Constants() {} + + // Network types: + Constants.TYPE_UNKNOWN = 0; + Constants.TYPE_ETHERNET = 1; + Constants.TYPE_WIFI = 2; + Constants.TYPE_WIMAX = 3; + Constants.TYPE_BLUETOOTH = 4; + Constants.TYPE_CELLULAR = 5; + Constants.TYPE_VPN = 6; + + /* + * Helper function to set hidden attribute for elements matching a selector. + * @param {string} selector CSS selector for extracting a list of elements. + * @param {bool} hidden New hidden value. + */ + function updateHidden(selector, hidden) { + var elements = cr.doc.querySelectorAll(selector); + for (var i = 0, el; el = elements[i]; i++) { + el.hidden = hidden; + } + } + + /** + * Monitor pref change of given element. + * @param {Element} el Target element. + */ + function observePrefsUI(el) { + Preferences.getInstance().addEventListener(el.pref, handlePrefUpdate); + } + + /** + * UI pref change handler. + * @param {Event} e The update event. + */ + function handlePrefUpdate(e) { + DetailsInternetPage.getInstance().updateControls(); + } + + ///////////////////////////////////////////////////////////////////////////// + // DetailsInternetPage class: + + /** + * Encapsulated handling of ChromeOS internet details overlay page. + * @constructor + */ + function DetailsInternetPage() { + OptionsPage.call(this, + 'detailsInternetPage', + null, + 'details-internet-page'); + } + + cr.addSingletonGetter(DetailsInternetPage); + + DetailsInternetPage.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Indicates if the list of proxy exceptions has been initialized. + * @type {boolean} + */ + proxyListInitialized_: false, + + /** + * Initializes DetailsInternetPage page. + * Calls base class implementation to starts preference initialization. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + options.internet.CellularPlanElement.decorate($('plan-list')); + var params = parseQueryParams(window.location); + this.initializePageContents_(params); + this.showNetworkDetails_(params); + }, + + /** + * Auto-activates the network details dialog if network information + * is included in the URL. + */ + showNetworkDetails_: function(params) { + var servicePath = params.servicePath; + var networkType = params.networkType; + if (!servicePath || !servicePath.length || + !networkType || !networkType.length) + return; + chrome.send('networkCommand', + [networkType, servicePath, 'options']); + }, + + /** + * Initializes the contents of the page. + */ + initializePageContents_: function(params) { + $('details-internet-dismiss').addEventListener('click', function(event) { + DetailsInternetPage.setDetails(); + }); + + $('details-internet-login').addEventListener('click', function(event) { + DetailsInternetPage.setDetails(); + DetailsInternetPage.loginFromDetails(); + }); + + $('details-internet-disconnect').addEventListener('click', + function(event) { + DetailsInternetPage.setDetails(); + DetailsInternetPage.disconnectNetwork(); + }); + + $('activate-details').addEventListener('click', function(event) { + DetailsInternetPage.activateFromDetails(); + }); + + $('buyplan-details').addEventListener('click', function(event) { + var data = $('connection-state').data; + chrome.send('buyDataPlan', [String(data.servicePath)]); + OptionsPage.closeOverlay(); + }); + + $('view-account-details').addEventListener('click', function(event) { + chrome.send('showMorePlanInfo'); + OptionsPage.closeOverlay(); + }); + + $('cellular-apn-use-default').addEventListener('click', function(event) { + var data = $('connection-state').data; + var apnSelector = $('select-apn'); + + if (data.userApnIndex != -1) { + apnSelector.remove(data.userApnIndex); + data.userApnIndex = -1; + } + + if (data.providerApnList.value.length > 0) { + var iApn = 0; + data.apn.apn = data.providerApnList.value[iApn].apn; + data.apn.username = data.providerApnList.value[iApn].username; + data.apn.password = data.providerApnList.value[iApn].password; + chrome.send('setApn', [String(data.servicePath), + String(data.apn.apn), + String(data.apn.username), + String(data.apn.password)]); + apnSelector.selectedIndex = iApn; + data.selectedApn = iApn; + } else { + data.apn.apn = ''; + data.apn.username = ''; + data.apn.password = ''; + apnSelector.selectedIndex = -1; + data.selectedApn = -1; + } + updateHidden('.apn-list-view', false); + updateHidden('.apn-details-view', true); + }); + + $('cellular-apn-set').addEventListener('click', function(event) { + if ($('cellular-apn').value == '') + return; + + var data = $('connection-state').data; + var apnSelector = $('select-apn'); + + data.apn.apn = String($('cellular-apn').value); + data.apn.username = String($('cellular-apn-username').value); + data.apn.password = String($('cellular-apn-password').value); + chrome.send('setApn', [String(data.servicePath), + String(data.apn.apn), + String(data.apn.username), + String(data.apn.password)]); + + if (data.userApnIndex != -1) { + apnSelector.remove(data.userApnIndex); + data.userApnIndex = -1; + } + + var option = document.createElement('option'); + option.textContent = data.apn.apn; + option.value = -1; + option.selected = true; + apnSelector.add(option, apnSelector[apnSelector.length - 1]); + data.userApnIndex = apnSelector.length - 2; + data.selectedApn = data.userApnIndex; + + updateHidden('.apn-list-view', false); + updateHidden('.apn-details-view', true); + }); + + $('cellular-apn-cancel').addEventListener('click', function(event) { + $('select-apn').selectedIndex = $('connection-state').data.selectedApn; + updateHidden('.apn-list-view', false); + updateHidden('.apn-details-view', true); + }); + + $('select-apn').addEventListener('change', function(event) { + var data = $('connection-state').data; + var apnSelector = $('select-apn'); + if (apnSelector[apnSelector.selectedIndex].value != -1) { + var apnList = data.providerApnList.value; + chrome.send('setApn', [String(data.servicePath), + String(apnList[apnSelector.selectedIndex].apn), + String(apnList[apnSelector.selectedIndex].username), + String(apnList[apnSelector.selectedIndex].password) + ]); + data.selectedApn = apnSelector.selectedIndex; + } else if (apnSelector.selectedIndex == data.userApnIndex) { + chrome.send('setApn', [String(data.servicePath), + String(data.apn.apn), + String(data.apn.username), + String(data.apn.password)]); + data.selectedApn = apnSelector.selectedIndex; + } else { + $('cellular-apn').value = data.apn.apn; + $('cellular-apn-username').value = data.apn.username; + $('cellular-apn-password').value = data.apn.password; + + updateHidden('.apn-list-view', true); + updateHidden('.apn-details-view', false); + } + }); + + $('sim-card-lock-enabled').addEventListener('click', function(event) { + var newValue = $('sim-card-lock-enabled').checked; + // Leave value as is because user needs to enter PIN code first. + // When PIN will be entered and value changed, + // we'll update UI to reflect that change. + $('sim-card-lock-enabled').checked = !newValue; + chrome.send('setSimCardLock', [newValue]); + }); + $('change-pin').addEventListener('click', function(event) { + chrome.send('changePin'); + }); + + // Proxy + options.proxyexceptions.ProxyExceptions.decorate($('ignored-host-list')); + $('remove-host').addEventListener('click', + this.handleRemoveProxyExceptions_); + $('add-host').addEventListener('click', this.handleAddProxyException_); + $('direct-proxy').addEventListener('click', this.disableManualProxy_); + $('manual-proxy').addEventListener('click', this.enableManualProxy_); + $('auto-proxy').addEventListener('click', this.disableManualProxy_); + $('proxy-all-protocols').addEventListener('click', + this.toggleSingleProxy_); + + observePrefsUI($('direct-proxy')); + observePrefsUI($('manual-proxy')); + observePrefsUI($('auto-proxy')); + observePrefsUI($('proxy-all-protocols')); + + $('ip-automatic-configuration-checkbox').addEventListener('click', + this.handleIpAutoConfig_); + $('automatic-dns-radio').addEventListener('click', + this.handleNameServerTypeChange_); + $('google-dns-radio').addEventListener('click', + this.handleNameServerTypeChange_); + $('user-dns-radio').addEventListener('click', + this.handleNameServerTypeChange_); + }, + + /** + * Handler for "add" event fired from userNameEdit. + * @param {Event} e Add event fired from userNameEdit. + * @private + */ + handleAddProxyException_: function(e) { + var exception = $('new-host').value; + $('new-host').value = ''; + + exception = exception.trim(); + if (exception) + $('ignored-host-list').addException(exception); + }, + + /** + * Handler for when the remove button is clicked + * @param {Event} e The click event. + * @private + */ + handleRemoveProxyExceptions_: function(e) { + var selectedItems = $('ignored-host-list').selectedItems; + for (var x = 0; x < selectedItems.length; x++) { + $('ignored-host-list').removeException(selectedItems[x]); + } + }, + + /** + * Handler for when the IP automatic configuration checkbox is clicked. + * @param {Event} e The click event. + * @private + */ + handleIpAutoConfig_: function(e) { + var checked = $('ip-automatic-configuration-checkbox').checked; + var fields = [$('ip-address'), $('ip-netmask'), $('ip-gateway')]; + for (var i = 0; i < fields.length; ++i) { + fields[i].editable = !checked; + if (checked) { + var model = fields[i].model; + model.value = model.automatic; + fields[i].model = model; + } + } + if (!checked) + $('ip-address').focus(); + }, + + /** + * Handler for when the name server selection changes. + * @param {Event} e The click event. + * @private + */ + handleNameServerTypeChange_: function(event) { + var type = event.target.value; + DetailsInternetPage.updateNameServerDisplay(type); + }, + + /** + * Update details page controls. + * @private + */ + updateControls: function() { + // Only show ipconfig section if network is connected OR if nothing on + // this device is connected. This is so that you can fix the ip configs + // if you can't connect to any network. + // TODO(chocobo): Once ipconfig is moved to flimflam service objects, + // we need to redo this logic to allow configuration of all networks. + $('ipconfig-section').hidden = !this.connected && this.deviceConnected; + $('ipconfig-dns-section').hidden = + !this.connected && this.deviceConnected; + + // Network type related. + updateHidden('#details-internet-page .cellular-details', !this.cellular); + updateHidden('#details-internet-page .wifi-details', !this.wireless); + updateHidden('#details-internet-page .wimax-details', !this.wimax); + updateHidden('#details-internet-page .vpn-details', !this.vpn); + updateHidden('#details-internet-page .proxy-details', !this.showProxy); + /* Network information merged into the Wifi tab for wireless networks + unless the option is set for enabling a static IP configuration. */ + updateHidden('#details-internet-page .network-details', + (this.wireless && !this.showStaticIPConfig) || this.vpn); + updateHidden('#details-internet-page .wifi-network-setting', + this.showStaticIPConfig); + + // Cell plan related. + $('plan-list').hidden = this.cellplanloading; + updateHidden('#details-internet-page .no-plan-info', + !this.cellular || this.cellplanloading || this.hascellplan); + updateHidden('#details-internet-page .plan-loading-info', + !this.cellular || this.nocellplan || this.hascellplan); + updateHidden('#details-internet-page .plan-details-info', + !this.cellular || this.nocellplan || this.cellplanloading); + updateHidden('#details-internet-page .gsm-only', + !this.cellular || !this.gsm); + updateHidden('#details-internet-page .cdma-only', + !this.cellular || this.gsm); + updateHidden('#details-internet-page .apn-list-view', + !this.cellular || !this.gsm); + updateHidden('#details-internet-page .apn-details-view', true); + + // Wifi - Password and shared. + updateHidden('#details-internet-page #password-details', + !this.wireless || !this.password); + updateHidden('#details-internet-page #wifi-shared-network', + !this.shared); + updateHidden('#details-internet-page #prefer-network', + !this.showPreferred); + + // WiMAX. + updateHidden('#details-internet-page #wimax-shared-network', + !this.shared); + + // Proxy + this.updateProxyBannerVisibility_(); + this.toggleSingleProxy_(); + if ($('manual-proxy').checked) + this.enableManualProxy_(); + else + this.disableManualProxy_(); + if (!this.proxyListInitialized_ && this.visible) { + this.proxyListInitialized_ = true; + $('ignored-host-list').redraw(); + } + }, + + /** + * Updates info banner visibility state. This function shows the banner + * if proxy is managed or shared-proxies is off for shared network. + * @private + */ + updateProxyBannerVisibility_: function() { + var bannerDiv = $('info-banner'); + // Show banner and determine its message if necessary. + var controlledBy = $('direct-proxy').controlledBy; + if (!controlledBy || controlledBy == '') { + bannerDiv.hidden = true; + } else { + bannerDiv.hidden = false; + // controlledBy must match strings loaded in proxy_handler.cc and + // set in proxy_cros_settings_provider.cc. + $('banner-text').textContent = loadTimeData.getString(controlledBy); + } + }, + + /** + * Handler for when the user clicks on the checkbox to allow a + * single proxy usage. + * @private + * @param {Event} e Click Event. + */ + toggleSingleProxy_: function(e) { + if ($('proxy-all-protocols').checked) { + $('multi-proxy').hidden = true; + $('single-proxy').hidden = false; + } else { + $('multi-proxy').hidden = false; + $('single-proxy').hidden = true; + } + }, + + /** + * Handler for selecting a radio button that will disable the manual + * controls. + * @private + * @param {Event} e Click event. + */ + disableManualProxy_: function(e) { + $('advanced-config').hidden = true; + $('proxy-all-protocols').disabled = true; + $('proxy-host-name').disabled = true; + $('proxy-host-port').disabled = true; + $('proxy-host-single-name').disabled = true; + $('proxy-host-single-port').disabled = true; + $('secure-proxy-host-name').disabled = true; + $('secure-proxy-port').disabled = true; + $('ftp-proxy').disabled = true; + $('ftp-proxy-port').disabled = true; + $('socks-host').disabled = true; + $('socks-port').disabled = true; + $('proxy-config').disabled = $('auto-proxy').disabled || + !$('auto-proxy').checked; + }, + + /** + * Handler for selecting a radio button that will enable the manual + * controls. + * @private + * @param {Event} e Click event. + */ + enableManualProxy_: function(e) { + $('advanced-config').hidden = false; + $('ignored-host-list').redraw(); + var all_disabled = $('manual-proxy').disabled; + $('new-host').disabled = all_disabled; + $('remove-host').disabled = all_disabled; + $('add-host').disabled = all_disabled; + $('proxy-all-protocols').disabled = all_disabled; + $('proxy-host-name').disabled = all_disabled; + $('proxy-host-port').disabled = all_disabled; + $('proxy-host-single-name').disabled = all_disabled; + $('proxy-host-single-port').disabled = all_disabled; + $('secure-proxy-host-name').disabled = all_disabled; + $('secure-proxy-port').disabled = all_disabled; + $('ftp-proxy').disabled = all_disabled; + $('ftp-proxy-port').disabled = all_disabled; + $('socks-host').disabled = all_disabled; + $('socks-port').disabled = all_disabled; + $('proxy-config').disabled = true; + }, + }; + + /** + * Performs minimal initialization of the InternetDetails dialog in + * preparation for showing proxy-setttings. + */ + DetailsInternetPage.initializeProxySettings = function() { + var detailsPage = DetailsInternetPage.getInstance(); + detailsPage.initializePageContents_(); + }; + + /** + * Displays the InternetDetails dialog with only the proxy settings visible. + */ + DetailsInternetPage.showProxySettings = function() { + var detailsPage = DetailsInternetPage.getInstance(); + $('network-details-header').hidden = true; + $('buyplan-details').hidden = true; + $('activate-details').hidden = true; + $('view-account-details').hidden = true; + detailsPage.cellular = false; + detailsPage.wireless = false; + detailsPage.vpn = false; + detailsPage.showProxy = true; + updateHidden('#internet-tab', true); + updateHidden('#details-tab-strip', true); + updateHidden('#details-internet-page .action-area', true); + detailsPage.updateControls(); + detailsPage.visible = true; + }; + + DetailsInternetPage.updateCellularPlans = function(data) { + var detailsPage = DetailsInternetPage.getInstance(); + detailsPage.cellplanloading = false; + if (data.plans && data.plans.length) { + detailsPage.nocellplan = false; + detailsPage.hascellplan = true; + $('plan-list').load(data.plans); + } else { + detailsPage.nocellplan = true; + detailsPage.hascellplan = false; + } + + detailsPage.hasactiveplan = !data.needsPlan; + detailsPage.activated = data.activated; + if (!data.activated) + $('details-internet-login').hidden = true; + + $('buyplan-details').hidden = !data.showBuyButton; + $('activate-details').hidden = !data.showActivateButton; + $('view-account-details').hidden = !data.showViewAccountButton; + }; + + DetailsInternetPage.updateSecurityTab = function(requirePin) { + $('sim-card-lock-enabled').checked = requirePin; + $('change-pin').hidden = !requirePin; + }; + + + DetailsInternetPage.loginFromDetails = function() { + var data = $('connection-state').data; + var servicePath = data.servicePath; + chrome.send('networkCommand', [String(data.type), + servicePath, + 'connect']); + OptionsPage.closeOverlay(); + }; + + DetailsInternetPage.disconnectNetwork = function() { + var data = $('connection-state').data; + var servicePath = data.servicePath; + chrome.send('networkCommand', [String(data.type), + servicePath, + 'disconnect']); + OptionsPage.closeOverlay(); + }; + + DetailsInternetPage.activateFromDetails = function() { + var data = $('connection-state').data; + var servicePath = data.servicePath; + if (data.type == Constants.TYPE_CELLULAR) { + chrome.send('networkCommand', [String(data.type), + String(servicePath), + 'activate']); + } + OptionsPage.closeOverlay(); + }; + + DetailsInternetPage.setDetails = function() { + var data = $('connection-state').data; + var servicePath = data.servicePath; + if (data.type == Constants.TYPE_WIFI) { + chrome.send('setPreferNetwork', + [String(servicePath), + $('prefer-network-wifi').checked ? 'true' : 'false']); + chrome.send('setAutoConnect', + [String(servicePath), + $('auto-connect-network-wifi').checked ? 'true' : 'false']); + } else if (data.type == Constants.TYPE_WIMAX) { + chrome.send('setAutoConnect', + [String(servicePath), + $('auto-connect-network-wimax').checked ? 'true' : 'false']); + } else if (data.type == Constants.TYPE_CELLULAR) { + chrome.send('setAutoConnect', + [String(servicePath), + $('auto-connect-network-cellular').checked ? 'true' : + 'false']); + } + + var nameServerTypes = ['automatic', 'google', 'user']; + var nameServerType = 'automatic'; + for (var i = 0; i < nameServerTypes.length; ++i) { + if ($(nameServerTypes[i] + '-dns-radio').checked) { + nameServerType = nameServerTypes[i]; + break; + } + } + + // Skip any empty values. + var userNameServers = []; + for (var i = 1; i <= 4; ++i) { + var nameServerField = $('ipconfig-dns' + i); + if (nameServerField && nameServerField.model && + nameServerField.model.value) { + userNameServers.push(nameServerField.model.value); + } + } + + userNameServers = userNameServers.join(','); + + chrome.send('setIPConfig', + [servicePath, + Boolean($('ip-automatic-configuration-checkbox').checked), + $('ip-address').model.value || '', + $('ip-netmask').model.value || '', + $('ip-gateway').model.value || '', + nameServerType, + userNameServers]); + OptionsPage.closeOverlay(); + }; + + DetailsInternetPage.updateNameServerDisplay = function(type) { + var editable = type == 'user'; + var fields = [$('ipconfig-dns1'), $('ipconfig-dns2'), + $('ipconfig-dns3'), $('ipconfig-dns4')]; + for (var i = 0; i < fields.length; ++i) { + fields[i].editable = editable; + } + if (editable) + $('ipconfig-dns1').focus(); + + var automaticDns = $('automatic-dns-display'); + var googleDns = $('google-dns-display'); + var userDns = $('user-dns-settings'); + switch (type) { + case 'automatic': + automaticDns.setAttribute('selected', ''); + googleDns.removeAttribute('selected'); + userDns.removeAttribute('selected'); + break; + case 'google': + automaticDns.removeAttribute('selected'); + googleDns.setAttribute('selected', ''); + userDns.removeAttribute('selected'); + break; + case 'user': + automaticDns.removeAttribute('selected'); + googleDns.removeAttribute('selected'); + userDns.setAttribute('selected', ''); + break; + } + }; + + DetailsInternetPage.showDetailedInfo = function(data) { + var detailsPage = DetailsInternetPage.getInstance(); + + // Populate header + $('network-details-title').textContent = data.networkName; + var statusKey = data.connected ? 'networkConnected' : + 'networkNotConnected'; + $('network-details-subtitle-status').textContent = + loadTimeData.getString(statusKey); + var typeKey = null; + switch (data.type) { + case Constants.TYPE_ETHERNET: + typeKey = 'ethernetTitle'; + break; + case Constants.TYPE_WIFI: + typeKey = 'wifiTitle'; + break; + case Constants.TYPE_WIMAX: + typeKey = 'wimaxTitle'; + break; + case Constants.TYPE_CELLULAR: + typeKey = 'cellularTitle'; + break; + case Constants.TYPE_VPN: + typeKey = 'vpnTitle'; + break; + } + var typeLabel = $('network-details-subtitle-type'); + var typeSeparator = $('network-details-subtitle-separator'); + if (typeKey) { + typeLabel.textContent = loadTimeData.getString(typeKey); + typeLabel.hidden = false; + typeSeparator.hidden = false; + } else { + typeLabel.hidden = true; + typeSeparator.hidden = true; + } + + // TODO(chocobo): Is this hack to cache the data here reasonable? + // TODO(kevers): Find more appropriate place to cache data. + $('connection-state').data = data; + + $('buyplan-details').hidden = true; + $('activate-details').hidden = true; + $('view-account-details').hidden = true; + $('details-internet-login').hidden = data.connected; + if (data.type == Constants.TYPE_ETHERNET) + $('details-internet-disconnect').hidden = true; + else + $('details-internet-disconnect').hidden = !data.connected; + + detailsPage.deviceConnected = data.deviceConnected; + detailsPage.connecting = data.connecting; + detailsPage.connected = data.connected; + detailsPage.showProxy = data.showProxy; + detailsPage.showStaticIPConfig = data.showStaticIPConfig; + $('connection-state').textContent = data.connectionState; + + var ipAutoConfig = data.ipAutoConfig ? 'automatic' : 'user'; + $('ip-automatic-configuration-checkbox').checked = data.ipAutoConfig; + var inetAddress = {autoConfig: ipAutoConfig}; + var inetNetmask = {autoConfig: ipAutoConfig}; + var inetGateway = {autoConfig: ipAutoConfig}; + + if (data.ipconfig.value) { + inetAddress.automatic = data.ipconfig.value.address; + inetAddress.value = data.ipconfig.value.address; + inetNetmask.automatic = data.ipconfig.value.netmask; + inetNetmask.value = data.ipconfig.value.netmask; + inetGateway.automatic = data.ipconfig.value.gateway; + inetGateway.value = data.ipconfig.value.gateway; + } + + // Override the "automatic" values with the real saved DHCP values, + // if they are set. + if (data.savedIP.address) { + inetAddress.automatic = data.savedIP.address; + inetAddress.value = data.savedIP.address; + } + if (data.savedIP.netmask) { + inetNetmask.automatic = data.savedIP.netmask; + inetNetmask.value = data.savedIP.netmask; + } + if (data.savedIP.gateway) { + inetGateway.automatic = data.savedIP.gateway; + inetGateway.value = data.savedIP.gateway; + } + + if (ipAutoConfig == 'user') { + if (data.staticIP.value.address) { + inetAddress.value = data.staticIP.value.address; + inetAddress.user = data.staticIP.value.address; + } + if (data.staticIP.value.netmask) { + inetNetmask.value = data.staticIP.value.netmask; + inetNetmask.user = data.staticIP.value.netmask; + } + if (data.staticIP.value.gateway) { + inetGateway.value = data.staticIP.value.gateway; + inetGateway.user = data.staticIP.value.gateway; + } + } + + var configureAddressField = function(field, model) { + IPAddressField.decorate(field); + field.model = model; + field.editable = model.autoConfig == 'user'; + }; + + configureAddressField($('ip-address'), inetAddress); + configureAddressField($('ip-netmask'), inetNetmask); + configureAddressField($('ip-gateway'), inetGateway); + + var inetNameServers = ''; + if (data.ipconfig.value && data.ipconfig.value.nameServers) { + inetNameServers = data.ipconfig.value.nameServers; + $('automatic-dns-display').textContent = inetNameServers; + } + + if (data.savedIP && data.savedIP.nameServers) + $('automatic-dns-display').textContent = data.savedIP.nameServers; + + if (data.nameServersGoogle) + $('google-dns-display').textContent = data.nameServersGoogle; + + var nameServersUser = []; + if (data.staticIP.value.nameServers) + nameServersUser = data.staticIP.value.nameServers.split(','); + + var nameServerModels = []; + for (var i = 0; i < 4; ++i) + nameServerModels.push({value: nameServersUser[i] || ''}); + + $(data.nameServerType + '-dns-radio').checked = true; + configureAddressField($('ipconfig-dns1'), nameServerModels[0]); + configureAddressField($('ipconfig-dns2'), nameServerModels[1]); + configureAddressField($('ipconfig-dns3'), nameServerModels[2]); + configureAddressField($('ipconfig-dns4'), nameServerModels[3]); + + DetailsInternetPage.updateNameServerDisplay(data.nameServerType); + + if (data.hardwareAddress) { + $('hardware-address').textContent = data.hardwareAddress; + $('hardware-address-row').style.display = 'table-row'; + } else { + // This is most likely a device without a hardware address. + $('hardware-address-row').style.display = 'none'; + } + if (data.type == Constants.TYPE_WIFI) { + OptionsPage.showTab($('wifi-network-nav-tab')); + detailsPage.wireless = true; + detailsPage.vpn = false; + detailsPage.ethernet = false; + detailsPage.cellular = false; + detailsPage.gsm = false; + detailsPage.wimax = false; + detailsPage.shared = data.shared; + $('wifi-connection-state').textContent = data.connectionState; + $('wifi-ssid').textContent = data.ssid; + if (data.bssid && data.bssid.length > 0) { + $('wifi-bssid').textContent = data.bssid; + $('wifi-bssid-entry').hidden = false; + } else { + $('wifi-bssid-entry').hidden = true; + } + $('wifi-ip-address').textContent = inetAddress.value; + $('wifi-netmask').textContent = inetNetmask.value; + $('wifi-gateway').textContent = inetGateway.value; + $('wifi-name-servers').textContent = inetNameServers; + if (data.encryption && data.encryption.length > 0) { + $('wifi-security').textContent = data.encryption; + $('wifi-security-entry').hidden = false; + } else { + $('wifi-security-entry').hidden = true; + } + // Frequency is in MHz. + var frequency = loadTimeData.getString('inetFrequencyFormat'); + frequency = frequency.replace('$1', data.frequency); + $('wifi-frequency').textContent = frequency; + // Signal strength as percentage. + var signalStrength = loadTimeData.getString('inetSignalStrengthFormat'); + signalStrength = signalStrength.replace('$1', data.strength); + $('wifi-signal-strength').textContent = signalStrength; + if (data.hardwareAddress) { + $('wifi-hardware-address').textContent = data.hardwareAddress; + $('wifi-hardware-address-entry').hidden = false; + } else { + $('wifi-hardware-address-entry').hidden = true; + } + detailsPage.showPreferred = data.showPreferred; + $('prefer-network-wifi').checked = data.preferred.value; + $('prefer-network-wifi').disabled = !data.remembered; + $('auto-connect-network-wifi').checked = data.autoConnect.value; + $('auto-connect-network-wifi').disabled = !data.remembered; + detailsPage.password = data.encrypted; + } else if (data.type == Constants.TYPE_WIMAX) { + OptionsPage.showTab($('wimax-network-nav-tab')); + detailsPage.wimax = true; + detailsPage.wireless = false; + detailsPage.vpn = false; + detailsPage.ethernet = false; + detailsPage.cellular = false; + detailsPage.gsm = false; + detailsPage.shared = data.shared; + detailsPage.showPreferred = data.showPreferred; + $('wimax-connection-state').textContent = data.connectionState; + $('auto-connect-network-wimax').checked = data.autoConnect.value; + $('auto-connect-network-wimax').disabled = !data.remembered; + if (data.identity) { + $('wimax-eap-identity').textContent = data.identity; + $('wimax-eap-identity-entry').hidden = false; + } else { + $('wimax-eap-identity-entry').hidden = true; + } + // Signal strength as percentage. + var signalStrength = loadTimeData.getString('inetSignalStrengthFormat'); + signalStrength = signalStrength.replace('$1', data.strength); + $('wimax-signal-strength').textContent = signalStrength; + } else if (data.type == Constants.TYPE_CELLULAR) { + if (!data.gsm) + OptionsPage.showTab($('cellular-plan-nav-tab')); + else + OptionsPage.showTab($('cellular-conn-nav-tab')); + detailsPage.ethernet = false; + detailsPage.wireless = false; + detailsPage.wimax = false; + detailsPage.vpn = false; + detailsPage.cellular = true; + $('service-name').textContent = data.serviceName; + $('network-technology').textContent = data.networkTechnology; + $('activation-state').textContent = data.activationState; + $('roaming-state').textContent = data.roamingState; + $('restricted-pool').textContent = data.restrictedPool; + $('error-state').textContent = data.errorState; + $('manufacturer').textContent = data.manufacturer; + $('model-id').textContent = data.modelId; + $('firmware-revision').textContent = data.firmwareRevision; + $('hardware-revision').textContent = data.hardwareRevision; + $('prl-version').textContent = data.prlVersion; + $('meid').textContent = data.meid; + $('imei').textContent = data.imei; + $('mdn').textContent = data.mdn; + $('esn').textContent = data.esn; + $('min').textContent = data.min; + detailsPage.gsm = data.gsm; + if (data.gsm) { + $('operator-name').textContent = data.operatorName; + $('operator-code').textContent = data.operatorCode; + $('imsi').textContent = data.imsi; + + var apnSelector = $('select-apn'); + // Clear APN lists, keep only last element that "other". + while (apnSelector.length != 1) + apnSelector.remove(0); + var otherOption = apnSelector[0]; + data.selectedApn = -1; + data.userApnIndex = -1; + var apnList = data.providerApnList.value; + for (var i = 0; i < apnList.length; i++) { + var option = document.createElement('option'); + var name = apnList[i].localizedName; + if (name == '' && apnList[i].name != '') + name = apnList[i].name; + if (name == '') + name = apnList[i].apn; + else + name = name + ' (' + apnList[i].apn + ')'; + option.textContent = name; + option.value = i; + if ((data.apn.apn == apnList[i].apn && + data.apn.username == apnList[i].username && + data.apn.password == apnList[i].password) || + (data.apn.apn == '' && + data.lastGoodApn.apn == apnList[i].apn && + data.lastGoodApn.username == apnList[i].username && + data.lastGoodApn.password == apnList[i].password)) { + data.selectedApn = i; + } + // Insert new option before "other" option. + apnSelector.add(option, otherOption); + } + if (data.selectedApn == -1 && data.apn.apn != '') { + var option = document.createElement('option'); + option.textContent = data.apn.apn; + option.value = -1; + apnSelector.add(option, otherOption); + data.selectedApn = apnSelector.length - 2; + data.userApnIndex = data.selectedApn; + } + apnSelector.selectedIndex = data.selectedApn; + updateHidden('.apn-list-view', false); + updateHidden('.apn-details-view', true); + DetailsInternetPage.updateSecurityTab(data.simCardLockEnabled.value); + } + $('auto-connect-network-cellular').checked = data.autoConnect.value; + $('auto-connect-network-cellular').disabled = false; + + $('buyplan-details').hidden = !data.showBuyButton; + $('view-account-details').hidden = !data.showViewAccountButton; + $('activate-details').hidden = !data.showActivateButton; + if (data.showActivateButton) { + $('details-internet-login').hidden = true; + } + + detailsPage.hascellplan = false; + if (data.connected) { + detailsPage.nocellplan = false; + detailsPage.cellplanloading = true; + chrome.send('refreshCellularPlan', [data.servicePath]); + } else { + detailsPage.nocellplan = true; + detailsPage.cellplanloading = false; + } + } else if (data.type == Constants.TYPE_VPN) { + OptionsPage.showTab($('vpn-nav-tab')); + detailsPage.wireless = false; + detailsPage.wimax = false; + detailsPage.vpn = true; + detailsPage.ethernet = false; + detailsPage.cellular = false; + detailsPage.gsm = false; + $('inet-service-name').textContent = data.service_name; + $('inet-server-hostname').textContent = data.server_hostname; + $('inet-provider-type').textContent = data.provider_type; + $('inet-username').textContent = data.username; + } else { + OptionsPage.showTab($('internet-nav-tab')); + detailsPage.ethernet = true; + detailsPage.wireless = false; + detailsPage.wimax = false; + detailsPage.vpn = false; + detailsPage.cellular = false; + detailsPage.gsm = false; + } + + // Update controlled option indicators. + indicators = cr.doc.querySelectorAll( + '#details-internet-page .controlled-setting-indicator'); + for (var i = 0; i < indicators.length; i++) { + var dataProperty = indicators[i].getAttribute('data'); + if (dataProperty && data[dataProperty]) { + var controlledBy = data[dataProperty].controlledBy; + if (controlledBy) { + indicators[i].controlledBy = controlledBy; + var forElement = $(indicators[i].getAttribute('for')); + if (forElement) + forElement.disabled = controlledBy != 'recommended'; + if (forElement.type == 'radio' && !forElement.checked) + indicators[i].hidden = true; + } else { + indicators[i].controlledBy = null; + } + } + } + + detailsPage.updateControls(); + + // Don't show page name in address bar and in history to prevent people + // navigate here by hand and solve issue with page session restore. + OptionsPage.showPageByName('detailsInternetPage', false); + }; + + return { + DetailsInternetPage: DetailsInternetPage + }; +}); diff --git a/chrome/browser/resources/options/chromeos/internet_detail_ip_address_field.js b/chrome/browser/resources/options/chromeos/internet_detail_ip_address_field.js new file mode 100644 index 0000000..5155ecd --- /dev/null +++ b/chrome/browser/resources/options/chromeos/internet_detail_ip_address_field.js @@ -0,0 +1,111 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.internet', function() { + /** @const */ var EditableTextField = options.EditableTextField; + + /** + * The regular expression that matches an IP address. String to match against + * should have all whitespace stripped already. + * @const + * @type {RegExp} + */ + var singleIp_ = /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/; + + /** + * Creates a new field specifically for entering IP addresses. + * @constructor + */ + function IPAddressField() { + var el = cr.doc.createElement('div'); + IPAddressField.decorate(el); + return el; + } + + /** + * Decorates an element as a inline-editable list item. Note that this is + * a subclass of IPAddressField. + * @param {!HTMLElement} el The element to decorate. + */ + IPAddressField.decorate = function(el) { + el.__proto__ = IPAddressField.prototype; + el.decorate(); + }; + + IPAddressField.prototype = { + __proto__: EditableTextField.prototype, + + /** @override */ + decorate: function() { + EditableTextField.prototype.decorate.call(this); + }, + + /** + * Indicates whether or not empty values are allowed. + * @type {boolean} + */ + get allowEmpty() { + return this.hasAttribute('allow-empty'); + }, + + /** @override */ + get currentInputIsValid() { + if (!this.editField.value && this.allowEmpty) + return true; + + // Make sure it's only got numbers and ".", there are the correct + // count of them, and they are all within the correct range. + var fieldValue = this.editField.value.replace(/\s/g, ''); + var matches = singleIp_.exec(fieldValue); + var rangeCorrect = true; + if (matches != null) { + for (var i = 1; i < matches.length; ++i) { + var value = parseInt(matches[i], 10); + if (value < 0 || value > 255) { + rangeCorrect = false; + break; + } + } + } + return this.editField.validity.valid && matches != null && + rangeCorrect && matches.length == 5; + }, + + /** @override */ + get hasBeenEdited() { + return this.editField.value != this.model.value; + }, + + /** + * Overrides superclass to mutate the input during a successful commit. For + * the purposes of entering IP addresses, this just means stripping off + * whitespace and leading zeros from each of the octets so that they conform + * to the normal format for IP addresses. + * @override + * @param {String} value Input IP address to be mutated. + * @return {String} mutated IP address. + */ + mutateInput: function(value) { + if (!value) + return value; + + var fieldValue = value.replace(/\s/g, ''); + var matches = singleIp_.exec(fieldValue); + var result = []; + + // If we got this far, matches shouldn't be null, but make sure. + if (matches != null) { + // starting at one because the first match element contains the entire + // match, and we don't care about that. + for (var i = 1; i < matches.length; ++i) + result.push(parseInt(matches[i], 10)); + } + return result.join('.'); + }, + }; + + return { + IPAddressField: IPAddressField, + }; +}); diff --git a/chrome/browser/resources/options/chromeos/keyboard_overlay.html b/chrome/browser/resources/options/chromeos/keyboard_overlay.html new file mode 100644 index 0000000..7e6c047 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/keyboard_overlay.html @@ -0,0 +1,52 @@ + diff --git a/chrome/browser/resources/options/chromeos/keyboard_overlay.js b/chrome/browser/resources/options/chromeos/keyboard_overlay.js new file mode 100644 index 0000000..45b6859 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/keyboard_overlay.js @@ -0,0 +1,28 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + /** + * Encapsulated handling of the keyboard overlay. + * @constructor + */ + function KeyboardOverlay() { + options.SettingsDialog.call(this, 'keyboard-overlay', + loadTimeData.getString('keyboardOverlayTitle'), + 'keyboard-overlay', + $('keyboard-confirm'), $('keyboard-cancel')); + } + + cr.addSingletonGetter(KeyboardOverlay); + + KeyboardOverlay.prototype = { + __proto__: options.SettingsDialog.prototype, + }; + + // Export + return { + KeyboardOverlay: KeyboardOverlay + }; +}); diff --git a/chrome/browser/resources/options/chromeos/language_chewing_options.html b/chrome/browser/resources/options/chromeos/language_chewing_options.html new file mode 100644 index 0000000..f5cd74c --- /dev/null +++ b/chrome/browser/resources/options/chromeos/language_chewing_options.html @@ -0,0 +1,147 @@ + diff --git a/chrome/browser/resources/options/chromeos/language_hangul_options.html b/chrome/browser/resources/options/chromeos/language_hangul_options.html new file mode 100644 index 0000000..1a8d094 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/language_hangul_options.html @@ -0,0 +1,24 @@ + diff --git a/chrome/browser/resources/options/chromeos/language_mozc_options.html b/chrome/browser/resources/options/chromeos/language_mozc_options.html new file mode 100644 index 0000000..6f3aec2 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/language_mozc_options.html @@ -0,0 +1,141 @@ + diff --git a/chrome/browser/resources/options/chromeos/language_pinyin_options.html b/chrome/browser/resources/options/chromeos/language_pinyin_options.html new file mode 100644 index 0000000..68d5013 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/language_pinyin_options.html @@ -0,0 +1,154 @@ + diff --git a/chrome/browser/resources/options/chromeos/network_list.js b/chrome/browser/resources/options/chromeos/network_list.js new file mode 100644 index 0000000..4408828 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/network_list.js @@ -0,0 +1,1098 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.network', function() { + + var ArrayDataModel = cr.ui.ArrayDataModel; + var List = cr.ui.List; + var ListItem = cr.ui.ListItem; + var Menu = cr.ui.Menu; + var MenuItem = cr.ui.MenuItem; + var ControlledSettingIndicator = options.ControlledSettingIndicator; + + /** + * Network settings constants. These enums usually match their C++ + * counterparts. + */ + function Constants() {} + + // Network types: + Constants.TYPE_UNKNOWN = 0; + Constants.TYPE_ETHERNET = 1; + Constants.TYPE_WIFI = 2; + Constants.TYPE_WIMAX = 3; + Constants.TYPE_BLUETOOTH = 4; + Constants.TYPE_CELLULAR = 5; + Constants.TYPE_VPN = 6; + + // Cellular activation states: + Constants.ACTIVATION_STATE_UNKNOWN = 0; + Constants.ACTIVATION_STATE_ACTIVATED = 1; + Constants.ACTIVATION_STATE_ACTIVATING = 2; + Constants.ACTIVATION_STATE_NOT_ACTIVATED = 3; + Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4; + + /** + * Order in which controls are to appear in the network list sorted by key. + */ + Constants.NETWORK_ORDER = ['ethernet', + 'wifi', + 'wimax', + 'cellular', + 'vpn', + 'airplaneMode', + 'addConnection']; + + /** + * Mapping of network category titles to the network type. + */ + var categoryMap = { + 'cellular': Constants.TYPE_CELLULAR, + 'ethernet': Constants.TYPE_ETHERNET, + 'wimax': Constants.TYPE_WIMAX, + 'wifi': Constants.TYPE_WIFI, + 'vpn': Constants.TYPE_VPN + }; + + /** + * ID of the menu that is currently visible. + * @type {?string} + * @private + */ + var activeMenu_ = null; + + /** + * Indicates if cellular networks are available. + * @type {boolean} + * @private + */ + var cellularAvailable_ = false; + + /** + * Indicates if cellular networks are enabled. + * @type {boolean} + * @private + */ + var cellularEnabled_ = false; + + /** + * Indicates if WiMAX networks are available. + * @type {boolean} + * @private + */ + var wimaxAvailable_ = false; + + /** + * Indicates if WiMAX networks are enabled. + * @type {boolean} + * @private + */ + var wimaxEnabled_ = false; + + /** + * Indicates if mobile data roaming is enabled. + * @type {boolean} + * @private + */ + var enableDataRoaming_ = false; + + /** + * Create an element in the network list for controlling network + * connectivity. + * @param {Object} data Description of the network list or command. + * @constructor + */ + function NetworkListItem(data) { + var el = cr.doc.createElement('li'); + el.data_ = {}; + for (var key in data) + el.data_[key] = data[key]; + NetworkListItem.decorate(el); + return el; + } + + /** + * Decorate an element as a NetworkListItem. + * @param {!Element} el The element to decorate. + */ + NetworkListItem.decorate = function(el) { + el.__proto__ = NetworkListItem.prototype; + el.decorate(); + }; + + NetworkListItem.prototype = { + __proto__: ListItem.prototype, + + /** + * Description of the network group or control. + * @type {Object.} + * @private + */ + data_: null, + + /** + * Element for the control's subtitle. + * @type {?Element} + * @private + */ + subtitle_: null, + + /** + * Icon for the network control. + * @type {?Element} + * @private + */ + icon_: null, + + /** + * Indicates if in the process of connecting to a network. + * @type {boolean} + * @private + */ + connecting_: false, + + /** + * Description of the network control. + * @type {Object} + */ + get data() { + return this.data_; + }, + + /** + * Text label for the subtitle. + * @type {string} + */ + set subtitle(text) { + if (text) + this.subtitle_.textContent = text; + this.subtitle_.hidden = !text; + }, + + /** + * URL for the network icon. + * @type {string} + */ + set iconURL(iconURL) { + this.icon_.style.backgroundImage = url(iconURL); + }, + + /** + * Type of network icon. Each type corresponds to a CSS rule. + * @type {string} + */ + set iconType(type) { + this.icon_.classList.add('network-' + type); + }, + + /** + * Indicates if the network is in the process of being connected. + * @type {boolean} + */ + set connecting(state) { + this.connecting_ = state; + if (state) + this.icon_.classList.add('network-connecting'); + else + this.icon_.classList.remove('network-connecting'); + }, + + /** + * Indicates if the network is in the process of being connected. + * @type {boolean} + */ + get connecting() { + return this.connecting_; + }, + + /** + * Set the direction of the text. + * @param {string} direction The direction of the text, e.g. 'ltr'. + */ + setSubtitleDirection: function(direction) { + this.subtitle_.dir = direction; + }, + + /** + * Indicate that the selector arrow should be shown. + */ + showSelector: function() { + this.subtitle_.classList.add('network-selector'); + }, + + /** + * Adds an indicator to show that the network is policy managed. + */ + showManagedNetworkIndicator: function() { + this.appendChild(new ManagedNetworkIndicator()); + }, + + /* @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + this.className = 'network-group'; + this.icon_ = this.ownerDocument.createElement('div'); + this.icon_.className = 'network-icon'; + this.appendChild(this.icon_); + var textContent = this.ownerDocument.createElement('div'); + textContent.className = 'network-group-labels'; + this.appendChild(textContent); + var categoryLabel = this.ownerDocument.createElement('div'); + var title = this.data_.key + 'Title'; + categoryLabel.className = 'network-title'; + categoryLabel.textContent = loadTimeData.getString(title); + textContent.appendChild(categoryLabel); + this.subtitle_ = this.ownerDocument.createElement('div'); + this.subtitle_.className = 'network-subtitle'; + textContent.appendChild(this.subtitle_); + }, + }; + + /** + * Creates a control that displays a popup menu when clicked. + * @param {Object} data Description of the control. + */ + function NetworkMenuItem(data) { + var el = new NetworkListItem(data); + el.__proto__ = NetworkMenuItem.prototype; + el.decorate(); + return el; + } + + NetworkMenuItem.prototype = { + __proto__: NetworkListItem.prototype, + + /** + * Popup menu element. + * @type {?Element} + * @private + */ + menu_: null, + + /* @inheritDoc */ + decorate: function() { + this.subtitle = null; + if (this.data.iconType) + this.iconType = this.data.iconType; + this.addEventListener('click', function() { + this.showMenu(); + }); + }, + + /** + * Retrieves the ID for the menu. + * @private + */ + getMenuName_: function() { + return this.data_.key + '-network-menu'; + }, + + /** + * Creates a popup menu for the control. + * @return {Element} The newly created menu. + */ + createMenu: function() { + if (this.data.menu) { + var menu = this.ownerDocument.createElement('div'); + menu.id = this.getMenuName_(); + menu.className = 'network-menu'; + menu.hidden = true; + Menu.decorate(menu); + for (var i = 0; i < this.data.menu.length; i++) { + var entry = this.data.menu[i]; + var button = this.ownerDocument.createElement('div'); + button.className = 'network-menu-item'; + var buttonLabel = this.ownerDocument.createElement('div'); + buttonLabel.className = 'network-menu-item-label'; + buttonLabel.textContent = entry.label; + button.appendChild(buttonLabel); + button.addEventListener('click', entry.command); + MenuItem.decorate(button); + menu.appendChild(button); + } + return menu; + } + return null; + }, + + canUpdateMenu: function() { + return false; + }, + + /** + * Displays a popup menu. + */ + showMenu: function() { + var rebuild = false; + // Force a rescan if opening the menu for WiFi networks to ensure the + // list is up to date. Networks are periodically rescanned, but depending + // on timing, there could be an excessive delay before the first rescan + // unless forced. + var rescan = !activeMenu_ && this.data_.key == 'wifi'; + if (!this.menu_) { + rebuild = true; + var existing = $(this.getMenuName_()); + if (existing) { + if (this.updateMenu()) + return; + closeMenu_(); + } + this.menu_ = this.createMenu(); + this.menu_.addEventListener('mousedown', function(e) { + // Prevent blurring of list, which would close the menu. + e.preventDefault(); + }, true); + var parent = $('network-menus'); + if (existing) + parent.replaceChild(this.menu_, existing); + else + parent.appendChild(this.menu_); + } + var top = this.offsetTop + this.clientHeight; + var menuId = this.getMenuName_(); + if (menuId != activeMenu_ || rebuild) { + closeMenu_(); + activeMenu_ = menuId; + this.menu_.style.setProperty('top', top + 'px'); + this.menu_.hidden = false; + } + if (rescan) + chrome.send('refreshNetworks'); + }, + }; + + /** + * Creates a control for selecting or configuring a network connection based + * on the type of connection (e.g. wifi versus vpn). + * @param {{key: string, + * networkList: Array.} data Description of the network. + * @constructor + */ + function NetworkSelectorItem(data) { + var el = new NetworkMenuItem(data); + el.__proto__ = NetworkSelectorItem.prototype; + el.decorate(); + return el; + } + + NetworkSelectorItem.prototype = { + __proto__: NetworkMenuItem.prototype, + + /* @inheritDoc */ + decorate: function() { + // TODO(kevers): Generalize method of setting default label. + var policyManaged = false; + var defaultMessage = this.data_.key == 'wifi' ? + 'networkOffline' : 'networkNotConnected'; + this.subtitle = loadTimeData.getString(defaultMessage); + var list = this.data_.networkList; + var candidateURL = null; + for (var i = 0; i < list.length; i++) { + var networkDetails = list[i]; + if (networkDetails.connecting || networkDetails.connected) { + this.subtitle = networkDetails.networkName; + this.setSubtitleDirection('ltr'); + policyManaged = networkDetails.policyManaged; + candidateURL = networkDetails.iconURL; + // Only break when we see a connecting network as it is possible to + // have a connected network and a connecting network at the same + // time. + if (networkDetails.connecting) { + this.connecting = true; + candidateURL = null; + break; + } + } + } + if (candidateURL) + this.iconURL = candidateURL; + else + this.iconType = this.data.key; + + this.showSelector(); + + if (policyManaged) + this.showManagedNetworkIndicator(); + + if (activeMenu_ == this.getMenuName_()) { + // Menu is already showing and needs to be updated. Explicitly calling + // show menu will force the existing menu to be replaced. The call + // is deferred in order to ensure that position of this element has + // beem properly updated. + var self = this; + setTimeout(function() {self.showMenu();}, 0); + } + }, + + /** + * Creates a menu for selecting, configuring or disconnecting from a + * network. + * @return {Element} The newly created menu. + */ + createMenu: function() { + var menu = this.ownerDocument.createElement('div'); + menu.id = this.getMenuName_(); + menu.className = 'network-menu'; + menu.hidden = true; + Menu.decorate(menu); + var addendum = []; + if (this.data_.key == 'wifi') { + addendum.push({label: loadTimeData.getString('joinOtherNetwork'), + command: 'connect', + data: {networkType: Constants.TYPE_WIFI, + servicePath: '?'}}); + } else if (this.data_.key == 'cellular') { + var label = enableDataRoaming_ ? 'disableDataRoaming' : + 'enableDataRoaming'; + var disabled = !UIAccountTweaks.currentUserIsOwner(); + var entry = {label: loadTimeData.getString(label), + data: {}}; + if (disabled) { + entry.command = null; + entry.tooltip = + loadTimeData.getString('dataRoamingDisableToggleTooltip'); + } else { + entry.command = function() { + options.Preferences.setBooleanPref( + 'cros.signed.data_roaming_enabled', + !enableDataRoaming_); + // Force revalidation of the menu the next time it is displayed. + this.menu_ = null; + }; + } + addendum.push(entry); + } + var list = this.data.rememberedNetworks; + if (list && list.length > 0) { + var callback = function(list) { + $('remembered-network-list').clear(); + var dialog = options.PreferredNetworks.getInstance(); + OptionsPage.showPageByName('preferredNetworksPage', false); + dialog.update(list); + }; + addendum.push({label: loadTimeData.getString('preferredNetworks'), + command: callback, + data: list}); + } + + var networkGroup = this.ownerDocument.createElement('div'); + networkGroup.className = 'network-menu-group'; + list = this.data.networkList; + var empty = !list || list.length == 0; + if (list) { + for (var i = 0; i < list.length; i++) { + var data = list[i]; + this.createNetworkOptionsCallback_(networkGroup, data); + if (data.connected) { + if (data.networkType == Constants.TYPE_VPN) { + // Add separator + addendum.push({}); + var i18nKey = 'disconnectNetwork'; + addendum.push({label: loadTimeData.getString(i18nKey), + command: 'disconnect', + data: data}); + } + } + } + } + if (this.data_.key == 'wifi' || this.data_.key == 'wimax' || + this.data_.key == 'cellular') { + addendum.push({}); + if (this.data_.key == 'wifi') { + addendum.push({label: loadTimeData.getString('turnOffWifi'), + command: function() { + chrome.send('disableWifi'); + }, + data: {}}); + } else if (this.data_.key == 'wimax') { + // TODO(zelidrag): Add proper strings for wimax. + addendum.push({label: loadTimeData.getString('turnOffCellular'), + command: function() { + chrome.send('disableWimax'); + }, + data: {}}); + } else if (this.data_.key == 'cellular') { + addendum.push({label: loadTimeData.getString('turnOffCellular'), + command: function() { + chrome.send('disableCellular'); + }, + data: {}}); + } + } + if (!empty) + menu.appendChild(networkGroup); + if (addendum.length > 0) { + var separator = false; + if (!empty) { + menu.appendChild(MenuItem.createSeparator()); + separator = true; + } + for (var i = 0; i < addendum.length; i++) { + var value = addendum[i]; + if (value.data) { + var item = this.createCallback_(menu, value.data, value.label, + value.command); + if (value.tooltip) + item.title = value.tooltip; + separator = false; + } else if (!separator) { + menu.appendChild(MenuItem.createSeparator()); + separator = true; + } + } + } + return menu; + }, + + /** + * Determines if a menu can be updated on the fly. Menus that cannot be + * updated are fully regenerated using createMenu. The advantage of + * updating a menu is that it can preserve ordering of networks avoiding + * entries from jumping around after an update. + */ + canUpdateMenu: function() { + return this.data_.key == 'wifi' && activeMenu_ == this.getMenuName_(); + }, + + /** + * Updates an existing menu. Updated menus preserve ordering of prior + * entries. During the update process, the ordering may differ from the + * preferred ordering as determined by the network library. If the + * ordering becomes potentially out of sync, then the updated menu is + * marked for disposal on close. Reopening the menu will force a + * regeneration, which will in turn fix the ordering. + * @return {boolean} True if successfully updated. + */ + updateMenu: function() { + if (!this.canUpdateMenu()) + return false; + var oldMenu = $(this.getMenuName_()); + var group = oldMenu.getElementsByClassName('network-menu-group')[0]; + if (!group) + return false; + var newMenu = this.createMenu(); + var discardOnClose = false; + var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu); + var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu); + for (var key in oldNetworkButtons) { + if (newNetworkButtons[key]) { + group.replaceChild(newNetworkButtons[key].button, + oldNetworkButtons[key].button); + if (newNetworkButtons[key].index != oldNetworkButtons[key].index) + discardOnClose = true; + newNetworkButtons[key] = null; + } else { + // Leave item in list to prevent network items from jumping due to + // deletions. + oldNetworkButtons[key].disabled = true; + discardOnClose = true; + } + } + for (var key in newNetworkButtons) { + var entry = newNetworkButtons[key]; + if (entry) { + group.appendChild(entry.button); + discardOnClose = true; + } + } + oldMenu.data = {discardOnClose: discardOnClose}; + return true; + }, + + /** + * Extracts a mapping of network names to menu element and position. + * @param {!Element} menu The menu to process. + * @return {Object.} Network mapping. + * @private + */ + extractNetworkConnectButtons_: function(menu) { + var group = menu.getElementsByClassName('network-menu-group')[0]; + var networkButtons = {}; + if (!group) + return networkButtons; + var buttons = group.getElementsByClassName('network-menu-item'); + for (var i = 0; i < buttons.length; i++) { + var label = buttons[i].data.label; + networkButtons[label] = {index: i, button: buttons[i]}; + } + return networkButtons; + }, + + /** + * Adds a command to a menu for modifying network settings. + * @param {!Element} menu Parent menu. + * @param {Object} data Description of the network. + * @param {string} label Display name for the menu item. + * @param {string|function} command Callback function or name + * of the command for |networkCommand|. + * @return {!Element} The created menu item. + * @private + */ + createCallback_: function(menu, data, label, command) { + var button = this.ownerDocument.createElement('div'); + button.className = 'network-menu-item'; + var buttonLabel = this.ownerDocument.createElement('span'); + buttonLabel.className = 'network-menu-item-label'; + buttonLabel.textContent = label; + button.appendChild(buttonLabel); + var callback = null; + if (typeof command == 'string') { + var type = String(data.networkType); + var path = data.servicePath; + callback = function() { + chrome.send('networkCommand', + [type, path, command]); + closeMenu_(); + }; + } else if (command != null) { + callback = function() { + command(data); + closeMenu_(); + }; + } + if (callback != null) + button.addEventListener('click', callback); + else + buttonLabel.classList.add('network-disabled-control'); + + button.data = {label: label}; + MenuItem.decorate(button); + menu.appendChild(button); + return button; + }, + + /** + * Adds a menu item for showing network details. + * @param {!Element} parent The parent element. + * @param {Object} data Description of the network. + * @private + */ + createNetworkOptionsCallback_: function(parent, data) { + var menuItem = this.createCallback_(parent, + data, + data.networkName, + 'options'); + menuItem.style.backgroundImage = url(data.iconURL); + if (data.policyManaged) + menuItem.appendChild(new ManagedNetworkIndicator()); + if (data.connected || data.connecting) { + var label = menuItem.getElementsByClassName( + 'network-menu-item-label')[0]; + label.classList.add('active-network'); + } + } + }; + + /** + * Creates a button-like control for configurating internet connectivity. + * @param {{key: string, + * subtitle: string, + * command: function} data Description of the network control. + * @constructor + */ + function NetworkButtonItem(data) { + var el = new NetworkListItem(data); + el.__proto__ = NetworkButtonItem.prototype; + el.decorate(); + return el; + } + + NetworkButtonItem.prototype = { + __proto__: NetworkListItem.prototype, + + /** @inheritDoc */ + decorate: function() { + if (this.data.subtitle) + this.subtitle = this.data.subtitle; + else + this.subtitle = null; + if (this.data.command) + this.addEventListener('click', this.data.command); + if (this.data.iconURL) + this.iconURL = this.data.iconURL; + else if (this.data.iconType) + this.iconType = this.data.iconType; + }, + }; + + /** + * A list of controls for manipulating network connectivity. + * @constructor + */ + var NetworkList = cr.ui.define('list'); + + NetworkList.prototype = { + __proto__: List.prototype, + + /** @inheritDoc */ + decorate: function() { + List.prototype.decorate.call(this); + this.startBatchUpdates(); + this.autoExpands = true; + this.addEventListener('blur', this.onBlur_); + this.dataModel = new ArrayDataModel([]); + + // Wi-Fi control is always visible. + this.update({key: 'wifi', networkList: []}); + + if (airplaneModeAvailable_()) { + this.update({key: 'airplaneMode', + subtitle: loadTimeData.getString('airplaneModeLabel'), + command: function() { + chrome.send('toggleAirplaneMode'); + }}); + } + + // Add connection control. + var addConnection = function(type) { + var callback = function() { + chrome.send('networkCommand', + [String(type), '?', 'connect']); + } + return callback; + } + this.update({key: 'addConnection', + iconType: 'add-connection', + menu: [{label: loadTimeData.getString('addConnectionWifi'), + command: addConnection(Constants.TYPE_WIFI)}, + {label: loadTimeData.getString('addConnectionVPN'), + command: addConnection(Constants.TYPE_VPN)}] + }); + + var prefs = options.Preferences.getInstance(); + prefs.addEventListener('cros.signed.data_roaming_enabled', + function(event) { + enableDataRoaming_ = event.value.value; + }); + this.endBatchUpdates(); + }, + + /** + * When the list loses focus, unselect all items in the list and close the + * active menu. + * @private + */ + onBlur_: function() { + this.selectionModel.unselectAll(); + closeMenu_(); + }, + + /** + * Finds the index of a network item within the data model based on + * category. + * @param {string} key Unique key for the item in the list. + * @return {number} The index of the network item, or |undefined| if it is + * not found. + */ + indexOf: function(key) { + var size = this.dataModel.length; + for (var i = 0; i < size; i++) { + var entry = this.dataModel.item(i); + if (entry.key == key) + return i; + } + }, + + /** + * Updates a network control. + * @param {Object.} data Description of the entry. + */ + update: function(data) { + this.startBatchUpdates(); + var index = this.indexOf(data.key); + if (index == undefined) { + // Find reference position for adding the element. We cannot hide + // individual list elements, thus we need to conditionally add or + // remove elements and cannot rely on any element having a fixed index. + for (var i = 0; i < Constants.NETWORK_ORDER.length; i++) { + if (data.key == Constants.NETWORK_ORDER[i]) { + data.sortIndex = i; + break; + } + } + var referenceIndex = -1; + for (var i = 0; i < this.dataModel.length; i++) { + var entry = this.dataModel.item(i); + if (entry.sortIndex < data.sortIndex) + referenceIndex = i; + else + break; + } + if (referenceIndex == -1) { + // Prepend to the start of the list. + this.dataModel.splice(0, 0, data); + } else if (referenceIndex == this.dataModel.length) { + // Append to the end of the list. + this.dataModel.push(data); + } else { + // Insert after the reference element. + this.dataModel.splice(referenceIndex + 1, 0, data); + } + } else { + var entry = this.dataModel.item(index); + data.sortIndex = entry.sortIndex; + this.dataModel.splice(index, 1, data); + } + this.endBatchUpdates(); + }, + + /** @inheritDoc */ + createItem: function(entry) { + if (entry.networkList) + return new NetworkSelectorItem(entry); + if (entry.command) + return new NetworkButtonItem(entry); + if (entry.menu) + return new NetworkMenuItem(entry); + }, + + /** + * Deletes an element from the list. + * @param {string} key Unique identifier for the element. + */ + deleteItem: function(key) { + var index = this.indexOf(key); + if (index != undefined) + this.dataModel.splice(index, 1); + }, + + /** + * Updates the state of a toggle button. + * @param {string} key Unique identifier for the element. + * @param {boolean} active Whether the control is active. + */ + updateToggleControl: function(key, active) { + var index = this.indexOf(key); + if (index != undefined) { + var entry = this.dataModel.item(index); + entry.iconType = active ? 'control-active' : + 'control-inactive'; + this.update(entry); + } + } + }; + + /** + * Chrome callback for updating network controls. + * @param {Object} data Description of available network devices and their + * corresponding state. + */ + NetworkList.refreshNetworkData = function(data) { + var networkList = $('network-list'); + networkList.startBatchUpdates(); + cellularAvailable_ = data.cellularAvailable; + cellularEnabled_ = data.cellularEnabled; + wimaxAvailable_ = data.wimaxAvailable; + wimaxEnabled_ = data.wimaxEnabled; + + if (data.accessLocked) { + $('network-locked-message').hidden = false; + networkList.disabled = true; + $('use-shared-proxies').disabled = true; + } else { + $('network-locked-message').hidden = true; + networkList.disabled = false; + $('use-shared-proxies').disabled = false; + } + + // Only show Ethernet control if connected. + var ethernetConnection = getConnection_(data.wiredList); + if (ethernetConnection) { + var type = String(Constants.TYPE_ETHERNET); + var path = ethernetConnection.servicePath; + var ethernetOptions = function() { + chrome.send('networkCommand', + [type, path, 'options']); + }; + networkList.update({key: 'ethernet', + subtitle: loadTimeData.getString('networkConnected'), + iconURL: ethernetConnection.iconURL, + command: ethernetOptions}); + } else { + networkList.deleteItem('ethernet'); + } + + if (data.wifiEnabled) { + loadData_('wifi', data.wirelessList, data.rememberedList); + } else { + var enableWifi = function() { + chrome.send('enableWifi'); + }; + networkList.update({key: 'wifi', + subtitle: loadTimeData.getString('networkDisabled'), + iconType: 'wifi', + command: enableWifi}); + } + + // Only show cellular control if available and not in airplane mode. + if (data.cellularAvailable && !data.airplaneMode) { + if (data.cellularEnabled) { + loadData_('cellular', data.wirelessList, data.rememberedList); + } else { + var subtitle = loadTimeData.getString('networkDisabled'); + var enableCellular = function() { + chrome.send('enableCellular'); + }; + networkList.update({key: 'cellular', + subtitle: subtitle, + iconType: 'cellular', + command: enableCellular}); + } + } else { + networkList.deleteItem('cellular'); + } + + // Only show cellular control if available and not in airplane mode. + if (data.wimaxAvailable && !data.airplaneMode) { + if (data.wimaxEnabled) { + loadData_('wimax', data.wirelessList, data.rememberedList); + } else { + var subtitle = loadTimeData.getString('networkDisabled'); + var enableWimax = function() { + chrome.send('enableWimax'); + }; + networkList.update({key: 'wimax', + subtitle: subtitle, + iconType: 'cellular', + command: enableWimax}); + } + } else { + networkList.deleteItem('wimax'); + } + + // Only show VPN control if there is an available network and an internet + // connection. + if (data.vpnList.length > 0 && (ethernetConnection || + isConnected_(data.wirelessList))) + loadData_('vpn', data.vpnList, data.rememberedList); + else + networkList.deleteItem('vpn'); + networkList.updateToggleControl('airplaneMode', data.airplaneMode); + networkList.endBatchUpdates(); + }; + + /** + * Element for indicating a policy managed network. + * @constructor + */ + function ManagedNetworkIndicator() { + var el = cr.doc.createElement('div'); + el.__proto__ = ManagedNetworkIndicator.prototype; + el.decorate(); + return el; + } + + ManagedNetworkIndicator.prototype = { + __proto__: ControlledSettingIndicator.prototype, + + /** @inheritDoc */ + decorate: function() { + ControlledSettingIndicator.prototype.decorate.call(this); + this.controlledBy = 'policy'; + var policyLabel = loadTimeData.getString('managedNetwork'); + this.setAttribute('textPolicy', policyLabel); + this.className = 'controlled-setting-indicator'; + // The default popup clips to the bounds of the list of networks in the + // drop-down because it has enforced size constraints with auto- + // scrolling. Use a tooltip in place of the bubble popup until the + // clipping issues are resolved. + this.setAttribute('title', policyLabel); + this.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + }); + } + }; + + /** + * Updates the list of available networks and their status, filtered by + * network type. + * @param {string} category The type of network. + * @param {Array} available The list of available networks and their status. + * @param {Array} remembered The list of remmebered networks. + */ + function loadData_(category, available, remembered) { + var data = {key: category}; + var type = categoryMap[category]; + var availableNetworks = []; + for (var i = 0; i < available.length; i++) { + if (available[i].networkType == type) + availableNetworks.push(available[i]); + } + data.networkList = availableNetworks; + if (remembered) { + var rememberedNetworks = []; + for (var i = 0; i < remembered.length; i++) { + if (remembered[i].networkType == type) + rememberedNetworks.push(remembered[i]); + } + data.rememberedNetworks = rememberedNetworks; + } + $('network-list').update(data); + } + + /** + * Hides the currently visible menu. + * @private + */ + function closeMenu_() { + if (activeMenu_) { + var menu = $(activeMenu_); + menu.hidden = true; + if (menu.data && menu.data.discardOnClose) + menu.parentNode.removeChild(menu); + activeMenu_ = null; + } + } + + /** + * Determines if the user is connected to or in the process of connecting to + * a wireless network. + * @param {Array.} networkList List of networks. + * @return {boolean} True if connected or connecting to a network. + * @private + */ + function isConnected_(networkList) { + return getConnection_(networkList) != null; + } + + /** + * Fetches the active connection. + * @param {Array.} networkList List of networks. + * @return {boolean} True if connected or connecting to a network. + * @private + */ + function getConnection_(networkList) { + if (!networkList) + return null; + for (var i = 0; i < networkList.length; i++) { + var entry = networkList[i]; + if (entry.connected || entry.connecting) + return entry; + } + return null; + } + + /** + * Queries if airplane mode is available. + * @return {boolean} Indicates if airplane mode is available. + * @private + */ + function airplaneModeAvailable_() { + // TODO(kevers): Use library callback to determine if airplane mode is + // available once back-end suport is in place. + return false; + } + + /** + * Whether the Network list is disabled. Only used for display purpose. + * @type {boolean} + */ + cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR); + + // Export + return { + NetworkList: NetworkList + }; +}); diff --git a/chrome/browser/resources/options/chromeos/pointer_overlay.css b/chrome/browser/resources/options/chromeos/pointer_overlay.css new file mode 100644 index 0000000..66924c2 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/pointer_overlay.css @@ -0,0 +1,7 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#pointer-overlay > .content-area > :not([hidden]) + :nth-child(2) { + margin-top: 20px; +} diff --git a/chrome/browser/resources/options/chromeos/pointer_overlay.html b/chrome/browser/resources/options/chromeos/pointer_overlay.html new file mode 100644 index 0000000..706ca9e --- /dev/null +++ b/chrome/browser/resources/options/chromeos/pointer_overlay.html @@ -0,0 +1,41 @@ + diff --git a/chrome/browser/resources/options/chromeos/pointer_overlay.js b/chrome/browser/resources/options/chromeos/pointer_overlay.js new file mode 100644 index 0000000..eaeaf94 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/pointer_overlay.js @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var SettingsDialog = options.SettingsDialog; + + /** + * PointerOverlay class + * Dialog that allows users to set pointer settings (touchpad/mouse). + * @extends {SettingsDialog} + */ + function PointerOverlay() { + // The title is updated dynamically in the setTitle method as pointer + // devices are discovered or removed. + SettingsDialog.call(this, 'pointer-overlay', + '', 'pointer-overlay', + $('pointer-overlay-confirm'), + $('pointer-overlay-cancel')); + } + + cr.addSingletonGetter(PointerOverlay); + + PointerOverlay.prototype = { + __proto__: SettingsDialog.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + SettingsDialog.prototype.initializePage.call(this); + }, + }; + + /** + * Sets the visibility state of the touchpad group. + * @param {boolean} show True to show, false to hide. + */ + PointerOverlay.showTouchpadControls = function(show) { + $('pointer-section-touchpad').hidden = !show; + }; + + /** + * Sets the visibility state of the mouse group. + * @param {boolean} show True to show, false to hide. + */ + PointerOverlay.showMouseControls = function(show) { + $('pointer-section-mouse').hidden = !show; + }; + + /** + * Updates the title of the pointer dialog. The title is set dynamically + * based on whether a touchpad, mouse or both are present. The label on the + * button that activates the overlay is also updated to stay in sync. A + * message is displayed in the main settings page if no pointer devices are + * available. + * @param {String} label i18n key for the overlay title. + */ + PointerOverlay.setTitle = function(label) { + var header = $('pointer-overlay-title'); + var button = $('pointer-settings-button'); + var noPointersLabel = $('no-pointing-devices'); + if (label.length > 0) { + var title = loadTimeData.getString(label); + header.textContent = title; + button.textContent = title; + button.hidden = false; + noPointersLabel.hidden = true; + } else { + header.textContent = ''; + button.hidden = true; + noPointersLabel.hidden = false; + } + }; + + // Export + return { + PointerOverlay: PointerOverlay + }; +}); diff --git a/chrome/browser/resources/options/chromeos/preferred_networks.html b/chrome/browser/resources/options/chromeos/preferred_networks.html new file mode 100644 index 0000000..d7f112e --- /dev/null +++ b/chrome/browser/resources/options/chromeos/preferred_networks.html @@ -0,0 +1,16 @@ + diff --git a/chrome/browser/resources/options/chromeos/preferred_networks.js b/chrome/browser/resources/options/chromeos/preferred_networks.js new file mode 100644 index 0000000..6c875d7 --- /dev/null +++ b/chrome/browser/resources/options/chromeos/preferred_networks.js @@ -0,0 +1,165 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + var ArrayDataModel = cr.ui.ArrayDataModel; + var DeletableItem = options.DeletableItem; + var DeletableItemList = options.DeletableItemList; + + ///////////////////////////////////////////////////////////////////////////// + // NetworkPreferences class: + + /** + * Encapsulated handling of ChromeOS network preferences page. + * @constructor + */ + function PreferredNetworks(model) { + OptionsPage.call(this, + 'preferredNetworksPage', + null, + 'preferredNetworksPage'); + } + + cr.addSingletonGetter(PreferredNetworks); + + PreferredNetworks.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initializes the preferred networks page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + PreferredNetworkList.decorate($('remembered-network-list')); + $('preferred-networks-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + update: function(rememberedNetworks) { + var list = $('remembered-network-list'); + list.clear(); + for (var i = 0; i < rememberedNetworks.length; i++) { + list.append(rememberedNetworks[i]); + } + list.redraw(); + } + + }; + + /** + * Creates a list entry for a remembered network. + * @param{{networkName: string, + networkType: string, + servicePath: string}} data + * Description of the network. + * @constructor + */ + function PreferredNetworkListItem(data) { + var el = cr.doc.createElement('div'); + el.__proto__ = PreferredNetworkListItem.prototype; + el.data = {}; + for (var key in data) + el.data[key] = data[key]; + el.decorate(); + return el; + } + + PreferredNetworkListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * Description of the network. + * @type {{networkName: string, + * networkType: string, + * servicePath: string}} + */ + data: null, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + var label = this.ownerDocument.createElement('div'); + label.textContent = this.data.networkName; + if (this.data.policyManaged) + this.deletable = false; + this.contentElement.appendChild(label); + } + }; + + /** + * Class for displaying a list of preferred networks. + * @constructor + * @extends {options.DeletableItemList} + */ + var PreferredNetworkList = cr.ui.define('list'); + + PreferredNetworkList.prototype = { + __proto__: DeletableItemList.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.addEventListener('blur', this.onBlur_); + this.clear(); + }, + + /** + * When the list loses focus, unselect all items in the list. + * @private + */ + onBlur_: function() { + this.selectionModel.unselectAll(); + }, + + /** @inheritDoc */ + createItem: function(entry) { + return new PreferredNetworkListItem(entry); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var item = this.dataModel.item(index); + if (item) { + // Inform the network library that we are forgetting this network. + chrome.send('networkCommand', + [String(item.networkType), + item.servicePath, + 'forget']); + } + this.dataModel.splice(index, 1); + // Invalidate the list since it has a stale cache after a splice + // involving a deletion. + this.invalidate(); + this.redraw(); + }, + + /** + * Purges all networks from the list. + */ + clear: function() { + this.dataModel = new ArrayDataModel([]); + this.redraw(); + }, + + /** + * Adds a remembered network to the list. + * @param {{networkName: string, + networkType: string, + servicePath: string} data + * Description of the network. + */ + append: function(data) { + this.dataModel.push(data); + } + }; + + // Export + return { + PreferredNetworks: PreferredNetworks + }; + +}); + diff --git a/chrome/browser/resources/options/chromeos/proxy_rules_list.js b/chrome/browser/resources/options/chromeos/proxy_rules_list.js new file mode 100644 index 0000000..fa7c2ec --- /dev/null +++ b/chrome/browser/resources/options/chromeos/proxy_rules_list.js @@ -0,0 +1,139 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.proxyexceptions', function() { + /** @const */ var List = cr.ui.List; + /** @const */ var ListItem = cr.ui.ListItem; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + /** + * Creates a new exception list. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {cr.ui.List} + */ + var ProxyExceptions = cr.ui.define('list'); + + ProxyExceptions.prototype = { + __proto__: List.prototype, + + pref: 'cros.session.proxy.ignorelist', + + /** @inheritDoc */ + decorate: function() { + List.prototype.decorate.call(this); + + // HACK(arv): http://crbug.com/40902 + window.addEventListener('resize', this.redraw.bind(this)); + + this.addEventListener('click', this.handleClick_); + + var self = this; + + // Listens to pref changes. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + self.load_(event.value.value); + }); + }, + + createItem: function(exception) { + return new ProxyExceptionsItem(exception); + }, + + /** + * Adds given exception to model and update backend. + * @param {Object} exception A exception to be added to exception list. + */ + addException: function(exception) { + this.dataModel.push(exception); + this.updateBackend_(); + }, + + /** + * Removes given exception from model and update backend. + */ + removeException: function(exception) { + var dataModel = this.dataModel; + + var index = dataModel.indexOf(exception); + if (index >= 0) { + dataModel.splice(index, 1); + this.updateBackend_(); + } + }, + + /** + * Handles the clicks on the list and triggers exception removal if the + * click is on the remove exception button. + * @private + * @param {!Event} e The click event object. + */ + handleClick_: function(e) { + // Handle left button click + if (e.button == 0) { + var el = e.target; + if (el.className == 'remove-exception-button') { + this.removeException(el.parentNode.exception); + } + } + }, + + /** + * Loads given exception list. + * @param {Array} exceptions An array of exception object. + */ + load_: function(exceptions) { + this.dataModel = new ArrayDataModel(exceptions); + }, + + /** + * Updates backend. + */ + updateBackend_: function() { + Preferences.setListPref(this.pref, this.dataModel.slice()); + } + }; + + /** + * Creates a new exception list item. + * @param {Object} exception The exception account this represents. + * @constructor + * @extends {cr.ui.ListItem} + */ + function ProxyExceptionsItem(exception) { + var el = cr.doc.createElement('div'); + el.exception = exception; + ProxyExceptionsItem.decorate(el); + return el; + } + + /** + * Decorates an element as a exception account item. + * @param {!HTMLElement} el The element to decorate. + */ + ProxyExceptionsItem.decorate = function(el) { + el.__proto__ = ProxyExceptionsItem.prototype; + el.decorate(); + }; + + ProxyExceptionsItem.prototype = { + __proto__: ListItem.prototype, + + /** @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + this.className = 'exception-list-item'; + + var labelException = this.ownerDocument.createElement('span'); + labelException.className = ''; + labelException.textContent = this.exception; + this.appendChild(labelException); + } + }; + + return { + ProxyExceptions: ProxyExceptions + }; +}); diff --git a/chrome/browser/resources/options/chromeos/set_wallpaper_options.css b/chrome/browser/resources/options/chromeos/set_wallpaper_options.css new file mode 100644 index 0000000..2732dea --- /dev/null +++ b/chrome/browser/resources/options/chromeos/set_wallpaper_options.css @@ -0,0 +1,51 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#wallpaper-title span { + -webkit-padding-start: 17px; + padding-top: 6px; +} + +#set-wallpaper-page .content-area { + height: 400px; +} + +#wallpaper-grid { + margin: 0; + overflow: hidden; + width: 580px; +} + +#wallpaper-grid img { + height: 80px; + width: 128px; +} + +.grayout img { + -webkit-filter: opacity(50%); +} + +.bottom-bar { + -webkit-box-orient: vertical; + display: -webkit-box; +} + +.bottom-bar .spacer { + -webkit-box-flex: 1; +} + +.wallpaper-image-attr { + -webkit-padding-start: 34px; + line-height: 26px; + padding-top: 14px; +} + +#wallpaper-author-website { + -webkit-padding-start: 5px; +} + +#wallpaper-action-area { + -webkit-padding-start: 34px; + padding-top: 0; +} diff --git a/chrome/browser/resources/options/chromeos/set_wallpaper_options.html b/chrome/browser/resources/options/chromeos/set_wallpaper_options.html new file mode 100644 index 0000000..e47da5b --- /dev/null +++ b/chrome/browser/resources/options/chromeos/set_wallpaper_options.html @@ -0,0 +1,33 @@ + diff --git a/chrome/browser/resources/options/chromeos/set_wallpaper_options.js b/chrome/browser/resources/options/chromeos/set_wallpaper_options.js new file mode 100644 index 0000000..4f01abb --- /dev/null +++ b/chrome/browser/resources/options/chromeos/set_wallpaper_options.js @@ -0,0 +1,324 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + var UserImagesGrid = options.UserImagesGrid; + + /** @const */ var CUSTOM_WALLPAPER_PREFIX = + 'chrome://wallpaper-thumb/custom_'; + + ///////////////////////////////////////////////////////////////////////////// + // SetWallpaperOptions class: + + /** + * Encapsulated handling of ChromeOS set wallpaper options page. + * @constructor + */ + function SetWallpaperOptions() { + OptionsPage.call( + this, + 'setWallpaper', + loadTimeData.getString('setWallpaper'), + 'set-wallpaper-page'); + } + + cr.addSingletonGetter(SetWallpaperOptions); + + SetWallpaperOptions.prototype = { + // Inherit SetWallpaperOptions from OptionsPage. + __proto__: options.OptionsPage.prototype, + + /** + * Initializes SetWallpaperOptions page. + */ + initializePage: function() { + // Call base class implementation to start preferences initialization. + OptionsPage.prototype.initializePage.call(this); + + var wallpaperGrid = $('wallpaper-grid'); + UserImagesGrid.decorate(wallpaperGrid); + + wallpaperGrid.addEventListener('change', + this.handleImageSelected_.bind(this)); + wallpaperGrid.addEventListener('dblclick', + this.handleImageDblClick_.bind(this)); + wallpaperGrid.addEventListener('activate', + function() { OptionsPage.closeOverlay() }); + + $('set-wallpaper-layout').addEventListener('change', + this.handleLayoutChange_); + $('set-custom-wallpaper').onclick = this.handleChooseFile_; + $('use-daily-wallpaper').onclick = this.handleCheckboxClick_.bind(this); + $('set-wallpaper-overlay-confirm').onclick = function() { + OptionsPage.closeOverlay(); + }; + + // @type {Array.} + this.wallpapers_ = []; + + // @type {Object} Old user custom wallpaper thumbnail. + this.oldImage_ = null; + + chrome.send('onSetWallpaperPageInitialized'); + }, + + /** + * Called right after the page has been shown to user. + */ + didShowPage: function() { + $('wallpaper-grid').updateAndFocus(); + // A quick hack to fix issue 118472. This is a general problem of list + // control and options overlay. + // TODO(bshe): Remove this hack when we fixed the general problem which + // tracked in issue 118829. + $('wallpaper-grid').redraw(); + chrome.send('onSetWallpaperPageShown'); + }, + + /** + * Called right before the page is hidden. + */ + willHidePage: function() { + var wallpaperGrid = $('wallpaper-grid'); + wallpaperGrid.blur(); + if (this.oldImage_) { + wallpaperGrid.removeItem(this.oldImage_); + this.oldImage_ = null; + } + $('set-wallpaper-layout').innerText = ''; + }, + + /** + * Set attributions of wallpaper with given URL. + * @param {string} url URL of the selected wallpaper. + * @private + */ + setWallpaperAttribution_: function(url) { + for (var i = 0; i < this.wallpapers_.length; i++) { + if (this.wallpapers_[i].url == url) { + $('wallpaper-author-name').textContent = this.wallpapers_[i].author; + $('wallpaper-author-website').textContent = + this.wallpapers_[i].website; + return; + } + } + $('wallpaper-author-name').textContent = ''; + $('wallpaper-author-website').textContent = ''; + }, + + /** + * Populates the drop down box for custom wallpaper layouts. + * param {string} layouts Available wallpaper layouts. + * param {number} selectedLayout The value of selected/default layout. + * @private + */ + populateWallpaperLayouts_: function(layouts, selectedLayout) { + var wallpaperLayout = $('set-wallpaper-layout'); + var selectedIndex = -1; + for (var i = 0; i < layouts.length; i++) { + var option = new Option(layouts[i]['name'], layouts[i]['index']); + if (selectedLayout == option.value) + selectedIndex = i; + wallpaperLayout.appendChild(option); + } + if (selectedIndex >= 0) + wallpaperLayout.selectedIndex = selectedIndex; + }, + + /** + * Handles "Custom..." button activation. + * @private + */ + handleChooseFile_: function() { + chrome.send('chooseWallpaper'); + }, + + /** + * Handle the wallpaper layout setting change. + * @private + */ + handleLayoutChange_: function() { + var setWallpaperLayout = $('set-wallpaper-layout'); + var layout = setWallpaperLayout.options[ + setWallpaperLayout.selectedIndex].value; + chrome.send('changeWallpaperLayout', [layout]); + }, + + /** + * Handles image selection change. + * @private + */ + handleImageSelected_: function() { + var wallpaperGrid = $('wallpaper-grid'); + var url = wallpaperGrid.selectedItemUrl; + if (url && + !wallpaperGrid.inProgramSelection) { + if (url.indexOf(CUSTOM_WALLPAPER_PREFIX) == 0) { + // User custom wallpaper is selected + this.isCustom = true; + // When users select the custom wallpaper thumbnail from picker UI, + // use the saved layout value and redraw the wallpaper. + this.handleLayoutChange_(); + } else { + this.isCustom = false; + chrome.send('selectDefaultWallpaper', [url]); + } + this.setWallpaperAttribution_(url); + } + }, + + /** + * Handles double click on the image grid. + * @param {Event} e Double click Event. + */ + handleImageDblClick_: function(e) { + var wallpaperGrid = $('wallpaper-grid'); + if (wallpaperGrid.disabled) + return; + // Close page unless the click target is the grid itself. + if (e.target instanceof HTMLImageElement) + OptionsPage.closeOverlay(); + }, + + /** + * Handles click on the "I'm feeling lucky" checkbox. + * @private + */ + handleCheckboxClick_: function() { + var wallpaperGrid = $('wallpaper-grid'); + if ($('use-daily-wallpaper').checked) { + wallpaperGrid.disabled = true; + $('wallpaper-attribution-label').hidden = false; + chrome.send('selectDailyWallpaper'); + wallpaperGrid.classList.add('grayout'); + $('set-wallpaper-layout').hidden = true; + } else { + wallpaperGrid.disabled = false; + wallpaperGrid.classList.remove('grayout'); + // Set the wallpaper type to User::DEFAULT. + this.handleImageSelected_(); + } + }, + + /** + * Selects corresponding wallpaper thumbnail with the given URL and toggle + * the "Change wallpaper daily..." checkbox. + * @param {string} url URL of the wallpaper thumbnail to select. + * @param {boolean} isDaily True if user checked "Change wallpaper daily..." + * checkbox. + * @private + */ + setSelectedImage_: function(url, isDaily) { + var wallpaperGrid = $('wallpaper-grid'); + wallpaperGrid.selectedItemUrl = url; + this.setWallpaperAttribution_(url); + if (isDaily) { + // Do not call chrome.send('selectDailyWallpaper'). + $('use-daily-wallpaper').checked = true; + wallpaperGrid.disabled = true; + wallpaperGrid.classList.add('grayout'); + } + }, + + /** + * Appends default images to the image grid. Should only be called once. + * @param {Array.<{author: string, url: string, website: string}>} + * wallpapers An array of wallpaper objects. + * @private + */ + setDefaultImages_: function(wallpapers) { + var wallpaperGrid = $('wallpaper-grid'); + // TODO(bshe): Ideally we should save author and website with the actual + // image (URL) and not use index related storage. This way this data is + // stored in one place rather than depending on the index to be + // consistent. + for (var i = 0, wallpaper; wallpaper = wallpapers[i]; i++) { + this.wallpapers_.push(wallpaper); + wallpaperGrid.addItem(wallpaper.url); + } + }, + + /** + * Display layout drop down box and disable daily mode if enabled. Called + * when user select a valid file from file system. + */ + didSelectFile_: function() { + $('set-wallpaper-layout').hidden = false; + var wallpaperGrid = $('wallpaper-grid'); + if ($('use-daily-wallpaper').checked) { + $('use-daily-wallpaper').checked = false; + wallpaperGrid.disabled = false; + wallpaperGrid.classList.remove('grayout'); + } + }, + + /** + * Returns url of current user's custom wallpaper thumbnail. + * @private + */ + currentWallpaperImageUrl_: function() { + return CUSTOM_WALLPAPER_PREFIX + BrowserOptions.getLoggedInUsername() + + '?id=' + (new Date()).getTime(); + }, + + /** + * Updates the visibility of attribution-label and set-wallpaper-layout. + * @param {boolean} isCustom True if users select custom wallpaper. + */ + set isCustom(isCustom) { + if (isCustom) { + // Clear attributions for custom wallpaper. + $('wallpaper-attribution-label').hidden = true; + // Enable the layout drop down box when custom wallpaper is selected. + $('set-wallpaper-layout').hidden = false; + } else { + $('wallpaper-attribution-label').hidden = false; + $('set-wallpaper-layout').hidden = true; + } + }, + + /** + * Adds or updates custom user wallpaper thumbnail from file. + * @private + */ + setCustomImage_: function() { + var wallpaperGrid = $('wallpaper-grid'); + var url = this.currentWallpaperImageUrl_(); + if (this.oldImage_) { + this.oldImage_ = wallpaperGrid.updateItem(this.oldImage_, url); + } else { + // Insert to the end of wallpaper list. + var pos = wallpaperGrid.length; + this.oldImage_ = wallpaperGrid.addItem(url, undefined, undefined, pos); + } + + this.isCustom = true; + this.setWallpaperAttribution_(''); + wallpaperGrid.selectedItem = this.oldImage_; + }, + }; + + // Forward public APIs to private implementations. + [ + 'setDefaultImages', + 'setSelectedImage', + 'populateWallpaperLayouts', + 'didSelectFile', + 'setCustomImage' + ].forEach(function(name) { + SetWallpaperOptions[name] = function() { + var instance = SetWallpaperOptions.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + // Export + return { + SetWallpaperOptions: SetWallpaperOptions + }; + +}); + diff --git a/chrome/browser/resources/options/clear_browser_data_overlay.css b/chrome/browser/resources/options/clear_browser_data_overlay.css new file mode 100644 index 0000000..0c70109 --- /dev/null +++ b/chrome/browser/resources/options/clear_browser_data_overlay.css @@ -0,0 +1,22 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#clear-browser-data-overlay { + min-width: 500px; +} + +#clear-data-checkboxes { + -webkit-padding-start: 8px; + margin: 5px 0; +} + +#cbd-throbber { + margin: 4px 10px; + vertical-align: middle; + visibility: hidden; +} + +#flash-storage-settings { + padding-top: 5px; +} diff --git a/chrome/browser/resources/options/clear_browser_data_overlay.html b/chrome/browser/resources/options/clear_browser_data_overlay.html new file mode 100644 index 0000000..24a43545 --- /dev/null +++ b/chrome/browser/resources/options/clear_browser_data_overlay.html @@ -0,0 +1,92 @@ + diff --git a/chrome/browser/resources/options/clear_browser_data_overlay.js b/chrome/browser/resources/options/clear_browser_data_overlay.js new file mode 100644 index 0000000..fa7aadd --- /dev/null +++ b/chrome/browser/resources/options/clear_browser_data_overlay.js @@ -0,0 +1,116 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + /** + * ClearBrowserDataOverlay class + * Encapsulated handling of the 'Clear Browser Data' overlay page. + * @class + */ + function ClearBrowserDataOverlay() { + OptionsPage.call(this, 'clearBrowserData', + loadTimeData.getString('clearBrowserDataOverlayTabTitle'), + 'clear-browser-data-overlay'); + } + + cr.addSingletonGetter(ClearBrowserDataOverlay); + + ClearBrowserDataOverlay.prototype = { + // Inherit ClearBrowserDataOverlay from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to starts preference initialization. + OptionsPage.prototype.initializePage.call(this); + + var f = this.updateCommitButtonState_.bind(this); + var types = ['browser.clear_data.browsing_history', + 'browser.clear_data.download_history', + 'browser.clear_data.cache', + 'browser.clear_data.cookies', + 'browser.clear_data.passwords', + 'browser.clear_data.form_data', + 'browser.clear_data.hosted_apps_data', + 'browser.clear_data.content_licenses']; + types.forEach(function(type) { + Preferences.getInstance().addEventListener(type, f); + }); + + var checkboxes = document.querySelectorAll( + '#cbd-content-area input[type=checkbox]'); + for (var i = 0; i < checkboxes.length; i++) { + checkboxes[i].onclick = f; + } + this.updateCommitButtonState_(); + + $('clear-browser-data-dismiss').onclick = function(event) { + ClearBrowserDataOverlay.dismiss(); + }; + $('clear-browser-data-commit').onclick = function(event) { + ClearBrowserDataOverlay.setClearingState(true); + chrome.send('performClearBrowserData'); + }; + }, + + // Set the enabled state of the commit button. + updateCommitButtonState_: function() { + var checkboxes = document.querySelectorAll( + '#cbd-content-area input[type=checkbox]'); + var isChecked = false; + for (var i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked) { + isChecked = true; + break; + } + } + $('clear-browser-data-commit').disabled = !isChecked; + }, + }; + + // + // Chrome callbacks + // + ClearBrowserDataOverlay.setClearingState = function(state) { + $('delete-browsing-history-checkbox').disabled = state; + $('delete-download-history-checkbox').disabled = state; + $('delete-cache-checkbox').disabled = state; + $('delete-cookies-checkbox').disabled = state; + $('delete-passwords-checkbox').disabled = state; + $('delete-form-data-checkbox').disabled = state; + $('delete-hosted-apps-data-checkbox').disabled = state; + $('deauthorize-content-licenses-checkbox').disabled = state; + $('clear-browser-data-time-period').disabled = state; + $('cbd-throbber').style.visibility = state ? 'visible' : 'hidden'; + $('clear-browser-data-dismiss').disabled = state; + + if (state) + $('clear-browser-data-commit').disabled = true; + else + ClearBrowserDataOverlay.getInstance().updateCommitButtonState_(); + }; + + ClearBrowserDataOverlay.doneClearing = function() { + // The delay gives the user some feedback that the clearing + // actually worked. Otherwise the dialog just vanishes instantly in most + // cases. + window.setTimeout(function() { + ClearBrowserDataOverlay.dismiss(); + }, 200); + }; + + ClearBrowserDataOverlay.dismiss = function() { + OptionsPage.closeOverlay(); + this.setClearingState(false); + }; + + // Export + return { + ClearBrowserDataOverlay: ClearBrowserDataOverlay + }; +}); diff --git a/chrome/browser/resources/options/content_settings.css b/chrome/browser/resources/options/content_settings.css new file mode 100644 index 0000000..53b7ca6 --- /dev/null +++ b/chrome/browser/resources/options/content_settings.css @@ -0,0 +1,84 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#content-settings-page { + min-width: 600px; +} + +#content-settings-exceptions-area { + min-width: 540px; +} + +.exception-pattern { + -webkit-box-flex: 1; + -webkit-margin-end: 10px; + -webkit-margin-start: 14px; +} + +.exception-setting { + display: inline-block; + width: 120px; +} + +select.exception-setting { + vertical-align: middle; +} + +#exception-column-headers { + -webkit-margin-start: 17px; + display: -webkit-box; + margin-top: 17px; +} + +#exception-column-headers > div { + font-weight: bold; +} + +#exception-pattern-column { + -webkit-box-flex: 1; +} + +#exception-behavior-column { + width: 145px; +} + +.otr-explanation { + font-style: italic; +} + +#content-settings-exceptions-area list { + margin-bottom: 10px; + margin-top: 4px; +} + +#disable-plugins-container { + /* Same as .checkbox and .radio padding. Using padding instead of margin + * to ensure minimum height for tap target. */ + padding: 7px 0; +} + +div[role='listitem'][managedby] { + color: #666; + font-style: italic; + position: relative; +} + +.settings-list div[role='listitem'][managedby='policy'], +.settings-list div[role='listitem'][managedby='extension'] { + background: -webkit-linear-gradient(rgb(250, 230, 146), rgb(250, 230, 146)); + border-bottom: 1px solid rgb(201, 189, 141); + border-top: 0; +} + +list div[role='listitem'][managedby='policy'] .close-button { + background-image: url('chrome://theme/IDR_MANAGED'); + background-size: 16px; + opacity: 1; +} + +list div[role='listitem'][managedby='extension'] .close-button { + background-image: url('chrome://theme/IDR_EXTENSIONS_SECTION_SMALL'); + background-size: 16px; + opacity: 1; +} diff --git a/chrome/browser/resources/options/content_settings.html b/chrome/browser/resources/options/content_settings.html new file mode 100644 index 0000000..03c25e0a --- /dev/null +++ b/chrome/browser/resources/options/content_settings.html @@ -0,0 +1,356 @@ + diff --git a/chrome/browser/resources/options/content_settings.js b/chrome/browser/resources/options/content_settings.js new file mode 100644 index 0000000..fd45aca --- /dev/null +++ b/chrome/browser/resources/options/content_settings.js @@ -0,0 +1,179 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + ////////////////////////////////////////////////////////////////////////////// + // ContentSettings class: + + /** + * Encapsulated handling of content settings page. + * @constructor + */ + function ContentSettings() { + this.activeNavTab = null; + OptionsPage.call(this, 'content', + loadTimeData.getString('contentSettingsPageTabTitle'), + 'content-settings-page'); + } + + cr.addSingletonGetter(ContentSettings); + + ContentSettings.prototype = { + __proto__: OptionsPage.prototype, + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + chrome.send('getContentFilterSettings'); + + var exceptionsButtons = + this.pageDiv.querySelectorAll('.exceptions-list-button'); + for (var i = 0; i < exceptionsButtons.length; i++) { + exceptionsButtons[i].onclick = function(event) { + var page = ContentSettingsExceptionsArea.getInstance(); + + // Add on the proper hash for the content type, and store that in the + // history so back/forward and tab restore works. + var hash = event.target.getAttribute('contentType'); + var url = page.name + '#' + hash; + window.history.replaceState({pageName: page.name}, + page.title, + '/' + url); + + // Navigate after the history has been replaced in order to have the + // correct hash loaded. + OptionsPage.navigateToPage('contentExceptions'); + + uber.invokeMethodOnParent('setPath', {path: url}); + uber.invokeMethodOnParent('setTitle', + {title: loadTimeData.getString(hash + 'TabTitle')}); + }; + } + + var manageHandlersButton = $('manage-handlers-button'); + if (manageHandlersButton) { + manageHandlersButton.onclick = function(event) { + OptionsPage.navigateToPage('handlers'); + }; + } + + $('manage-galleries-button').onclick = function(event) { + OptionsPage.navigateToPage('manageGalleries'); + }; + + if (cr.isChromeOS) + UIAccountTweaks.applyGuestModeVisibility(document); + + // Cookies filter page --------------------------------------------------- + $('show-cookies-button').onclick = function(event) { + chrome.send('coreOptionsUserMetricsAction', ['Options_ShowCookies']); + OptionsPage.navigateToPage('cookies'); + }; + $('show-app-cookies-button').onclick = function(event) { + OptionsPage.navigateToPage('app-cookies'); + }; + + var intentsSection = $('intents-section'); + if (!loadTimeData.getBoolean('enable_web_intents') && intentsSection) + intentsSection.parentNode.removeChild(intentsSection); + + $('content-settings-overlay-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + + $('pepper-flash-cameramic-section').style.display = 'none'; + $('pepper-flash-cameramic-exceptions-div').style.display = 'none'; + }, + }; + + ContentSettings.updateHandlersEnabledRadios = function(enabled) { + var selector = '#content-settings-page input[type=radio][value=' + + (enabled ? 'allow' : 'block') + '].handler-radio'; + document.querySelector(selector).checked = true; + }; + + /** + * Sets the values for all the content settings radios. + * @param {Object} dict A mapping from radio groups to the checked value for + * that group. + */ + ContentSettings.setContentFilterSettingsValue = function(dict) { + for (var group in dict) { + document.querySelector('input[type=radio][name=' + group + '][value=' + + dict[group]['value'] + ']').checked = true; + var radios = document.querySelectorAll('input[type=radio][name=' + + group + ']'); + var managedBy = dict[group]['managedBy']; + for (var i = 0, len = radios.length; i < len; i++) { + radios[i].disabled = (managedBy != 'default'); + radios[i].controlledBy = managedBy; + } + } + OptionsPage.updateManagedBannerVisibility(); + }; + + /** + * Initializes an exceptions list. + * @param {string} type The content type that we are setting exceptions for. + * @param {Array} list An array of pairs, where the first element of each pair + * is the filter string, and the second is the setting (allow/block). + */ + ContentSettings.setExceptions = function(type, list) { + var exceptionsList = + document.querySelector('div[contentType=' + type + ']' + + ' list[mode=normal]'); + exceptionsList.setExceptions(list); + }; + + ContentSettings.setHandlers = function(list) { + $('handlers-list').setHandlers(list); + }; + + ContentSettings.setIgnoredHandlers = function(list) { + $('ignored-handlers-list').setHandlers(list); + }; + + ContentSettings.setOTRExceptions = function(type, list) { + var exceptionsList = + document.querySelector('div[contentType=' + type + ']' + + ' list[mode=otr]'); + + exceptionsList.parentNode.hidden = false; + exceptionsList.setExceptions(list); + }; + + /** + * The browser's response to a request to check the validity of a given URL + * pattern. + * @param {string} type The content type. + * @param {string} mode The browser mode. + * @param {string} pattern The pattern. + * @param {bool} valid Whether said pattern is valid in the context of + * a content exception setting. + */ + ContentSettings.patternValidityCheckComplete = + function(type, mode, pattern, valid) { + var exceptionsList = + document.querySelector('div[contentType=' + type + '] ' + + 'list[mode=' + mode + ']'); + exceptionsList.patternValidityCheckComplete(pattern, valid); + }; + + /** + * Enables the Pepper Flash camera and microphone settings. + * Please note that whether the settings are actually showed or not is also + * affected by the style class pepper-flash-settings. + */ + ContentSettings.enablePepperFlashCameraMicSettings = function() { + $('pepper-flash-cameramic-section').style.display = ''; + $('pepper-flash-cameramic-exceptions-div').style.display = ''; + } + + // Export + return { + ContentSettings: ContentSettings + }; + +}); diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.html b/chrome/browser/resources/options/content_settings_exceptions_area.html new file mode 100644 index 0000000..cbcfd77 --- /dev/null +++ b/chrome/browser/resources/options/content_settings_exceptions_area.html @@ -0,0 +1,99 @@ + diff --git a/chrome/browser/resources/options/content_settings_exceptions_area.js b/chrome/browser/resources/options/content_settings_exceptions_area.js new file mode 100644 index 0000000..b9bb96d --- /dev/null +++ b/chrome/browser/resources/options/content_settings_exceptions_area.js @@ -0,0 +1,590 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.contentSettings', function() { + /** @const */ var InlineEditableItemList = options.InlineEditableItemList; + /** @const */ var InlineEditableItem = options.InlineEditableItem; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + /** + * Creates a new exceptions list item. + * + * @param {string} contentType The type of the list. + * @param {string} mode The browser mode, 'otr' or 'normal'. + * @param {boolean} enableAskOption Whether to show an 'ask every time' + * option in the select. + * @param {Object} exception A dictionary that contains the data of the + * exception. + * @constructor + * @extends {options.InlineEditableItem} + */ + function ExceptionsListItem(contentType, mode, enableAskOption, exception) { + var el = cr.doc.createElement('div'); + el.mode = mode; + el.contentType = contentType; + el.enableAskOption = enableAskOption; + el.dataItem = exception; + el.__proto__ = ExceptionsListItem.prototype; + el.decorate(); + + return el; + } + + ExceptionsListItem.prototype = { + __proto__: InlineEditableItem.prototype, + + /** + * Called when an element is decorated as a list item. + */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + this.isPlaceholder = !this.pattern; + var patternCell = this.createEditableTextCell(this.pattern); + patternCell.className = 'exception-pattern'; + patternCell.classList.add('weakrtl'); + this.contentElement.appendChild(patternCell); + if (this.pattern) + this.patternLabel = patternCell.querySelector('.static-text'); + var input = patternCell.querySelector('input'); + + // TODO(stuartmorgan): Create an createEditableSelectCell abstracting + // this code. + // Setting label for display mode. |pattern| will be null for the 'add new + // exception' row. + if (this.pattern) { + var settingLabel = cr.doc.createElement('span'); + settingLabel.textContent = this.settingForDisplay(); + settingLabel.className = 'exception-setting'; + settingLabel.setAttribute('displaymode', 'static'); + this.contentElement.appendChild(settingLabel); + this.settingLabel = settingLabel; + } + + // Setting select element for edit mode. + var select = cr.doc.createElement('select'); + var optionAllow = cr.doc.createElement('option'); + optionAllow.textContent = loadTimeData.getString('allowException'); + optionAllow.value = 'allow'; + select.appendChild(optionAllow); + + if (this.enableAskOption) { + var optionAsk = cr.doc.createElement('option'); + optionAsk.textContent = loadTimeData.getString('askException'); + optionAsk.value = 'ask'; + select.appendChild(optionAsk); + } + + if (this.contentType == 'cookies') { + var optionSession = cr.doc.createElement('option'); + optionSession.textContent = loadTimeData.getString('sessionException'); + optionSession.value = 'session'; + select.appendChild(optionSession); + } + + if (this.contentType != 'fullscreen') { + var optionBlock = cr.doc.createElement('option'); + optionBlock.textContent = loadTimeData.getString('blockException'); + optionBlock.value = 'block'; + select.appendChild(optionBlock); + } + + this.contentElement.appendChild(select); + select.className = 'exception-setting'; + if (this.pattern) + select.setAttribute('displaymode', 'edit'); + + // Used to track whether the URL pattern in the input is valid. + // This will be true if the browser process has informed us that the + // current text in the input is valid. Changing the text resets this to + // false, and getting a response from the browser sets it back to true. + // It starts off as false for empty string (new exceptions) or true for + // already-existing exceptions (which we assume are valid). + this.inputValidityKnown = this.pattern; + // This one tracks the actual validity of the pattern in the input. This + // starts off as true so as not to annoy the user when he adds a new and + // empty input. + this.inputIsValid = true; + + this.input = input; + this.select = select; + + this.updateEditables(); + + // Editing notifications, geolocation and media-stream is disabled for + // now. + if (this.contentType == 'notifications' || + this.contentType == 'location' || + this.contentType == 'media-stream') { + this.editable = false; + } + + // If the source of the content setting exception is not the user + // preference, then the content settings exception is managed and the user + // can't edit it. + if (this.dataItem.source && + this.dataItem.source != 'preference') { + this.setAttribute('managedby', this.dataItem.source); + this.deletable = false; + this.editable = false; + } + + // If the exception comes from a hosted app, display the name and the + // icon of the app. + if (this.dataItem.source == 'HostedApp') { + this.title = + loadTimeData.getString('set_by') + ' ' + this.dataItem.appName; + var button = this.querySelector('.row-delete-button'); + // Use the host app's favicon (16px, match bigger size). + // See c/b/ui/webui/extensions/extension_icon_source.h + // for a description of the chrome://extension-icon URL. + button.style.backgroundImage = + 'url(\'chrome://extension-icon/' + this.dataItem.appId + '/16/1\')'; + } + + var listItem = this; + // Handle events on the editable nodes. + input.oninput = function(event) { + listItem.inputValidityKnown = false; + chrome.send('checkExceptionPatternValidity', + [listItem.contentType, listItem.mode, input.value]); + }; + + // Listen for edit events. + this.addEventListener('canceledit', this.onEditCancelled_); + this.addEventListener('commitedit', this.onEditCommitted_); + }, + + /** + * The pattern (e.g., a URL) for the exception. + * + * @type {string} + */ + get pattern() { + return this.dataItem['displayPattern']; + }, + set pattern(pattern) { + this.dataItem['displayPattern'] = pattern; + }, + + /** + * The setting (allow/block) for the exception. + * + * @type {string} + */ + get setting() { + return this.dataItem['setting']; + }, + set setting(setting) { + this.dataItem['setting'] = setting; + }, + + /** + * Gets a human-readable setting string. + * + * @type {string} + */ + settingForDisplay: function() { + var setting = this.setting; + if (setting == 'allow') + return loadTimeData.getString('allowException'); + else if (setting == 'block') + return loadTimeData.getString('blockException'); + else if (setting == 'ask') + return loadTimeData.getString('askException'); + else if (setting == 'session') + return loadTimeData.getString('sessionException'); + }, + + /** + * Update this list item to reflect whether the input is a valid pattern. + * + * @param {boolean} valid Whether said pattern is valid in the context of a + * content exception setting. + */ + setPatternValid: function(valid) { + if (valid || !this.input.value) + this.input.setCustomValidity(''); + else + this.input.setCustomValidity(' '); + this.inputIsValid = valid; + this.inputValidityKnown = true; + }, + + /** + * Set the to its original contents. Used when the user quits + * editing. + */ + resetInput: function() { + this.input.value = this.pattern; + }, + + /** + * Copy the data model values to the editable nodes. + */ + updateEditables: function() { + this.resetInput(); + + var settingOption = + this.select.querySelector('[value=\'' + this.setting + '\']'); + if (settingOption) + settingOption.selected = true; + }, + + /** @inheritDoc */ + get currentInputIsValid() { + return this.inputValidityKnown && this.inputIsValid; + }, + + /** @inheritDoc */ + get hasBeenEdited() { + var livePattern = this.input.value; + var liveSetting = this.select.value; + return livePattern != this.pattern || liveSetting != this.setting; + }, + + /** + * Called when committing an edit. + * + * @param {Event} e The end event. + * @private + */ + onEditCommitted_: function(e) { + var newPattern = this.input.value; + var newSetting = this.select.value; + + this.finishEdit(newPattern, newSetting); + }, + + /** + * Called when cancelling an edit; resets the control states. + * + * @param {Event} e The cancel event. + * @private + */ + onEditCancelled_: function() { + this.updateEditables(); + this.setPatternValid(true); + }, + + /** + * Editing is complete; update the model. + * + * @param {string} newPattern The pattern that the user entered. + * @param {string} newSetting The setting the user chose. + */ + finishEdit: function(newPattern, newSetting) { + this.patternLabel.textContent = newPattern; + this.settingLabel.textContent = this.settingForDisplay(); + var oldPattern = this.pattern; + this.pattern = newPattern; + this.setting = newSetting; + + // TODO(estade): this will need to be updated if geolocation/notifications + // become editable. + if (oldPattern != newPattern) { + chrome.send('removeException', + [this.contentType, this.mode, oldPattern]); + } + + chrome.send('setException', + [this.contentType, this.mode, newPattern, newSetting]); + } + }; + + /** + * Creates a new list item for the Add New Item row, which doesn't represent + * an actual entry in the exceptions list but allows the user to add new + * exceptions. + * + * @param {string} contentType The type of the list. + * @param {string} mode The browser mode, 'otr' or 'normal'. + * @param {boolean} enableAskOption Whether to show an 'ask every time' option + * in the select. + * @constructor + * @extends {cr.ui.ExceptionsListItem} + */ + function ExceptionsAddRowListItem(contentType, mode, enableAskOption) { + var el = cr.doc.createElement('div'); + el.mode = mode; + el.contentType = contentType; + el.enableAskOption = enableAskOption; + el.dataItem = []; + el.__proto__ = ExceptionsAddRowListItem.prototype; + el.decorate(); + + return el; + } + + ExceptionsAddRowListItem.prototype = { + __proto__: ExceptionsListItem.prototype, + + decorate: function() { + ExceptionsListItem.prototype.decorate.call(this); + + this.input.placeholder = + loadTimeData.getString('addNewExceptionInstructions'); + + // Do we always want a default of allow? + this.setting = 'allow'; + }, + + /** + * Clear the and let the placeholder text show again. + */ + resetInput: function() { + this.input.value = ''; + }, + + /** @inheritDoc */ + get hasBeenEdited() { + return this.input.value != ''; + }, + + /** + * Editing is complete; update the model. As long as the pattern isn't + * empty, we'll just add it. + * + * @param {string} newPattern The pattern that the user entered. + * @param {string} newSetting The setting the user chose. + */ + finishEdit: function(newPattern, newSetting) { + this.resetInput(); + chrome.send('setException', + [this.contentType, this.mode, newPattern, newSetting]); + }, + }; + + /** + * Creates a new exceptions list. + * + * @constructor + * @extends {cr.ui.List} + */ + var ExceptionsList = cr.ui.define('list'); + + ExceptionsList.prototype = { + __proto__: InlineEditableItemList.prototype, + + /** + * Called when an element is decorated as a list. + */ + decorate: function() { + InlineEditableItemList.prototype.decorate.call(this); + + this.classList.add('settings-list'); + + for (var parentNode = this.parentNode; parentNode; + parentNode = parentNode.parentNode) { + if (parentNode.hasAttribute('contentType')) { + this.contentType = parentNode.getAttribute('contentType'); + break; + } + } + + this.mode = this.getAttribute('mode'); + + var exceptionList = this; + + // Whether the exceptions in this list allow an 'Ask every time' option. + this.enableAskOption = this.contentType == 'plugins' || + this.contentType == 'pepper-flash-cameramic'; + + this.autoExpands = true; + this.reset(); + }, + + /** + * Creates an item to go in the list. + * + * @param {Object} entry The element from the data model for this row. + */ + createItem: function(entry) { + if (entry) { + return new ExceptionsListItem(this.contentType, + this.mode, + this.enableAskOption, + entry); + } else { + var addRowItem = new ExceptionsAddRowListItem(this.contentType, + this.mode, + this.enableAskOption); + addRowItem.deletable = false; + return addRowItem; + } + }, + + /** + * Sets the exceptions in the js model. + * + * @param {Object} entries A list of dictionaries of values, each dictionary + * represents an exception. + */ + setExceptions: function(entries) { + var deleteCount = this.dataModel.length; + + if (this.isEditable()) { + // We don't want to remove the Add New Exception row. + deleteCount = deleteCount - 1; + } + + var args = [0, deleteCount]; + args.push.apply(args, entries); + this.dataModel.splice.apply(this.dataModel, args); + }, + + /** + * The browser has finished checking a pattern for validity. Update the list + * item to reflect this. + * + * @param {string} pattern The pattern. + * @param {bool} valid Whether said pattern is valid in the context of a + * content exception setting. + */ + patternValidityCheckComplete: function(pattern, valid) { + var listItems = this.items; + for (var i = 0; i < listItems.length; i++) { + var listItem = listItems[i]; + // Don't do anything for messages for the item if it is not the intended + // recipient, or if the response is stale (i.e. the input value has + // changed since we sent the request to analyze it). + if (pattern == listItem.input.value) + listItem.setPatternValid(valid); + } + }, + + /** + * Returns whether the rows are editable in this list. + */ + isEditable: function() { + // Exceptions of the following lists are not editable for now. + return !(this.contentType == 'notifications' || + this.contentType == 'location' || + this.contentType == 'fullscreen' || + this.contentType == 'media-stream'); + }, + + /** + * Removes all exceptions from the js model. + */ + reset: function() { + if (this.isEditable()) { + // The null creates the Add New Exception row. + this.dataModel = new ArrayDataModel([null]); + } else { + this.dataModel = new ArrayDataModel([]); + } + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var listItem = this.getListItemByIndex(index); + if (listItem.undeletable) + return; + + var dataItem = listItem.dataItem; + var args = [listItem.contentType]; + if (listItem.contentType == 'location') + args.push(dataItem['origin'], dataItem['embeddingOrigin']); + else if (listItem.contentType == 'notifications') + args.push(dataItem['origin'], dataItem['setting']); + else + args.push(listItem.mode, listItem.pattern); + + chrome.send('removeException', args); + }, + }; + + var OptionsPage = options.OptionsPage; + + /** + * Encapsulated handling of content settings list subpage. + * + * @constructor + */ + function ContentSettingsExceptionsArea() { + OptionsPage.call(this, 'contentExceptions', + loadTimeData.getString('contentSettingsPageTabTitle'), + 'content-settings-exceptions-area'); + } + + cr.addSingletonGetter(ContentSettingsExceptionsArea); + + ContentSettingsExceptionsArea.prototype = { + __proto__: OptionsPage.prototype, + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var exceptionsLists = this.pageDiv.querySelectorAll('list'); + for (var i = 0; i < exceptionsLists.length; i++) { + options.contentSettings.ExceptionsList.decorate(exceptionsLists[i]); + } + + ContentSettingsExceptionsArea.hideOTRLists(false); + + // If the user types in the URL without a hash, show just cookies. + this.showList('cookies'); + + $('content-settings-exceptions-overlay-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + /** + * Shows one list and hides all others. + * + * @param {string} type The content type. + */ + showList: function(type) { + var header = this.pageDiv.querySelector('h1'); + header.textContent = loadTimeData.getString(type + '_header'); + + var divs = this.pageDiv.querySelectorAll('div[contentType]'); + for (var i = 0; i < divs.length; i++) { + if (divs[i].getAttribute('contentType') == type) + divs[i].hidden = false; + else + divs[i].hidden = true; + } + }, + + /** + * Called after the page has been shown. Show the content type for the + * location's hash. + */ + didShowPage: function() { + var hash = location.hash; + if (hash) + this.showList(hash.slice(1)); + }, + }; + + /** + * Called when the last incognito window is closed. + */ + ContentSettingsExceptionsArea.OTRProfileDestroyed = function() { + this.hideOTRLists(true); + }; + + /** + * Hides the incognito exceptions lists and optionally clears them as well. + * @param {boolean} clear Whether to clear the lists. + */ + ContentSettingsExceptionsArea.hideOTRLists = function(clear) { + var otrLists = document.querySelectorAll('list[mode=otr]'); + + for (var i = 0; i < otrLists.length; i++) { + otrLists[i].parentNode.hidden = true; + if (clear) + otrLists[i].reset(); + } + }; + + return { + ExceptionsListItem: ExceptionsListItem, + ExceptionsAddRowListItem: ExceptionsAddRowListItem, + ExceptionsList: ExceptionsList, + ContentSettingsExceptionsArea: ContentSettingsExceptionsArea, + }; +}); diff --git a/chrome/browser/resources/options/content_settings_ui.js b/chrome/browser/resources/options/content_settings_ui.js new file mode 100644 index 0000000..dc7b0c4 --- /dev/null +++ b/chrome/browser/resources/options/content_settings_ui.js @@ -0,0 +1,67 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + ////////////////////////////////////////////////////////////////////////////// + // ContentSettingsRadio class: + + // Define a constructor that uses an input element as its underlying element. + var ContentSettingsRadio = cr.ui.define('input'); + + ContentSettingsRadio.prototype = { + __proto__: HTMLInputElement.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'radio'; + var self = this; + + this.addEventListener('change', + function(e) { + chrome.send('setContentFilter', [this.name, this.value]); + }); + }, + }; + + /** + * Whether the content setting is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(ContentSettingsRadio, 'controlledBy', cr.PropertyKind.ATTR); + + ////////////////////////////////////////////////////////////////////////////// + // HandlersEnabledRadio class: + + // Define a constructor that uses an input element as its underlying element. + var HandlersEnabledRadio = cr.ui.define('input'); + + HandlersEnabledRadio.prototype = { + __proto__: HTMLInputElement.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'radio'; + var self = this; + + this.addEventListener('change', + function(e) { + chrome.send('setHandlersEnabled', [this.value == 'allow']); + }); + }, + }; + + // Export + return { + ContentSettingsRadio: ContentSettingsRadio, + HandlersEnabledRadio: HandlersEnabledRadio + }; + +}); + diff --git a/chrome/browser/resources/options/controlled_setting.js b/chrome/browser/resources/options/controlled_setting.js new file mode 100644 index 0000000..9ff806c --- /dev/null +++ b/chrome/browser/resources/options/controlled_setting.js @@ -0,0 +1,138 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var Preferences = options.Preferences; + + /** + * A controlled setting indicator that can be placed on a setting as an + * indicator that the value is controlled by some external entity such as + * policy or an extension. + * @constructor + * @extends {HTMLSpanElement} + */ + var ControlledSettingIndicator = cr.ui.define('span'); + + ControlledSettingIndicator.prototype = { + __proto__: HTMLSpanElement.prototype, + + /** + * Decorates the base element to show the proper icon. + */ + decorate: function() { + var self = this; + var doc = self.ownerDocument; + + // Create the details and summary elements. + var detailsContainer = doc.createElement('details'); + detailsContainer.appendChild(doc.createElement('summary')); + + // This should really create a div element, but that breaks :hover. See + // https://bugs.webkit.org/show_bug.cgi?id=72957 + var bubbleContainer = doc.createElement('p'); + bubbleContainer.className = 'controlled-setting-bubble'; + detailsContainer.appendChild(bubbleContainer); + + self.appendChild(detailsContainer); + + // If there is a pref, track its controlledBy property in order to be able + // to bring up the correct bubble. + if (this.hasAttribute('pref')) { + Preferences.getInstance().addEventListener( + this.getAttribute('pref'), + function(event) { + if (event.value) { + var controlledBy = event.value['controlledBy']; + self.controlledBy = controlledBy ? controlledBy : null; + } + }); + } + + self.addEventListener('click', self.show_); + }, + + + /** + * Closes the bubble. + */ + close: function() { + this.querySelector('details').removeAttribute('open'); + this.ownerDocument.removeEventListener('click', this.closeHandler_, true); + }, + + /** + * Constructs the bubble DOM tree and shows it. + * @private + */ + show_: function() { + var self = this; + var doc = self.ownerDocument; + + // Clear out the old bubble contents. + var bubbleContainer = this.querySelector('.controlled-setting-bubble'); + if (bubbleContainer) { + while (bubbleContainer.hasChildNodes()) + bubbleContainer.removeChild(bubbleContainer.lastChild); + } + + // Work out the bubble text. + defaultStrings = { + policy: loadTimeData.getString('controlledSettingPolicy'), + extension: loadTimeData.getString('controlledSettingExtension'), + recommended: loadTimeData.getString('controlledSettingRecommended'), + }; + + // No controller, no bubble. + if (!self.controlledBy || !self.controlledBy in defaultStrings) + return; + + var text = defaultStrings[self.controlledBy]; + + // Apply text overrides. + if (self.hasAttribute('text' + self.controlledBy)) + text = self.getAttribute('text' + self.controlledBy); + + // Create the DOM tree. + var bubbleText = doc.createElement('p'); + bubbleText.className = 'controlled-setting-bubble-text'; + bubbleText.textContent = text; + + var pref = self.getAttribute('pref'); + if (self.controlledBy == 'recommended' && pref) { + var container = doc.createElement('div'); + var action = doc.createElement('button'); + action.classList.add('link-button'); + action.classList.add('controlled-setting-bubble-action'); + action.textContent = + loadTimeData.getString('controlledSettingApplyRecommendation'); + action.addEventListener( + 'click', + function(e) { + Preferences.clearPref(pref); + }); + container.appendChild(action); + bubbleText.appendChild(container); + } + + bubbleContainer.appendChild(bubbleText); + + // One-time bubble-closing event handler. + self.closeHandler_ = this.close.bind(this); + doc.addEventListener('click', self.closeHandler_, true); + } + }; + + /** + * The controlling entity of the setting. Can take the values "policy", + * "extension", "recommended" or be unset. + */ + cr.defineProperty(ControlledSettingIndicator, 'controlledBy', + cr.PropertyKind.ATTR, + ControlledSettingIndicator.prototype.close); + + // Export. + return { + ControlledSettingIndicator: ControlledSettingIndicator + }; +}); diff --git a/chrome/browser/resources/options/cookies_list.js b/chrome/browser/resources/options/cookies_list.js new file mode 100644 index 0000000..0968f3c --- /dev/null +++ b/chrome/browser/resources/options/cookies_list.js @@ -0,0 +1,903 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var DeletableItemList = options.DeletableItemList; + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; + + // This structure maps the various cookie type names from C++ (hence the + // underscores) to arrays of the different types of data each has, along with + // the i18n name for the description of that data type. + /** @const */ var cookieInfo = { + 'cookie': [['name', 'label_cookie_name'], + ['content', 'label_cookie_content'], + ['domain', 'label_cookie_domain'], + ['path', 'label_cookie_path'], + ['sendfor', 'label_cookie_send_for'], + ['accessibleToScript', 'label_cookie_accessible_to_script'], + ['created', 'label_cookie_created'], + ['expires', 'label_cookie_expires']], + 'app_cache': [['manifest', 'label_app_cache_manifest'], + ['size', 'label_local_storage_size'], + ['created', 'label_cookie_created'], + ['accessed', 'label_cookie_last_accessed']], + 'database': [['name', 'label_cookie_name'], + ['desc', 'label_webdb_desc'], + ['size', 'label_local_storage_size'], + ['modified', 'label_local_storage_last_modified']], + 'local_storage': [['origin', 'label_local_storage_origin'], + ['size', 'label_local_storage_size'], + ['modified', 'label_local_storage_last_modified']], + 'indexed_db': [['origin', 'label_indexed_db_origin'], + ['size', 'label_indexed_db_size'], + ['modified', 'label_indexed_db_last_modified']], + 'file_system': [['origin', 'label_file_system_origin'], + ['persistent', 'label_file_system_persistent_usage'], + ['temporary', 'label_file_system_temporary_usage']], + 'server_bound_cert': [['serverId', 'label_server_bound_cert_server_id'], + ['certType', 'label_server_bound_cert_type'], + ['created', 'label_server_bound_cert_created'], + ['expires', 'label_server_bound_cert_expires']], + 'flash_lso': [['domain', 'label_cookie_domain']], + }; + + /** + * Returns the item's height, like offsetHeight but such that it works better + * when the page is zoomed. See the similar calculation in @{code cr.ui.List}. + * This version also accounts for the animation done in this file. + * @param {Element} item The item to get the height of. + * @return {number} The height of the item, calculated with zooming in mind. + */ + function getItemHeight(item) { + var height = item.style.height; + // Use the fixed animation target height if set, in case the element is + // currently being animated and we'd get an intermediate height below. + if (height && height.substr(-2) == 'px') + return parseInt(height.substr(0, height.length - 2)); + return item.getBoundingClientRect().height; + } + + /** + * Create tree nodes for the objects in the data array, and insert them all + * into the given list using its @{code splice} method at the given index. + * @param {Array.} data The data objects for the nodes to add. + * @param {number} start The index at which to start inserting the nodes. + * @return {Array.} An array of CookieTreeNodes added. + */ + function spliceTreeNodes(data, start, list) { + var nodes = data.map(function(x) { return new CookieTreeNode(x); }); + // Insert [start, 0] at the beginning of the array of nodes, making it + // into the arguments we want to pass to @{code list.splice} below. + nodes.splice(0, 0, start, 0); + list.splice.apply(list, nodes); + // Remove the [start, 0] prefix and return the array of nodes. + nodes.splice(0, 2); + return nodes; + } + + /** + * Adds information about an app that protects this data item to the + * @{code element}. + * @param {Element} element The DOM element the information should be + appended to. + * @param {{id: string, name: string}} appInfo Information about an app. + */ + function addAppInfo(element, appInfo) { + var img = element.ownerDocument.createElement('img'); + img.src = 'chrome://extension-icon/' + appInfo.id + '/16/1'; + img.title = loadTimeData.getString('label_protected_by_apps') + + ' ' + appInfo.name; + img.className = 'protecting-app'; + element.appendChild(img); + } + + var parentLookup = {}; + var lookupRequests = {}; + + /** + * Creates a new list item for sites data. Note that these are created and + * destroyed lazily as they scroll into and out of view, so they must be + * stateless. We cache the expanded item in @{code CookiesList} though, so it + * can keep state. (Mostly just which item is selected.) + * @param {Object} origin Data used to create a cookie list item. + * @param {CookiesList} list The list that will contain this item. + * @constructor + * @extends {DeletableItem} + */ + function CookieListItem(origin, list) { + var listItem = new DeletableItem(null); + listItem.__proto__ = CookieListItem.prototype; + + listItem.origin = origin; + listItem.list = list; + listItem.decorate(); + + // This hooks up updateOrigin() to the list item, makes the top-level + // tree nodes (i.e., origins) register their IDs in parentLookup, and + // causes them to request their children if they have none. Note that we + // have special logic in the setter for the parent property to make sure + // that we can still garbage collect list items when they scroll out of + // view, even though it appears that we keep a direct reference. + if (origin) { + origin.parent = listItem; + origin.updateOrigin(); + } + + return listItem; + } + + CookieListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + this.siteChild = this.ownerDocument.createElement('div'); + this.siteChild.className = 'cookie-site'; + this.dataChild = this.ownerDocument.createElement('div'); + this.dataChild.className = 'cookie-data'; + this.sizeChild = this.ownerDocument.createElement('div'); + this.sizeChild.className = 'cookie-size'; + this.itemsChild = this.ownerDocument.createElement('div'); + this.itemsChild.className = 'cookie-items'; + this.infoChild = this.ownerDocument.createElement('div'); + this.infoChild.className = 'cookie-details'; + this.infoChild.hidden = true; + + if (this.origin.data.appId) { + this.siteChild.classList.add('app-cookie-site'); + this.itemsChild.classList.add('app-cookie-items'); + } + + var remove = this.ownerDocument.createElement('button'); + remove.textContent = loadTimeData.getString('remove_cookie'); + remove.onclick = this.removeCookie_.bind(this); + this.infoChild.appendChild(remove); + var content = this.contentElement; + content.appendChild(this.siteChild); + content.appendChild(this.dataChild); + content.appendChild(this.sizeChild); + content.appendChild(this.itemsChild); + this.itemsChild.appendChild(this.infoChild); + if (this.origin && this.origin.data) { + this.siteChild.textContent = this.origin.data.title; + this.siteChild.setAttribute('title', this.origin.data.title); + } + this.itemList_ = []; + }, + + /** @type {boolean} */ + get expanded() { + return this.expanded_; + }, + set expanded(expanded) { + if (this.expanded_ == expanded) + return; + this.expanded_ = expanded; + if (expanded) { + var oldExpanded = this.list.expandedItem; + this.list.expandedItem = this; + this.updateItems_(); + if (oldExpanded) + oldExpanded.expanded = false; + this.classList.add('show-items'); + } else { + if (this.list.expandedItem == this) { + this.list.expandedItem = null; + } + this.style.height = ''; + this.itemsChild.style.height = ''; + this.classList.remove('show-items'); + } + }, + + /** + * The callback for the "remove" button shown when an item is selected. + * Requests that the currently selected cookie be removed. + * @private + */ + removeCookie_: function() { + if (this.selectedIndex_ >= 0) { + var item = this.itemList_[this.selectedIndex_]; + if (item && item.node) + chrome.send('removeCookie', [item.node.pathId]); + } + }, + + /** + * Disable animation within this cookie list item, in preparation for making + * changes that will need to be animated. Makes it possible to measure the + * contents without displaying them, to set animation targets. + * @private + */ + disableAnimation_: function() { + this.itemsHeight_ = getItemHeight(this.itemsChild); + this.classList.add('measure-items'); + }, + + /** + * Enable animation after changing the contents of this cookie list item. + * See @{code disableAnimation_}. + * @private + */ + enableAnimation_: function() { + if (!this.classList.contains('measure-items')) + this.disableAnimation_(); + this.itemsChild.style.height = ''; + // This will force relayout in order to calculate the new heights. + var itemsHeight = getItemHeight(this.itemsChild); + var fixedHeight = getItemHeight(this) + itemsHeight - this.itemsHeight_; + this.itemsChild.style.height = this.itemsHeight_ + 'px'; + // Force relayout before enabling animation, so that if we have + // changed things since the last layout, they will not be animated + // during subsequent layouts. + this.itemsChild.offsetHeight; + this.classList.remove('measure-items'); + this.itemsChild.style.height = itemsHeight + 'px'; + this.style.height = fixedHeight + 'px'; + }, + + /** + * Updates the origin summary to reflect changes in its items. + * Both CookieListItem and CookieTreeNode implement this API. + * This implementation scans the descendants to update the text. + */ + updateOrigin: function() { + var info = { + cookies: 0, + database: false, + localStorage: false, + appCache: false, + indexedDb: false, + fileSystem: false, + serverBoundCerts: 0, + }; + if (this.origin) + this.origin.collectSummaryInfo(info); + + var list = []; + if (info.cookies > 1) + list.push(loadTimeData.getStringF('cookie_plural', info.cookies)); + else if (info.cookies > 0) + list.push(loadTimeData.getString('cookie_singular')); + if (info.database || info.indexedDb) + list.push(loadTimeData.getString('cookie_database_storage')); + if (info.localStorage) + list.push(loadTimeData.getString('cookie_local_storage')); + if (info.appCache) + list.push(loadTimeData.getString('cookie_app_cache')); + if (info.fileSystem) + list.push(loadTimeData.getString('cookie_file_system')); + if (info.serverBoundCerts) + list.push(loadTimeData.getString('cookie_server_bound_cert')); + if (info.flashLSO) + list.push(loadTimeData.getString('cookie_flash_lso')); + + var text = ''; + for (var i = 0; i < list.length; ++i) { + if (text.length > 0) + text += ', ' + list[i]; + else + text = list[i]; + } + this.dataChild.textContent = text; + + for (var key in info.appsProtectingThis) { + addAppInfo(this.dataChild, apps[key]); + } + + if (info.quota && info.quota.totalUsage) + this.sizeChild.textContent = info.quota.totalUsage; + + if (this.expanded) + this.updateItems_(); + }, + + /** + * Updates the items section to reflect changes, animating to the new state. + * Removes existing contents and calls @{code CookieTreeNode.createItems}. + * @private + */ + updateItems_: function() { + this.disableAnimation_(); + this.itemsChild.textContent = ''; + this.infoChild.hidden = true; + this.selectedIndex_ = -1; + this.itemList_ = []; + if (this.origin) + this.origin.createItems(this); + this.itemsChild.appendChild(this.infoChild); + this.enableAnimation_(); + }, + + /** + * Append a new cookie node "bubble" to this list item. + * @param {CookieTreeNode} node The cookie node to add a bubble for. + * @param {Element} div The DOM element for the bubble itself. + * @return {number} The index the bubble was added at. + */ + appendItem: function(node, div) { + this.itemList_.push({node: node, div: div}); + this.itemsChild.appendChild(div); + return this.itemList_.length - 1; + }, + + /** + * The currently selected cookie node ("cookie bubble") index. + * @type {number} + * @private + */ + selectedIndex_: -1, + + /** + * Get the currently selected cookie node ("cookie bubble") index. + * @type {number} + */ + get selectedIndex() { + return this.selectedIndex_; + }, + + /** + * Set the currently selected cookie node ("cookie bubble") index to + * @{code itemIndex}, unselecting any previously selected node first. + * @param {number} itemIndex The index to set as the selected index. + */ + set selectedIndex(itemIndex) { + // Get the list index up front before we change anything. + var index = this.list.getIndexOfListItem(this); + // Unselect any previously selected item. + if (this.selectedIndex_ >= 0) { + var item = this.itemList_[this.selectedIndex_]; + if (item && item.div) + item.div.removeAttribute('selected'); + } + // Special case: decrementing -1 wraps around to the end of the list. + if (itemIndex == -2) + itemIndex = this.itemList_.length - 1; + // Check if we're going out of bounds and hide the item details. + if (itemIndex < 0 || itemIndex >= this.itemList_.length) { + this.selectedIndex_ = -1; + this.disableAnimation_(); + this.infoChild.hidden = true; + this.enableAnimation_(); + return; + } + // Set the new selected item and show the item details for it. + this.selectedIndex_ = itemIndex; + this.itemList_[itemIndex].div.setAttribute('selected', ''); + this.disableAnimation_(); + this.itemList_[itemIndex].node.setDetailText(this.infoChild, + this.list.infoNodes); + this.infoChild.hidden = false; + this.enableAnimation_(); + // If we're near the bottom of the list this may cause the list item to go + // beyond the end of the visible area. Fix it after the animation is done. + var list = this.list; + window.setTimeout(function() { list.scrollIndexIntoView(index); }, 150); + }, + }; + + /** + * {@code CookieTreeNode}s mirror the structure of the cookie tree lazily, and + * contain all the actual data used to generate the {@code CookieListItem}s. + * @param {Object} data The data object for this node. + * @constructor + */ + function CookieTreeNode(data) { + this.data = data; + this.children = []; + } + + CookieTreeNode.prototype = { + /** + * Insert the given list of cookie tree nodes at the given index. + * Both CookiesList and CookieTreeNode implement this API. + * @param {Array.} data The data objects for the nodes to add. + * @param {number} start The index at which to start inserting the nodes. + */ + insertAt: function(data, start) { + var nodes = spliceTreeNodes(data, start, this.children); + for (var i = 0; i < nodes.length; i++) + nodes[i].parent = this; + this.updateOrigin(); + }, + + /** + * Remove a cookie tree node from the given index. + * Both CookiesList and CookieTreeNode implement this API. + * @param {number} index The index of the tree node to remove. + */ + remove: function(index) { + if (index < this.children.length) { + this.children.splice(index, 1); + this.updateOrigin(); + } + }, + + /** + * Clears all children. + * Both CookiesList and CookieTreeNode implement this API. + * It is used by CookiesList.loadChildren(). + */ + clear: function() { + // We might leave some garbage in parentLookup for removed children. + // But that should be OK because parentLookup is cleared when we + // reload the tree. + this.children = []; + this.updateOrigin(); + }, + + /** + * The counter used by startBatchUpdates() and endBatchUpdates(). + * @type {number} + */ + batchCount_: 0, + + /** + * See cr.ui.List.startBatchUpdates(). + * Both CookiesList (via List) and CookieTreeNode implement this API. + */ + startBatchUpdates: function() { + this.batchCount_++; + }, + + /** + * See cr.ui.List.endBatchUpdates(). + * Both CookiesList (via List) and CookieTreeNode implement this API. + */ + endBatchUpdates: function() { + if (!--this.batchCount_) + this.updateOrigin(); + }, + + /** + * Requests updating the origin summary to reflect changes in this item. + * Both CookieListItem and CookieTreeNode implement this API. + */ + updateOrigin: function() { + if (!this.batchCount_ && this.parent) + this.parent.updateOrigin(); + }, + + /** + * Summarize the information in this node and update @{code info}. + * This will recurse into child nodes to summarize all descendants. + * @param {Object} info The info object from @{code updateOrigin}. + */ + collectSummaryInfo: function(info) { + if (this.children.length > 0) { + for (var i = 0; i < this.children.length; ++i) + this.children[i].collectSummaryInfo(info); + } else if (this.data && !this.data.hasChildren) { + if (this.data.type == 'cookie') { + info.cookies++; + } else if (this.data.type == 'database') { + info.database = true; + } else if (this.data.type == 'local_storage') { + info.localStorage = true; + } else if (this.data.type == 'app_cache') { + info.appCache = true; + } else if (this.data.type == 'indexed_db') { + info.indexedDb = true; + } else if (this.data.type == 'file_system') { + info.fileSystem = true; + } else if (this.data.type == 'quota') { + info.quota = this.data; + } else if (this.data.type == 'server_bound_cert') { + info.serverBoundCerts++; + } else if (this.data.type == 'flash_lso') { + info.flashLSO = true; + } + + var apps = this.data.appsProtectingThis; + if (apps) { + if (!info.appsProtectingThis) + info.appsProtectingThis = {}; + apps.forEach(function(appInfo) { + info.appsProtectingThis[appInfo.id] = appInfo; + }); + } + } + }, + + /** + * Create the cookie "bubbles" for this node, recursing into children + * if there are any. Append the cookie bubbles to @{code item}. + * @param {CookieListItem} item The cookie list item to create items in. + */ + createItems: function(item) { + if (this.children.length > 0) { + for (var i = 0; i < this.children.length; ++i) + this.children[i].createItems(item); + return; + } + + if (!this.data || this.data.hasChildren) + return; + + var text = ''; + switch (this.data.type) { + case 'cookie': + case 'database': + text = this.data.name; + break; + default: + text = loadTimeData.getString('cookie_' + this.data.type); + } + if (!text) + return; + + var div = item.ownerDocument.createElement('div'); + div.className = 'cookie-item'; + // Help out screen readers and such: this is a clickable thing. + div.setAttribute('role', 'button'); + div.tabIndex = 0; + div.textContent = text; + var apps = this.data.appsProtectingThis; + if (apps) + apps.forEach(addAppInfo.bind(null, div)); + + var index = item.appendItem(this, div); + div.onclick = function() { + item.selectedIndex = (item.selectedIndex == index) ? -1 : index; + }; + }, + + /** + * Set the detail text to be displayed to that of this cookie tree node. + * Uses preallocated DOM elements for each cookie node type from @{code + * infoNodes}, and inserts the appropriate elements to @{code element}. + * @param {Element} element The DOM element to insert elements to. + * @param {Object.}>} infoNodes The map from cookie node types to maps from + * cookie attribute names to DOM elements to display cookie attribute + * values, created by @{code CookiesList.decorate}. + */ + setDetailText: function(element, infoNodes) { + var table; + if (this.data && !this.data.hasChildren && cookieInfo[this.data.type]) { + var info = cookieInfo[this.data.type]; + var nodes = infoNodes[this.data.type].info; + for (var i = 0; i < info.length; ++i) { + var name = info[i][0]; + if (name != 'id' && this.data[name]) + nodes[name].textContent = this.data[name]; + else + nodes[name].textContent = ''; + } + table = infoNodes[this.data.type].table; + } + + while (element.childNodes.length > 1) + element.removeChild(element.firstChild); + + if (table) + element.insertBefore(table, element.firstChild); + }, + + /** + * The parent of this cookie tree node. + * @type {?CookieTreeNode|CookieListItem} + */ + get parent() { + // See below for an explanation of this special case. + if (typeof this.parent_ == 'number') + return this.list_.getListItemByIndex(this.parent_); + return this.parent_; + }, + set parent(parent) { + if (parent == this.parent) + return; + + if (parent instanceof CookieListItem) { + // If the parent is to be a CookieListItem, then we keep the reference + // to it by its containing list and list index, rather than directly. + // This allows the list items to be garbage collected when they scroll + // out of view (except the expanded item, which we cache). This is + // transparent except in the setter and getter, where we handle it. + this.parent_ = parent.listIndex; + this.list_ = parent.list; + parent.addEventListener('listIndexChange', + this.parentIndexChanged_.bind(this)); + } else { + this.parent_ = parent; + } + + if (this.data && this.data.id) { + if (parent) + parentLookup[this.data.id] = this; + else + delete parentLookup[this.data.id]; + } + + if (this.data && this.data.hasChildren && + !this.children.length && !lookupRequests[this.data.id]) { + lookupRequests[this.data.id] = true; + chrome.send('loadCookie', [this.pathId]); + } + }, + + /** + * Called when the parent is a CookieListItem whose index has changed. + * See the code above that avoids keeping a direct reference to + * CookieListItem parents, to allow them to be garbage collected. + * @private + */ + parentIndexChanged_: function(event) { + if (typeof this.parent_ == 'number') { + this.parent_ = event.newValue; + // We set a timeout to update the origin, rather than doing it right + // away, because this callback may occur while the list items are + // being repopulated following a scroll event. Calling updateOrigin() + // immediately could trigger relayout that would reset the scroll + // position within the list, among other things. + window.setTimeout(this.updateOrigin.bind(this), 0); + } + }, + + /** + * The cookie tree path id. + * @type {string} + */ + get pathId() { + var parent = this.parent; + if (parent && parent instanceof CookieTreeNode) + return parent.pathId + ',' + this.data.id; + return this.data.id; + }, + }; + + /** + * Creates a new cookies list. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {DeletableItemList} + */ + var CookiesList = cr.ui.define('list'); + + CookiesList.prototype = { + __proto__: DeletableItemList.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.classList.add('cookie-list'); + this.dataModel = new ArrayDataModel([]); + this.addEventListener('keydown', this.handleKeyLeftRight_.bind(this)); + var sm = new ListSingleSelectionModel(); + sm.addEventListener('change', this.cookieSelectionChange_.bind(this)); + sm.addEventListener('leadIndexChange', this.cookieLeadChange_.bind(this)); + this.selectionModel = sm; + this.infoNodes = {}; + this.fixedHeight = false; + var doc = this.ownerDocument; + // Create a table for each type of site data (e.g. cookies, databases, + // etc.) and save it so that we can reuse it for all origins. + for (var type in cookieInfo) { + var table = doc.createElement('table'); + table.className = 'cookie-details-table'; + var tbody = doc.createElement('tbody'); + table.appendChild(tbody); + var info = {}; + for (var i = 0; i < cookieInfo[type].length; i++) { + var tr = doc.createElement('tr'); + var name = doc.createElement('td'); + var data = doc.createElement('td'); + var pair = cookieInfo[type][i]; + name.className = 'cookie-details-label'; + name.textContent = loadTimeData.getString(pair[1]); + data.className = 'cookie-details-value'; + data.textContent = ''; + tr.appendChild(name); + tr.appendChild(data); + tbody.appendChild(tr); + info[pair[0]] = data; + } + this.infoNodes[type] = {table: table, info: info}; + } + }, + + /** + * Handles key down events and looks for left and right arrows, then + * dispatches to the currently expanded item, if any. + * @param {Event} e The keydown event. + * @private + */ + handleKeyLeftRight_: function(e) { + var id = e.keyIdentifier; + if ((id == 'Left' || id == 'Right') && this.expandedItem) { + var cs = this.ownerDocument.defaultView.getComputedStyle(this); + var rtl = cs.direction == 'rtl'; + if ((!rtl && id == 'Left') || (rtl && id == 'Right')) + this.expandedItem.selectedIndex--; + else + this.expandedItem.selectedIndex++; + this.scrollIndexIntoView(this.expandedItem.listIndex); + // Prevent the page itself from scrolling. + e.preventDefault(); + } + }, + + /** + * Called on selection model selection changes. + * @param {Event} ce The selection change event. + * @private + */ + cookieSelectionChange_: function(ce) { + ce.changes.forEach(function(change) { + var listItem = this.getListItemByIndex(change.index); + if (listItem) { + if (!change.selected) { + // We set a timeout here, rather than setting the item unexpanded + // immediately, so that if another item gets set expanded right + // away, it will be expanded before this item is unexpanded. It + // will notice that, and unexpand this item in sync with its own + // expansion. Later, this callback will end up having no effect. + window.setTimeout(function() { + if (!listItem.selected || !listItem.lead) + listItem.expanded = false; + }, 0); + } else if (listItem.lead) { + listItem.expanded = true; + } + } + }, this); + }, + + /** + * Called on selection model lead changes. + * @param {Event} pe The lead change event. + * @private + */ + cookieLeadChange_: function(pe) { + if (pe.oldValue != -1) { + var listItem = this.getListItemByIndex(pe.oldValue); + if (listItem) { + // See cookieSelectionChange_ above for why we use a timeout here. + window.setTimeout(function() { + if (!listItem.lead || !listItem.selected) + listItem.expanded = false; + }, 0); + } + } + if (pe.newValue != -1) { + var listItem = this.getListItemByIndex(pe.newValue); + if (listItem && listItem.selected) + listItem.expanded = true; + } + }, + + /** + * The currently expanded item. Used by CookieListItem above. + * @type {?CookieListItem} + */ + expandedItem: null, + + // from cr.ui.List + /** @inheritDoc */ + createItem: function(data) { + // We use the cached expanded item in order to allow it to maintain some + // state (like its fixed height, and which bubble is selected). + if (this.expandedItem && this.expandedItem.origin == data) + return this.expandedItem; + return new CookieListItem(data, this); + }, + + // from options.DeletableItemList + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var item = this.dataModel.item(index); + if (item) { + var pathId = item.pathId; + if (pathId) + chrome.send('removeCookie', [pathId]); + } + }, + + /** + * Insert the given list of cookie tree nodes at the given index. + * Both CookiesList and CookieTreeNode implement this API. + * @param {Array.} data The data objects for the nodes to add. + * @param {number} start The index at which to start inserting the nodes. + */ + insertAt: function(data, start) { + spliceTreeNodes(data, start, this.dataModel); + }, + + /** + * Remove a cookie tree node from the given index. + * Both CookiesList and CookieTreeNode implement this API. + * @param {number} index The index of the tree node to remove. + */ + remove: function(index) { + if (index < this.dataModel.length) + this.dataModel.splice(index, 1); + }, + + /** + * Clears the list. + * Both CookiesList and CookieTreeNode implement this API. + * It is used by CookiesList.loadChildren(). + */ + clear: function() { + parentLookup = {}; + this.dataModel.splice(0, this.dataModel.length); + this.redraw(); + }, + + /** + * Add tree nodes by given parent. + * @param {Object} parent The parent node. + * @param {number} start The index at which to start inserting the nodes. + * @param {Array} nodesData Nodes data array. + * @private + */ + addByParent_: function(parent, start, nodesData) { + if (!parent) + return; + + parent.startBatchUpdates(); + parent.insertAt(nodesData, start); + parent.endBatchUpdates(); + + cr.dispatchSimpleEvent(this, 'change'); + }, + + /** + * Add tree nodes by parent id. + * This is used by cookies_view.js. + * @param {string} parentId Id of the parent node. + * @param {number} start The index at which to start inserting the nodes. + * @param {Array} nodesData Nodes data array. + */ + addByParentId: function(parentId, start, nodesData) { + var parent = parentId ? parentLookup[parentId] : this; + this.addByParent_(parent, start, nodesData); + }, + + /** + * Removes tree nodes by parent id. + * This is used by cookies_view.js. + * @param {string} parentId Id of the parent node. + * @param {number} start The index at which to start removing the nodes. + * @param {number} count Number of nodes to remove. + */ + removeByParentId: function(parentId, start, count) { + var parent = parentId ? parentLookup[parentId] : this; + if (!parent) + return; + + parent.startBatchUpdates(); + while (count-- > 0) + parent.remove(start); + parent.endBatchUpdates(); + + cr.dispatchSimpleEvent(this, 'change'); + }, + + /** + * Loads the immediate children of given parent node. + * This is used by cookies_view.js. + * @param {string} parentId Id of the parent node. + * @param {Array} children The immediate children of parent node. + */ + loadChildren: function(parentId, children) { + if (parentId) + delete lookupRequests[parentId]; + var parent = parentId ? parentLookup[parentId] : this; + if (!parent) + return; + + parent.startBatchUpdates(); + parent.clear(); + this.addByParent_(parent, 0, children); + parent.endBatchUpdates(); + }, + }; + + return { + CookiesList: CookiesList + }; +}); diff --git a/chrome/browser/resources/options/cookies_view.css b/chrome/browser/resources/options/cookies_view.css new file mode 100644 index 0000000..99d24e8 --- /dev/null +++ b/chrome/browser/resources/options/cookies_view.css @@ -0,0 +1,212 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +/* Styles for the cookies list page. */ +.cookies-view-page { + height: 90%; + margin-left: -15px; + width: 720px; +} + +/* Styles for the cookies list elements in cookies_view.html. */ +.cookies-list { + -webkit-box-flex: 1; + /* This property overrides the |min-height: 192px;| property above due to + * special behavior of the cookies list. */ + border: 1px solid #D9D9D9; + margin: 0; + margin-top: 5px; + min-height: 0; +} + +.cookies-list-content-area { + -webkit-box-orient: vertical; + display: -webkit-box; + overflow-y: hidden; +} + +.cookies-column-headers { + -webkit-box-align: baseline; + -webkit-box-orient: horizontal; + display: -webkit-box; + position: relative; + width: 100%; +} + +.cookies-column-headers > * { + display: block; +} + +.cookies-column-headers h3 { + font-size: 105%; + font-weight: bold; + margin: 10px 0; +} + +/* Notice the width and padding for these columns match up with those below. */ +.cookies-site-column { + -webkit-padding-start: 7px; + width: 14em; +} + +.cookies-data-column { + -webkit-box-flex: 1; + -webkit-padding-start: 7px; +} + +/* Enable animating the height of items. */ +list.cookie-list .deletable-item { + -webkit-transition: height 150ms ease-in-out; +} + +/* Disable webkit-box display. */ +list.cookie-list .deletable-item > :first-child { + display: block; +} + +/* Force the X for deleting an origin to stay at the top. */ +list.cookie-list > .deletable-item > .close-button { + position: absolute; + right: 2px; + top: 8px; +} + +html[dir=rtl] list.cookie-list > .deletable-item > .close-button { + left: 2px; + right: auto; +} + +/* Styles for the site (aka origin) and its summary. */ +.cookie-site { + /* Notice that the width, margin, and padding match up with those above. */ + -webkit-margin-end: 2px; + -webkit-padding-start: 5px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + width: 14em; +} + +list.cookie-list > .deletable-item[selected] .cookie-site { + -webkit-user-select: text; +} + +.cookie-data { + display: inline-block; + max-width: 410px; + overflow: hidden; + text-overflow: ellipsis; +} + +.cookie-size { + display: inline-block; + float: right; + margin-right: 0; +} + +list.cookie-list > .deletable-item[selected] .cookie-data { + -webkit-user-select: text; +} + + +/* Styles for the individual items (cookies, etc.). */ +.cookie-items { + /* Notice that the margin and padding match up with those above. */ + -webkit-margin-start: 14em; + -webkit-padding-start: 7px; + -webkit-transition: 150ms ease-in-out; + height: 0; + opacity: 0; + /* Make the cookie items wrap correctly. */ + white-space: normal; +} + +.measure-items .cookie-items { + -webkit-transition: none; + height: auto; + visibility: hidden; +} + +.show-items .cookie-items { + opacity: 1; +} + +.cookie-items .cookie-item { + background: rgb(224, 233, 245); + border: 1px solid rgb(131, 146, 174); + border-radius: 5px; + display: inline-block; + font-size: 85%; + height: auto; + margin: 2px 4px 2px 0; + max-width: 120px; + min-width: 40px; + overflow: hidden; + padding: 0 3px; + text-align: center; + text-overflow: ellipsis; +} + +.cookie-items .cookie-item:hover { + background: rgb(238, 243, 249); + border-color: rgb(100, 113, 135); +} + +.cookie-items .cookie-item[selected] { + background: rgb(245, 248, 248); + border-color: #B2B2B2; +} + +.cookie-items .cookie-item[selected]:hover { + background: rgb(245, 248, 248); + border-color: rgb(100, 113, 135); +} + +.cookie-items .cookie-item .protecting-app, +.cookie-data .protecting-app { + margin-bottom: -3px; + margin-left: 4px; +} + +/* Styles for the cookie details box. */ +.cookie-details { + background: rgb(245, 248, 248); + border: 1px solid #B2B2B2; + border-radius: 5px; + margin-top: 2px; + padding: 5px; +} + +list.cookie-list > .deletable-item[selected] .cookie-details { + -webkit-user-select: text; +} + +.cookie-details-table { + table-layout: fixed; + width: 100%; +} + +.cookie-details-label { + vertical-align: top; + white-space: pre; + width: 10em; +} + +.cookie-details-value { + word-wrap: break-word; +} + + +/* Styles specific to the app cookies window */ +#app-cookies-site-column { + width: 20em; +} + +.app-cookie-site { + width: 20em; +} + +.app-cookie-items { + -webkit-margin-start: 20em; +} diff --git a/chrome/browser/resources/options/cookies_view.html b/chrome/browser/resources/options/cookies_view.html new file mode 100644 index 0000000..2cc7b07 --- /dev/null +++ b/chrome/browser/resources/options/cookies_view.html @@ -0,0 +1,26 @@ + diff --git a/chrome/browser/resources/options/cookies_view.js b/chrome/browser/resources/options/cookies_view.js new file mode 100644 index 0000000..c80b93f --- /dev/null +++ b/chrome/browser/resources/options/cookies_view.js @@ -0,0 +1,144 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + + ///////////////////////////////////////////////////////////////////////////// + // CookiesView class: + + /** + * Encapsulated handling of the cookies and other site data page. + * @constructor + */ + function CookiesView(model) { + OptionsPage.call(this, 'cookies', + loadTimeData.getString('cookiesViewPageTabTitle'), + 'cookies-view-page'); + } + + cr.addSingletonGetter(CookiesView); + + CookiesView.prototype = { + __proto__: OptionsPage.prototype, + + /** + * The timer id of the timer set on search query change events. + * @type {number} + * @private + */ + queryDelayTimerId_: 0, + + /** + * The most recent search query, empty string if the query is empty. + * @type {string} + * @private + */ + lastQuery_: '', + + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.pageDiv.querySelector('.cookies-search-box').addEventListener( + 'search', this.handleSearchQueryChange_.bind(this)); + + this.pageDiv.querySelector('.remove-all-cookies-button').onclick = + function(e) { + chrome.send('removeAllCookies'); + }; + + var cookiesList = this.pageDiv.querySelector('.cookies-list'); + options.CookiesList.decorate(cookiesList); + + this.addEventListener('visibleChange', this.handleVisibleChange_); + + this.pageDiv.querySelector('.cookies-view-overlay-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + /** + * Clear search filter when the dialog is displayed. + * @inheritDoc + */ + didShowPage: function() { + this.pageDiv.querySelector('.cookies-search-box').value = ''; + }, + + /** + * Search cookie using text in |cookies-search-box|. + */ + searchCookie: function() { + this.queryDelayTimerId_ = 0; + var filter = this.pageDiv.querySelector('.cookies-search-box').value; + if (this.lastQuery_ != filter) { + this.lastQuery_ = filter; + chrome.send('updateCookieSearchResults', [filter]); + } + }, + + /** + * Handles search query changes. + * @param {!Event} e The event object. + * @private + */ + handleSearchQueryChange_: function(e) { + if (this.queryDelayTimerId_) + window.clearTimeout(this.queryDelayTimerId_); + + this.queryDelayTimerId_ = window.setTimeout( + this.searchCookie.bind(this), 500); + }, + + initialized_: false, + + /** + * Handler for OptionsPage's visible property change event. + * @param {Event} e Property change event. + * @private + */ + handleVisibleChange_: function(e) { + if (!this.visible) + return; + + // Inform the CookiesViewHandler whether we are operating in regular + // cookies dialog or the apps one. + chrome.send('setViewContext', [this.isAppContext()]); + + chrome.send('reloadCookies'); + + if (!this.initialized_) { + this.initialized_ = true; + this.searchCookie(); + } else { + this.pageDiv.querySelector('.cookies-list').redraw(); + } + + this.pageDiv.querySelector('.cookies-search-box').focus(); + }, + + isAppContext: function() { + return false; + }, + }; + + // CookiesViewHandler callbacks. + CookiesView.onTreeItemAdded = function(args) { + $('cookies-list').addByParentId(args[0], args[1], args[2]); + }; + + CookiesView.onTreeItemRemoved = function(args) { + $('cookies-list').removeByParentId(args[0], args[1], args[2]); + }; + + CookiesView.loadChildren = function(args) { + $('cookies-list').loadChildren(args[0], args[1]); + }; + + // Export + return { + CookiesView: CookiesView + }; + +}); diff --git a/chrome/browser/resources/options/cookies_view_app.html b/chrome/browser/resources/options/cookies_view_app.html new file mode 100644 index 0000000..e0db2aa --- /dev/null +++ b/chrome/browser/resources/options/cookies_view_app.html @@ -0,0 +1,26 @@ + diff --git a/chrome/browser/resources/options/cookies_view_app.js b/chrome/browser/resources/options/cookies_view_app.js new file mode 100644 index 0000000..b9ee641 --- /dev/null +++ b/chrome/browser/resources/options/cookies_view_app.js @@ -0,0 +1,48 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + ///////////////////////////////////////////////////////////////////////////// + // CookiesViewApp class: + + /** + * Encapsulated handling of app cookies and other data page. It + * derives from the regular CookiesView. + * @constructor + */ + function CookiesViewApp(model) { + options.OptionsPage.call(this, 'app-cookies', + loadTimeData.getString('cookiesViewPageTabTitle'), + 'app-cookies-view-page'); + } + + cr.addSingletonGetter(CookiesViewApp); + + CookiesViewApp.prototype = { + __proto__: options.CookiesView.prototype, + + isAppContext: function() { + return true; + }, + }; + + // CookiesViewHandler callbacks. + CookiesViewApp.onTreeItemAdded = function(args) { + $('app-cookies-list').addByParentId(args[0], args[1], args[2]); + }; + + CookiesViewApp.onTreeItemRemoved = function(args) { + $('app-cookies-list').removeByParentId(args[0], args[1], args[2]); + }; + + CookiesViewApp.loadChildren = function(args) { + $('app-cookies-list').loadChildren(args[0], args[1]); + }; + + // Export + return { + CookiesViewApp: CookiesViewApp + }; +}); diff --git a/chrome/browser/resources/options/deletable_item_list.js b/chrome/browser/resources/options/deletable_item_list.js new file mode 100644 index 0000000..8d79902 --- /dev/null +++ b/chrome/browser/resources/options/deletable_item_list.js @@ -0,0 +1,193 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var List = cr.ui.List; + /** @const */ var ListItem = cr.ui.ListItem; + + /** + * Creates a deletable list item, which has a button that will trigger a call + * to deleteItemAtIndex(index) in the list. + */ + var DeletableItem = cr.ui.define('li'); + + DeletableItem.prototype = { + __proto__: ListItem.prototype, + + /** + * The element subclasses should populate with content. + * @type {HTMLElement} + * @private + */ + contentElement_: null, + + /** + * The close button element. + * @type {HTMLElement} + * @private + */ + closeButtonElement_: null, + + /** + * Whether or not this item can be deleted. + * @type {boolean} + * @private + */ + deletable_: true, + + /** @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + + this.classList.add('deletable-item'); + + this.contentElement_ = this.ownerDocument.createElement('div'); + this.appendChild(this.contentElement_); + + this.closeButtonElement_ = this.ownerDocument.createElement('button'); + this.closeButtonElement_.className = + 'raw-button row-delete-button custom-appearance'; + this.closeButtonElement_.addEventListener('mousedown', + this.handleMouseDownUpOnClose_); + this.closeButtonElement_.addEventListener('mouseup', + this.handleMouseDownUpOnClose_); + this.closeButtonElement_.addEventListener('focus', + this.handleFocus_.bind(this)); + this.appendChild(this.closeButtonElement_); + }, + + /** + * Returns the element subclasses should add content to. + * @return {HTMLElement} The element subclasses should popuplate. + */ + get contentElement() { + return this.contentElement_; + }, + + /** + * Returns the close button element. + * @return {HTMLElement} The close | + + + diff --git a/chrome/browser/resources/options/font_settings.js b/chrome/browser/resources/options/font_settings.js new file mode 100644 index 0000000..df02798 --- /dev/null +++ b/chrome/browser/resources/options/font_settings.js @@ -0,0 +1,236 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + var OptionsPage = options.OptionsPage; + + /** + * FontSettings class + * Encapsulated handling of the 'Fonts and Encoding' page. + * @class + */ + function FontSettings() { + OptionsPage.call(this, + 'fonts', + loadTimeData.getString('fontSettingsPageTabTitle'), + 'font-settings'); + } + + cr.addSingletonGetter(FontSettings); + + FontSettings.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var standardFontRange = $('standard-font-size'); + standardFontRange.valueMap = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, + 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 48, 56, 64, 72]; + standardFontRange.continuous = false; + standardFontRange.notifyChange = this.standardRangeChanged_.bind(this); + standardFontRange.notifyPrefChange = + this.standardFontSizeChanged_.bind(this); + + var minimumFontRange = $('minimum-font-size'); + minimumFontRange.valueMap = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 20, 22, 24]; + minimumFontRange.continuous = false; + minimumFontRange.notifyChange = this.minimumRangeChanged_.bind(this); + minimumFontRange.notifyPrefChange = + this.minimumFontSizeChanged_.bind(this); + + var placeholder = loadTimeData.getString('fontSettingsPlaceholder'); + var elements = [$('standard-font-family'), $('serif-font-family'), + $('sans-serif-font-family'), $('fixed-font-family'), + $('font-encoding')]; + elements.forEach(function(el) { + el.appendChild(new Option(placeholder)); + el.setDisabled('noFontsAvailable', true); + }); + + $('font-settings-confirm').onclick = function() { + OptionsPage.closeOverlay(); + }; + }, + + /** + * Called by the options page when this page has been shown. + */ + didShowPage: function() { + // The fonts list may be large so we only load it when this page is + // loaded for the first time. This makes opening the options window + // faster and consume less memory if the user never opens the fonts + // dialog. + if (!this.hasShown) { + chrome.send('fetchFontsData'); + this.hasShown = true; + } + }, + + /** + * Called as the user changes the standard font size. This allows for + * reflecting the change in the UI before the preference has been changed. + * @param {Element} el The slider input element. + * @param {number} value The mapped value currently set by the slider. + * @private + */ + standardRangeChanged_: function(el, value) { + var fontSampleEl = $('standard-font-sample'); + this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, + true); + + fontSampleEl = $('serif-font-sample'); + this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, + true); + + fontSampleEl = $('sans-serif-font-sample'); + this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, + true); + + fontSampleEl = $('fixed-font-sample'); + this.setUpFontSample_(fontSampleEl, + value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, + fontSampleEl.style.fontFamily, false); + }, + + /** + * Sets the 'default_fixed_font_size' preference when the standard font + * size has been changed by the user. + * @param {Element} el The slider input element. + * @param {number} value The mapped value that has been saved. + * @private + */ + standardFontSizeChanged_: function(el, value) { + Preferences.setIntegerPref( + 'webkit.webprefs.default_fixed_font_size', + value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, ''); + }, + + /** + * Called as the user changes the miniumum font size. This allows for + * reflecting the change in the UI before the preference has been changed. + * @param {Element} el The slider input element. + * @param {number} value The mapped value currently set by the slider. + * @private + */ + minimumRangeChanged_: function(el, value) { + var fontSampleEl = $('minimum-font-sample'); + this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, + true); + }, + + /** + * Sets the 'minimum_logical_font_size' preference when the minimum font + * size has been changed by the user. + * @param {Element} el The slider input element. + * @param {number} value The mapped value that has been saved. + * @private + */ + minimumFontSizeChanged_: function(el, value) { + Preferences.setIntegerPref( + 'webkit.webprefs.minimum_logical_font_size', value, ''); + }, + + /** + * Sets the text, font size and font family of the sample text. + * @param {Element} el The div containing the sample text. + * @param {number} size The font size of the sample text. + * @param {string} font The font family of the sample text. + * @param {bool} showSize True if the font size should appear in the sample. + * @private + */ + setUpFontSample_: function(el, size, font, showSize) { + var prefix = showSize ? (size + ': ') : ''; + el.textContent = prefix + + loadTimeData.getString('fontSettingsLoremIpsum'); + el.style.fontSize = size + 'px'; + if (font) + el.style.fontFamily = font; + }, + + /** + * Populates a select list and selects the specified item. + * @param {Element} element The select element to populate. + * @param {Array} items The array of items from which to populate. + * @param {string} selectedValue The selected item. + * @private + */ + populateSelect_: function(element, items, selectedValue) { + // Remove any existing content. + element.textContent = ''; + + // Insert new child nodes into select element. + var value, text, selected, option; + for (var i = 0; i < items.length; i++) { + value = items[i][0]; + text = items[i][1]; + dir = items[i][2]; + if (text) { + selected = value == selectedValue; + var option = new Option(text, value, false, selected); + option.dir = dir; + element.appendChild(option); + } else { + element.appendChild(document.createElement('hr')); + } + } + + element.setDisabled('noFontsAvailable', false); + } + }; + + // Chrome callbacks + FontSettings.setFontsData = function(fonts, encodings, selectedValues) { + FontSettings.getInstance().populateSelect_($('standard-font-family'), fonts, + selectedValues[0]); + FontSettings.getInstance().populateSelect_($('serif-font-family'), fonts, + selectedValues[1]); + FontSettings.getInstance().populateSelect_($('sans-serif-font-family'), + fonts, selectedValues[2]); + FontSettings.getInstance().populateSelect_($('fixed-font-family'), fonts, + selectedValues[3]); + FontSettings.getInstance().populateSelect_($('font-encoding'), encodings, + selectedValues[4]); + }; + + FontSettings.setUpStandardFontSample = function(font, size) { + FontSettings.getInstance().setUpFontSample_($('standard-font-sample'), size, + font, true); + }; + + FontSettings.setUpSerifFontSample = function(font, size) { + FontSettings.getInstance().setUpFontSample_($('serif-font-sample'), size, + font, true); + }; + + FontSettings.setUpSansSerifFontSample = function(font, size) { + FontSettings.getInstance().setUpFontSample_($('sans-serif-font-sample'), + size, font, true); + }; + + FontSettings.setUpFixedFontSample = function(font, size) { + FontSettings.getInstance().setUpFontSample_($('fixed-font-sample'), + size, font, false); + }; + + FontSettings.setUpMinimumFontSample = function(size) { + // If size is less than 6, represent it as six in the sample to account + // for the minimum logical font size. + if (size < 6) + size = 6; + FontSettings.getInstance().setUpFontSample_($('minimum-font-sample'), size, + null, true); + }; + + // Export + return { + FontSettings: FontSettings + }; +}); + diff --git a/chrome/browser/resources/options/handler_options.css b/chrome/browser/resources/options/handler_options.css new file mode 100644 index 0000000..116e5e7 --- /dev/null +++ b/chrome/browser/resources/options/handler_options.css @@ -0,0 +1,55 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.handlers-column-headers { + display: -webkit-box; + font-size: 13px; + font-weight: bold; +} + +.handlers-type-column { + -webkit-margin-end: 10px; + -webkit-margin-start: 14px; + width: 100px; +} + +.handlers-site-column { + max-width: 180px; +} + +.handlers-site-column select { + max-width: 170px; +} + +.handlers-remove-column { + -webkit-box-flex: 1; +} + +.handlers-remove-link { + -webkit-transition: 150ms opacity; + color: #555; + cursor: pointer; + opacity: 0; + padding-left: 14px; + text-decoration: underline; +} + +div > .handlers-remove-column { + opacity: 0; +} + +div:not(.none):hover > .handlers-remove-column { + opacity: 1; +} + +#handlers { + min-height: 250px; +} + +#handler-options list { + border: solid 1px #D9D9D9; + border-radius: 2px; + margin-bottom: 10px; + margin-top: 4px; +} diff --git a/chrome/browser/resources/options/handler_options.html b/chrome/browser/resources/options/handler_options.html new file mode 100644 index 0000000..ffa95b7 --- /dev/null +++ b/chrome/browser/resources/options/handler_options.html @@ -0,0 +1,36 @@ + diff --git a/chrome/browser/resources/options/handler_options.js b/chrome/browser/resources/options/handler_options.js new file mode 100644 index 0000000..c3d641a --- /dev/null +++ b/chrome/browser/resources/options/handler_options.js @@ -0,0 +1,80 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + ///////////////////////////////////////////////////////////////////////////// + // HandlerOptions class: + + /** + * Encapsulated handling of handler options page. + * @constructor + */ + function HandlerOptions() { + this.activeNavTab = null; + OptionsPage.call(this, + 'handlers', + loadTimeData.getString('handlersPageTabTitle'), + 'handler-options'); + } + + cr.addSingletonGetter(HandlerOptions); + + HandlerOptions.prototype = { + __proto__: OptionsPage.prototype, + + /** + * The handlers list. + * @type {DeletableItemList} + * @private + */ + handlersList_: null, + + /** @inheritDoc */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.createHandlersList_(); + + $('handler-options-overlay-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + /** + * Creates, decorates and initializes the handlers list. + * @private + */ + createHandlersList_: function() { + this.handlersList_ = $('handlers-list'); + options.HandlersList.decorate(this.handlersList_); + this.handlersList_.autoExpands = true; + + this.ignoredHandlersList_ = $('ignored-handlers-list'); + options.IgnoredHandlersList.decorate(this.ignoredHandlersList_); + this.ignoredHandlersList_.autoExpands = true; + }, + }; + + /** + * Sets the list of handlers shown by the view. + * @param {Array} Handlers to be shown in the view. + */ + HandlerOptions.setHandlers = function(handlers) { + $('handlers-list').setHandlers(handlers); + }; + + /** + * Sets the list of ignored handlers shown by the view. + * @param {Array} Handlers to be shown in the view. + */ + HandlerOptions.setIgnoredHandlers = function(handlers) { + $('ignored-handlers-section').hidden = handlers.length == 0; + $('ignored-handlers-list').setHandlers(handlers); + }; + + return { + HandlerOptions: HandlerOptions + }; +}); diff --git a/chrome/browser/resources/options/handler_options_list.js b/chrome/browser/resources/options/handler_options_list.js new file mode 100644 index 0000000..93c139a --- /dev/null +++ b/chrome/browser/resources/options/handler_options_list.js @@ -0,0 +1,227 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var List = cr.ui.List; + /** @const */ var ListItem = cr.ui.ListItem; + /** @const */ var HandlerOptions = options.HandlerOptions; + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + + /** + * Creates a new ignored protocol / content handler list item. + * + * Accepts values in the form + * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'], + * @param {Object} entry A dictionary describing the handlers for a given + * protocol. + * @constructor + * @extends {cr.ui.DeletableItemList} + */ + function IgnoredHandlersListItem(entry) { + var el = cr.doc.createElement('div'); + el.dataItem = entry; + el.__proto__ = IgnoredHandlersListItem.prototype; + el.decorate(); + return el; + } + + IgnoredHandlersListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + // Protocol. + var protocolElement = document.createElement('div'); + protocolElement.textContent = this.dataItem[0]; + protocolElement.className = 'handlers-type-column'; + this.contentElement_.appendChild(protocolElement); + + // Site title. + var titleElement = document.createElement('div'); + titleElement.textContent = this.dataItem[2]; + titleElement.className = 'handlers-site-column'; + titleElement.title = this.dataItem[1]; + this.contentElement_.appendChild(titleElement); + }, + }; + + + var IgnoredHandlersList = cr.ui.define('list'); + + IgnoredHandlersList.prototype = { + __proto__: DeletableItemList.prototype, + + createItem: function(entry) { + return new IgnoredHandlersListItem(entry); + }, + + deleteItemAtIndex: function(index) { + chrome.send('removeIgnoredHandler', [this.dataModel.item(index)]); + }, + + /** + * The length of the list. + */ + get length() { + return this.dataModel.length; + }, + + /** + * Set the protocol handlers displayed by this list. See + * IgnoredHandlersListItem for an example of the format the list should + * take. + * + * @param {Object} list A list of ignored protocol handlers. + */ + setHandlers: function(list) { + this.dataModel = new ArrayDataModel(list); + }, + }; + + + + /** + * Creates a new protocol / content handler list item. + * + * Accepts values in the form + * { protocol: 'mailto', + * handlers: [ + * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'], + * ..., + * ], + * } + * @param {Object} entry A dictionary describing the handlers for a given + * protocol. + * @constructor + * @extends {cr.ui.ListItem} + */ + function HandlerListItem(entry) { + var el = cr.doc.createElement('div'); + el.dataItem = entry; + el.__proto__ = HandlerListItem.prototype; + el.decorate(); + return el; + } + + HandlerListItem.prototype = { + __proto__: ListItem.prototype, + + buildWidget_: function(data, delegate) { + // Protocol. + var protocolElement = document.createElement('div'); + protocolElement.textContent = data.protocol; + protocolElement.className = 'handlers-type-column'; + this.appendChild(protocolElement); + + // Handler selection. + var handlerElement = document.createElement('div'); + var selectElement = document.createElement('select'); + var defaultOptionElement = document.createElement('option'); + defaultOptionElement.selected = data.default_handler == -1; + defaultOptionElement.textContent = + loadTimeData.getString('handlers_none_handler'); + defaultOptionElement.value = -1; + selectElement.appendChild(defaultOptionElement); + + for (var i = 0; i < data.handlers.length; ++i) { + var optionElement = document.createElement('option'); + optionElement.selected = i == data.default_handler; + optionElement.textContent = data.handlers[i][2]; + optionElement.value = i; + selectElement.appendChild(optionElement); + } + + selectElement.addEventListener('change', function(e) { + var index = e.target.value; + if (index == -1) { + this.classList.add('none'); + delegate.clearDefault(data.protocol); + } else { + handlerElement.classList.remove('none'); + delegate.setDefault(data.handlers[index]); + } + }); + handlerElement.appendChild(selectElement); + handlerElement.className = 'handlers-site-column'; + if (data.default_handler == -1) + this.classList.add('none'); + this.appendChild(handlerElement); + + // Remove link. + var removeElement = document.createElement('div'); + removeElement.textContent = + loadTimeData.getString('handlers_remove_link'); + removeElement.addEventListener('click', function(e) { + var value = selectElement ? selectElement.value : 0; + delegate.removeHandler(value, data.handlers[value]); + }); + removeElement.className = 'handlers-remove-column handlers-remove-link'; + this.appendChild(removeElement); + }, + + /** @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + + var self = this; + var delegate = { + removeHandler: function(index, handler) { + chrome.send('removeHandler', [handler]); + }, + setDefault: function(handler) { + chrome.send('setDefault', [handler]); + }, + clearDefault: function(protocol) { + chrome.send('clearDefault', [protocol]); + }, + }; + + this.buildWidget_(this.dataItem, delegate); + }, + }; + + /** + * Create a new passwords list. + * @constructor + * @extends {cr.ui.List} + */ + var HandlersList = cr.ui.define('list'); + + HandlersList.prototype = { + __proto__: List.prototype, + + /** @inheritDoc */ + createItem: function(entry) { + return new HandlerListItem(entry); + }, + + /** + * The length of the list. + */ + get length() { + return this.dataModel.length; + }, + + /** + * Set the protocol handlers displayed by this list. + * See HandlerListItem for an example of the format the list should take. + * + * @param {Object} list A list of protocols with their registered handlers. + */ + setHandlers: function(list) { + this.dataModel = new ArrayDataModel(list); + }, + }; + + return { + IgnoredHandlersListItem: IgnoredHandlersListItem, + IgnoredHandlersList: IgnoredHandlersList, + HandlerListItem: HandlerListItem, + HandlersList: HandlersList, + }; +}); diff --git a/chrome/browser/resources/options/home_page_overlay.css b/chrome/browser/resources/options/home_page_overlay.css new file mode 100644 index 0000000..bcece11 --- /dev/null +++ b/chrome/browser/resources/options/home_page_overlay.css @@ -0,0 +1,9 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#homepage-url-field { + display: inline-block; + margin-left: 10px; + width: 225px; +} diff --git a/chrome/browser/resources/options/home_page_overlay.html b/chrome/browser/resources/options/home_page_overlay.html new file mode 100644 index 0000000..2329507 --- /dev/null +++ b/chrome/browser/resources/options/home_page_overlay.html @@ -0,0 +1,32 @@ + diff --git a/chrome/browser/resources/options/home_page_overlay.js b/chrome/browser/resources/options/home_page_overlay.js new file mode 100644 index 0000000..9753ab4 --- /dev/null +++ b/chrome/browser/resources/options/home_page_overlay.js @@ -0,0 +1,157 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var SettingsDialog = options.SettingsDialog; + + /** + * HomePageOverlay class + * Dialog that allows users to set the home page. + * @extends {SettingsDialog} + */ + function HomePageOverlay() { + SettingsDialog.call(this, 'homePageOverlay', + loadTimeData.getString('homePageOverlayTabTitle'), + 'home-page-overlay', + $('home-page-confirm'), $('home-page-cancel')); + } + + cr.addSingletonGetter(HomePageOverlay); + + HomePageOverlay.prototype = { + __proto__: SettingsDialog.prototype, + + /** + * An autocomplete list that can be attached to the home page URL field. + * @type {cr.ui.AutocompleteList} + * @private + */ + autocompleteList_: null, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + SettingsDialog.prototype.initializePage.call(this); + + var self = this; + $('homepage-use-ntp').onchange = this.updateHomePageInput_.bind(this); + $('homepage-use-url').onchange = this.updateHomePageInput_.bind(this); + + var urlField = $('homepage-url-field'); + urlField.addEventListener('keydown', function(event) { + // Focus the 'OK' button when the user hits enter since people expect + // feedback indicating that they are done editing. + if (event.keyIdentifier == 'Enter' && self.autocompleteList_.hidden) + $('home-page-confirm').focus(); + }); + urlField.addEventListener('change', this.updateFavicon_.bind(this)); + + var suggestionList = new cr.ui.AutocompleteList(); + suggestionList.autoExpands = true; + suggestionList.suggestionUpdateRequestCallback = + this.requestAutocompleteSuggestions_.bind(this); + $('home-page-overlay').appendChild(suggestionList); + this.autocompleteList_ = suggestionList; + + urlField.addEventListener('focus', function(event) { + self.autocompleteList_.attachToInput(urlField); + }); + urlField.addEventListener('blur', function(event) { + self.autocompleteList_.detach(); + }); + + // Text fields may change widths when the window changes size, so make + // sure the suggestion list stays in sync. + window.addEventListener('resize', function() { + self.autocompleteList_.syncWidthToInput(); + }); + }, + + /** @inheritDoc */ + didShowPage: function() { + this.updateHomePageInput_(); + this.updateFavicon_(); + }, + + /** + * Updates the state of the homepage text input. The input is enabled only + * if the |homepage-use-url| radio button is checked. + * @private + */ + updateHomePageInput_: function() { + var urlField = $('homepage-url-field'); + var homePageUseURL = $('homepage-use-url'); + urlField.setDisabled('radio-choice', !homePageUseURL.checked); + }, + + /** + * Updates the background of the url field to show the favicon for the + * URL that is currently typed in. + * @private + */ + updateFavicon_: function() { + var urlField = $('homepage-url-field'); + urlField.style.backgroundImage = url('chrome://favicon/' + + urlField.value); + }, + + /** + * Sends an asynchronous request for new autocompletion suggestions for the + * the given query. When new suggestions are available, the C++ handler will + * call updateAutocompleteSuggestions_. + * @param {string} query List of autocomplete suggestions. + * @private + */ + requestAutocompleteSuggestions_: function(query) { + chrome.send('requestAutocompleteSuggestionsForHomePage', [query]); + }, + + /** + * Updates the autocomplete suggestion list with the given entries. + * @param {Array} pages List of autocomplete suggestions. + * @private + */ + updateAutocompleteSuggestions_: function(suggestions) { + var list = this.autocompleteList_; + // If the trigger for this update was a value being selected from the + // current list, do nothing. + if (list.targetInput && list.selectedItem && + list.selectedItem['url'] == list.targetInput.value) + return; + list.suggestions = suggestions; + }, + + /** + * Sets the 'show home button' and 'home page is new tab page' preferences. + * (The home page url preference is set automatically by the SettingsDialog + * code.) + */ + handleConfirm: function() { + // Strip whitespace. + var urlField = $('homepage-url-field'); + var homePageValue = urlField.value.replace(/\s*/g, ''); + urlField.value = homePageValue; + + // Don't save an empty URL for the home page. If the user left the field + // empty, switch to the New Tab page. + if (!homePageValue) + $('homepage-use-ntp').checked = true; + + SettingsDialog.prototype.handleConfirm.call(this); + }, + }; + + HomePageOverlay.updateAutocompleteSuggestions = function() { + var instance = HomePageOverlay.getInstance(); + instance.updateAutocompleteSuggestions_.apply(instance, arguments); + }; + + // Export + return { + HomePageOverlay: HomePageOverlay + }; +}); diff --git a/chrome/browser/resources/options/import_data_overlay.css b/chrome/browser/resources/options/import_data_overlay.css new file mode 100644 index 0000000..6cb5262 --- /dev/null +++ b/chrome/browser/resources/options/import_data_overlay.css @@ -0,0 +1,27 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#import-from-div { + margin-bottom: 20px; +} + +#import-checkboxes > div:not(:first-child) { + -webkit-padding-start: 8px; + margin: 5px 0; +} + +#import-throbber { + margin: 4px 10px; + vertical-align: middle; + visibility: hidden; +} + +#import-success-header { + font-size: 1.2em; +} + +#import-success-image { + margin: 20px; + text-align: center; +} diff --git a/chrome/browser/resources/options/import_data_overlay.html b/chrome/browser/resources/options/import_data_overlay.html new file mode 100644 index 0000000..2f5880a --- /dev/null +++ b/chrome/browser/resources/options/import_data_overlay.html @@ -0,0 +1,71 @@ + diff --git a/chrome/browser/resources/options/import_data_overlay.js b/chrome/browser/resources/options/import_data_overlay.js new file mode 100644 index 0000000..1a38402 --- /dev/null +++ b/chrome/browser/resources/options/import_data_overlay.js @@ -0,0 +1,247 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + + /** + * ImportDataOverlay class + * Encapsulated handling of the 'Import Data' overlay page. + * @class + */ + function ImportDataOverlay() { + OptionsPage.call(this, + 'importData', + loadTimeData.getString('importDataOverlayTabTitle'), + 'import-data-overlay'); + } + + cr.addSingletonGetter(ImportDataOverlay); + + ImportDataOverlay.prototype = { + // Inherit from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + OptionsPage.prototype.initializePage.call(this); + + var self = this; + var checkboxes = + document.querySelectorAll('#import-checkboxes input[type=checkbox]'); + for (var i = 0; i < checkboxes.length; i++) { + checkboxes[i].onchange = function() { + self.validateCommitButton_(); + }; + } + + $('import-browsers').onchange = function() { + self.updateCheckboxes_(); + self.validateCommitButton_(); + }; + + $('import-data-commit').onclick = function() { + chrome.send('importData', [ + String($('import-browsers').selectedIndex), + String($('import-history').checked), + String($('import-favorites').checked), + String($('import-passwords').checked), + String($('import-search').checked)]); + }; + + $('import-data-cancel').onclick = function() { + ImportDataOverlay.dismiss(); + }; + + $('import-data-show-bookmarks-bar').onchange = function() { + // Note: The callback 'toggleShowBookmarksBar' is handled within the + // browser options handler -- rather than the import data handler -- + // as the implementation is shared by several clients. + chrome.send('toggleShowBookmarksBar'); + } + + $('import-data-confirm').onclick = function() { + ImportDataOverlay.dismiss(); + }; + + // Form controls are disabled until the profile list has been loaded. + self.setControlsSensitive_(false); + }, + + /** + * Set enabled and checked state of the commit button. + * @private + */ + validateCommitButton_: function() { + var somethingToImport = + $('import-history').checked || $('import-favorites').checked || + $('import-passwords').checked || $('import-search').checked; + $('import-data-commit').disabled = !somethingToImport; + }, + + /** + * Sets the sensitivity of all the checkboxes and the commit button. + * @private + */ + setControlsSensitive_: function(sensitive) { + var checkboxes = + document.querySelectorAll('#import-checkboxes input[type=checkbox]'); + for (var i = 0; i < checkboxes.length; i++) + this.setUpCheckboxState_(checkboxes[i], sensitive); + $('import-data-commit').disabled = !sensitive; + }, + + /** + * Set enabled and checked states a checkbox element. + * @param {Object} checkbox A checkbox element. + * @param {boolean} enabled The enabled state of the chekbox. + * @private + */ + setUpCheckboxState_: function(checkbox, enabled) { + checkbox.setDisabled('noProfileData', !enabled); + }, + + /** + * Update the enabled and checked states of all checkboxes. + * @private + */ + updateCheckboxes_: function() { + var index = $('import-browsers').selectedIndex; + var browserProfile; + if (this.browserProfiles.length > index) + browserProfile = this.browserProfiles[index]; + var importOptions = ['history', 'favorites', 'passwords', 'search']; + for (var i = 0; i < importOptions.length; i++) { + var checkbox = $('import-' + importOptions[i]); + var enable = browserProfile && browserProfile[importOptions[i]]; + this.setUpCheckboxState_(checkbox, enable); + } + }, + + /** + * Update the supported browsers popup with given entries. + * @param {array} browsers List of supported browsers name. + * @private + */ + updateSupportedBrowsers_: function(browsers) { + this.browserProfiles = browsers; + var browserSelect = $('import-browsers'); + browserSelect.remove(0); // Remove the 'Loading...' option. + browserSelect.textContent = ''; + var browserCount = browsers.length; + + if (browserCount == 0) { + var option = new Option(loadTimeData.getString('noProfileFound'), 0); + browserSelect.appendChild(option); + + this.setControlsSensitive_(false); + } else { + this.setControlsSensitive_(true); + for (var i = 0; i < browserCount; i++) { + var browser = browsers[i]; + var option = new Option(browser['name'], browser['index']); + browserSelect.appendChild(option); + } + + this.updateCheckboxes_(); + this.validateCommitButton_(); + } + }, + + /** + * Clear import prefs set when user checks/unchecks a checkbox so that each + * checkbox goes back to the default "checked" state (or alternatively, to + * the state set by a recommended policy). + * @private + */ + clearUserPrefs_: function() { + var importPrefs = ['import_history', + 'import_bookmarks', + 'import_saved_passwords', + 'import_search_engine']; + for (var i = 0; i < importPrefs.length; i++) + Preferences.clearPref(importPrefs[i], undefined); + }, + + /** + * Update the dialog layout to reflect success state. + * @param {boolean} success If true, show success dialog elements. + * @private + */ + updateSuccessState_: function(success) { + var sections = document.querySelectorAll('.import-data-configure'); + for (var i = 0; i < sections.length; i++) + sections[i].hidden = success; + + sections = document.querySelectorAll('.import-data-success'); + for (var i = 0; i < sections.length; i++) + sections[i].hidden = !success; + }, + }; + + ImportDataOverlay.clearUserPrefs = function() { + ImportDataOverlay.getInstance().clearUserPrefs_(); + }; + + /** + * Update the supported browsers popup with given entries. + * @param {array} list of supported browsers name. + */ + ImportDataOverlay.updateSupportedBrowsers = function(browsers) { + ImportDataOverlay.getInstance().updateSupportedBrowsers_(browsers); + }; + + /** + * Update the UI to reflect whether an import operation is in progress. + * @param {boolean} state True if an import operation is in progress. + */ + ImportDataOverlay.setImportingState = function(state) { + var checkboxes = + document.querySelectorAll('#import-checkboxes input[type=checkbox]'); + for (var i = 0; i < checkboxes.length; i++) + checkboxes[i].setDisabled('Importing', state); + if (!state) + ImportDataOverlay.getInstance().updateCheckboxes_(); + $('import-browsers').disabled = state; + $('import-throbber').style.visibility = state ? 'visible' : 'hidden'; + ImportDataOverlay.getInstance().validateCommitButton_(); + }; + + /** + * Remove the import overlay from display. + */ + ImportDataOverlay.dismiss = function() { + ImportDataOverlay.clearUserPrefs(); + OptionsPage.closeOverlay(); + }; + + /** + * Show a message confirming the success of the import operation. + */ + ImportDataOverlay.confirmSuccess = function() { + var showBookmarksMessage = $('import-favorites').checked; + ImportDataOverlay.setImportingState(false); + $('import-find-your-bookmarks').hidden = !showBookmarksMessage; + ImportDataOverlay.getInstance().updateSuccessState_(true); + }; + + /** + * Show the import data overlay. + */ + ImportDataOverlay.show = function() { + // Make sure that any previous import success message is hidden, and + // we're showing the UI to import further data. + ImportDataOverlay.getInstance().updateSuccessState_(false); + + OptionsPage.navigateToPage('importData'); + }; + + // Export + return { + ImportDataOverlay: ImportDataOverlay + }; +}); diff --git a/chrome/browser/resources/options/inline_editable_list.js b/chrome/browser/resources/options/inline_editable_list.js new file mode 100644 index 0000000..c9d6a93 --- /dev/null +++ b/chrome/browser/resources/options/inline_editable_list.js @@ -0,0 +1,415 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + + /** + * Creates a new list item with support for inline editing. + * @constructor + * @extends {options.DeletableListItem} + */ + function InlineEditableItem() { + var el = cr.doc.createElement('div'); + InlineEditableItem.decorate(el); + return el; + } + + /** + * Decorates an element as a inline-editable list item. Note that this is + * a subclass of DeletableItem. + * @param {!HTMLElement} el The element to decorate. + */ + InlineEditableItem.decorate = function(el) { + el.__proto__ = InlineEditableItem.prototype; + el.decorate(); + }; + + InlineEditableItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * Whether or not this item can be edited. + * @type {boolean} + * @private + */ + editable_: true, + + /** + * Whether or not this is a placeholder for adding a new item. + * @type {boolean} + * @private + */ + isPlaceholder_: false, + + /** + * Fields associated with edit mode. + * @type {array} + * @private + */ + editFields_: null, + + /** + * Whether or not the current edit should be considered cancelled, rather + * than committed, when editing ends. + * @type {boolean} + * @private + */ + editCancelled_: true, + + /** + * The editable item corresponding to the last click, if any. Used to decide + * initial focus when entering edit mode. + * @type {HTMLElement} + * @private + */ + editClickTarget_: null, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + this.editFields_ = []; + this.addEventListener('mousedown', this.handleMouseDown_); + this.addEventListener('keydown', this.handleKeyDown_); + this.addEventListener('leadChange', this.handleLeadChange_); + }, + + /** @inheritDoc */ + selectionChanged: function() { + this.updateEditState(); + }, + + /** + * Called when this element gains or loses 'lead' status. Updates editing + * mode accordingly. + * @private + */ + handleLeadChange_: function() { + this.updateEditState(); + }, + + /** + * Updates the edit state based on the current selected and lead states. + */ + updateEditState: function() { + if (this.editable) + this.editing = this.selected && this.lead; + }, + + /** + * Whether the user is currently editing the list item. + * @type {boolean} + */ + get editing() { + return this.hasAttribute('editing'); + }, + set editing(editing) { + if (this.editing == editing) + return; + + if (editing) + this.setAttribute('editing', ''); + else + this.removeAttribute('editing'); + + if (editing) { + this.editCancelled_ = false; + + cr.dispatchSimpleEvent(this, 'edit', true); + + var focusElement = this.editClickTarget_ || this.initialFocusElement; + this.editClickTarget_ = null; + + // When this is called in response to the selectedChange event, + // the list grabs focus immediately afterwards. Thus we must delay + // our focus grab. + var self = this; + if (focusElement) { + window.setTimeout(function() { + // Make sure we are still in edit mode by the time we execute. + if (self.editing) { + focusElement.focus(); + focusElement.select(); + } + }, 50); + } + } else { + if (!this.editCancelled_ && this.hasBeenEdited && + this.currentInputIsValid) { + if (this.isPlaceholder) + this.parentNode.focusPlaceholder = true; + + this.updateStaticValues_(); + cr.dispatchSimpleEvent(this, 'commitedit', true); + } else { + this.resetEditableValues_(); + cr.dispatchSimpleEvent(this, 'canceledit', true); + } + } + }, + + /** + * Whether the item is editable. + * @type {boolean} + */ + get editable() { + return this.editable_; + }, + set editable(editable) { + this.editable_ = editable; + if (!editable) + this.editing = false; + }, + + /** + * Whether the item is a new item placeholder. + * @type {boolean} + */ + get isPlaceholder() { + return this.isPlaceholder_; + }, + set isPlaceholder(isPlaceholder) { + this.isPlaceholder_ = isPlaceholder; + if (isPlaceholder) + this.deletable = false; + }, + + /** + * The HTML element that should have focus initially when editing starts, + * if a specific element wasn't clicked. + * Defaults to the first element; can be overridden by subclasses if + * a different element should be focused. + * @type {HTMLElement} + */ + get initialFocusElement() { + return this.contentElement.querySelector('input'); + }, + + /** + * Whether the input in currently valid to submit. If this returns false + * when editing would be submitted, either editing will not be ended, + * or it will be cancelled, depending on the context. + * Can be overridden by subclasses to perform input validation. + * @type {boolean} + */ + get currentInputIsValid() { + return true; + }, + + /** + * Returns true if the item has been changed by an edit. + * Can be overridden by subclasses to return false when nothing has changed + * to avoid unnecessary commits. + * @type {boolean} + */ + get hasBeenEdited() { + return true; + }, + + /** + * Returns a div containing an , as well as static text if + * isPlaceholder is not true. + * @param {string} text The text of the cell. + * @return {HTMLElement} The HTML element for the cell. + * @private + */ + createEditableTextCell: function(text) { + var container = this.ownerDocument.createElement('div'); + + if (!this.isPlaceholder) { + var textEl = this.ownerDocument.createElement('div'); + textEl.className = 'static-text'; + textEl.textContent = text; + textEl.setAttribute('displaymode', 'static'); + container.appendChild(textEl); + } + + var inputEl = this.ownerDocument.createElement('input'); + inputEl.type = 'text'; + inputEl.value = text; + if (!this.isPlaceholder) { + inputEl.setAttribute('displaymode', 'edit'); + inputEl.staticVersion = textEl; + } else { + // At this point |this| is not attached to the parent list yet, so give + // a short timeout in order for the attachment to occur. + var self = this; + window.setTimeout(function() { + var list = self.parentNode; + if (list && list.focusPlaceholder) { + list.focusPlaceholder = false; + if (list.shouldFocusPlaceholder()) + inputEl.focus(); + } + }, 50); + } + + inputEl.addEventListener('focus', this.handleFocus_.bind(this)); + container.appendChild(inputEl); + this.editFields_.push(inputEl); + + return container; + }, + + /** + * Resets the editable version of any controls created by createEditable* + * to match the static text. + * @private + */ + resetEditableValues_: function() { + var editFields = this.editFields_; + for (var i = 0; i < editFields.length; i++) { + var staticLabel = editFields[i].staticVersion; + if (!staticLabel && !this.isPlaceholder) + continue; + + if (editFields[i].tagName == 'INPUT') { + editFields[i].value = + this.isPlaceholder ? '' : staticLabel.textContent; + } + // Add more tag types here as new createEditable* methods are added. + + editFields[i].setCustomValidity(''); + } + }, + + /** + * Sets the static version of any controls created by createEditable* + * to match the current value of the editable version. Called on commit so + * that there's no flicker of the old value before the model updates. + * @private + */ + updateStaticValues_: function() { + var editFields = this.editFields_; + for (var i = 0; i < editFields.length; i++) { + var staticLabel = editFields[i].staticVersion; + if (!staticLabel) + continue; + + if (editFields[i].tagName == 'INPUT') + staticLabel.textContent = editFields[i].value; + // Add more tag types here as new createEditable* methods are added. + } + }, + + /** + * Called when a key is pressed. Handles committing and canceling edits. + * @param {Event} e The key down event. + * @private + */ + handleKeyDown_: function(e) { + if (!this.editing) + return; + + var endEdit = false; + switch (e.keyIdentifier) { + case 'U+001B': // Esc + this.editCancelled_ = true; + endEdit = true; + break; + case 'Enter': + if (this.currentInputIsValid) + endEdit = true; + break; + } + + if (endEdit) { + // Blurring will trigger the edit to end; see InlineEditableItemList. + this.ownerDocument.activeElement.blur(); + // Make sure that handled keys aren't passed on and double-handled. + // (e.g., esc shouldn't both cancel an edit and close a subpage) + e.stopPropagation(); + } + }, + + /** + * Called when the list item is clicked. If the click target corresponds to + * an editable item, stores that item to focus when edit mode is started. + * @param {Event} e The mouse down event. + * @private + */ + handleMouseDown_: function(e) { + if (!this.editable || this.editing) + return; + + var clickTarget = e.target; + var editFields = this.editFields_; + for (var i = 0; i < editFields.length; i++) { + if (editFields[i] == clickTarget || + editFields[i].staticVersion == clickTarget) { + this.editClickTarget_ = editFields[i]; + return; + } + } + }, + }; + + /** + * Takes care of committing changes to inline editable list items when the + * window loses focus. + */ + function handleWindowBlurs() { + window.addEventListener('blur', function(e) { + var itemAncestor = findAncestor(document.activeElement, function(node) { + return node instanceof InlineEditableItem; + }); + if (itemAncestor) + document.activeElement.blur(); + }); + } + handleWindowBlurs(); + + var InlineEditableItemList = cr.ui.define('list'); + + InlineEditableItemList.prototype = { + __proto__: DeletableItemList.prototype, + + /** + * Focuses the input element of the placeholder if true. + * @type {boolean} + */ + focusPlaceholder: false, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.setAttribute('inlineeditable', ''); + this.addEventListener('hasElementFocusChange', + this.handleListFocusChange_); + }, + + /** + * Called when the list hierarchy as a whole loses or gains focus; starts + * or ends editing for the lead item if necessary. + * @param {Event} e The change event. + * @private + */ + handleListFocusChange_: function(e) { + var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex); + if (leadItem) { + if (e.newValue) + leadItem.updateEditState(); + else + leadItem.editing = false; + } + }, + + /** + * May be overridden by subclasses to disable focusing the placeholder. + * @return {boolean} True if the placeholder element should be focused on + * edit commit. + */ + shouldFocusPlaceholder: function() { + return true; + }, + }; + + // Export + return { + InlineEditableItem: InlineEditableItem, + InlineEditableItemList: InlineEditableItemList, + }; +}); diff --git a/chrome/browser/resources/options/instant_confirm_overlay.css b/chrome/browser/resources/options/instant_confirm_overlay.css new file mode 100644 index 0000000..3f6d77f --- /dev/null +++ b/chrome/browser/resources/options/instant_confirm_overlay.css @@ -0,0 +1,16 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#instantConfirmOverlay { + width: 500px; +} + +#instant-action-spacer { + -webkit-box-flex: 1; +} + +#instant-confirm-text { + white-space: pre-line; +} diff --git a/chrome/browser/resources/options/instant_confirm_overlay.html b/chrome/browser/resources/options/instant_confirm_overlay.html new file mode 100644 index 0000000..54653b2 --- /dev/null +++ b/chrome/browser/resources/options/instant_confirm_overlay.html @@ -0,0 +1,17 @@ + diff --git a/chrome/browser/resources/options/instant_confirm_overlay.js b/chrome/browser/resources/options/instant_confirm_overlay.js new file mode 100644 index 0000000..6cadd5c --- /dev/null +++ b/chrome/browser/resources/options/instant_confirm_overlay.js @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var SettingsDialog = options.SettingsDialog; + + /* + * InstantConfirmOverlay class + * Dialog to confirm that the user really wants to enable Chrome Instant. + * @extends {SettingsDialog} + */ + function InstantConfirmOverlay() { + SettingsDialog.call(this, + 'instantConfirm', + loadTimeData.getString('instantConfirmTitle'), + 'instantConfirmOverlay', + $('instantConfirmOk'), + $('instantConfirmCancel')); + }; + + cr.addSingletonGetter(InstantConfirmOverlay); + + InstantConfirmOverlay.prototype = { + __proto__: SettingsDialog.prototype, + + /** @inheritDoc */ + initializePage: function() { + SettingsDialog.prototype.initializePage.call(this); + }, + + /** @inheritDoc */ + handleConfirm: function() { + SettingsDialog.prototype.handleConfirm.call(this); + Preferences.setBooleanPref('instant.confirm_dialog_shown', true); + Preferences.setBooleanPref('instant.enabled', true); + }, + + /** @inheritDoc */ + handleCancel: function() { + SettingsDialog.prototype.handleCancel.call(this); + $('instant-enabled-control').checked = false; + }, + }; + + // Export + return { + InstantConfirmOverlay: InstantConfirmOverlay + }; +}); diff --git a/chrome/browser/resources/options/language_add_language_overlay.html b/chrome/browser/resources/options/language_add_language_overlay.html new file mode 100644 index 0000000..45a68c0 --- /dev/null +++ b/chrome/browser/resources/options/language_add_language_overlay.html @@ -0,0 +1,26 @@ + diff --git a/chrome/browser/resources/options/language_add_language_overlay.js b/chrome/browser/resources/options/language_add_language_overlay.js new file mode 100644 index 0000000..d5ebf93 --- /dev/null +++ b/chrome/browser/resources/options/language_add_language_overlay.js @@ -0,0 +1,73 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/////////////////////////////////////////////////////////////////////////////// +// AddLanguageOverlay class: + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * Encapsulated handling of ChromeOS add language overlay page. + * @constructor + */ + function AddLanguageOverlay() { + OptionsPage.call(this, 'addLanguage', + loadTimeData.getString('add_button'), + 'add-language-overlay-page'); + } + + cr.addSingletonGetter(AddLanguageOverlay); + + AddLanguageOverlay.prototype = { + // Inherit AddLanguageOverlay from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * Initializes AddLanguageOverlay page. + * Calls base class implementation to starts preference initialization. + */ + initializePage: function() { + // Call base class implementation to starts preference initialization. + OptionsPage.prototype.initializePage.call(this); + + // Set up the cancel button. + $('add-language-overlay-cancel-button').onclick = function(e) { + OptionsPage.closeOverlay(); + }; + + // Create the language list with which users can add a language. + var addLanguageList = $('add-language-overlay-language-list'); + var languageListData = loadTimeData.getValue('languageList'); + for (var i = 0; i < languageListData.length; i++) { + var language = languageListData[i]; + var displayText = language.displayName; + // If the native name is different, add it. + if (language.displayName != language.nativeDisplayName) { + displayText += ' - ' + language.nativeDisplayName; + } + + if (cr.isChromeOS) { + var button = document.createElement('button'); + button.className = 'link-button'; + button.textContent = displayText; + button.languageCode = language.code; + var li = document.createElement('li'); + li.languageCode = language.code; + li.appendChild(button); + addLanguageList.appendChild(li); + } else { + var option = document.createElement('option'); + option.value = language.code; + option.textContent = displayText; + addLanguageList.appendChild(option); + } + } + }, + }; + + return { + AddLanguageOverlay: AddLanguageOverlay + }; +}); diff --git a/chrome/browser/resources/options/language_list.js b/chrome/browser/resources/options/language_list.js new file mode 100644 index 0000000..e036490 --- /dev/null +++ b/chrome/browser/resources/options/language_list.js @@ -0,0 +1,468 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + /** @const */ var List = cr.ui.List; + /** @const */ var ListItem = cr.ui.ListItem; + /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; + + /** + * Creates a new Language list item. + * @param {Object} languageInfo The information of the language. + * @constructor + * @extends {DeletableItem.ListItem} + */ + function LanguageListItem(languageInfo) { + var el = cr.doc.createElement('li'); + el.__proto__ = LanguageListItem.prototype; + el.language_ = languageInfo; + el.decorate(); + return el; + }; + + LanguageListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * The language code of this language. + * @type {String} + * @private + */ + languageCode_: null, + + /** @inheritDoc */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + var languageCode = this.language_.code; + var languageOptions = options.LanguageOptions.getInstance(); + this.deletable = languageOptions.languageIsDeletable(languageCode); + this.languageCode = languageCode; + this.languageName = cr.doc.createElement('div'); + this.languageName.className = 'language-name'; + this.languageName.dir = this.language_.textDirection; + this.languageName.textContent = this.language_.displayName; + this.contentElement.appendChild(this.languageName); + this.title = this.language_.nativeDisplayName; + this.draggable = true; + }, + }; + + /** + * Creates a new language list. + * @param {Object=} opt_propertyBag Optional properties. + * @constructor + * @extends {cr.ui.List} + */ + var LanguageList = cr.ui.define('list'); + + /** + * Gets information of a language from the given language code. + * @param {string} languageCode Language code (ex. "fr"). + */ + LanguageList.getLanguageInfoFromLanguageCode = function(languageCode) { + // Build the language code to language info dictionary at first time. + if (!this.languageCodeToLanguageInfo_) { + this.languageCodeToLanguageInfo_ = {}; + var languageList = loadTimeData.getValue('languageList'); + for (var i = 0; i < languageList.length; i++) { + var languageInfo = languageList[i]; + this.languageCodeToLanguageInfo_[languageInfo.code] = languageInfo; + } + } + + return this.languageCodeToLanguageInfo_[languageCode]; + } + + /** + * Returns true if the given language code is valid. + * @param {string} languageCode Language code (ex. "fr"). + */ + LanguageList.isValidLanguageCode = function(languageCode) { + // Having the display name for the language code means that the + // language code is valid. + if (LanguageList.getLanguageInfoFromLanguageCode(languageCode)) { + return true; + } + return false; + } + + LanguageList.prototype = { + __proto__: DeletableItemList.prototype, + + // The list item being dragged. + draggedItem: null, + // The drop position information: "below" or "above". + dropPos: null, + // The preference is a CSV string that describes preferred languages + // in Chrome OS. The language list is used for showing the language + // list in "Language and Input" options page. + preferredLanguagesPref: 'settings.language.preferred_languages', + // The preference is a CSV string that describes accept languages used + // for content negotiation. To be more precise, the list will be used + // in "Accept-Language" header in HTTP requests. + acceptLanguagesPref: 'intl.accept_languages', + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.selectionModel = new ListSingleSelectionModel; + + // HACK(arv): http://crbug.com/40902 + window.addEventListener('resize', this.redraw.bind(this)); + + // Listen to pref change. + if (cr.isChromeOS) { + Preferences.getInstance().addEventListener(this.preferredLanguagesPref, + this.handlePreferredLanguagesPrefChange_.bind(this)); + } else { + Preferences.getInstance().addEventListener(this.acceptLanguagesPref, + this.handleAcceptLanguagesPrefChange_.bind(this)); + } + + // Listen to drag and drop events. + this.addEventListener('dragstart', this.handleDragStart_.bind(this)); + this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); + this.addEventListener('dragover', this.handleDragOver_.bind(this)); + this.addEventListener('drop', this.handleDrop_.bind(this)); + this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); + }, + + createItem: function(languageCode) { + languageInfo = LanguageList.getLanguageInfoFromLanguageCode(languageCode); + return new LanguageListItem(languageInfo); + }, + + /* + * For each item, determines whether it's deletable. + */ + updateDeletable: function() { + var items = this.items; + for (var i = 0; i < items.length; ++i) { + var item = items[i]; + var languageCode = item.languageCode; + var languageOptions = options.LanguageOptions.getInstance(); + item.deletable = languageOptions.languageIsDeletable(languageCode); + } + }, + + /* + * Adds a language to the language list. + * @param {string} languageCode language code (ex. "fr"). + */ + addLanguage: function(languageCode) { + // It shouldn't happen but ignore the language code if it's + // null/undefined, or already present. + if (!languageCode || this.dataModel.indexOf(languageCode) >= 0) { + return; + } + this.dataModel.push(languageCode); + // Select the last item, which is the language added. + this.selectionModel.selectedIndex = this.dataModel.length - 1; + + this.savePreference_(); + }, + + /* + * Gets the language codes of the currently listed languages. + */ + getLanguageCodes: function() { + return this.dataModel.slice(); + }, + + /* + * Gets the language code of the selected language. + */ + getSelectedLanguageCode: function() { + return this.selectedItem; + }, + + /* + * Selects the language by the given language code. + * @returns {boolean} True if the operation is successful. + */ + selectLanguageByCode: function(languageCode) { + var index = this.dataModel.indexOf(languageCode); + if (index >= 0) { + this.selectionModel.selectedIndex = index; + return true; + } + return false; + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + if (index >= 0) { + this.dataModel.splice(index, 1); + // Once the selected item is removed, there will be no selected item. + // Select the item pointed by the lead index. + index = this.selectionModel.leadIndex; + this.savePreference_(); + } + return index; + }, + + /* + * Computes the target item of drop event. + * @param {Event} e The drop or dragover event. + * @private + */ + getTargetFromDropEvent_: function(e) { + var target = e.target; + // e.target may be an inner element of the list item + while (target != null && !(target instanceof ListItem)) { + target = target.parentNode; + } + return target; + }, + + /* + * Handles the dragstart event. + * @param {Event} e The dragstart event. + * @private + */ + handleDragStart_: function(e) { + var target = e.target; + // ListItem should be the only draggable element type in the page, + // but just in case. + if (target instanceof ListItem) { + this.draggedItem = target; + e.dataTransfer.effectAllowed = 'move'; + // We need to put some kind of data in the drag or it will be + // ignored. Use the display name in case the user drags to a text + // field or the desktop. + e.dataTransfer.setData('text/plain', target.title); + } + }, + + /* + * Handles the dragenter event. + * @param {Event} e The dragenter event. + * @private + */ + handleDragEnter_: function(e) { + e.preventDefault(); + }, + + /* + * Handles the dragover event. + * @param {Event} e The dragover event. + * @private + */ + handleDragOver_: function(e) { + var dropTarget = this.getTargetFromDropEvent_(e); + // Determines whether the drop target is to accept the drop. + // The drop is only successful on another ListItem. + if (!(dropTarget instanceof ListItem) || + dropTarget == this.draggedItem) { + this.hideDropMarker_(); + return; + } + // Compute the drop postion. Should we move the dragged item to + // below or above the drop target? + var rect = dropTarget.getBoundingClientRect(); + var dy = e.clientY - rect.top; + var yRatio = dy / rect.height; + var dropPos = yRatio <= .5 ? 'above' : 'below'; + this.dropPos = dropPos; + this.showDropMarker_(dropTarget, dropPos); + e.preventDefault(); + }, + + /* + * Handles the drop event. + * @param {Event} e The drop event. + * @private + */ + handleDrop_: function(e) { + var dropTarget = this.getTargetFromDropEvent_(e); + this.hideDropMarker_(); + + // Delete the language from the original position. + var languageCode = this.draggedItem.languageCode; + var originalIndex = this.dataModel.indexOf(languageCode); + this.dataModel.splice(originalIndex, 1); + // Insert the language to the new position. + var newIndex = this.dataModel.indexOf(dropTarget.languageCode); + if (this.dropPos == 'below') + newIndex += 1; + this.dataModel.splice(newIndex, 0, languageCode); + // The cursor should move to the moved item. + this.selectionModel.selectedIndex = newIndex; + // Save the preference. + this.savePreference_(); + }, + + /* + * Handles the dragleave event. + * @param {Event} e The dragleave event + * @private + */ + handleDragLeave_: function(e) { + this.hideDropMarker_(); + }, + + /* + * Shows and positions the marker to indicate the drop target. + * @param {HTMLElement} target The current target list item of drop + * @param {string} pos 'below' or 'above' + * @private + */ + showDropMarker_: function(target, pos) { + window.clearTimeout(this.hideDropMarkerTimer_); + var marker = $('language-options-list-dropmarker'); + var rect = target.getBoundingClientRect(); + var markerHeight = 8; + if (pos == 'above') { + marker.style.top = (rect.top - markerHeight / 2) + 'px'; + } else { + marker.style.top = (rect.bottom - markerHeight / 2) + 'px'; + } + marker.style.width = rect.width + 'px'; + marker.style.left = rect.left + 'px'; + marker.style.display = 'block'; + }, + + /* + * Hides the drop marker. + * @private + */ + hideDropMarker_: function() { + // Hide the marker in a timeout to reduce flickering as we move between + // valid drop targets. + window.clearTimeout(this.hideDropMarkerTimer_); + this.hideDropMarkerTimer_ = window.setTimeout(function() { + $('language-options-list-dropmarker').style.display = ''; + }, 100); + }, + + /** + * Handles preferred languages pref change. + * @param {Event} e The change event object. + * @private + */ + handlePreferredLanguagesPrefChange_: function(e) { + var languageCodesInCsv = e.value.value; + var languageCodes = languageCodesInCsv.split(','); + + // Add the UI language to the initial list of languages. This is to avoid + // a bug where the UI language would be removed from the preferred + // language list by sync on first login. + // See: crosbug.com/14283 + languageCodes.push(navigator.language); + languageCodes = this.filterBadLanguageCodes_(languageCodes); + this.load_(languageCodes); + }, + + /** + * Handles accept languages pref change. + * @param {Event} e The change event object. + * @private + */ + handleAcceptLanguagesPrefChange_: function(e) { + var languageCodesInCsv = e.value.value; + var languageCodes = this.filterBadLanguageCodes_( + languageCodesInCsv.split(',')); + this.load_(languageCodes); + }, + + /** + * Loads given language list. + * @param {Array} languageCodes List of language codes. + * @private + */ + load_: function(languageCodes) { + // Preserve the original selected index. See comments below. + var originalSelectedIndex = (this.selectionModel ? + this.selectionModel.selectedIndex : -1); + this.dataModel = new ArrayDataModel(languageCodes); + if (originalSelectedIndex >= 0 && + originalSelectedIndex < this.dataModel.length) { + // Restore the original selected index if the selected index is + // valid after the data model is loaded. This is neeeded to keep + // the selected language after the languge is added or removed. + this.selectionModel.selectedIndex = originalSelectedIndex; + // The lead index should be updated too. + this.selectionModel.leadIndex = originalSelectedIndex; + } else if (this.dataModel.length > 0) { + // Otherwise, select the first item if it's not empty. + // Note that ListSingleSelectionModel won't select an item + // automatically, hence we manually select the first item here. + this.selectionModel.selectedIndex = 0; + } + }, + + /** + * Saves the preference. + */ + savePreference_: function() { + // Encode the language codes into a CSV string. + if (cr.isChromeOS) + Preferences.setStringPref(this.preferredLanguagesPref, + this.dataModel.slice().join(',')); + // Save the same language list as accept languages preference as + // well, but we need to expand the language list, to make it more + // acceptable. For instance, some web sites don't understand 'en-US' + // but 'en'. See crosbug.com/9884. + var acceptLanguages = this.expandLanguageCodes(this.dataModel.slice()); + Preferences.setStringPref(this.acceptLanguagesPref, + acceptLanguages.join(',')); + cr.dispatchSimpleEvent(this, 'save'); + }, + + /** + * Expands language codes to make these more suitable for Accept-Language. + * Example: ['en-US', 'ja', 'en-CA'] => ['en-US', 'en', 'ja', 'en-CA']. + * 'en' won't appear twice as this function eliminates duplicates. + * @param {Array} languageCodes List of language codes. + * @private + */ + expandLanguageCodes: function(languageCodes) { + var expandedLanguageCodes = []; + var seen = {}; // Used to eliminiate duplicates. + for (var i = 0; i < languageCodes.length; i++) { + var languageCode = languageCodes[i]; + if (!(languageCode in seen)) { + expandedLanguageCodes.push(languageCode); + seen[languageCode] = true; + } + var parts = languageCode.split('-'); + if (!(parts[0] in seen)) { + expandedLanguageCodes.push(parts[0]); + seen[parts[0]] = true; + } + } + return expandedLanguageCodes; + }, + + /** + * Filters bad language codes in case bad language codes are + * stored in the preference. Removes duplicates as well. + * @param {Array} languageCodes List of language codes. + * @private + */ + filterBadLanguageCodes_: function(languageCodes) { + var filteredLanguageCodes = []; + var seen = {}; + for (var i = 0; i < languageCodes.length; i++) { + // Check if the the language code is valid, and not + // duplicate. Otherwise, skip it. + if (LanguageList.isValidLanguageCode(languageCodes[i]) && + !(languageCodes[i] in seen)) { + filteredLanguageCodes.push(languageCodes[i]); + seen[languageCodes[i]] = true; + } + } + return filteredLanguageCodes; + }, + }; + + return { + LanguageList: LanguageList, + LanguageListItem: LanguageListItem + }; +}); diff --git a/chrome/browser/resources/options/language_options.css b/chrome/browser/resources/options/language_options.css new file mode 100644 index 0000000..470788d --- /dev/null +++ b/chrome/browser/resources/options/language_options.css @@ -0,0 +1,215 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.language-options { + display: -webkit-box; + margin: 10px 0; +} + +.language-options-lower-left button, +.language-options-right button { + min-width: 70px; +} + +.language-options h3 { + -webkit-margin-start: 12px; + font-size: 100%; + font-weight: bold; + margin-bottom: 12px; + margin-top: 12px; +} + +.language-options-contents { + padding: 0 12px 4px; +} + +.language-options-contents span:not(.input-method-label) { + display: inline-block; + margin: 1px; + padding: 0.42em 10px; +} + +.language-options-header, +.language-options-footer { + line-height: 1.2em; + margin: 10px 0; +} + +.language-options-left, +.language-options-right { + border: 1px solid #ccc; + height: 400px; + padding: 0; + vertical-align: top; +} + +.language-options-left { + -webkit-box-orient: vertical; + + background-color: rgb(235, 239, 249); + + + background-color: white; + + display: -webkit-box; + width: 300px; +} + +.language-options-lower-left { + -webkit-box-flex: 0; + -webkit-padding-start: 12px; + padding-bottom: 10px; +} + +.language-options-right { + /* To share the center line with the left pane. */ + -webkit-margin-start: -1px; + width: 360px; +} + +.language-options-right h3:not(:first-of-type) { + margin-top: 24px; +} + +.language-options-notification { + background-color: rgb(255, 242, 158); + border-bottom: 1px solid #ccc; + border-top: 1px solid #ccc; + padding: 8px 30px 8px 12px; +} + +#language-options-input-method-list button { + -webkit-margin-start: 20px; + display: block; + /* Same margin as .settings-row. */ + margin-bottom: 0.65em; + margin-top: 0.65em; +} + +#language-options-list { + -webkit-box-flex: 1; + outline: none; + padding: 1px 0 0; + width: 100%; +} + +#language-options-list .language-name { + -webkit-box-flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +#language-options-list li { + -webkit-padding-start: 12px; + padding-bottom: 2px; + padding-top: 2px; +} + +#language-options-list-dropmarker { + background-clip: padding-box; + background-color: hsl(214, 91%, 65%); + border: 3px solid hsl(214, 91%, 65%); + border-bottom-color: transparent; + border-radius: 0; + border-top-color: transparent; + box-sizing: border-box; + display: none; + height: 8px; + overflow: hidden; + pointer-events: none; + position: fixed; + z-index: 10; +} + +#language-options-ui-restart-button { + margin-top: 4px; +} + +/* In ChromeOS we present the language choices as a big page of links. */ + +#add-language-overlay-language-list { + -webkit-column-count: 2; + -webkit-column-gap: 20px; +} + +#add-language-overlay-language-list > li > button.link-button { + padding-bottom: 8px; + padding-top: 8px; +} + +#add-language-overlay-page { + width: 800px; +} + +#add-language-overlay-page button.link-button { + padding-left: 0; + padding-right: 0; + text-align: left; +} + +#add-language-overlay-page ul { + margin: 0; + padding: 0; +} + + +/* TODO(kochi): This is temporary copy from new_tab.css */ +/* Notification */ + +#notification { + -webkit-transition: opacity 150ms; + background-color: hsl(52, 100%, 80%); + border: 1px solid rgb(211, 211, 211); + border-radius: 6px; + color: black; + display: table; + font-weight: bold; + /* Set the height and margin so that the element does not use any vertical + space. */ + height: 16px; + margin: -44px auto 12px auto; + opacity: 0; + padding: 7px 15px; + pointer-events: none; + position: relative; + white-space: nowrap; + z-index: 1; +} + +#notification > * { + display: table-cell; + max-width: 500px; + overflow: hidden; + text-overflow: ellipsis; +} + +#notification.show { + -webkit-transition: opacity 1s; + opacity: 1; + pointer-events: all; +} + +#notification .link { + -webkit-appearance: none; + -webkit-padding-start: 20px; + background: none; + border: 0; + color: rgba(0, 102, 204, 0.3); + cursor: pointer; + text-decoration: underline; +} + +#notification .link-color { + color: rgb(0, 102, 204); +} + +#chewing-max-chi-symbol-len { + height: 30%; + width: 100px; +} + +#add-language-overlay-page .content-area { + padding-bottom: 10px; +} diff --git a/chrome/browser/resources/options/language_options.html b/chrome/browser/resources/options/language_options.html new file mode 100644 index 0000000..c668018 --- /dev/null +++ b/chrome/browser/resources/options/language_options.html @@ -0,0 +1,103 @@ + diff --git a/chrome/browser/resources/options/language_options.js b/chrome/browser/resources/options/language_options.js new file mode 100644 index 0000000..4beb0f5 --- /dev/null +++ b/chrome/browser/resources/options/language_options.js @@ -0,0 +1,860 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(kochi): Generalize the notification as a component and put it +// in js/cr/ui/notification.js . + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var LanguageList = options.LanguageList; + + // Some input methods like Chinese Pinyin have config pages. + // This is the map of the input method names to their config page names. + /** @const */ var INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME = { + 'mozc': 'languageMozc', + 'mozc-chewing': 'languageChewing', + 'mozc-dv': 'languageMozc', + 'mozc-hangul': 'languageHangul', + 'mozc-jp': 'languageMozc', + 'pinyin': 'languagePinyin', + 'pinyin-dv': 'languagePinyin', + }; + + ///////////////////////////////////////////////////////////////////////////// + // LanguageOptions class: + + /** + * Encapsulated handling of ChromeOS language options page. + * @constructor + */ + function LanguageOptions(model) { + OptionsPage.call(this, 'languages', + loadTimeData.getString('languagePageTabTitle'), + 'languagePage'); + } + + cr.addSingletonGetter(LanguageOptions); + + // Inherit LanguageOptions from OptionsPage. + LanguageOptions.prototype = { + __proto__: OptionsPage.prototype, + + /* For recording the prospective language (the next locale after relaunch). + * @type {?string} + * @private + */ + prospectiveUiLanguageCode_: null, + + /** + * Initializes LanguageOptions page. + * Calls base class implementation to start preference initialization. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + var languageOptionsList = $('language-options-list'); + LanguageList.decorate(languageOptionsList); + + languageOptionsList.addEventListener('change', + this.handleLanguageOptionsListChange_.bind(this)); + languageOptionsList.addEventListener('save', + this.handleLanguageOptionsListSave_.bind(this)); + + this.prospectiveUiLanguageCode_ = + loadTimeData.getString('prospectiveUiLanguageCode'); + this.addEventListener('visibleChange', + this.handleVisibleChange_.bind(this)); + + if (cr.isChromeOS) { + $('chewing-confirm').onclick = $('hangul-confirm').onclick = + $('mozc-confirm').onclick = $('pinyin-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + + this.initializeInputMethodList_(); + this.initializeLanguageCodeToInputMethodIdsMap_(); + } + Preferences.getInstance().addEventListener(this.spellCheckDictionaryPref, + this.handleSpellCheckDictionaryPrefChange_.bind(this)); + + // Set up add button. + $('language-options-add-button').onclick = function(e) { + // Add the language without showing the overlay if it's specified in + // the URL hash (ex. lang_add=ja). Used for automated testing. + var match = document.location.hash.match(/\blang_add=([\w-]+)/); + if (match) { + var addLanguageCode = match[1]; + $('language-options-list').addLanguage(addLanguageCode); + } else { + OptionsPage.navigateToPage('addLanguage'); + } + }; + + if (cr.isChromeOS) { + // Listen to user clicks on the add language list. + var addLanguageList = $('add-language-overlay-language-list'); + addLanguageList.addEventListener('click', + this.handleAddLanguageListClick_.bind(this)); + } else { + // Listen to add language dialog ok button. + var addLanguageOkButton = $('add-language-overlay-ok-button'); + addLanguageOkButton.addEventListener('click', + this.handleAddLanguageOkButtonClick_.bind(this)); + + // Show experimental features if enabled. + if (loadTimeData.getBoolean('experimentalSpellCheckFeatures')) + $('auto-spell-correction-option').hidden = false; + + // Handle spell check enable/disable. + if (!cr.isMac) { + Preferences.getInstance().addEventListener( + this.enableSpellCheckPref, + this.updateEnableSpellCheck_.bind(this)); + + var spellCheckLanguageButton = getRequiredElement( + 'language-options-spell-check-language-button'); + spellCheckLanguageButton.addEventListener( + 'click', + this.handleSpellCheckLanguageButtonClick_.bind(this)); + } + } + + if (cr.isChromeOS) { + $('language-options-ui-restart-button').onclick = function() { + chrome.send('uiLanguageRestart'); + }; + } + + $('language-confirm').onclick = + OptionsPage.closeOverlay.bind(OptionsPage); + }, + + // The preference is a boolean that enables/disables spell checking. + enableSpellCheckPref: 'browser.enable_spellchecking', + // The preference is a CSV string that describes preload engines + // (i.e. active input methods). + preloadEnginesPref: 'settings.language.preload_engines', + // The list of preload engines, like ['mozc', 'pinyin']. + preloadEngines_: [], + // The preference is a string that describes the spell check + // dictionary language, like "en-US". + spellCheckDictionaryPref: 'spellcheck.dictionary', + spellCheckDictionary_: '', + // The map of language code to input method IDs, like: + // {'ja': ['mozc', 'mozc-jp'], 'zh-CN': ['pinyin'], ...} + languageCodeToInputMethodIdsMap_: {}, + + /** + * Initializes the input method list. + */ + initializeInputMethodList_: function() { + var inputMethodList = $('language-options-input-method-list'); + var inputMethodListData = loadTimeData.getValue('inputMethodList'); + var inputMethodPrototype = $('language-options-input-method-proto'); + + // Add all input methods, but make all of them invisible here. We'll + // change the visibility in handleLanguageOptionsListChange_() based + // on the selected language. Note that we only have less than 100 + // input methods, so creating DOM nodes at once here should be ok. + for (var i = 0; i < inputMethodListData.length; i++) { + var inputMethod = inputMethodListData[i]; + var element = inputMethodPrototype.cloneNode(true); + element.id = ''; + element.languageCodeSet = inputMethod.languageCodeSet; + var input = element.querySelectorAll('input')[0]; + input.inputMethodId = inputMethod.id; + var span = element.querySelectorAll('span')[0]; + span.textContent = inputMethod.displayName; + + // Listen to user clicks. + input.addEventListener('click', + this.handleCheckboxClick_.bind(this)); + + // Add the configure button if the config page is present for this + // input method. + if (inputMethod.id in INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME) { + var pageName = INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME[inputMethod.id]; + var button = this.createConfigureInputMethodButton_(inputMethod.id, + pageName); + element.appendChild(button); + } + inputMethodList.appendChild(element); + } + // Listen to pref change once the input method list is initialized. + Preferences.getInstance().addEventListener(this.preloadEnginesPref, + this.handlePreloadEnginesPrefChange_.bind(this)); + }, + + /** + * Creates a configure button for the given input method ID. + * @param {string} inputMethodId Input method ID (ex. "pinyin"). + * @param {string} pageName Name of the config page (ex. "languagePinyin"). + * @private + */ + createConfigureInputMethodButton_: function(inputMethodId, pageName) { + var button = document.createElement('button'); + button.textContent = loadTimeData.getString('configure'); + button.onclick = function(e) { + // Prevent the default action (i.e. changing the checked property + // of the checkbox). The button click here should not be handled + // as checkbox click. + e.preventDefault(); + chrome.send('inputMethodOptionsOpen', [inputMethodId]); + OptionsPage.navigateToPage(pageName); + }; + return button; + }, + + /** + * Handles OptionsPage's visible property change event. + * @param {Event} e Property change event. + * @private + */ + handleVisibleChange_: function(e) { + if (this.visible) { + $('language-options-list').redraw(); + chrome.send('languageOptionsOpen'); + } + }, + + /** + * Handles languageOptionsList's change event. + * @param {Event} e Change event. + * @private + */ + handleLanguageOptionsListChange_: function(e) { + var languageOptionsList = $('language-options-list'); + var languageCode = languageOptionsList.getSelectedLanguageCode(); + + // Select the language if it's specified in the URL hash (ex. lang=ja). + // Used for automated testing. + var match = document.location.hash.match(/\blang=([\w-]+)/); + if (match) { + var specifiedLanguageCode = match[1]; + if (languageOptionsList.selectLanguageByCode(specifiedLanguageCode)) { + languageCode = specifiedLanguageCode; + } + } + + if (cr.isWindows || cr.isChromeOS) + this.updateUiLanguageButton_(languageCode); + + if (!cr.isMac) { + this.updateSelectedLanguageName_(languageCode); + this.updateSpellCheckLanguageButton_(languageCode); + } + + if (cr.isChromeOS) + this.updateInputMethodList_(languageCode); + + this.updateLanguageListInAddLanguageOverlay_(); + }, + + /** + * Happens when a user changes back to the language they're currently using. + */ + currentLocaleWasReselected: function() { + this.updateUiLanguageButton_( + loadTimeData.getString('currentUiLanguageCode')); + }, + + /** + * Handles languageOptionsList's save event. + * @param {Event} e Save event. + * @private + */ + handleLanguageOptionsListSave_: function(e) { + if (cr.isChromeOS) { + // Sort the preload engines per the saved languages before save. + this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); + this.savePreloadEnginesPref_(); + } + }, + + /** + * Sorts preloadEngines_ by languageOptionsList's order. + * @param {Array} preloadEngines List of preload engines. + * @return {Array} Returns sorted preloadEngines. + * @private + */ + sortPreloadEngines_: function(preloadEngines) { + // For instance, suppose we have two languages and associated input + // methods: + // + // - Korean: hangul + // - Chinese: pinyin + // + // The preloadEngines preference should look like "hangul,pinyin". + // If the user reverse the order, the preference should be reorderd + // to "pinyin,hangul". + var languageOptionsList = $('language-options-list'); + var languageCodes = languageOptionsList.getLanguageCodes(); + + // Convert the list into a dictonary for simpler lookup. + var preloadEngineSet = {}; + for (var i = 0; i < preloadEngines.length; i++) { + preloadEngineSet[preloadEngines[i]] = true; + } + + // Create the new preload engine list per the language codes. + var newPreloadEngines = []; + for (var i = 0; i < languageCodes.length; i++) { + var languageCode = languageCodes[i]; + var inputMethodIds = this.languageCodeToInputMethodIdsMap_[ + languageCode]; + // Check if we have active input methods associated with the language. + for (var j = 0; j < inputMethodIds.length; j++) { + var inputMethodId = inputMethodIds[j]; + if (inputMethodId in preloadEngineSet) { + // If we have, add it to the new engine list. + newPreloadEngines.push(inputMethodId); + // And delete it from the set. This is necessary as one input + // method can be associated with more than one language thus + // we should avoid having duplicates in the new list. + delete preloadEngineSet[inputMethodId]; + } + } + } + + return newPreloadEngines; + }, + + /** + * Initializes the map of language code to input method IDs. + * @private + */ + initializeLanguageCodeToInputMethodIdsMap_: function() { + var inputMethodList = loadTimeData.getValue('inputMethodList'); + for (var i = 0; i < inputMethodList.length; i++) { + var inputMethod = inputMethodList[i]; + for (var languageCode in inputMethod.languageCodeSet) { + if (languageCode in this.languageCodeToInputMethodIdsMap_) { + this.languageCodeToInputMethodIdsMap_[languageCode].push( + inputMethod.id); + } else { + this.languageCodeToInputMethodIdsMap_[languageCode] = + [inputMethod.id]; + } + } + } + }, + + /** + * Updates the currently selected language name. + * @param {string} languageCode Language code (ex. "fr"). + * @private + */ + updateSelectedLanguageName_: function(languageCode) { + var languageInfo = LanguageList.getLanguageInfoFromLanguageCode( + languageCode); + var languageDisplayName = languageInfo.displayName; + var languageNativeDisplayName = languageInfo.nativeDisplayName; + var textDirection = languageInfo.textDirection; + + // If the native name is different, add it. + if (languageDisplayName != languageNativeDisplayName) { + languageDisplayName += ' - ' + languageNativeDisplayName; + } + + // Update the currently selected language name. + var languageName = $('language-options-language-name'); + languageName.textContent = languageDisplayName; + languageName.dir = textDirection; + }, + + /** + * Updates the UI language button. + * @param {string} languageCode Language code (ex. "fr"). + * @private + */ + updateUiLanguageButton_: function(languageCode) { + var uiLanguageButton = $('language-options-ui-language-button'); + var uiLanguageMessage = $('language-options-ui-language-message'); + var uiLanguageNotification = $('language-options-ui-notification-bar'); + + // Remove the event listener and add it back if useful. + uiLanguageButton.onclick = null; + + // Unhide the language button every time, as it could've been previously + // hidden by a language change. + uiLanguageButton.hidden = false; + + if (languageCode == this.prospectiveUiLanguageCode_) { + uiLanguageMessage.textContent = + loadTimeData.getString('is_displayed_in_this_language'); + showMutuallyExclusiveNodes( + [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); + } else if (languageCode in loadTimeData.getValue('uiLanguageCodeSet')) { + if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { + // In the guest mode for ChromeOS, changing UI language does not make + // sense because it does not take effect after browser restart. + uiLanguageButton.hidden = true; + uiLanguageMessage.hidden = true; + } else { + uiLanguageButton.textContent = + loadTimeData.getString('display_in_this_language'); + showMutuallyExclusiveNodes( + [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 0); + uiLanguageButton.onclick = function(e) { + chrome.send('uiLanguageChange', [languageCode]); + }; + } + } else { + uiLanguageMessage.textContent = + loadTimeData.getString('cannot_be_displayed_in_this_language'); + showMutuallyExclusiveNodes( + [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); + } + }, + + /** + * Updates the spell check language button. + * @param {string} languageCode Language code (ex. "fr"). + * @private + */ + updateSpellCheckLanguageButton_: function(languageCode) { + var spellCheckLanguageButton = + $('language-options-spell-check-language-button'); + var spellCheckLanguageMessage = + $('language-options-spell-check-language-message'); + + if (languageCode == this.spellCheckDictionary_) { + spellCheckLanguageMessage.textContent = + loadTimeData.getString('is_used_for_spell_checking'); + showMutuallyExclusiveNodes( + [spellCheckLanguageButton, spellCheckLanguageMessage], 1); + } else if (languageCode in + loadTimeData.getValue('spellCheckLanguageCodeSet')) { + spellCheckLanguageButton.textContent = + loadTimeData.getString('use_this_for_spell_checking'); + showMutuallyExclusiveNodes( + [spellCheckLanguageButton, spellCheckLanguageMessage], 0); + spellCheckLanguageButton.languageCode = languageCode; + } else if (!languageCode) { + spellCheckLanguageButton.hidden = true; + spellCheckLanguageMessage.hidden = true; + } else { + spellCheckLanguageMessage.textContent = + loadTimeData.getString('cannot_be_used_for_spell_checking'); + showMutuallyExclusiveNodes( + [spellCheckLanguageButton, spellCheckLanguageMessage], 1); + } + }, + + /** + * Updates the input method list. + * @param {string} languageCode Language code (ex. "fr"). + * @private + */ + updateInputMethodList_: function(languageCode) { + // Give one of the checkboxes or buttons focus, if it's specified in the + // URL hash (ex. focus=mozc). Used for automated testing. + var focusInputMethodId = -1; + var match = document.location.hash.match(/\bfocus=([\w:-]+)\b/); + if (match) { + focusInputMethodId = match[1]; + } + // Change the visibility of the input method list. Input methods that + // matches |languageCode| will become visible. + var inputMethodList = $('language-options-input-method-list'); + var methods = inputMethodList.querySelectorAll('.input-method'); + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + if (languageCode in method.languageCodeSet) { + method.hidden = false; + var input = method.querySelectorAll('input')[0]; + // Give it focus if the ID matches. + if (input.inputMethodId == focusInputMethodId) { + input.focus(); + } + } else { + method.hidden = true; + } + } + + if (focusInputMethodId == 'add') { + $('language-options-add-button').focus(); + } + }, + + /** + * Updates the language list in the add language overlay. + * @param {string} languageCode Language code (ex. "fr"). + * @private + */ + updateLanguageListInAddLanguageOverlay_: function(languageCode) { + // Change the visibility of the language list in the add language + // overlay. Languages that are already active will become invisible, + // so that users don't add the same language twice. + var languageOptionsList = $('language-options-list'); + var languageCodes = languageOptionsList.getLanguageCodes(); + var languageCodeSet = {}; + for (var i = 0; i < languageCodes.length; i++) { + languageCodeSet[languageCodes[i]] = true; + } + var addLanguageList = $('add-language-overlay-language-list'); + var lis = addLanguageList.querySelectorAll('li'); + for (var i = 0; i < lis.length; i++) { + // The first child button knows the language code. + var button = lis[i].childNodes[0]; + if (button.languageCode in languageCodeSet) { + lis[i].style.display = 'none'; + } else { + lis[i].style.display = 'block'; + } + } + }, + + /** + * Handles preloadEnginesPref change. + * @param {Event} e Change event. + * @private + */ + handlePreloadEnginesPrefChange_: function(e) { + var value = e.value.value; + this.preloadEngines_ = this.filterBadPreloadEngines_(value.split(',')); + this.updateCheckboxesFromPreloadEngines_(); + $('language-options-list').updateDeletable(); + }, + + /** + * Handles input method checkbox's click event. + * @param {Event} e Click event. + * @private + */ + handleCheckboxClick_: function(e) { + var checkbox = e.target; + if (this.preloadEngines_.length == 1 && !checkbox.checked) { + // Don't allow disabling the last input method. + this.showNotification_( + loadTimeData.getString('please_add_another_input_method'), + loadTimeData.getString('ok_button')); + checkbox.checked = true; + return; + } + if (checkbox.checked) { + chrome.send('inputMethodEnable', [checkbox.inputMethodId]); + } else { + chrome.send('inputMethodDisable', [checkbox.inputMethodId]); + } + this.updatePreloadEnginesFromCheckboxes_(); + this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); + this.savePreloadEnginesPref_(); + }, + + /** + * Handles add language list's click event. + * @param {Event} e Click event. + */ + handleAddLanguageListClick_: function(e) { + var languageOptionsList = $('language-options-list'); + var languageCode = e.target.languageCode; + // languageCode can be undefined, if click was made on some random + // place in the overlay, rather than a button. Ignore it. + if (!languageCode) { + return; + } + languageOptionsList.addLanguage(languageCode); + var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; + // Enable the first input method for the language added. + if (inputMethodIds && inputMethodIds[0] && + // Don't add the input method it's already present. This can + // happen if the same input method is shared among multiple + // languages (ex. English US keyboard is used for English US and + // Filipino). + this.preloadEngines_.indexOf(inputMethodIds[0]) == -1) { + this.preloadEngines_.push(inputMethodIds[0]); + this.updateCheckboxesFromPreloadEngines_(); + this.savePreloadEnginesPref_(); + } + OptionsPage.closeOverlay(); + }, + + /** + * Handles add language dialog ok button. + */ + handleAddLanguageOkButtonClick_: function() { + var languagesSelect = $('add-language-overlay-language-list'); + var selectedIndex = languagesSelect.selectedIndex; + if (selectedIndex >= 0) { + var selection = languagesSelect.options[selectedIndex]; + $('language-options-list').addLanguage(String(selection.value)); + OptionsPage.closeOverlay(); + } + }, + + /** + * Checks if languageCode is deletable or not. + * @param {String} languageCode the languageCode to check for deletability. + */ + languageIsDeletable: function(languageCode) { + // Don't allow removing the language if it's a UI language. + if (languageCode == this.prospectiveUiLanguageCode_) + return false; + return (!cr.isChromeOS || + this.canDeleteLanguage_(languageCode)); + }, + + /** + * Handles browse.enable_spellchecking change. + * @param {Event} e Change event. + * @private + */ + updateEnableSpellCheck_: function() { + var value = !$('enable-spell-check').checked; + + $('language-options-spell-check-language-button').disabled = value; + }, + + /** + * Handles spellCheckDictionaryPref change. + * @param {Event} e Change event. + * @private + */ + handleSpellCheckDictionaryPrefChange_: function(e) { + var languageCode = e.value.value; + this.spellCheckDictionary_ = languageCode; + var languageOptionsList = $('language-options-list'); + var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode(); + if (!cr.isMac) + this.updateSpellCheckLanguageButton_(selectedLanguageCode); + }, + + /** + * Handles spellCheckLanguageButton click. + * @param {Event} e Click event. + * @private + */ + handleSpellCheckLanguageButtonClick_: function(e) { + var languageCode = e.target.languageCode; + // Save the preference. + Preferences.setStringPref(this.spellCheckDictionaryPref, + languageCode); + chrome.send('spellCheckLanguageChange', [languageCode]); + }, + + /** + * Checks whether it's possible to remove the language specified by + * languageCode and returns true if possible. This function returns false + * if the removal causes the number of preload engines to be zero. + * + * @param {string} languageCode Language code (ex. "fr"). + * @return {boolean} Returns true on success. + * @private + */ + canDeleteLanguage_: function(languageCode) { + // First create the set of engines to be removed from input methods + // associated with the language code. + var enginesToBeRemovedSet = {}; + var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; + for (var i = 0; i < inputMethodIds.length; i++) { + enginesToBeRemovedSet[inputMethodIds[i]] = true; + } + + // Then eliminate engines that are also used for other active languages. + // For instance, if "xkb:us::eng" is used for both English and Filipino. + var languageCodes = $('language-options-list').getLanguageCodes(); + for (var i = 0; i < languageCodes.length; i++) { + // Skip the target language code. + if (languageCodes[i] == languageCode) { + continue; + } + // Check if input methods used in this language are included in + // enginesToBeRemovedSet. If so, eliminate these from the set, so + // we don't remove this time. + var inputMethodIdsForAnotherLanguage = + this.languageCodeToInputMethodIdsMap_[languageCodes[i]]; + for (var j = 0; j < inputMethodIdsForAnotherLanguage.length; j++) { + var inputMethodId = inputMethodIdsForAnotherLanguage[j]; + if (inputMethodId in enginesToBeRemovedSet) { + delete enginesToBeRemovedSet[inputMethodId]; + } + } + } + + // Update the preload engine list with the to-be-removed set. + var newPreloadEngines = []; + for (var i = 0; i < this.preloadEngines_.length; i++) { + if (!(this.preloadEngines_[i] in enginesToBeRemovedSet)) { + newPreloadEngines.push(this.preloadEngines_[i]); + } + } + // Don't allow this operation if it causes the number of preload + // engines to be zero. + return (newPreloadEngines.length > 0); + }, + + /** + * Saves the preload engines preference. + * @private + */ + savePreloadEnginesPref_: function() { + Preferences.setStringPref(this.preloadEnginesPref, + this.preloadEngines_.join(',')); + }, + + /** + * Updates the checkboxes in the input method list from the preload + * engines preference. + * @private + */ + updateCheckboxesFromPreloadEngines_: function() { + // Convert the list into a dictonary for simpler lookup. + var dictionary = {}; + for (var i = 0; i < this.preloadEngines_.length; i++) { + dictionary[this.preloadEngines_[i]] = true; + } + + var inputMethodList = $('language-options-input-method-list'); + var checkboxes = inputMethodList.querySelectorAll('input'); + for (var i = 0; i < checkboxes.length; i++) { + checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary); + } + }, + + /** + * Updates the preload engines preference from the checkboxes in the + * input method list. + * @private + */ + updatePreloadEnginesFromCheckboxes_: function() { + this.preloadEngines_ = []; + var inputMethodList = $('language-options-input-method-list'); + var checkboxes = inputMethodList.querySelectorAll('input'); + for (var i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked) { + this.preloadEngines_.push(checkboxes[i].inputMethodId); + } + } + var languageOptionsList = $('language-options-list'); + languageOptionsList.updateDeletable(); + }, + + /** + * Filters bad preload engines in case bad preload engines are + * stored in the preference. Removes duplicates as well. + * @param {Array} preloadEngines List of preload engines. + * @private + */ + filterBadPreloadEngines_: function(preloadEngines) { + // Convert the list into a dictonary for simpler lookup. + var dictionary = {}; + var list = loadTimeData.getValue('inputMethodList'); + for (var i = 0; i < list.length; i++) { + dictionary[list[i].id] = true; + } + + var filteredPreloadEngines = []; + var seen = {}; + for (var i = 0; i < preloadEngines.length; i++) { + // Check if the preload engine is present in the + // dictionary, and not duplicate. Otherwise, skip it. + if (preloadEngines[i] in dictionary && !(preloadEngines[i] in seen)) { + filteredPreloadEngines.push(preloadEngines[i]); + seen[preloadEngines[i]] = true; + } + } + return filteredPreloadEngines; + }, + + // TODO(kochi): This is an adapted copy from new_tab.js. + // If this will go as final UI, refactor this to share the component with + // new new tab page. + /** + * Shows notification + * @private + */ + notificationTimeout_: null, + showNotification_: function(text, actionText, opt_delay) { + var notificationElement = $('notification'); + var actionLink = notificationElement.querySelector('.link-color'); + var delay = opt_delay || 10000; + + function show() { + window.clearTimeout(this.notificationTimeout_); + notificationElement.classList.add('show'); + document.body.classList.add('notification-shown'); + } + + function hide() { + window.clearTimeout(this.notificationTimeout_); + notificationElement.classList.remove('show'); + document.body.classList.remove('notification-shown'); + // Prevent tabbing to the hidden link. + actionLink.tabIndex = -1; + // Setting tabIndex to -1 only prevents future tabbing to it. If, + // however, the user switches window or a tab and then moves back to + // this tab the element may gain focus. We therefore make sure that we + // blur the element so that the element focus is not restored when + // coming back to this window. + actionLink.blur(); + } + + function delayedHide() { + this.notificationTimeout_ = window.setTimeout(hide, delay); + } + + notificationElement.firstElementChild.textContent = text; + actionLink.textContent = actionText; + + actionLink.onclick = hide; + actionLink.onkeydown = function(e) { + if (e.keyIdentifier == 'Enter') { + hide(); + } + }; + notificationElement.onmouseover = show; + notificationElement.onmouseout = delayedHide; + actionLink.onfocus = show; + actionLink.onblur = delayedHide; + // Enable tabbing to the link now that it is shown. + actionLink.tabIndex = 0; + + show(); + delayedHide(); + } + }; + + /** + * Shows the node at |index| in |nodes|, hides all others. + * @param {Array} nodes The nodes to be shown or hidden. + * @param {number} index The index of |nodes| to show. + */ + function showMutuallyExclusiveNodes(nodes, index) { + assert(index >= 0 && index < nodes.length); + for (var i = 0; i < nodes.length; ++i) { + assert(nodes[i] instanceof HTMLElement); // TODO(dbeam): Ignore null? + nodes[i].hidden = i != index; + } + } + + /** + * Chrome callback for when the UI language preference is saved. + * @param {string} languageCode The newly selected language to use. + */ + LanguageOptions.uiLanguageSaved = function(languageCode) { + this.prospectiveUiLanguageCode_ = languageCode; + + // If the user is no longer on the same language code, ignore. + if ($('language-options-list').getSelectedLanguageCode() != languageCode) + return; + + // Special case for when a user changes to a different language, and changes + // back to the same language without having restarted Chrome or logged + // in/out of ChromeOS. + if (languageCode == loadTimeData.getString('currentUiLanguageCode')) { + LanguageOptions.getInstance().currentLocaleWasReselected(); + return; + } + + // Otherwise, show a notification telling the user that their changes will + // only take effect after restart. + showMutuallyExclusiveNodes([$('language-options-ui-language-button'), + $('language-options-ui-notification-bar')], 1); + }; + + // Export + return { + LanguageOptions: LanguageOptions + }; +}); diff --git a/chrome/browser/resources/options/manage_profile_overlay.css b/chrome/browser/resources/options/manage_profile_overlay.css new file mode 100644 index 0000000..8c17101 --- /dev/null +++ b/chrome/browser/resources/options/manage_profile_overlay.css @@ -0,0 +1,147 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#manage-profile-overlay { + width: 512px; +} + +.profile-icon-grid-item { + height: 31px; + margin: 2px 4px; + padding: 4px; + width: 38px; +} + +.profile-icon { + height: 31px; + width: 38px; +} + +#create-profile-name-label-container, +#create-profile-name-input-container, +#manage-profile-name-label-container, +#manage-profile-name-input-container { + -webkit-box-align: center; + -webkit-box-orient: horizontal; + -webkit-box-pack: center; + display: -webkit-box; + margin-left: auto; + margin-right: auto; + width: 50%; +} + +#create-profile-name:invalid { + background-color: pink; +} + +#manage-profile-name:invalid { + background-color: pink; +} + +#create-profile-error-bubble { + -webkit-transition: max-height 200ms, padding 200ms; + background-color: rgb(238, 185, 57); + border-radius: 4px; + font-weight: bold; + margin-left: auto; + margin-right: auto; + max-height: 50px; + overflow: hidden; + padding: 1px 10px; + text-align: center; + width: 80%; +} + +#manage-profile-error-bubble { + -webkit-transition: max-height 200ms, padding 200ms; + background-color: rgb(238, 185, 57); + border-radius: 4px; + font-weight: bold; + margin-left: auto; + margin-right: auto; + max-height: 50px; + overflow: hidden; + padding: 1px 10px; + text-align: center; + width: 80%; +} + +#create-profile-error-bubble[hidden] { + display: block !important; + max-height: 0; + padding: 0 10px; +} + +#manage-profile-error-bubble[hidden] { + display: block !important; + max-height: 0; + padding: 0 10px; +} + +#create-profile-icon-grid, +#manage-profile-icon-grid { + background-color: rgba(255, 255, 255, 0.75); + padding: 2px; +} + +/* Adds a grey horizontal line below content area */ +#create-profile-content:after, +#manage-profile-content:after { + background: #c0c0c0; + content: ''; + display: block; + height: 1px; + margin-top: 25px; + width: 100%; +} + +#create-profile-content > :first-child { + margin-bottom: 10px; +} + +#manage-profile-content > :first-child { + margin-bottom: 10px; +} + +#create-profile-content > :last-child { + margin-top: 10px; +} + +#manage-profile-content > :last-child { + margin-top: 10px; +} + +#create-profile-content > :not(:first-child):not(:last-child) { + margin-bottom: 10px; + margin-top: 10px; +} + +#manage-profile-content > :not(:first-child):not(:last-child) { + margin-bottom: 10px; + margin-top: 10px; +} + +#create-profile-content > #create-profile-name-input-container, +#manage-profile-content > #manage-profile-name-input-container { + margin-top: 5px; +} + +#create-profile-content > #create-profile-name-label-container, +#manage-profile-content > #manage-profile-name-label-container { + margin-bottom: 5px; + margin-top: 10px; +} + +.action-area-checkbox-container { + -webkit-box-flex: 1; +} + +#delete-profile-message { + -webkit-padding-start: 48px; + background-repeat: no-repeat; +} + +html[dir='rtl'] #delete-profile-message { + background-position: right; + diff --git a/chrome/browser/resources/options/manage_profile_overlay.html b/chrome/browser/resources/options/manage_profile_overlay.html new file mode 100644 index 0000000..3170dcc --- /dev/null +++ b/chrome/browser/resources/options/manage_profile_overlay.html @@ -0,0 +1,92 @@ + diff --git a/chrome/browser/resources/options/manage_profile_overlay.js b/chrome/browser/resources/options/manage_profile_overlay.js new file mode 100644 index 0000000..c4997f7 --- /dev/null +++ b/chrome/browser/resources/options/manage_profile_overlay.js @@ -0,0 +1,317 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var OptionsPage = options.OptionsPage; + var ArrayDataModel = cr.ui.ArrayDataModel; + + /** + * ManageProfileOverlay class + * Encapsulated handling of the 'Manage profile...' overlay page. + * @constructor + * @class + */ + function ManageProfileOverlay() { + OptionsPage.call(this, 'manageProfile', + loadTimeData.getString('manageProfileTabTitle'), + 'manage-profile-overlay'); + }; + + cr.addSingletonGetter(ManageProfileOverlay); + + ManageProfileOverlay.prototype = { + // Inherit from OptionsPage. + __proto__: OptionsPage.prototype, + + // Info about the currently managed/deleted profile. + profileInfo_: null, + + // An object containing all known profile names. + profileNames_: {}, + + // The currently selected icon in the icon grid. + iconGridSelectedURL_: null, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + OptionsPage.prototype.initializePage.call(this); + + var self = this; + var iconGrid = $('manage-profile-icon-grid'); + var createIconGrid = $('create-profile-icon-grid'); + options.ProfilesIconGrid.decorate(iconGrid); + options.ProfilesIconGrid.decorate(createIconGrid); + iconGrid.addEventListener('change', function(e) { + self.onIconGridSelectionChanged_('manage'); + }); + createIconGrid.addEventListener('change', function(e) { + self.onIconGridSelectionChanged_('create'); + }); + + $('manage-profile-name').oninput = function(event) { + self.onNameChanged_(event, 'manage'); + }; + $('create-profile-name').oninput = function(event) { + self.onNameChanged_(event, 'create'); + }; + $('manage-profile-cancel').onclick = + $('delete-profile-cancel').onclick = + $('create-profile-cancel').onclick = function(event) { + OptionsPage.closeOverlay(); + }; + $('manage-profile-ok').onclick = function(event) { + OptionsPage.closeOverlay(); + self.submitManageChanges_(); + }; + $('delete-profile-ok').onclick = function(event) { + OptionsPage.closeOverlay(); + chrome.send('deleteProfile', [self.profileInfo_.filePath]); + }; + $('create-profile-ok').onclick = function(event) { + OptionsPage.closeOverlay(); + // Get the user's chosen name and icon, or default if they do not + // wish to customize their profile. + var name = $('create-profile-name').value; + var icon_url = createIconGrid.selectedItem; + chrome.send('createProfile', [name, icon_url]); + }; + }, + + /** @inheritDoc */ + didShowPage: function() { + chrome.send('requestDefaultProfileIcons'); + + // Just ignore the manage profile dialog on Chrome OS, they use /accounts. + if (!cr.isChromeOS && window.location.pathname == '/manageProfile') + ManageProfileOverlay.getInstance().prepareForManageDialog_(); + + $('manage-profile-name').focus(); + $('create-profile-name').focus(); + }, + + /** + * Set the profile info used in the dialog. + * @param {Object} profileInfo An object of the form: + * profileInfo = { + * name: "Profile Name", + * iconURL: "chrome://path/to/icon/image", + * filePath: "/path/to/profile/data/on/disk" + * isCurrentProfile: false, + * }; + * @param {String} mode A label that specifies the type of dialog + * box which is currently being viewed (i.e. 'create' or + * 'manage'). + * @private + */ + setProfileInfo_: function(profileInfo, mode) { + this.iconGridSelectedURL_ = profileInfo.iconURL; + this.profileInfo_ = profileInfo; + $(mode + '-profile-name').value = profileInfo.name; + $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL; + }, + + /** + * Sets the name of the currently edited profile. + * @private + */ + setProfileName_: function(name) { + if (this.profileInfo_) + this.profileInfo_.name = name; + $('manage-profile-name').value = name; + }, + + /** + * the user will use to choose their profile icon. + * @param {Array.} iconURLs An array of icon URLs. + * @private + */ + receiveDefaultProfileIcons_: function(iconGrid, iconURLs) { + $(iconGrid).dataModel = new ArrayDataModel(iconURLs); + + if (this.profileInfo_) + $(iconGrid).selectedItem = this.profileInfo_.iconURL; + + var grid = $(iconGrid); + // Recalculate the measured item size. + grid.measured_ = null; + grid.columns = 0; + grid.redraw(); + }, + + /** + * Set a dictionary of all profile names. These are used to prevent the + * user from naming two profiles the same. + * @param {Object} profileNames A dictionary of profile names. + * @private + */ + receiveProfileNames_: function(profileNames) { + this.profileNames_ = profileNames; + }, + + /** + * Display the error bubble, with |errorText| in the bubble. + * @param {string} errorText The localized string id to display as an error. + * @param {String} mode A label that specifies the type of dialog + * box which is currently being viewed (i.e. 'create' or + * 'manage'). + * @private + */ + showErrorBubble_: function(errorText, mode) { + var nameErrorEl = $(mode + '-profile-error-bubble'); + nameErrorEl.hidden = false; + nameErrorEl.textContent = loadTimeData.getString(errorText); + + $(mode + '-profile-ok').disabled = true; + }, + + /** + * Hide the error bubble. + * @param {String} mode A label that specifies the type of dialog + * box which is currently being viewed (i.e. 'create' or + * 'manage'). + * @private + */ + hideErrorBubble_: function(mode) { + $(mode + '-profile-error-bubble').hidden = true; + $(mode + '-profile-ok').disabled = false; + }, + + /** + * oninput callback for field. + * @param {Event} event The event object. + * @param {String} mode A label that specifies the type of dialog + * box which is currently being viewed (i.e. 'create' or + * 'manage'). + * @private + */ + onNameChanged_: function(event, mode) { + var newName = event.target.value; + var oldName = this.profileInfo_.name; + + if (newName == oldName) { + this.hideErrorBubble_(mode); + } else if (this.profileNames_[newName] != undefined) { + this.showErrorBubble_('manageProfilesDuplicateNameError', mode); + } else { + this.hideErrorBubble_(mode); + + var nameIsValid = $(mode + '-profile-name').validity.valid; + $(mode + '-profile-ok').disabled = !nameIsValid; + } + }, + + /** + * Called when the user clicks "OK". Saves the newly changed profile info. + * @private + */ + submitManageChanges_: function() { + var name = $('manage-profile-name').value; + var iconURL = $('manage-profile-icon-grid').selectedItem; + chrome.send('setProfileNameAndIcon', + [this.profileInfo_.filePath, name, iconURL]); + }, + + /** + * Called when the selected icon in the icon grid changes. + * @param {String} mode A label that specifies the type of dialog + * box which is currently being viewed (i.e. 'create' or + * 'manage'). + * @private + */ + onIconGridSelectionChanged_: function(mode) { + var iconURL = $(mode + '-profile-icon-grid').selectedItem; + if (!iconURL || iconURL == this.iconGridSelectedURL_) + return; + this.iconGridSelectedURL_ = iconURL; + chrome.send('profileIconSelectionChanged', + [this.profileInfo_.filePath, iconURL]); + }, + + /** + * Updates the contents of the "Manage Profile" section of the dialog, + * and shows that section. + * @private + */ + prepareForManageDialog_: function() { + var profileInfo = BrowserOptions.getCurrentProfile(); + ManageProfileOverlay.setProfileInfo(profileInfo, 'manage'); + $('manage-profile-overlay-create').hidden = true; + $('manage-profile-overlay-manage').hidden = false; + $('manage-profile-overlay-delete').hidden = true; + this.hideErrorBubble_('manage'); + }, + + /** + * Display the "Manage Profile" dialog. + * @private + */ + showManageDialog_: function() { + this.prepareForManageDialog_(); + OptionsPage.navigateToPage('manageProfile'); + }, + + /** + * Display the "Delete Profile" dialog. + * @param {Object} profileInfo The profile object of the profile to delete. + * @private + */ + showDeleteDialog_: function(profileInfo) { + ManageProfileOverlay.setProfileInfo(profileInfo, 'manage'); + $('manage-profile-overlay-create').hidden = true; + $('manage-profile-overlay-manage').hidden = true; + $('manage-profile-overlay-delete').hidden = false; + $('delete-profile-message').textContent = + loadTimeData.getStringF('deleteProfileMessage', profileInfo.name); + $('delete-profile-message').style.backgroundImage = 'url("' + + profileInfo.iconURL + '")'; + + // Because this dialog isn't useful when refreshing or as part of the + // history, don't create a history entry for it when showing. + OptionsPage.showPageByName('manageProfile', false); + }, + + /** + * Display the "Create Profile" dialog. + * @param {Object} profileInfo The profile object of the profile to + * create. Upon creation, this object only needs a name and an avatar. + * @private + */ + showCreateDialog_: function(profileInfo) { + ManageProfileOverlay.setProfileInfo(profileInfo, 'create'); + $('manage-profile-overlay-create').hidden = false; + $('manage-profile-overlay-manage').hidden = true; + $('manage-profile-overlay-delete').hidden = true; + $('create-profile-instructions').textContent = + loadTimeData.getStringF('createProfileInstructions'); + ManageProfileOverlay.getInstance().hideErrorBubble_('create'); + + OptionsPage.showPageByName('manageProfile', false); + }, + + }; + + // Forward public APIs to private implementations. + [ + 'receiveDefaultProfileIcons', + 'receiveProfileNames', + 'setProfileInfo', + 'setProfileName', + 'showManageDialog', + 'showDeleteDialog', + 'showCreateDialog', + ].forEach(function(name) { + ManageProfileOverlay[name] = function() { + var instance = ManageProfileOverlay.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + // Export + return { + ManageProfileOverlay: ManageProfileOverlay + }; +}); diff --git a/chrome/browser/resources/options/media_galleries_list.js b/chrome/browser/resources/options/media_galleries_list.js new file mode 100644 index 0000000..417084b --- /dev/null +++ b/chrome/browser/resources/options/media_galleries_list.js @@ -0,0 +1,59 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var DeletableItemList = options.DeletableItemList; + + /** + * @constructor + * @extends {DeletableItem} + */ + function MediaGalleriesListItem(galleryInfo) { + var el = cr.doc.createElement('div'); + el.galleryInfo_ = galleryInfo; + el.__proto__ = MediaGalleriesListItem.prototype; + el.decorate(); + return el; + } + + MediaGalleriesListItem.prototype = { + __proto__: DeletableItem.prototype, + + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + var span = this.ownerDocument.createElement('span'); + span.textContent = this.galleryInfo_.displayName; + this.contentElement.appendChild(span); + this.contentElement.title = this.galleryInfo_.path; + }, + }; + + var MediaGalleriesList = cr.ui.define('list'); + + MediaGalleriesList.prototype = { + __proto__: DeletableItemList.prototype, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + this.autoExpands_ = true; + }, + + /** @inheritDoc */ + createItem: function(galleryInfo) { + return new MediaGalleriesListItem(galleryInfo); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + chrome.send('forgetGallery', [this.dataModel.item(index).id]); + }, + }; + + return { + MediaGalleriesList: MediaGalleriesList + }; +}); diff --git a/chrome/browser/resources/options/media_galleries_manager_overlay.html b/chrome/browser/resources/options/media_galleries_manager_overlay.html new file mode 100644 index 0000000..5f4e10d --- /dev/null +++ b/chrome/browser/resources/options/media_galleries_manager_overlay.html @@ -0,0 +1,17 @@ + diff --git a/chrome/browser/resources/options/media_galleries_manager_overlay.js b/chrome/browser/resources/options/media_galleries_manager_overlay.js new file mode 100644 index 0000000..e1a24ff --- /dev/null +++ b/chrome/browser/resources/options/media_galleries_manager_overlay.js @@ -0,0 +1,82 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * This class is an overlay which allows the user to add or remove media + * galleries, and displays known media galleries. + * @constructor + * @extends {OptionsPage} + */ + function MediaGalleriesManager() { + OptionsPage.call(this, 'manageGalleries', + loadTimeData.getString('manageMediaGalleriesTabTitle'), + 'manage-media-galleries-overlay'); + } + + cr.addSingletonGetter(MediaGalleriesManager); + + MediaGalleriesManager.prototype = { + __proto__: OptionsPage.prototype, + + /** + * Decorate the overlay and set up event handlers. + */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.availableGalleriesList_ = $('available-galleries-list'); + options.MediaGalleriesList.decorate(this.availableGalleriesList_); + + $('new-media-gallery').addEventListener('click', function() { + chrome.send('addNewGallery'); + }); + + $('manage-media-confirm').addEventListener( + 'click', OptionsPage.closeOverlay.bind(OptionsPage)); + + this.addEventListener('visibleChange', this.handleVisibleChange_); + }, + + /** + * @inheritDoc + * @private + */ + handleVisibleChange_: function() { + if (!this.visible) + return; + + if (this.availableGalleriesList_) + this.availableGalleriesList_.redraw(); + }, + + /** + * @param {Array} galleries List of structs describibing galleries. + * @private + */ + setAvailableMediaGalleries_: function(galleries) { + $('available-galleries-list').dataModel = new ArrayDataModel(galleries); + // TODO(estade): show this section by default. + $('media-galleries-section').hidden = false; + }, + }, + + // Forward public APIs to private implementations. + [ + 'setAvailableMediaGalleries', + ].forEach(function(name) { + MediaGalleriesManager[name] = function() { + var instance = MediaGalleriesManager.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + // Export + return { + MediaGalleriesManager: MediaGalleriesManager + }; +}); diff --git a/chrome/browser/resources/options/options.html b/chrome/browser/resources/options/options.html new file mode 100644 index 0000000..c5bcac0 --- /dev/null +++ b/chrome/browser/resources/options/options.html @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + +
+
+
+
+ + + + + + + diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js new file mode 100644 index 0000000..487c517 --- /dev/null +++ b/chrome/browser/resources/options/options.js @@ -0,0 +1,236 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var AddLanguageOverlay = options.AddLanguageOverlay; +var AlertOverlay = options.AlertOverlay; +var AutofillEditAddressOverlay = options.AutofillEditAddressOverlay; +var AutofillEditCreditCardOverlay = options.AutofillEditCreditCardOverlay; +var AutofillOptions = options.AutofillOptions; +var BrowserOptions = options.BrowserOptions; +var ClearBrowserDataOverlay = options.ClearBrowserDataOverlay; +var ContentSettings = options.ContentSettings; +var ContentSettingsExceptionsArea = + options.contentSettings.ContentSettingsExceptionsArea; +var CookiesView = options.CookiesView; +var CookiesViewApp = options.CookiesViewApp; +var FontSettings = options.FontSettings; +var HandlerOptions = options.HandlerOptions; +var HomePageOverlay = options.HomePageOverlay; +var ImportDataOverlay = options.ImportDataOverlay; +var InstantConfirmOverlay = options.InstantConfirmOverlay; +var LanguageOptions = options.LanguageOptions; +var MediaGalleriesManager = options.MediaGalleriesManager; +var OptionsFocusManager = options.OptionsFocusManager; +var OptionsPage = options.OptionsPage; +var PasswordManager = options.PasswordManager; +var Preferences = options.Preferences; +var PreferredNetworks = options.PreferredNetworks; +var ManageProfileOverlay = options.ManageProfileOverlay; +var SearchEngineManager = options.SearchEngineManager; +var SearchPage = options.SearchPage; +var SpellingConfirmOverlay = options.SpellingConfirmOverlay; +var StartupOverlay = options.StartupOverlay; +var SyncSetupOverlay = options.SyncSetupOverlay; + +/** + * DOMContentLoaded handler, sets up the page. + */ +function load() { + // Decorate the existing elements in the document. + cr.ui.decorate('input[pref][type=checkbox]', options.PrefCheckbox); + cr.ui.decorate('input[pref][type=number]', options.PrefNumber); + cr.ui.decorate('input[pref][type=radio]', options.PrefRadio); + cr.ui.decorate('input[pref][type=range]', options.PrefRange); + cr.ui.decorate('select[pref]', options.PrefSelect); + cr.ui.decorate('input[pref][type=text]', options.PrefTextField); + cr.ui.decorate('input[pref][type=url]', options.PrefTextField); + cr.ui.decorate('button[pref]', options.PrefButton); + cr.ui.decorate('#content-settings-page input[type=radio]:not(.handler-radio)', + options.ContentSettingsRadio); + cr.ui.decorate('#content-settings-page input[type=radio].handler-radio', + options.HandlersEnabledRadio); + cr.ui.decorate('span.controlled-setting-indicator', + options.ControlledSettingIndicator); + + // Top level pages. + OptionsPage.register(SearchPage.getInstance()); + OptionsPage.register(BrowserOptions.getInstance()); + + // Overlays. + OptionsPage.registerOverlay(AddLanguageOverlay.getInstance(), + LanguageOptions.getInstance()); + OptionsPage.registerOverlay(AlertOverlay.getInstance()); + OptionsPage.registerOverlay(AutofillEditAddressOverlay.getInstance(), + AutofillOptions.getInstance()); + OptionsPage.registerOverlay(AutofillEditCreditCardOverlay.getInstance(), + AutofillOptions.getInstance()); + OptionsPage.registerOverlay(AutofillOptions.getInstance(), + BrowserOptions.getInstance(), + [$('autofill-settings')]); + OptionsPage.registerOverlay(ClearBrowserDataOverlay.getInstance(), + BrowserOptions.getInstance(), + [$('privacyClearDataButton')]); + OptionsPage.registerOverlay(ContentSettings.getInstance(), + BrowserOptions.getInstance(), + [$('privacyContentSettingsButton')]); + OptionsPage.registerOverlay(ContentSettingsExceptionsArea.getInstance(), + ContentSettings.getInstance()); + OptionsPage.registerOverlay(CookiesView.getInstance(), + ContentSettings.getInstance(), + [$('privacyContentSettingsButton'), + $('show-cookies-button')]); + OptionsPage.registerOverlay(CookiesViewApp.getInstance(), + ContentSettings.getInstance(), + [$('privacyContentSettingsButton'), + $('show-app-cookies-button')]); + OptionsPage.registerOverlay(FontSettings.getInstance(), + BrowserOptions.getInstance(), + [$('fontSettingsCustomizeFontsButton')]); + if (HandlerOptions && $('manage-handlers-button')) { + OptionsPage.registerOverlay(HandlerOptions.getInstance(), + ContentSettings.getInstance(), + [$('manage-handlers-button')]); + } + OptionsPage.registerOverlay(HomePageOverlay.getInstance(), + BrowserOptions.getInstance(), + [$('change-home-page')]); + OptionsPage.registerOverlay(ImportDataOverlay.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(InstantConfirmOverlay.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(LanguageOptions.getInstance(), + BrowserOptions.getInstance(), + [$('language-button')]); + OptionsPage.registerOverlay(ManageProfileOverlay.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(MediaGalleriesManager.getInstance(), + ContentSettings.getInstance(), + [$('manage-galleries-button')]); + OptionsPage.registerOverlay(PasswordManager.getInstance(), + BrowserOptions.getInstance(), + [$('manage-passwords')]); + OptionsPage.registerOverlay(SearchEngineManager.getInstance(), + BrowserOptions.getInstance(), + [$('manage-default-search-engines')]); + OptionsPage.registerOverlay(SpellingConfirmOverlay.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(StartupOverlay.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(SyncSetupOverlay.getInstance(), + BrowserOptions.getInstance()); + if (cr.isChromeOS) { + OptionsPage.registerOverlay(AccountsOptions.getInstance(), + BrowserOptions.getInstance(), + [$('manage-accounts-button')]); + OptionsPage.registerOverlay(BluetoothOptions.getInstance(), + BrowserOptions.getInstance(), + [$('bluetooth-add-device')]); + OptionsPage.registerOverlay(BluetoothPairing.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(ChangePictureOptions.getInstance(), + BrowserOptions.getInstance(), + [$('account-picture')]); + OptionsPage.registerOverlay(DetailsInternetPage.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay(DisplayOptions.getInstance(), + BrowserOptions.getInstance(), + [$('display-options-button')]); + OptionsPage.registerOverlay(KeyboardOverlay.getInstance(), + BrowserOptions.getInstance(), + [$('keyboard-settings-button')]); + OptionsPage.registerOverlay(PointerOverlay.getInstance(), + BrowserOptions.getInstance(), + [$('pointer-settings-button')]); + OptionsPage.registerOverlay(PreferredNetworks.getInstance(), + BrowserOptions.getInstance()); + OptionsPage.registerOverlay( + new OptionsPage('languageChewing', + loadTimeData.getString('languageChewingPageTabTitle'), + 'languageChewingPage'), + LanguageOptions.getInstance()); + OptionsPage.registerOverlay( + new OptionsPage('languageHangul', + loadTimeData.getString('languageHangulPageTabTitle'), + 'languageHangulPage'), + LanguageOptions.getInstance()); + OptionsPage.registerOverlay( + new OptionsPage('languageMozc', + loadTimeData.getString('languageMozcPageTabTitle'), + 'languageMozcPage'), + LanguageOptions.getInstance()); + OptionsPage.registerOverlay( + new OptionsPage('languagePinyin', + loadTimeData.getString('languagePinyinPageTabTitle'), + 'languagePinyinPage'), + LanguageOptions.getInstance()); + } + + + if (SetWallpaperOptions) { + OptionsPage.registerOverlay(SetWallpaperOptions.getInstance(), + BrowserOptions.getInstance(), + [$('set-wallpaper')]); + } + + + if (!cr.isWindows && !cr.isMac) { + OptionsPage.registerOverlay(CertificateBackupOverlay.getInstance(), + CertificateManager.getInstance()); + OptionsPage.registerOverlay(CertificateEditCaTrustOverlay.getInstance(), + CertificateManager.getInstance()); + OptionsPage.registerOverlay(CertificateImportErrorOverlay.getInstance(), + CertificateManager.getInstance()); + OptionsPage.registerOverlay(CertificateManager.getInstance(), + BrowserOptions.getInstance(), + [$('certificatesManageButton')]); + OptionsPage.registerOverlay(CertificateRestoreOverlay.getInstance(), + CertificateManager.getInstance()); + } + + OptionsFocusManager.getInstance().initialize(); + Preferences.getInstance().initialize(); + OptionsPage.initialize(); + + var path = document.location.pathname; + + if (path.length > 1) { + // Skip starting slash and remove trailing slash (if any). + var pageName = path.slice(1).replace(/\/$/, ''); + OptionsPage.showPageByName(pageName, true, {replaceState: true}); + } else { + OptionsPage.showDefaultPage(); + } + + var subpagesNavTabs = document.querySelectorAll('.subpages-nav-tabs'); + for (var i = 0; i < subpagesNavTabs.length; i++) { + subpagesNavTabs[i].onclick = function(event) { + OptionsPage.showTab(event.srcElement); + }; + } + + if (navigator.plugins['Shockwave Flash']) + document.documentElement.setAttribute('hasFlashPlugin', ''); + + window.setTimeout(function() { + document.documentElement.classList.remove('loading'); + }); +} + +document.documentElement.classList.add('loading'); +document.addEventListener('DOMContentLoaded', load); + +/** + * Listener for the |beforeunload| event. + */ +window.onbeforeunload = function() { + options.OptionsPage.willClose(); +}; + +/** + * Listener for the |popstate| event. + * @param {Event} e The |popstate| event. + */ +window.onpopstate = function(e) { + options.OptionsPage.setState(e.state); +}; diff --git a/chrome/browser/resources/options/options_bundle.js b/chrome/browser/resources/options/options_bundle.js new file mode 100644 index 0000000..2bbd43d47 --- /dev/null +++ b/chrome/browser/resources/options/options_bundle.js @@ -0,0 +1,100 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file exists to aggregate all of the javascript used by the +// settings page into a single file which will be flattened and served +// as a single resource. + + + + + + + + + + + + + + + + + + + + + + + + + + + + var AccountsOptions = options.AccountsOptions; + var ChangePictureOptions = options.ChangePictureOptions; + var DetailsInternetPage = options.internet.DetailsInternetPage; + var DisplayOptions = options.DisplayOptions; + var BluetoothOptions = options.BluetoothOptions; + var BluetoothPairing = options.BluetoothPairing; + var KeyboardOverlay = options.KeyboardOverlay; + var PointerOverlay = options.PointerOverlay; + var UIAccountTweaks = uiAccountTweaks.UIAccountTweaks; + + + + var SetWallpaperOptions = options.SetWallpaperOptions; + + + + + + + + + var CertificateManager = options.CertificateManager; + var CertificateRestoreOverlay = options.CertificateRestoreOverlay; + var CertificateBackupOverlay = options.CertificateBackupOverlay; + var CertificateEditCaTrustOverlay = options.CertificateEditCaTrustOverlay; + var CertificateImportErrorOverlay = options.CertificateImportErrorOverlay; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chrome/browser/resources/options/options_focus_manager.js b/chrome/browser/resources/options/options_focus_manager.js new file mode 100644 index 0000000..d608141 --- /dev/null +++ b/chrome/browser/resources/options/options_focus_manager.js @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var FocusManager = cr.ui.FocusManager; + var OptionsPage = options.OptionsPage; + + function OptionsFocusManager() { + } + + cr.addSingletonGetter(OptionsFocusManager); + + OptionsFocusManager.prototype = { + __proto__: FocusManager.prototype, + + /** @inheritDoc */ + getFocusParent: function() { + var topPage = OptionsPage.getTopmostVisiblePage().pageDiv; + + // The default page and search page include a search field that is a + // sibling of the rest of the page instead of a child. Thus, use the + // parent node to allow the search field to receive focus. + if (topPage.parentNode.id == 'page-container') + return topPage.parentNode; + + return topPage; + }, + }; + + return { + OptionsFocusManager: OptionsFocusManager, + }; +}); diff --git a/chrome/browser/resources/options/options_page.css b/chrome/browser/resources/options/options_page.css new file mode 100644 index 0000000..90d9c70 --- /dev/null +++ b/chrome/browser/resources/options/options_page.css @@ -0,0 +1,584 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +body { + position: relative; +} + +#main-content { + bottom: 0; + display: -webkit-box; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +#mainview { + -webkit-box-align: stretch; + bottom: 0; + left: 0; + margin: 0; + position: absolute; + right: 0; + top: 0; + z-index: 1; +} + +#mainview-content { + min-height: 100%; + position: relative; +} + +#page-container { + box-sizing: border-box; + max-width: 888px; + min-width: 600px; +} + +body.uber-frame #searchBox { + position: fixed; + z-index: 4; +} + +div.disabled { + color: #999; +} + +.settings-row { + display: block; + margin: 0.65em 0; +} + +.hbox { + -webkit-box-orient: horizontal; + display: -webkit-box; +} + +.vbox { + -webkit-box-orient: vertical; + display: -webkit-box; +} + +.box-align-center { + -webkit-box-align: center; +} + +.stretch { + -webkit-box-flex: 1; +} + +.frozen { + position: fixed; +} + +#overlay-container-1 { + z-index: 11; +} +#overlay-container-2 { + z-index: 12; +} +#overlay-container-3 { + z-index: 13; +} + +.raw-button, +.raw-button:hover, +.raw-button:active { + -webkit-box-shadow: none; + background-color: transparent; + background-repeat: no-repeat; + border: none; + min-width: 0; + padding: 1px 6px; +} + +.bottom-strip { + border-top: none; + bottom: 0; + padding: 12px; + position: absolute; + right: 0; +} + +/* Omit top padding (currently only on #settings) whenever the search page is + * showing. + */ +#searchPage:not([hidden]) + #settings { + padding-top: 0; +} + +.page list { + /* Min height is a multiple of the list item height (32) */ + min-height: 192px; +} + +.option { + margin-top: 0; +} + +.transparent { + opacity: 0; +} + +.touch-slider { + -webkit-appearance: slider-horizontal; +} + +.settings-list, +.settings-list-empty { + border: 1px solid #d9d9d9; + border-radius: 2px; +} + +.settings-list-empty { + background-color: #f4f4f4; + box-sizing: border-box; + min-height: 125px; + padding-left: 20px; + padding-top: 20px; +} + + +/* Editable text field properties */ +.editable-text-field > * { + -webkit-box-align: center; + -webkit-transition: 150ms background-color; + border: none; + box-sizing: border-box; + display: -webkit-box; + height: 20px; + margin: 0; +} + +.editable-text-field > .spacer { + /* The above height rule should not apply to spacers. */ + height: 0; +} + +.editable-text-field .editable-text { + padding: 2px 3px; +} + +.editable-text-field .static-text { + height: 24px; + overflow: hidden; + padding: 3px 4px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.editable-text-field:not([editable]) > [displaymode='edit'] { + display: none; +} + +.editable-text-field[editable] > [displaymode='static'] { + display: none; +} + +.editable-text-field[empty] > input[type='text'] { + color: #ccc; + font-style: italic; +} + +.editable-text-field[disabled] { + opacity: 0.6; +} + +/* Editable List properties */ +list > * { + -webkit-box-align: center; + -webkit-transition: 150ms background-color; + border: none; + border-radius: 0; /* TODO(dbeam): Is this necessary? */ + box-sizing: border-box; + display: -webkit-box; + height: 32px; + margin: 0; +} + +list > .spacer { + /* The above height rule should not apply to spacers. When redraw is called + on the list they will be given an explicit element height but this ensures + they have 0 height to begin with. */ + height: 0; +} + +list:not([disabled]) > :hover { + background-color: rgb(228, 236, 247); +} + +/* TODO(stuartmorgan): Once this becomes the list style for other WebUI pages + * these rules can be simplified (since they wont need to override other rules). + */ + +list:not([hasElementFocus]) > [selected], +list:not([hasElementFocus]) > [lead][selected] { + background-color: #d0d0d0; + background-image: none; +} + +list[hasElementFocus] > [selected], +list[hasElementFocus] > [lead][selected], +list:not([hasElementFocus]) > [selected]:hover, +list:not([hasElementFocus]) > [selected][lead]:hover { + background-color: rgb(187, 206, 233); + background-image: none; +} + +list[hasElementFocus] > [lead], +list[hasElementFocus] > [lead][selected] { + border-bottom: 1px solid rgb(120, 146, 180); + border-top: 1px solid rgb(120, 146, 180); +} + +list[hasElementFocus] > [lead]:nth-child(2), +list[hasElementFocus] > [lead][selected]:nth-child(2) { + border-top: 1px solid transparent; +} + +list[hasElementFocus] > [lead]:nth-last-child(2), +list[hasElementFocus] > [lead][selected]:nth-last-child(2) { + border-bottom: 1px solid transparent; +} + +list[disabled] > [lead][selected], +list[disabled]:focus > [lead][selected] { + border: none; +} + +list[disabled] { + opacity: 0.6; +} + +list > .heading { + color: #666; +} + +list > .heading:hover { + background-color: transparent; + border-color: transparent; +} + +list .deletable-item { + -webkit-box-align: center; +} + +list .deletable-item > :first-child { + -webkit-box-align: center; + -webkit-box-flex: 1; + -webkit-padding-end: 5px; + display: -webkit-box; +} + +list .row-delete-button { + -webkit-transition: 150ms opacity; + background-color: transparent; + /* TODO(stuartmorgan): Replace with real images once they are available. */ + background-image: -webkit-image-set( + url('../../../../ui/resources/default_100_percent/close_bar.png') 1x, + url('../../../../ui/resources/default_200_percent/close_bar.png') 2x); + border: none; + display: block; + height: 16px; + opacity: 1; + width: 16px; +} + +list > *:not(:hover):not([selected]):not([lead]) .row-delete-button, +list:not([hasElementFocus]) > *:not(:hover):not([selected]) .row-delete-button, +list[disabled] .row-delete-button, +list .row-delete-button[disabled] { + opacity: 0; + pointer-events: none; +} + +/* HostedApp entries use the disabled closing button to display the App's + * favicon, as an indicator that instead of deleting the permission here + * the user has to remove the hosted app.*/ +list div[role='listitem'][managedby='HostedApp'] .row-delete-button { + opacity: 1; +} + +list .row-delete-button:hover { + background-image: -webkit-image-set( + url('../../../../ui/resources/default_100_percent/close_bar_hover.png') + 1x, + url('../../../../ui/resources/default_200_percent/close_bar_hover.png') + 2x); +} + +list .row-delete-button:active { + background-image: -webkit-image-set( + url('../../../../ui/resources/default_100_percent/close_bar_pressed.png') + 1x, + url('../../../../ui/resources/default_200_percent/close_bar_pressed.png') + 2x); +} + +list .static-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +list[type='text'][inlineeditable] input { + box-sizing: border-box; + margin: 0; + width: 100%; +} + +list > :not([editing]) [displaymode='edit'] { + display: none; +} + +list > [editing] [displaymode='static'] { + display: none; +} + +list > [editing] input:invalid { + /* TODO(stuartmorgan): Replace with validity badge */ + background-color: pink; +} + +.list-inline-button { + -webkit-appearance: none; + -webkit-transition: opacity 150ms; + background: rgb(138, 170, 237); + border: none; + border-radius: 2px; + color: white; + font-weight: bold; + opacity: 0.7; +} + +.list-inline-button:hover { + opacity: 1; +} + +.option-name { + padding-right: 5px; +} + +html[dir=rtl].option-name { + padding-left: 5px; +} + +.favicon-cell { + -webkit-padding-start: 20px; + background-position: left; + background-repeat: no-repeat; +} + +input[type='url'].favicon-cell { + -webkit-padding-start: 22px; + background-position-x: 4px; +} + +/* TODO(jhawkins): Use something better than 99.3% when CSS3 background + * positioning is available. + */ +html[dir=rtl] input.favicon-cell { + background-position-x: 99.3%; +} + +list .favicon-cell { + -webkit-margin-start: 7px; + -webkit-padding-start: 26px; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +html[dir=rtl] list .favicon-cell { + background-position: right; +} + +html[enable-background-mode=false] #background-mode-section { + display: none; +} + +/* UI Controls */ + +/* LIST */ +list[hasElementFocus] { + + outline: 1px solid rgba(0, 128, 256, 0.5); + outline-offset: -2px; + + + /* This matches the native list outline on Mac */ + outline-color: rgb(117, 154, 217); + outline-offset: -1px; + outline-style: auto; + outline-width: 5px; + +} + +.suboption { + -webkit-margin-start: 23px; +} + +list.autocomplete-suggestions { + background-color: white; + border: 1px solid #aaa; + border-radius: 2px; + min-height: 0; + opacity: 0.9; + position: fixed; + z-index: 3; +} + +list.autocomplete-suggestions > div { + height: auto; +} + +list.autocomplete-suggestions:not([hasElementFocus]) > [selected], +list.autocomplete-suggestions:not([hasElementFocus]) > [lead][selected] { + background-color: rgb(187, 206, 233); +} + +html:not([hasFlashPlugin]) .flash-plugin-area, +/* If the Flash plug-in supports the NPP_ClearSiteData API, we don't need to + * show the link to the Flash storage settings manager: + */ +html[flashPluginSupportsClearSiteData] .flash-plugin-area, +html:not([flashPluginSupportsClearSiteData]) .clear-plugin-lso-data-enabled, +html[flashPluginSupportsClearSiteData] .clear-plugin-lso-data-disabled, +html:not([enablePepperFlashSettings]) .pepper-flash-settings { + display: none; +} + +/* Controlled setting indicator and bubble. */ +.controlled-setting-indicator { + display: inline-block; + /* Establish a containing block for absolutely positioning the bubble. */ + position: relative; + vertical-align: text-bottom; +} + +.controlled-setting-indicator[controlled-by] summary { + background-size: contain; + height: 16px; + width: 16px; +} + +.controlled-setting-indicator summary::-webkit-details-marker { + display: none; +} + +.controlled-setting-indicator[controlled-by='policy'] summary { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY_GRAY'); + background-size: 16px; +} + +.controlled-setting-indicator[controlled-by='policy'] summary:hover { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY'); + background-size: 16px; +} + +.controlled-setting-indicator[controlled-by='extension'] summary { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION_GRAY'); + background-size: 16px; +} + +.controlled-setting-indicator[controlled-by='extension'] summary:hover { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION'); + background-size: 16px; +} + +.controlled-setting-indicator[controlled-by='recommended'] summary { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED_GRAY'); + background-size: 16px; +} + +.controlled-setting-indicator[controlled-by='recommended'] summary:hover { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED'); + background-size: 16px; +} + +.controlled-setting-bubble { + -webkit-margin-start: -20px; + background-color: white; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0 2px 2px #ddd; + margin-top: 10px; + padding: 10px; + position: absolute; + top: 50%; + z-index: 10; +} + +html[dir='ltr'] .controlled-setting-bubble { + left: 50%; +} + +html[dir='rtl'] .controlled-setting-bubble { + right: 50%; +} + +.controlled-setting-bubble::before { + -webkit-margin-start: 4px; + border-color: #ccc transparent; + border-style: solid; + border-width: 0 5px 5px; + content: ''; + position: absolute; + top: -5px; +} + +.controlled-setting-bubble::after { + -webkit-margin-start: 5px; + border-color: white transparent; + border-style: solid; + border-width: 0 4px 4px; + content: ''; + position: absolute; + top: -4px; +} + +.controlled-setting-bubble-text { + -webkit-padding-start: 30px; + background-repeat: no-repeat; + margin: 0; + min-height: 32px; + min-width: 200px; +} + +.controlled-setting-indicator[controlled-by='policy'] + .controlled-setting-bubble-text { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY_LARGE'); + background-size: 22px; +} + +.controlled-setting-indicator[controlled-by='extension'] + .controlled-setting-bubble-text { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION_LARGE'); + background-size: 22px; +} + +.controlled-setting-indicator[controlled-by='recommended'] + .controlled-setting-bubble-text { + background-image: + url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED_LARGE'); + background-size: 22px; +} + +html[dir='rtl'] .controlled-setting-bubble-text { + background-position: right top; +} + +.controlled-setting-bubble-action { + padding: 0 !important; +} diff --git a/chrome/browser/resources/options/options_page.js b/chrome/browser/resources/options/options_page.js new file mode 100644 index 0000000..7401694 --- /dev/null +++ b/chrome/browser/resources/options/options_page.js @@ -0,0 +1,908 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + ///////////////////////////////////////////////////////////////////////////// + // OptionsPage class: + + /** + * Base class for options page. + * @constructor + * @param {string} name Options page name. + * @param {string} title Options page title, used for history. + * @extends {EventTarget} + */ + function OptionsPage(name, title, pageDivName) { + this.name = name; + this.title = title; + this.pageDivName = pageDivName; + this.pageDiv = $(this.pageDivName); + this.tab = null; + this.lastFocusedElement = null; + } + + /** @const */ var HORIZONTAL_OFFSET = 155; + + /** + * This is the absolute difference maintained between standard and + * fixed-width font sizes. Refer http://crbug.com/91922. + */ + OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3; + + /** + * Main level option pages. Maps lower-case page names to the respective page + * object. + * @protected + */ + OptionsPage.registeredPages = {}; + + /** + * Pages which are meant to behave like modal dialogs. Maps lower-case overlay + * names to the respective overlay object. + * @protected + */ + OptionsPage.registeredOverlayPages = {}; + + /** + * Gets the default page (to be shown on initial load). + */ + OptionsPage.getDefaultPage = function() { + return BrowserOptions.getInstance(); + }; + + /** + * Shows the default page. + */ + OptionsPage.showDefaultPage = function() { + this.navigateToPage(this.getDefaultPage().name); + }; + + /** + * "Navigates" to a page, meaning that the page will be shown and the + * appropriate entry is placed in the history. + * @param {string} pageName Page name. + */ + OptionsPage.navigateToPage = function(pageName) { + this.showPageByName(pageName, true); + }; + + /** + * Shows a registered page. This handles both top-level and overlay pages. + * @param {string} pageName Page name. + * @param {boolean} updateHistory True if we should update the history after + * showing the page. + * @param {Object=} opt_propertyBag An optional bag of properties including + * replaceState (if history state should be replaced instead of pushed). + * @private + */ + OptionsPage.showPageByName = function(pageName, + updateHistory, + opt_propertyBag) { + // If |opt_propertyBag| is non-truthy, homogenize to object. + opt_propertyBag = opt_propertyBag || {}; + + // Find the currently visible root-level page. + var rootPage = null; + for (var name in this.registeredPages) { + var page = this.registeredPages[name]; + if (page.visible && !page.parentPage) { + rootPage = page; + break; + } + } + + // Find the target page. + var targetPage = this.registeredPages[pageName.toLowerCase()]; + if (!targetPage || !targetPage.canShowPage()) { + // If it's not a page, try it as an overlay. + if (!targetPage && this.showOverlay_(pageName, rootPage)) { + if (updateHistory) + this.updateHistoryState_(!!opt_propertyBag.replaceState); + return; + } else { + targetPage = this.getDefaultPage(); + } + } + + pageName = targetPage.name.toLowerCase(); + var targetPageWasVisible = targetPage.visible; + + // Determine if the root page is 'sticky', meaning that it + // shouldn't change when showing an overlay. This can happen for special + // pages like Search. + var isRootPageLocked = + rootPage && rootPage.sticky && targetPage.parentPage; + + var allPageNames = Array.prototype.concat.call( + Object.keys(this.registeredPages), + Object.keys(this.registeredOverlayPages)); + + // Notify pages if they will be hidden. + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; + if (!page.parentPage && isRootPageLocked) + continue; + if (page.willHidePage && name != pageName && + !page.isAncestorOfPage(targetPage)) { + page.willHidePage(); + } + } + + // Update visibilities to show only the hierarchy of the target page. + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; + if (!page.parentPage && isRootPageLocked) + continue; + page.visible = name == pageName || page.isAncestorOfPage(targetPage); + } + + // Update the history and current location. + if (updateHistory) + this.updateHistoryState_(!!opt_propertyBag.replaceState); + + // Update tab title. + this.setTitle_(targetPage.title); + + // Notify pages if they were shown. + for (var i = 0; i < allPageNames.length; ++i) { + var name = allPageNames[i]; + var page = this.registeredPages[name] || + this.registeredOverlayPages[name]; + if (!page.parentPage && isRootPageLocked) + continue; + if (!targetPageWasVisible && page.didShowPage && + (name == pageName || page.isAncestorOfPage(targetPage))) { + page.didShowPage(); + } + } + }; + + /** + * Sets the title of the page. This is accomplished by calling into the + * parent page API. + * @param {String} title The title string. + * @private + */ + OptionsPage.setTitle_ = function(title) { + uber.invokeMethodOnParent('setTitle', {title: title}); + }; + + /** + * Scrolls the page to the correct position (the top when opening an overlay, + * or the old scroll position a previously hidden overlay becomes visible). + * @private + */ + OptionsPage.updateScrollPosition_ = function() { + var container = $('page-container'); + var scrollTop = container.oldScrollTop || 0; + container.oldScrollTop = undefined; + window.scroll(document.body.scrollLeft, scrollTop); + }; + + /** + * Pushes the current page onto the history stack, overriding the last page + * if it is the generic chrome://settings/. + * @param {boolean} replace If true, allow no history events to be created. + * @param {object=} opt_params A bag of optional params, including: + * {boolean} ignoreHash Whether to include the hash or not. + * @private + */ + OptionsPage.updateHistoryState_ = function(replace, opt_params) { + var page = this.getTopmostVisiblePage(); + var path = window.location.pathname + window.location.hash; + if (path) + path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash. + // The page is already in history (the user may have clicked the same link + // twice). Do nothing. + if (path == page.name && + !document.documentElement.classList.contains('loading')) { + return; + } + + var hash = opt_params && opt_params.ignoreHash ? '' : window.location.hash; + + // If settings are embedded, tell the outer page to set its "path" to the + // inner frame's path. + var outerPath = (page == this.getDefaultPage() ? '' : page.name) + hash; + uber.invokeMethodOnParent('setPath', {path: outerPath}); + + // If there is no path, the current location is chrome://settings/. + // Override this with the new page. + var historyFunction = path && !replace ? window.history.pushState : + window.history.replaceState; + historyFunction.call(window.history, + {pageName: page.name}, + page.title, + '/' + page.name + hash); + + // Update tab title. + this.setTitle_(page.title); + }; + + /** + * Shows a registered Overlay page. Does not update history. + * @param {string} overlayName Page name. + * @param {OptionPage} rootPage The currently visible root-level page. + * @return {boolean} whether we showed an overlay. + */ + OptionsPage.showOverlay_ = function(overlayName, rootPage) { + var overlay = this.registeredOverlayPages[overlayName.toLowerCase()]; + if (!overlay || !overlay.canShowPage()) + return false; + + // Save the currently focused element in the page for restoration later. + var currentPage = this.getTopmostVisiblePage(); + if (currentPage) + currentPage.lastFocusedElement = document.activeElement; + + if ((!rootPage || !rootPage.sticky) && overlay.parentPage) + this.showPageByName(overlay.parentPage.name, false); + + if (!overlay.visible) { + overlay.visible = true; + if (overlay.didShowPage) overlay.didShowPage(); + } + + // Update tab title. + this.setTitle_(overlay.title); + + $('searchBox').setAttribute('aria-hidden', true); + + return true; + }; + + /** + * Returns whether or not an overlay is visible. + * @return {boolean} True if an overlay is visible. + * @private + */ + OptionsPage.isOverlayVisible_ = function() { + return this.getVisibleOverlay_() != null; + }; + + /** + * Returns the currently visible overlay, or null if no page is visible. + * @return {OptionPage} The visible overlay. + */ + OptionsPage.getVisibleOverlay_ = function() { + var topmostPage = null; + for (var name in this.registeredOverlayPages) { + var page = this.registeredOverlayPages[name]; + if (page.visible && + (!topmostPage || page.nestingLevel > topmostPage.nestingLevel)) { + topmostPage = page; + } + } + return topmostPage; + }; + + /** + * Restores the last focused element on a given page. + */ + OptionsPage.restoreLastFocusedElement_ = function() { + var currentPage = this.getTopmostVisiblePage(); + if (currentPage.lastFocusedElement) + currentPage.lastFocusedElement.focus(); + }; + + /** + * Closes the visible overlay. Updates the history state after closing the + * overlay. + */ + OptionsPage.closeOverlay = function() { + var overlay = this.getVisibleOverlay_(); + if (!overlay) + return; + + overlay.visible = false; + + if (overlay.didClosePage) overlay.didClosePage(); + this.updateHistoryState_(false, {ignoreHash: true}); + + this.restoreLastFocusedElement_(); + if (!this.isOverlayVisible_()) + $('searchBox').removeAttribute('aria-hidden'); + }; + + /** + * Cancels (closes) the overlay, due to the user pressing . + */ + OptionsPage.cancelOverlay = function() { + // Blur the active element to ensure any changed pref value is saved. + document.activeElement.blur(); + var overlay = this.getVisibleOverlay_(); + // Let the overlay handle the if it wants to. + if (overlay.handleCancel) { + overlay.handleCancel(); + this.restoreLastFocusedElement_(); + } else { + this.closeOverlay(); + } + }; + + /** + * Hides the visible overlay. Does not affect the history state. + * @private + */ + OptionsPage.hideOverlay_ = function() { + var overlay = this.getVisibleOverlay_(); + if (overlay) + overlay.visible = false; + }; + + /** + * Returns the pages which are currently visible, ordered by nesting level + * (ascending). + * @return {Array.OptionPage} The pages which are currently visible, ordered + * by nesting level (ascending). + */ + OptionsPage.getVisiblePages_ = function() { + var visiblePages = []; + for (var name in this.registeredPages) { + var page = this.registeredPages[name]; + if (page.visible) + visiblePages[page.nestingLevel] = page; + } + return visiblePages; + }; + + /** + * Returns the topmost visible page (overlays excluded). + * @return {OptionPage} The topmost visible page aside any overlay. + * @private + */ + OptionsPage.getTopmostVisibleNonOverlayPage_ = function() { + var topPage = null; + for (var name in this.registeredPages) { + var page = this.registeredPages[name]; + if (page.visible && + (!topPage || page.nestingLevel > topPage.nestingLevel)) + topPage = page; + } + + return topPage; + }; + + /** + * Returns the topmost visible page, or null if no page is visible. + * @return {OptionPage} The topmost visible page. + */ + OptionsPage.getTopmostVisiblePage = function() { + // Check overlays first since they're top-most if visible. + return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_(); + }; + + /** + * Updates managed banner visibility state based on the topmost page. + */ + OptionsPage.updateManagedBannerVisibility = function() { + var topPage = this.getTopmostVisiblePage(); + if (topPage) + topPage.updateManagedBannerVisibility(); + }; + + /** + * Shows the tab contents for the given navigation tab. + * @param {!Element} tab The tab that the user clicked. + */ + OptionsPage.showTab = function(tab) { + // Search parents until we find a tab, or the nav bar itself. This allows + // tabs to have child nodes, e.g. labels in separately-styled spans. + while (tab && !tab.classList.contains('subpages-nav-tabs') && + !tab.classList.contains('tab')) { + tab = tab.parentNode; + } + if (!tab || !tab.classList.contains('tab')) + return; + + // Find tab bar of the tab. + var tabBar = tab; + while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) { + tabBar = tabBar.parentNode; + } + if (!tabBar) + return; + + if (tabBar.activeNavTab != null) { + tabBar.activeNavTab.classList.remove('active-tab'); + $(tabBar.activeNavTab.getAttribute('tab-contents')).classList. + remove('active-tab-contents'); + } + + tab.classList.add('active-tab'); + $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents'); + tabBar.activeNavTab = tab; + }; + + /** + * Registers new options page. + * @param {OptionsPage} page Page to register. + */ + OptionsPage.register = function(page) { + this.registeredPages[page.name.toLowerCase()] = page; + page.initializePage(); + }; + + /** + * Find an enclosing section for an element if it exists. + * @param {Element} element Element to search. + * @return {OptionPage} The section element, or null. + * @private + */ + OptionsPage.findSectionForNode_ = function(node) { + while (node = node.parentNode) { + if (node.nodeName == 'SECTION') + return node; + } + return null; + }; + + /** + * Registers a new Overlay page. + * @param {OptionsPage} overlay Overlay to register. + * @param {OptionsPage} parentPage Associated parent page for this overlay. + * @param {Array} associatedControls Array of control elements associated with + * this page. + */ + OptionsPage.registerOverlay = function(overlay, + parentPage, + associatedControls) { + this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; + overlay.parentPage = parentPage; + if (associatedControls) { + overlay.associatedControls = associatedControls; + if (associatedControls.length) { + overlay.associatedSection = + this.findSectionForNode_(associatedControls[0]); + } + } + + // Reverse the button strip for views. See the documentation of + // reverseButtonStrip_() for an explanation of why this is necessary. + if (cr.isViews) + this.reverseButtonStrip_(overlay); + + overlay.tab = undefined; + overlay.isOverlay = true; + overlay.initializePage(); + }; + + /** + * Reverses the child elements of a button strip. This is necessary because + * WebKit does not alter the tab order for elements that are visually reversed + * using -webkit-box-direction: reverse, and the button order is reversed for + * views. See https://bugs.webkit.org/show_bug.cgi?id=62664 for more + * information. + * @param {Object} overlay The overlay containing the button strip to reverse. + * @private + */ + OptionsPage.reverseButtonStrip_ = function(overlay) { + var buttonStrips = overlay.pageDiv.querySelectorAll('.button-strip'); + + // Reverse all button-strips in the overlay. + for (var j = 0; j < buttonStrips.length; j++) { + var buttonStrip = buttonStrips[j]; + + var childNodes = buttonStrip.childNodes; + for (var i = childNodes.length - 1; i >= 0; i--) + buttonStrip.appendChild(childNodes[i]); + } + }; + + /** + * Callback for window.onpopstate. + * @param {Object} data State data pushed into history. + */ + OptionsPage.setState = function(data) { + if (data && data.pageName) { + this.willClose(); + this.showPageByName(data.pageName, false); + } + }; + + /** + * Callback for window.onbeforeunload. Used to notify overlays that they will + * be closed. + */ + OptionsPage.willClose = function() { + var overlay = this.getVisibleOverlay_(); + if (overlay && overlay.didClosePage) + overlay.didClosePage(); + }; + + /** + * Freezes/unfreezes the scroll position of the root page container. + * @param {boolean} freeze Whether the page should be frozen. + * @private + */ + OptionsPage.setRootPageFrozen_ = function(freeze) { + var container = $('page-container'); + if (container.classList.contains('frozen') == freeze) + return; + + if (freeze) { + // Lock the width, since auto width computation may change. + container.style.width = window.getComputedStyle(container).width; + container.oldScrollTop = document.body.scrollTop; + container.classList.add('frozen'); + var verticalPosition = + container.getBoundingClientRect().top - container.oldScrollTop; + container.style.top = verticalPosition + 'px'; + this.updateFrozenElementHorizontalPosition_(container); + } else { + container.classList.remove('frozen'); + container.style.top = ''; + container.style.left = ''; + container.style.right = ''; + container.style.width = ''; + } + }; + + /** + * Freezes/unfreezes the scroll position of the root page based on the current + * page stack. + */ + OptionsPage.updateRootPageFreezeState = function() { + var topPage = OptionsPage.getTopmostVisiblePage(); + if (topPage) + this.setRootPageFrozen_(topPage.isOverlay); + }; + + /** + * Initializes the complete options page. This will cause all C++ handlers to + * be invoked to do final setup. + */ + OptionsPage.initialize = function() { + chrome.send('coreOptionsInitialize'); + uber.onContentFrameLoaded(); + + document.addEventListener('scroll', this.handleScroll_.bind(this)); + + // Trigger the scroll handler manually to set the initial state. + this.handleScroll_(); + + // Shake the dialog if the user clicks outside the dialog bounds. + var containers = [$('overlay-container-1'), $('overlay-container-2')]; + for (var i = 0; i < containers.length; i++) { + var overlay = containers[i]; + cr.ui.overlay.setupOverlay(overlay); + overlay.addEventListener('cancelOverlay', + OptionsPage.cancelOverlay.bind(OptionsPage)); + } + }; + + /** + * Does a bounds check for the element on the given x, y client coordinates. + * @param {Element} e The DOM element. + * @param {number} x The client X to check. + * @param {number} y The client Y to check. + * @return {boolean} True if the point falls within the element's bounds. + * @private + */ + OptionsPage.elementContainsPoint_ = function(e, x, y) { + var clientRect = e.getBoundingClientRect(); + return x >= clientRect.left && x <= clientRect.right && + y >= clientRect.top && y <= clientRect.bottom; + }; + + /** + * Called when the page is scrolled; moves elements that are position:fixed + * but should only behave as if they are fixed for vertical scrolling. + * @private + */ + OptionsPage.handleScroll_ = function() { + this.updateAllFrozenElementPositions_(); + }; + + /** + * Updates all frozen pages to match the horizontal scroll position. + * @private + */ + OptionsPage.updateAllFrozenElementPositions_ = function() { + var frozenElements = document.querySelectorAll('.frozen'); + for (var i = 0; i < frozenElements.length; i++) + this.updateFrozenElementHorizontalPosition_(frozenElements[i]); + }; + + /** + * Updates the given frozen element to match the horizontal scroll position. + * @param {HTMLElement} e The frozen element to update. + * @private + */ + OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) { + if (isRTL()) + e.style.right = HORIZONTAL_OFFSET + 'px'; + else + e.style.left = HORIZONTAL_OFFSET - document.body.scrollLeft + 'px'; + }; + + OptionsPage.setClearPluginLSODataEnabled = function(enabled) { + if (enabled) { + document.documentElement.setAttribute( + 'flashPluginSupportsClearSiteData', ''); + } else { + document.documentElement.removeAttribute( + 'flashPluginSupportsClearSiteData'); + } + }; + + OptionsPage.setPepperFlashSettingsEnabled = function(enabled) { + if (enabled) { + document.documentElement.setAttribute( + 'enablePepperFlashSettings', ''); + } else { + document.documentElement.removeAttribute( + 'enablePepperFlashSettings'); + } + }; + + OptionsPage.prototype = { + __proto__: cr.EventTarget.prototype, + + /** + * The parent page of this option page, or null for top-level pages. + * @type {OptionsPage} + */ + parentPage: null, + + /** + * The section on the parent page that is associated with this page. + * Can be null. + * @type {Element} + */ + associatedSection: null, + + /** + * An array of controls that are associated with this page. The first + * control should be located on a top-level page. + * @type {OptionsPage} + */ + associatedControls: null, + + /** + * Initializes page content. + */ + initializePage: function() {}, + + /** + * Updates managed banner visibility state. This function iterates over + * all input fields of a page and if any of these is marked as managed + * it triggers the managed banner to be visible. The banner can be enforced + * being on through the managed flag of this class but it can not be forced + * being off if managed items exist. + */ + updateManagedBannerVisibility: function() { + var bannerDiv = this.pageDiv.querySelector('.managed-prefs-banner'); + // Create a banner for the overlay if we don't have one. + if (!bannerDiv) { + bannerDiv = $('managed-prefs-banner').cloneNode(true); + bannerDiv.id = null; + + if (this.isOverlay) { + var content = this.pageDiv.querySelector('.content-area'); + content.parentElement.insertBefore(bannerDiv, content); + } else { + bannerDiv.classList.add('main-page-banner'); + var header = this.pageDiv.querySelector('header'); + header.appendChild(bannerDiv); + } + } + + var controlledByPolicy = false; + var controlledByExtension = false; + var inputElements = this.pageDiv.querySelectorAll('input[controlled-by]'); + for (var i = 0; i < inputElements.length; i++) { + if (inputElements[i].controlledBy == 'policy') + controlledByPolicy = true; + else if (inputElements[i].controlledBy == 'extension') + controlledByExtension = true; + } + + if (!controlledByPolicy && !controlledByExtension) { + this.pageDiv.classList.remove('showing-banner'); + } else { + this.pageDiv.classList.add('showing-banner'); + + var text = bannerDiv.querySelector('#managed-prefs-text'); + if (controlledByPolicy && !controlledByExtension) { + text.textContent = + loadTimeData.getString('policyManagedPrefsBannerText'); + } else if (!controlledByPolicy && controlledByExtension) { + text.textContent = + loadTimeData.getString('extensionManagedPrefsBannerText'); + } else if (controlledByPolicy && controlledByExtension) { + text.textContent = loadTimeData.getString( + 'policyAndExtensionManagedPrefsBannerText'); + } + } + }, + + /** + * Gets the container div for this page if it is an overlay. + * @type {HTMLElement} + */ + get container() { + assert(this.isOverlay); + return this.pageDiv.parentNode; + }, + + /** + * Gets page visibility state. + * @type {boolean} + */ + get visible() { + // If this is an overlay dialog it is no longer considered visible while + // the overlay is fading out. See http://crbug.com/118629. + if (this.isOverlay && + this.container.classList.contains('transparent')) { + return false; + } + return !this.pageDiv.hidden; + }, + + /** + * Sets page visibility. + * @type {boolean} + */ + set visible(visible) { + if ((this.visible && visible) || (!this.visible && !visible)) + return; + + // If using an overlay, the visibility of the dialog is toggled at the + // same time as the overlay to show the dialog's out transition. This + // is handled in setOverlayVisible. + if (this.isOverlay) { + this.setOverlayVisible_(visible); + } else { + this.pageDiv.hidden = !visible; + this.onVisibilityChanged_(); + } + + cr.dispatchPropertyChange(this, 'visible', visible, !visible); + }, + + /** + * Shows or hides an overlay (including any visible dialog). + * @param {boolean} visible Whether the overlay should be visible or not. + * @private + */ + setOverlayVisible_: function(visible) { + assert(this.isOverlay); + var pageDiv = this.pageDiv; + var container = this.container; + + if (visible) { + uber.invokeMethodOnParent('beginInterceptingEvents'); + this.pageDiv.removeAttribute('aria-hidden'); + if (this.parentPage) + this.parentPage.pageDiv.setAttribute('aria-hidden', true); + } else { + if (this.parentPage) + this.parentPage.pageDiv.removeAttribute('aria-hidden'); + } + + if (container.hidden != visible) { + if (visible) { + // If the container is set hidden and then immediately set visible + // again, the fadeCompleted_ callback would cause it to be erroneously + // hidden again. Removing the transparent tag avoids that. + container.classList.remove('transparent'); + + // Hide all dialogs in this container since a different one may have + // been previously visible before fading out. + var pages = container.querySelectorAll('.page'); + for (var i = 0; i < pages.length; i++) + pages[i].hidden = true; + // Show the new dialog. + pageDiv.hidden = false; + } + return; + } + + if (visible) { + container.hidden = false; + pageDiv.hidden = false; + // NOTE: This is a hacky way to force the container to layout which + // will allow us to trigger the webkit transition. + container.scrollTop; + container.classList.remove('transparent'); + this.onVisibilityChanged_(); + } else { + var self = this; + // TODO: Use an event delegate to avoid having to subscribe and + // unsubscribe for webkitTransitionEnd events. + container.addEventListener('webkitTransitionEnd', function f(e) { + if (e.target != e.currentTarget || e.propertyName != 'opacity') + return; + container.removeEventListener('webkitTransitionEnd', f); + self.fadeCompleted_(); + }); + container.classList.add('transparent'); + } + }, + + /** + * Called when a container opacity transition finishes. + * @private + */ + fadeCompleted_: function() { + if (this.container.classList.contains('transparent')) { + this.pageDiv.hidden = true; + this.container.hidden = true; + this.onVisibilityChanged_(); + if (this.nestingLevel == 1) + uber.invokeMethodOnParent('stopInterceptingEvents'); + } + }, + + /** + * Called when a page is shown or hidden to update the root options page + * based on this page's visibility. + * @private + */ + onVisibilityChanged_: function() { + OptionsPage.updateRootPageFreezeState(); + OptionsPage.updateManagedBannerVisibility(); + + if (this.isOverlay && !this.visible) + OptionsPage.updateScrollPosition_(); + }, + + /** + * The nesting level of this page. + * @type {number} The nesting level of this page (0 for top-level page) + */ + get nestingLevel() { + var level = 0; + var parent = this.parentPage; + while (parent) { + level++; + parent = parent.parentPage; + } + return level; + }, + + /** + * Whether the page is considered 'sticky', such that it will + * remain a top-level page even if sub-pages change. + * @type {boolean} True if this page is sticky. + */ + get sticky() { + return false; + }, + + /** + * Checks whether this page is an ancestor of the given page in terms of + * subpage nesting. + * @param {OptionsPage} page The potential descendent of this page. + * @return {boolean} True if |page| is nested under this page. + */ + isAncestorOfPage: function(page) { + var parent = page.parentPage; + while (parent) { + if (parent == this) + return true; + parent = parent.parentPage; + } + return false; + }, + + /** + * Whether it should be possible to show the page. + * @return {boolean} True if the page should be shown. + */ + canShowPage: function() { + return true; + }, + }; + + // Export + return { + OptionsPage: OptionsPage + }; +}); diff --git a/chrome/browser/resources/options/password_manager.css b/chrome/browser/resources/options/password_manager.css new file mode 100644 index 0000000..18314ef --- /dev/null +++ b/chrome/browser/resources/options/password_manager.css @@ -0,0 +1,28 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#password-manager > div.content-area { + width: 600px; +} + +#password-search-column { + bottom: 10px; + position: absolute; + right: 0; +} + +html[dir=rtl] #password-search-column { + left: 0; + right: auto; +} + +#password-list-headers { + position: relative; + width: 100%; +} + +#passwords-title { + display: inline-block; +} diff --git a/chrome/browser/resources/options/password_manager.html b/chrome/browser/resources/options/password_manager.html new file mode 100644 index 0000000..656e786 --- /dev/null +++ b/chrome/browser/resources/options/password_manager.html @@ -0,0 +1,35 @@ + diff --git a/chrome/browser/resources/options/password_manager.js b/chrome/browser/resources/options/password_manager.js new file mode 100644 index 0000000..43b5e44 --- /dev/null +++ b/chrome/browser/resources/options/password_manager.js @@ -0,0 +1,230 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + ///////////////////////////////////////////////////////////////////////////// + // PasswordManager class: + + /** + * Encapsulated handling of password and exceptions page. + * @constructor + */ + function PasswordManager() { + this.activeNavTab = null; + OptionsPage.call(this, + 'passwords', + loadTimeData.getString('passwordsPageTabTitle'), + 'password-manager'); + } + + cr.addSingletonGetter(PasswordManager); + + PasswordManager.prototype = { + __proto__: OptionsPage.prototype, + + /** + * The saved passwords list. + * @type {DeletableItemList} + * @private + */ + savedPasswordsList_: null, + + /** + * The password exceptions list. + * @type {DeletableItemList} + * @private + */ + passwordExceptionsList_: null, + + /** + * The timer id of the timer set on search query change events. + * @type {number} + * @private + */ + queryDelayTimerId_: 0, + + /** + * The most recent search query, or null if the query is empty. + * @type {?string} + * @private + */ + lastQuery_: null, + + /** @inheritDoc */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + $('password-manager-confirm').onclick = function() { + OptionsPage.closeOverlay(); + }; + + $('password-search-box').addEventListener('search', + this.handleSearchQueryChange_.bind(this)); + + this.createSavedPasswordsList_(); + this.createPasswordExceptionsList_(); + }, + + /** @inheritDoc */ + canShowPage: function() { + return !(cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()); + }, + + /** @inheritDoc */ + didShowPage: function() { + // Updating the password lists may cause a blocking platform dialog pop up + // (Mac, Linux), so we delay this operation until the page is shown. + chrome.send('updatePasswordLists'); + $('password-search-box').focus(); + }, + + /** + * Creates, decorates and initializes the saved passwords list. + * @private + */ + createSavedPasswordsList_: function() { + this.savedPasswordsList_ = $('saved-passwords-list'); + options.passwordManager.PasswordsList.decorate(this.savedPasswordsList_); + this.savedPasswordsList_.autoExpands = true; + }, + + /** + * Creates, decorates and initializes the password exceptions list. + * @private + */ + createPasswordExceptionsList_: function() { + this.passwordExceptionsList_ = $('password-exceptions-list'); + options.passwordManager.PasswordExceptionsList.decorate( + this.passwordExceptionsList_); + this.passwordExceptionsList_.autoExpands = true; + }, + + /** + * Handles search query changes. + * @param {!Event} e The event object. + * @private + */ + handleSearchQueryChange_: function(e) { + if (this.queryDelayTimerId_) + window.clearTimeout(this.queryDelayTimerId_); + + // Searching cookies uses a timeout of 500ms. We use a shorter timeout + // because there are probably fewer passwords and we want the UI to be + // snappier since users will expect that it's "less work." + this.queryDelayTimerId_ = window.setTimeout( + this.searchPasswords_.bind(this), 250); + }, + + /** + * Search passwords using text in |password-search-box|. + * @private + */ + searchPasswords_: function() { + this.queryDelayTimerId_ = 0; + var filter = $('password-search-box').value; + filter = (filter == '') ? null : filter; + if (this.lastQuery_ != filter) { + this.lastQuery_ = filter; + // Searching for passwords has the side effect of requerying the + // underlying password store. This is done intentionally, as on OS X and + // Linux they can change from outside and we won't be notified of it. + chrome.send('updatePasswordLists'); + } + }, + + /** + * Updates the visibility of the list and empty list placeholder. + * @param {!List} list The list to toggle visilibility for. + */ + updateListVisibility_: function(list) { + var empty = list.dataModel.length == 0; + var listPlaceHolderID = list.id + '-empty-placeholder'; + list.hidden = empty; + $(listPlaceHolderID).hidden = !empty; + }, + + /** + * Updates the data model for the saved passwords list with the values from + * |entries|. + * @param {Array} entries The list of saved password data. + */ + setSavedPasswordsList_: function(entries) { + if (this.lastQuery_) { + // Implement password searching here in javascript, rather than in C++. + // The number of saved passwords shouldn't be too big for us to handle. + var query = this.lastQuery_; + var filter = function(entry, index, list) { + // Search both URL and username. + if (entry[0].indexOf(query) >= 0 || entry[1].indexOf(query) >= 0) { + // Keep the original index so we can delete correctly. See also + // deleteItemAtIndex() in password_manager_list.js that uses this. + entry[3] = index; + return true; + } + return false; + }; + entries = entries.filter(filter); + } + this.savedPasswordsList_.dataModel = new ArrayDataModel(entries); + this.updateListVisibility_(this.savedPasswordsList_); + }, + + /** + * Updates the data model for the password exceptions list with the values + * from |entries|. + * @param {Array} entries The list of password exception data. + */ + setPasswordExceptionsList_: function(entries) { + this.passwordExceptionsList_.dataModel = new ArrayDataModel(entries); + this.updateListVisibility_(this.passwordExceptionsList_); + }, + }; + + /** + * Removes a saved password. + * @param {number} rowIndex indicating the row to remove. + */ + PasswordManager.removeSavedPassword = function(rowIndex) { + chrome.send('removeSavedPassword', [String(rowIndex)]); + }; + + /** + * Removes a password exception. + * @param {number} rowIndex indicating the row to remove. + */ + PasswordManager.removePasswordException = function(rowIndex) { + chrome.send('removePasswordException', [String(rowIndex)]); + }; + + /** + * Removes all saved passwords. + */ + PasswordManager.removeAllPasswords = function() { + chrome.send('removeAllSavedPasswords'); + }; + + /** + * Removes all password exceptions. + */ + PasswordManager.removeAllPasswordExceptions = function() { + chrome.send('removeAllPasswordExceptions'); + }; + + PasswordManager.setSavedPasswordsList = function(entries) { + PasswordManager.getInstance().setSavedPasswordsList_(entries); + }; + + PasswordManager.setPasswordExceptionsList = function(entries) { + PasswordManager.getInstance().setPasswordExceptionsList_(entries); + }; + + // Export + return { + PasswordManager: PasswordManager + }; + +}); diff --git a/chrome/browser/resources/options/password_manager_list.css b/chrome/browser/resources/options/password_manager_list.css new file mode 100644 index 0000000..8688619 --- /dev/null +++ b/chrome/browser/resources/options/password_manager_list.css @@ -0,0 +1,58 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#saved-passwords-list .list-inline-button { + -webkit-transition: opacity 150ms; + background: rgb(138, 170, 237); + font-size: 0.9em; + height: 18px; + padding: 0 2px; + position: absolute; + top: 3px; +} + +html[dir='ltr'] #saved-passwords-list .list-inline-button { + right: 2px; +} + +html[dir='rtl'] #saved-passwords-list .list-inline-button { + left: 2px; +} + +input[type='password'].inactive-password { + background: transparent; + border: none; +} + +#saved-passwords-list .url { + box-sizing: border-box; + width: 40%; +} + +#saved-passwords-list .name { + -webkit-box-flex: 1; + width: 20%; +} + +#saved-passwords-list .password { + -webkit-box-flex: 1; + position: relative; +} + +#saved-passwords-list .password input[type='password'], +#saved-passwords-list .password input[type='text'] { + box-sizing: border-box; + width: 100%; +} + +#password-exceptions-list .url { + -webkit-box-flex: 1; +} + +#saved-passwords-list .url, +#saved-passwords-list .name, +#password-exceptions-list .url { + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/chrome/browser/resources/options/password_manager_list.js b/chrome/browser/resources/options/password_manager_list.js new file mode 100644 index 0000000..483c271 --- /dev/null +++ b/chrome/browser/resources/options/password_manager_list.js @@ -0,0 +1,297 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.passwordManager', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var DeletableItemList = options.DeletableItemList; + /** @const */ var DeletableItem = options.DeletableItem; + /** @const */ var List = cr.ui.List; + + /** + * Creates a new passwords list item. + * @param {Array} entry An array of the form [url, username, password]. When + * the list has been filtered, a fourth element [index] may be present. + * @constructor + * @extends {cr.ui.ListItem} + */ + function PasswordListItem(entry, showPasswords) { + var el = cr.doc.createElement('div'); + el.dataItem = entry; + el.__proto__ = PasswordListItem.prototype; + el.decorate(showPasswords); + + return el; + } + + PasswordListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** @inheritDoc */ + decorate: function(showPasswords) { + DeletableItem.prototype.decorate.call(this); + + // The URL of the site. + var urlLabel = this.ownerDocument.createElement('div'); + urlLabel.classList.add('favicon-cell'); + urlLabel.classList.add('weakrtl'); + urlLabel.classList.add('url'); + urlLabel.setAttribute('title', this.url); + urlLabel.textContent = this.url; + + // The favicon URL is prefixed with "origin/", which essentially removes + // the URL path past the top-level domain and ensures that a scheme (e.g., + // http) is being used. This ensures that the favicon returned is the + // default favicon for the domain and that the URL has a scheme if none + // is present in the password manager. + urlLabel.style.backgroundImage = + url('chrome://favicon/origin/' + this.url); + this.contentElement.appendChild(urlLabel); + + // The stored username. + var usernameLabel = this.ownerDocument.createElement('div'); + usernameLabel.className = 'name'; + usernameLabel.textContent = this.username; + this.contentElement.appendChild(usernameLabel); + + // The stored password. + var passwordInputDiv = this.ownerDocument.createElement('div'); + passwordInputDiv.className = 'password'; + + // The password input field. + var passwordInput = this.ownerDocument.createElement('input'); + passwordInput.type = 'password'; + passwordInput.className = 'inactive-password'; + passwordInput.readOnly = true; + passwordInput.value = showPasswords ? this.password : '********'; + passwordInputDiv.appendChild(passwordInput); + + // The show/hide button. + if (showPasswords) { + var button = this.ownerDocument.createElement('button'); + button.hidden = true; + button.className = 'list-inline-button custom-appearance'; + button.textContent = loadTimeData.getString('passwordShowButton'); + button.addEventListener('click', this.onClick_, true); + passwordInputDiv.appendChild(button); + } + + this.contentElement.appendChild(passwordInputDiv); + }, + + /** @inheritDoc */ + selectionChanged: function() { + var passwordInput = this.querySelector('input[type=password]'); + var textInput = this.querySelector('input[type=text]'); + var input = passwordInput || textInput; + var button = input.nextSibling; + // |button| doesn't exist when passwords can't be shown. + if (!button) + return; + if (this.selected) { + input.classList.remove('inactive-password'); + button.hidden = false; + } else { + input.classList.add('inactive-password'); + button.hidden = true; + } + }, + + /** + * On-click event handler. Swaps the type of the input field from password + * to text and back. + * @private + */ + onClick_: function(event) { + // The password is the input element previous to the button. + var button = event.currentTarget; + var passwordInput = button.previousSibling; + if (passwordInput.type == 'password') { + passwordInput.type = 'text'; + button.textContent = loadTimeData.getString('passwordHideButton'); + } else { + passwordInput.type = 'password'; + button.textContent = loadTimeData.getString('passwordShowButton'); + } + }, + + /** + * Get and set the URL for the entry. + * @type {string} + */ + get url() { + return this.dataItem[0]; + }, + set url(url) { + this.dataItem[0] = url; + }, + + /** + * Get and set the username for the entry. + * @type {string} + */ + get username() { + return this.dataItem[1]; + }, + set username(username) { + this.dataItem[1] = username; + }, + + /** + * Get and set the password for the entry. + * @type {string} + */ + get password() { + return this.dataItem[2]; + }, + set password(password) { + this.dataItem[2] = password; + }, + }; + + /** + * Creates a new PasswordExceptions list item. + * @param {Array} entry A pair of the form [url, username]. + * @constructor + * @extends {Deletable.ListItem} + */ + function PasswordExceptionsListItem(entry) { + var el = cr.doc.createElement('div'); + el.dataItem = entry; + el.__proto__ = PasswordExceptionsListItem.prototype; + el.decorate(); + + return el; + } + + PasswordExceptionsListItem.prototype = { + __proto__: DeletableItem.prototype, + + /** + * Call when an element is decorated as a list item. + */ + decorate: function() { + DeletableItem.prototype.decorate.call(this); + + // The URL of the site. + var urlLabel = this.ownerDocument.createElement('div'); + urlLabel.className = 'url'; + urlLabel.classList.add('favicon-cell'); + urlLabel.classList.add('weakrtl'); + urlLabel.textContent = this.url; + + // The favicon URL is prefixed with "origin/", which essentially removes + // the URL path past the top-level domain and ensures that a scheme (e.g., + // http) is being used. This ensures that the favicon returned is the + // default favicon for the domain and that the URL has a scheme if none + // is present in the password manager. + urlLabel.style.backgroundImage = + url('chrome://favicon/origin/' + this.url); + this.contentElement.appendChild(urlLabel); + }, + + /** + * Get the url for the entry. + * @type {string} + */ + get url() { + return this.dataItem; + }, + set url(url) { + this.dataItem = url; + }, + }; + + /** + * Create a new passwords list. + * @constructor + * @extends {cr.ui.List} + */ + var PasswordsList = cr.ui.define('list'); + + PasswordsList.prototype = { + __proto__: DeletableItemList.prototype, + + /** + * Whether passwords can be revealed or not. + * @type {boolean} + * @private + */ + showPasswords_: true, + + /** @inheritDoc */ + decorate: function() { + DeletableItemList.prototype.decorate.call(this); + Preferences.getInstance().addEventListener( + 'profile.password_manager_allow_show_passwords', + this.onPreferenceChanged_.bind(this)); + }, + + /** + * Listener for changes on the preference. + * @param {Event} event The preference update event. + * @private + */ + onPreferenceChanged_: function(event) { + this.showPasswords_ = event.value.value; + this.redraw(); + }, + + /** @inheritDoc */ + createItem: function(entry) { + return new PasswordListItem(entry, this.showPasswords_); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var item = this.dataModel.item(index); + if (item && item.length > 3) { + // The fourth element, if present, is the original index to delete. + index = item[3]; + } + PasswordManager.removeSavedPassword(index); + }, + + /** + * The length of the list. + */ + get length() { + return this.dataModel.length; + }, + }; + + /** + * Create a new passwords list. + * @constructor + * @extends {cr.ui.List} + */ + var PasswordExceptionsList = cr.ui.define('list'); + + PasswordExceptionsList.prototype = { + __proto__: DeletableItemList.prototype, + + /** @inheritDoc */ + createItem: function(entry) { + return new PasswordExceptionsListItem(entry); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + PasswordManager.removePasswordException(index); + }, + + /** + * The length of the list. + */ + get length() { + return this.dataModel.length; + }, + }; + + return { + PasswordListItem: PasswordListItem, + PasswordExceptionsListItem: PasswordExceptionsListItem, + PasswordsList: PasswordsList, + PasswordExceptionsList: PasswordExceptionsList, + }; +}); diff --git a/chrome/browser/resources/options/pref_ui.js b/chrome/browser/resources/options/pref_ui.js new file mode 100644 index 0000000..82bab86 --- /dev/null +++ b/chrome/browser/resources/options/pref_ui.js @@ -0,0 +1,846 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(jhawkins): Add dialog-pref support to all preference controls. + +cr.define('options', function() { + + var Preferences = options.Preferences; + + /** + * Allows an element to be disabled for several reasons. + * The element is disabled if at least one reason is true, and the reasons + * can be set separately. + * @private + * @param {!HTMLElement} el The element to update. + * @param {string} reason The reason for disabling the element. + * @param {boolean} disabled Whether the element should be disabled or enabled + * for the given |reason|. + */ + function updateDisabledState_(el, reason, disabled) { + if (!el.disabledReasons) + el.disabledReasons = {}; + if (el.disabled && (Object.keys(el.disabledReasons).length == 0)) { + // The element has been previously disabled without a reason, so we add + // one to keep it disabled. + el.disabledReasons['other'] = true; + } + if (!el.disabled) { + // If the element is not disabled, there should be no reason, except for + // 'other'. + delete el.disabledReasons['other']; + if (Object.keys(el.disabledReasons).length > 0) + console.error('Element is not disabled but should be'); + } + if (disabled) { + el.disabledReasons[reason] = true; + } else { + delete el.disabledReasons[reason]; + } + el.disabled = Object.keys(el.disabledReasons).length > 0; + } + + /** + * Helper function to update element's state from pref change event. + * @private + * @param {!HTMLElement} el The element to update. + * @param {!Event} event The pref change event. + */ + function updateElementState_(el, event) { + el.controlledBy = null; + + if (!event.value) + return; + + updateDisabledState_(el, 'notUserModifiable', event.value.disabled); + + el.controlledBy = event.value.controlledBy; + + OptionsPage.updateManagedBannerVisibility(); + } + + ///////////////////////////////////////////////////////////////////////////// + // PrefCheckbox class: + // TODO(jhawkins): Refactor all this copy-pasted code! + + // Define a constructor that uses an input element as its underlying element. + var PrefCheckbox = cr.ui.define('input'); + + PrefCheckbox.prototype = { + // Set up the prototype chain + __proto__: HTMLInputElement.prototype, + + /** + * The stored value of the preference that this checkbox controls. + * @type {boolean} + */ + prefValue_: null, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'checkbox'; + var self = this; + + self.initializeValueType(self.getAttribute('value-type')); + + // Listen to pref changes. + Preferences.getInstance().addEventListener(this.pref, function(event) { + self.prefValue_ = Boolean(event.value.value); + self.resetPrefState(); + + updateElementState_(self, event); + }); + + // Listen to user events. + this.addEventListener('change', function(e) { + if (self.customChangeHandler(e)) + return; + + if (!this.dialogPref) + this.updatePreferenceValue_(); + }); + }, + + /** + * Update the preference value based on the checkbox state. + * @private + */ + updatePreferenceValue_: function() { + var value = this.inverted_pref ? !this.checked : this.checked; + switch (this.valueType) { + case 'number': + Preferences.setIntegerPref(this.pref, Number(value), this.metric); + break; + case 'boolean': + Preferences.setBooleanPref(this.pref, value, this.metric); + break; + } + }, + + /** + * Called by SettingsDialog to save the preference. + */ + savePrefState: function() { + this.updatePreferenceValue_(); + }, + + /** + * Called by SettingsDialog to reset the UI to match the current preference + * value. + */ + resetPrefState: function() { + this.checked = this.inverted_pref ? !this.prefValue_ : this.prefValue_; + }, + + /** + * Sets up options in checkbox element. + * @param {String} valueType The preference type for this checkbox. + */ + initializeValueType: function(valueType) { + this.valueType = valueType || 'boolean'; + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + + /** + * This method is called first while processing an onchange event. If it + * returns false, regular onchange processing continues (setting the + * associated pref, etc). If it returns true, the rest of the onchange is + * not performed. I.e., this works like stopPropagation or cancelBubble. + * @param {Event} event Change event. + */ + customChangeHandler: function(event) { + return false; + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefCheckbox, 'pref', cr.PropertyKind.ATTR); + + /** + * A special preference type specific to dialogs. These preferences are reset + * when the dialog is shown and are not saved until the user confirms the + * dialog. + * @type {boolean} + */ + cr.defineProperty(PrefCheckbox, 'dialogPref', cr.PropertyKind.BOOL_ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefCheckbox, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefCheckbox, 'metric', cr.PropertyKind.ATTR); + + /** + * Whether to use inverted pref value. + * @type {boolean} + */ + cr.defineProperty(PrefCheckbox, 'inverted_pref', cr.PropertyKind.BOOL_ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefRadio class: + + //Define a constructor that uses an input element as its underlying element. + var PrefRadio = cr.ui.define('input'); + + PrefRadio.prototype = { + // Set up the prototype chain + __proto__: HTMLInputElement.prototype, + + // Stores the initialized value of the preference used to reset the input + // in resetPrefState(). + storedValue_: null, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'radio'; + var self = this; + + // Listen to preference changes. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + if (self.customChangeHandler(event)) + return; + self.checked = String(event.value.value) == self.value; + self.storedValue_ = self.checked; + + updateElementState_(self, event); + }); + + // Dialog preferences are not saved until savePrefState() is explicitly + // called. + if (!this.dialogPref) + this.onchange = this.savePrefState.bind(this); + }, + + /** + * Resets the input to the stored value. + */ + resetPrefState: function() { + this.checked = this.storedValue_; + }, + + /** + * Saves the value of the input back into the preference. May be called + * directly to save dialog preferences. + */ + savePrefState: function() { + this.storedValue_ = this.checked; + if (this.value == 'true' || this.value == 'false') { + var value = String(this.value); + Preferences.setBooleanPref(this.pref, value == String(this.checked), + this.metric); + } else { + Preferences.setIntegerPref(this.pref, parseInt(this.value, 10), + this.metric); + } + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + + /** + * This method is called first while processing an onchange event. If it + * returns false, regular onchange processing continues (setting the + * associated pref, etc). If it returns true, the rest of the onchange is + * not performed. I.e., this works like stopPropagation or cancelBubble. + * @param {Event} event Change event. + */ + customChangeHandler: function(event) { + return false; + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefRadio, 'pref', cr.PropertyKind.ATTR); + + /** + * A special preference type specific to dialogs. These preferences are reset + * when the dialog is shown and are not saved until the user confirms the + * dialog. + * @type {boolean} + */ + cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefRadio, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefRadio, 'metric', cr.PropertyKind.ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefNumeric class: + + // Define a constructor that uses an input element as its underlying element. + var PrefNumeric = function() {}; + PrefNumeric.prototype = { + // Set up the prototype chain + __proto__: HTMLInputElement.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + var self = this; + + // Listen to pref changes. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + self.value = event.value.value; + + updateElementState_(self, event); + }); + + // Listen to user events. + this.addEventListener('change', + function(e) { + if (this.validity.valid) { + Preferences.setIntegerPref(self.pref, self.value, self.metric); + } + }); + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefNumeric, 'pref', cr.PropertyKind.ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefNumeric, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefNumeric, 'metric', cr.PropertyKind.ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefNumber class: + + // Define a constructor that uses an input element as its underlying element. + var PrefNumber = cr.ui.define('input'); + + PrefNumber.prototype = { + // Set up the prototype chain + __proto__: PrefNumeric.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'number'; + PrefNumeric.prototype.decorate.call(this); + + // Listen to user events. + this.addEventListener('input', + function(e) { + if (this.validity.valid) { + Preferences.setIntegerPref(self.pref, self.value, self.metric); + } + }); + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + ///////////////////////////////////////////////////////////////////////////// + // PrefRange class: + + // Define a constructor that uses an input element as its underlying element. + var PrefRange = cr.ui.define('input'); + + PrefRange.prototype = { + // Set up the prototype chain + __proto__: HTMLInputElement.prototype, + + /** + * The map from input range value to the corresponding preference value. + */ + valueMap: undefined, + + /** + * If true, the associated pref will be modified on each onchange event; + * otherwise, the pref will only be modified on the onmouseup event after + * the drag. + */ + continuous: true, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + this.type = 'range'; + + // Update the UI when the pref changes. + Preferences.getInstance().addEventListener( + this.pref, this.onPrefChange_.bind(this)); + + // Listen to user events. + // TODO(jhawkins): Add onmousewheel handling once the associated WK bug is + // fixed. + // https://bugs.webkit.org/show_bug.cgi?id=52256 + this.onchange = this.onChange_.bind(this); + this.onkeyup = this.onmouseup = this.onInputUp_.bind(this); + }, + + /** + * Event listener that updates the UI when the underlying pref changes. + * @param {Event} event The event that details the pref change. + * @private + */ + onPrefChange_: function(event) { + var value = event.value.value; + if (value != undefined) + this.value = this.valueMap ? this.valueMap.indexOf(value) : value; + }, + + /** + * onchange handler that sets the pref when the user changes the value of + * the input element. + * @private + */ + onChange_: function(event) { + if (this.continuous) + this.setRangePref_(); + + if (this.notifyChange) + this.notifyChange(this, this.mapValueToRange_(this.value)); + }, + + /** + * Sets the integer value of |pref| to the value of this element. + * @private + */ + setRangePref_: function() { + Preferences.setIntegerPref( + this.pref, this.mapValueToRange_(this.value), this.metric); + + if (this.notifyPrefChange) + this.notifyPrefChange(this, this.mapValueToRange_(this.value)); + }, + + /** + * onkeyup/onmouseup handler that modifies the pref if |continuous| is + * false. + * @private + */ + onInputUp_: function(event) { + if (!this.continuous) + this.setRangePref_(); + }, + + /** + * Maps the value of this element into the range provided by the client, + * represented by |valueMap|. + * @param {number} value The value to map. + * @private + */ + mapValueToRange_: function(value) { + return this.valueMap ? this.valueMap[value] : value; + }, + + /** + * Called when the client has specified non-continuous mode and the value of + * the range control changes. + * @param {Element} el This element. + * @param {number} value The value of this element. + */ + notifyChange: function(el, value) { + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefRange, 'pref', cr.PropertyKind.ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefRange, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefRange, 'metric', cr.PropertyKind.ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefSelect class: + + // Define a constructor that uses a select element as its underlying element. + var PrefSelect = cr.ui.define('select'); + + PrefSelect.prototype = { + // Set up the prototype chain + __proto__: HTMLSelectElement.prototype, + + /** + * @type {string} The stored value of the preference that this select + * controls. + */ + prefValue_: null, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + var self = this; + + // Listen to pref changes. + Preferences.getInstance().addEventListener(this.pref, function(event) { + // Make sure |value| is a string, because the value is stored as a + // string in the HTMLOptionElement. + value = event.value.value.toString(); + + updateElementState_(self, event); + self.prefValue_ = value; + self.resetPrefState(); + }); + + // Listen to user events. + this.addEventListener('change', function(event) { + if (!self.dialogPref) + self.updatePreference_(self.prefValue_); + }); + }, + + /** + * Resets the input to the stored value. + */ + resetPrefState: function() { + var found = false; + for (var i = 0; i < this.options.length; i++) { + if (this.options[i].value == this.prefValue_) { + this.selectedIndex = i; + found = true; + } + } + + // Item not found, select first item. + if (!found) + this.selectedIndex = 0; + + if (this.onchange) + this.onchange(event); + }, + + /** + * Updates the preference to the currently selected value. + */ + updatePreference_: function() { + if (!this.dataType) { + console.error('undefined data type for pref: ' + + this.dataType); + } + }, + + /** + * Called by SettingsDialog to save the stored value to preferences. + */ + savePrefState: function() { + this.updatePreference_(); + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefSelect, 'pref', cr.PropertyKind.ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefSelect, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * A special preference type specific to dialogs. These preferences are reset + * when the dialog is shown and are not saved until the user confirms the + * dialog. + * @type {boolean} + */ + cr.defineProperty(PrefSelect, 'dialogPref', cr.PropertyKind.BOOL_ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefSelect, 'metric', cr.PropertyKind.ATTR); + + /** + * The data type for the preference options. + * @type {string} + */ + cr.defineProperty(PrefSelect, 'dataType', cr.PropertyKind.ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefTextField class: + + // Define a constructor that uses an input element as its underlying element. + var PrefTextField = cr.ui.define('input'); + + PrefTextField.prototype = { + // Set up the prototype chain + __proto__: HTMLInputElement.prototype, + + /** + * @type {string} The stored value of the preference that this text field + * controls. + */ + prefValue_: null, + + /** + * Saves the value of the input back into the preference. May be called + * directly to save dialog preferences. + */ + savePrefState: function() { + switch (this.dataType) { + case 'number': + Preferences.setIntegerPref(this.pref, this.value, this.metric); + break; + case 'double': + Preferences.setDoublePref(this.pref, this.value, this.metric); + break; + case 'url': + Preferences.setURLPref(this.pref, this.value, this.metric); + break; + default: + Preferences.setStringPref(this.pref, this.value, this.metric); + break; + } + }, + + /** + * Resets the input to the stored value. + */ + resetPrefState: function() { + this.value = this.prefValue_; + }, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + var self = this; + + // Listen to pref changes. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + self.value = event.value.value; + + updateElementState_(self, event); + + self.prefValue_ = self.value; + }); + + // Listen to user events. + if (!this.dialogPref) + this.addEventListener('change', this.savePrefState.bind(this)); + + window.addEventListener('unload', + function() { + if (document.activeElement == self) + self.blur(); + }); + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefTextField, 'pref', cr.PropertyKind.ATTR); + + /** + * A special preference type specific to dialogs. These preferences are reset + * when the dialog is shown and are not saved until the user confirms the + * dialog. + * @type {boolean} + */ + cr.defineProperty(PrefTextField, 'dialogPref', cr.PropertyKind.BOOL_ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefTextField, 'controlledBy', cr.PropertyKind.ATTR); + + /** + * The user metric string. + * @type {string} + */ + cr.defineProperty(PrefTextField, 'metric', cr.PropertyKind.ATTR); + + /** + * The data type for the preference options. + * @type {string} + */ + cr.defineProperty(PrefTextField, 'dataType', cr.PropertyKind.ATTR); + + ///////////////////////////////////////////////////////////////////////////// + // PrefButton class: + + // Define a constructor that uses a button element as its underlying element. + var PrefButton = cr.ui.define('button'); + + PrefButton.prototype = { + // Set up the prototype chain + __proto__: HTMLButtonElement.prototype, + + /** + * Initialization function for the cr.ui framework. + */ + decorate: function() { + var self = this; + + // Listen to pref changes. This element behaves like a normal button and + // doesn't affect the underlying preference; it just becomes disabled + // when the preference is managed, and its value is false. + // This is useful for buttons that should be disabled when the underlying + // boolean preference is set to false by a policy or extension. + Preferences.getInstance().addEventListener(this.pref, + function(event) { + var e = { + value: { + 'disabled': event.value.disabled && !event.value.value, + 'controlledBy': event.value.controlledBy + } + }; + updateElementState_(self, e); + }); + }, + + /** + * See |updateDisabledState_| above. + */ + setDisabled: function(reason, disabled) { + updateDisabledState_(this, reason, disabled); + }, + }; + + /** + * The preference name. + * @type {string} + */ + cr.defineProperty(PrefButton, 'pref', cr.PropertyKind.ATTR); + + /** + * Whether the preference is controlled by something else than the user's + * settings (either 'policy' or 'extension'). + * @type {string} + */ + cr.defineProperty(PrefButton, 'controlledBy', cr.PropertyKind.ATTR); + + // Export + return { + PrefCheckbox: PrefCheckbox, + PrefNumber: PrefNumber, + PrefNumeric: PrefNumeric, + PrefRadio: PrefRadio, + PrefRange: PrefRange, + PrefSelect: PrefSelect, + PrefTextField: PrefTextField, + PrefButton: PrefButton + }; + +}); diff --git a/chrome/browser/resources/options/preferences.js b/chrome/browser/resources/options/preferences.js new file mode 100644 index 0000000..807e45e --- /dev/null +++ b/chrome/browser/resources/options/preferences.js @@ -0,0 +1,185 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + + ///////////////////////////////////////////////////////////////////////////// + // Preferences class: + + /** + * Preferences class manages access to Chrome profile preferences. + * @constructor + */ + function Preferences() { + } + + cr.addSingletonGetter(Preferences); + + /** + * Sets value of a boolean preference. + * and signals its changed value. + * @param {string} name Preference name. + * @param {boolean} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setBooleanPref = function(name, value, metric) { + var argumentList = [name, Boolean(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setBooleanPref', argumentList); + }; + + /** + * Sets value of an integer preference. + * and signals its changed value. + * @param {string} name Preference name. + * @param {number} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setIntegerPref = function(name, value, metric) { + var argumentList = [name, Number(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setIntegerPref', argumentList); + }; + + /** + * Sets value of a double-valued preference. + * and signals its changed value. + * @param {string} name Preference name. + * @param {number} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setDoublePref = function(name, value, metric) { + var argumentList = [name, Number(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setDoublePref', argumentList); + }; + + /** + * Sets value of a string preference. + * and signals its changed value. + * @param {string} name Preference name. + * @param {string} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setStringPref = function(name, value, metric) { + var argumentList = [name, String(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setStringPref', argumentList); + }; + + /** + * Sets value of a string preference that represents a URL + * and signals its changed value. The value will be fixed to be a valid URL. + * @param {string} name Preference name. + * @param {string} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setURLPref = function(name, value, metric) { + var argumentList = [name, String(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setURLPref', argumentList); + }; + + /** + * Sets value of a JSON list preference. + * and signals its changed value. + * @param {string} name Preference name. + * @param {Array} value New preference value. + * @param {string} metric User metrics identifier. + */ + Preferences.setListPref = function(name, value, metric) { + var argumentList = [name, JSON.stringify(value)]; + if (metric != undefined) argumentList.push(metric); + chrome.send('setListPref', argumentList); + }; + + /** + * Clears value of a JSON preference. + * @param {string} name Preference name. + * @param {string} metric User metrics identifier. + */ + Preferences.clearPref = function(name, metric) { + var argumentList = [name]; + if (metric != undefined) argumentList.push(metric); + chrome.send('clearPref', argumentList); + }; + + Preferences.prototype = { + __proto__: cr.EventTarget.prototype, + + // Map of registered preferences. + registeredPreferences_: {}, + + /** + * Adds an event listener to the target. + * @param {string} type The name of the event. + * @param {!Function|{handleEvent:Function}} handler The handler for the + * event. This is called when the event is dispatched. + */ + addEventListener: function(type, handler) { + cr.EventTarget.prototype.addEventListener.call(this, type, handler); + this.registeredPreferences_[type] = true; + }, + + /** + * Initializes preference reading and change notifications. + */ + initialize: function() { + var params1 = ['Preferences.prefsFetchedCallback']; + var params2 = ['Preferences.prefsChangedCallback']; + for (var prefName in this.registeredPreferences_) { + params1.push(prefName); + params2.push(prefName); + } + chrome.send('fetchPrefs', params1); + chrome.send('observePrefs', params2); + }, + + /** + * Helper function for flattening of dictionary passed via fetchPrefs + * callback. + * @param {string} prefix Preference name prefix. + * @param {object} dict Map with preference values. + */ + flattenMapAndDispatchEvent_: function(prefix, dict) { + for (var prefName in dict) { + if (typeof dict[prefName] == 'object' && + !this.registeredPreferences_[prefix + prefName]) { + this.flattenMapAndDispatchEvent_(prefix + prefName + '.', + dict[prefName]); + } else { + var event = new cr.Event(prefix + prefName); + event.value = dict[prefName]; + this.dispatchEvent(event); + } + } + } + }; + + /** + * Callback for fetchPrefs method. + * @param {object} dict Map of fetched property values. + */ + Preferences.prefsFetchedCallback = function(dict) { + Preferences.getInstance().flattenMapAndDispatchEvent_('', dict); + }; + + /** + * Callback for observePrefs method. + * @param {array} notification An array defining changed preference values. + * notification[0] contains name of the change preference while its new value + * is stored in notification[1]. + */ + Preferences.prefsChangedCallback = function(notification) { + var event = new cr.Event(notification[0]); + event.value = notification[1]; + Preferences.getInstance().dispatchEvent(event); + }; + + // Export + return { + Preferences: Preferences + }; + +}); diff --git a/chrome/browser/resources/options/profiles_icon_grid.js b/chrome/browser/resources/options/profiles_icon_grid.js new file mode 100644 index 0000000..287231c --- /dev/null +++ b/chrome/browser/resources/options/profiles_icon_grid.js @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var ListItem = cr.ui.ListItem; + /** @const */ var Grid = cr.ui.Grid; + /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; + + /** + * Creates a new profile icon grid item. + * @param {Object} iconURL The profile icon URL. + * @constructor + * @extends {cr.ui.GridItem} + */ + function ProfilesIconGridItem(iconURL) { + var el = cr.doc.createElement('span'); + el.iconURL_ = iconURL; + ProfilesIconGridItem.decorate(el); + return el; + } + + /** + * Decorates an element as a profile grid item. + * @param {!HTMLElement} el The element to decorate. + */ + ProfilesIconGridItem.decorate = function(el) { + el.__proto__ = ProfilesIconGridItem.prototype; + el.decorate(); + }; + + ProfilesIconGridItem.prototype = { + __proto__: ListItem.prototype, + + /** @inheritDoc */ + decorate: function() { + ListItem.prototype.decorate.call(this); + var imageEl = cr.doc.createElement('img'); + imageEl.className = 'profile-icon'; + imageEl.src = this.iconURL_; + this.appendChild(imageEl); + + this.className = 'profile-icon-grid-item'; + }, + }; + + var ProfilesIconGrid = cr.ui.define('grid'); + + ProfilesIconGrid.prototype = { + __proto__: Grid.prototype, + + /** @inheritDoc */ + decorate: function() { + Grid.prototype.decorate.call(this); + this.selectionModel = new ListSingleSelectionModel(); + }, + + /** @inheritDoc */ + createItem: function(iconURL) { + return new ProfilesIconGridItem(iconURL); + }, + }; + + return { + ProfilesIconGrid: ProfilesIconGrid + }; +}); + diff --git a/chrome/browser/resources/options/search_box.html b/chrome/browser/resources/options/search_box.html new file mode 100644 index 0000000..f27e1e7 --- /dev/null +++ b/chrome/browser/resources/options/search_box.html @@ -0,0 +1,10 @@ + diff --git a/chrome/browser/resources/options/search_engine_manager.css b/chrome/browser/resources/options/search_engine_manager.css new file mode 100644 index 0000000..f9c17c8 --- /dev/null +++ b/chrome/browser/resources/options/search_engine_manager.css @@ -0,0 +1,78 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#search-engine-manager-page { + width: 700px; +} + +.search-engine-list input { + -webkit-box-flex: 1; + display: -webkit-box; +} + +.search-engine-list > div { + display: -webkit-box; +} + +.search-engine-list .favicon { + height: 16px; + line-height: 16px; + padding: 0 7px; + width: 16px; +} + +.search-engine-list .name-column { + -webkit-box-align: center; + -webkit-padding-end: 1ex; + box-sizing: border-box; + display: -webkit-box; + width: 30%; +} + +.search-engine-list .name-column :last-child { + -webkit-box-flex: 1; +} + +.search-engine-list .keyword-column { + -webkit-padding-end: 1ex; + box-sizing: border-box; + width: 26%; +} + +.search-engine-list .url-column { + box-sizing: border-box; + width: 44%; +} + +.search-engine-list .keyword-column, +.search-engine-list .url-column { + color: #666; +} + +.search-engine-list .default .name-column, +.search-engine-list .default .keyword-column { + font-weight: bold; +} + +/* For temporary Make Default button */ +.search-engine-list .url-column { + -webkit-box-align: center; + display: -webkit-box; +} + +.search-engine-list .url-column :first-child { + -webkit-box-flex: 1; +} + +.search-engine-list .url-column .list-inline-button { + margin-top: 0; + padding: 1px 6px 2px 6px; +} + +.search-engine-list > :not(:hover):not([editing]) .url-column + .list-inline-button { + display: none; +} + +/* End temporary Make Default button styling */ diff --git a/chrome/browser/resources/options/search_engine_manager.html b/chrome/browser/resources/options/search_engine_manager.html new file mode 100644 index 0000000..0e300b2 --- /dev/null +++ b/chrome/browser/resources/options/search_engine_manager.html @@ -0,0 +1,25 @@ + diff --git a/chrome/browser/resources/options/search_engine_manager.js b/chrome/browser/resources/options/search_engine_manager.js new file mode 100644 index 0000000..f986aa1 --- /dev/null +++ b/chrome/browser/resources/options/search_engine_manager.js @@ -0,0 +1,129 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + + /** + * Encapsulated handling of search engine management page. + * @constructor + */ + function SearchEngineManager() { + this.activeNavTab = null; + OptionsPage.call(this, 'searchEngines', + loadTimeData.getString('searchEngineManagerPageTabTitle'), + 'search-engine-manager-page'); + } + + cr.addSingletonGetter(SearchEngineManager); + + SearchEngineManager.prototype = { + __proto__: OptionsPage.prototype, + + /** + * List for default search engine options. + * @private + */ + defaultsList_: null, + + /** + * List for other search engine options. + * @private + */ + othersList_: null, + + /** + * List for extension keywords. + * @private + extensionList_ : null, + + /** inheritDoc */ + initializePage: function() { + OptionsPage.prototype.initializePage.call(this); + + this.defaultsList_ = $('default-search-engine-list'); + this.setUpList_(this.defaultsList_); + + this.othersList_ = $('other-search-engine-list'); + this.setUpList_(this.othersList_); + + this.extensionList_ = $('extension-keyword-list'); + this.setUpList_(this.extensionList_); + + $('search-engine-manager-confirm').onclick = function() { + OptionsPage.closeOverlay(); + }; + }, + + /** + * Sets up the given list as a search engine list + * @param {List} list The list to set up. + * @private + */ + setUpList_: function(list) { + options.search_engines.SearchEngineList.decorate(list); + list.autoExpands = true; + }, + + /** + * Updates the search engine list with the given entries. + * @private + * @param {Array} defaultEngines List of possible default search engines. + * @param {Array} otherEngines List of other search engines. + * @param {Array} keywords List of keywords from extensions. + */ + updateSearchEngineList_: function(defaultEngines, otherEngines, keywords) { + this.defaultsList_.dataModel = new ArrayDataModel(defaultEngines); + + otherEngines = otherEngines.map(function(x) { + return [x, x['name'].toLocaleLowerCase()]; + }).sort(function(a, b) { + return a[1].localeCompare(b[1]); + }).map(function(x) { + return x[0]; + }); + + var othersModel = new ArrayDataModel(otherEngines); + // Add a "new engine" row. + othersModel.push({ + 'modelIndex': '-1', + 'canBeEdited': true + }); + this.othersList_.dataModel = othersModel; + + if (keywords.length > 0) { + $('extension-keyword-div').hidden = false; + var extensionsModel = new ArrayDataModel(keywords); + this.extensionList_.dataModel = extensionsModel; + } else { + $('extension-keyword-div').hidden = true; + } + }, + }; + + SearchEngineManager.updateSearchEngineList = function(defaultEngines, + otherEngines, + keywords) { + SearchEngineManager.getInstance().updateSearchEngineList_(defaultEngines, + otherEngines, + keywords); + }; + + SearchEngineManager.validityCheckCallback = function(validity, modelIndex) { + // Forward to both lists; the one without a matching modelIndex will ignore + // it. + SearchEngineManager.getInstance().defaultsList_.validationComplete( + validity, modelIndex); + SearchEngineManager.getInstance().othersList_.validationComplete( + validity, modelIndex); + }; + + // Export + return { + SearchEngineManager: SearchEngineManager + }; + +}); + diff --git a/chrome/browser/resources/options/search_engine_manager_engine_list.js b/chrome/browser/resources/options/search_engine_manager_engine_list.js new file mode 100644 index 0000000..9d6b5ae --- /dev/null +++ b/chrome/browser/resources/options/search_engine_manager_engine_list.js @@ -0,0 +1,315 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options.search_engines', function() { + /** @const */ var InlineEditableItemList = options.InlineEditableItemList; + /** @const */ var InlineEditableItem = options.InlineEditableItem; + /** @const */ var ListSelectionController = cr.ui.ListSelectionController; + + /** + * Creates a new search engine list item. + * @param {Object} searchEnigne The search engine this represents. + * @constructor + * @extends {cr.ui.ListItem} + */ + function SearchEngineListItem(searchEngine) { + var el = cr.doc.createElement('div'); + el.searchEngine_ = searchEngine; + SearchEngineListItem.decorate(el); + return el; + } + + /** + * Decorates an element as a search engine list item. + * @param {!HTMLElement} el The element to decorate. + */ + SearchEngineListItem.decorate = function(el) { + el.__proto__ = SearchEngineListItem.prototype; + el.decorate(); + }; + + SearchEngineListItem.prototype = { + __proto__: InlineEditableItem.prototype, + + /** + * Input field for editing the engine name. + * @type {HTMLElement} + * @private + */ + nameField_: null, + + /** + * Input field for editing the engine keyword. + * @type {HTMLElement} + * @private + */ + keywordField_: null, + + /** + * Input field for editing the engine url. + * @type {HTMLElement} + * @private + */ + urlField_: null, + + /** + * Whether or not an input validation request is currently outstanding. + * @type {boolean} + * @private + */ + waitingForValidation_: false, + + /** + * Whether or not the current set of input is known to be valid. + * @type {boolean} + * @private + */ + currentlyValid_: false, + + /** @inheritDoc */ + decorate: function() { + InlineEditableItem.prototype.decorate.call(this); + + var engine = this.searchEngine_; + + if (engine['modelIndex'] == '-1') { + this.isPlaceholder = true; + engine['name'] = ''; + engine['keyword'] = ''; + engine['url'] = ''; + } + + this.currentlyValid_ = !this.isPlaceholder; + + if (engine['default']) + this.classList.add('default'); + + this.deletable = engine['canBeRemoved']; + + // Construct the name column. + var nameColEl = this.ownerDocument.createElement('div'); + nameColEl.className = 'name-column'; + nameColEl.classList.add('weakrtl'); + this.contentElement.appendChild(nameColEl); + + // Add the favicon. + var faviconDivEl = this.ownerDocument.createElement('div'); + faviconDivEl.className = 'favicon'; + var imgEl = this.ownerDocument.createElement('img'); + imgEl.src = 'chrome://favicon/iconurl/' + engine['iconURL']; + faviconDivEl.appendChild(imgEl); + nameColEl.appendChild(faviconDivEl); + + var nameEl = this.createEditableTextCell(engine['displayName']); + nameEl.classList.add('weakrtl'); + nameColEl.appendChild(nameEl); + + // Then the keyword column. + var keywordEl = this.createEditableTextCell(engine['keyword']); + keywordEl.className = 'keyword-column'; + keywordEl.classList.add('weakrtl'); + this.contentElement.appendChild(keywordEl); + + // And the URL column. + var urlEl = this.createEditableTextCell(engine['url']); + var urlWithButtonEl = this.ownerDocument.createElement('div'); + urlWithButtonEl.appendChild(urlEl); + urlWithButtonEl.className = 'url-column'; + urlWithButtonEl.classList.add('weakrtl'); + this.contentElement.appendChild(urlWithButtonEl); + // Add the Make Default button. Temporary until drag-and-drop re-ordering + // is implemented. When this is removed, remove the extra div above. + if (engine['canBeDefault']) { + var makeDefaultButtonEl = this.ownerDocument.createElement('button'); + makeDefaultButtonEl.className = 'custom-appearance list-inline-button'; + makeDefaultButtonEl.textContent = + loadTimeData.getString('makeDefaultSearchEngineButton'); + makeDefaultButtonEl.onclick = function(e) { + chrome.send('managerSetDefaultSearchEngine', [engine['modelIndex']]); + }; + // Don't select the row when clicking the button. + makeDefaultButtonEl.onmousedown = function(e) { + e.stopPropagation(); + }; + urlWithButtonEl.appendChild(makeDefaultButtonEl); + } + + // Do final adjustment to the input fields. + this.nameField_ = nameEl.querySelector('input'); + // The editable field uses the raw name, not the display name. + this.nameField_.value = engine['name']; + this.keywordField_ = keywordEl.querySelector('input'); + this.urlField_ = urlEl.querySelector('input'); + + if (engine['urlLocked']) + this.urlField_.disabled = true; + + if (this.isPlaceholder) { + this.nameField_.placeholder = + loadTimeData.getString('searchEngineTableNamePlaceholder'); + this.keywordField_.placeholder = + loadTimeData.getString('searchEngineTableKeywordPlaceholder'); + this.urlField_.placeholder = + loadTimeData.getString('searchEngineTableURLPlaceholder'); + } + + var fields = [this.nameField_, this.keywordField_, this.urlField_]; + for (var i = 0; i < fields.length; i++) { + fields[i].oninput = this.startFieldValidation_.bind(this); + } + + // Listen for edit events. + if (engine['canBeEdited']) { + this.addEventListener('edit', this.onEditStarted_.bind(this)); + this.addEventListener('canceledit', this.onEditCancelled_.bind(this)); + this.addEventListener('commitedit', this.onEditCommitted_.bind(this)); + } else { + this.editable = false; + } + }, + + /** @inheritDoc */ + get currentInputIsValid() { + return !this.waitingForValidation_ && this.currentlyValid_; + }, + + /** @inheritDoc */ + get hasBeenEdited() { + var engine = this.searchEngine_; + return this.nameField_.value != engine['name'] || + this.keywordField_.value != engine['keyword'] || + this.urlField_.value != engine['url']; + }, + + /** + * Called when entering edit mode; starts an edit session in the model. + * @param {Event} e The edit event. + * @private + */ + onEditStarted_: function(e) { + var editIndex = this.searchEngine_['modelIndex']; + chrome.send('editSearchEngine', [String(editIndex)]); + this.startFieldValidation_(); + }, + + /** + * Called when committing an edit; updates the model. + * @param {Event} e The end event. + * @private + */ + onEditCommitted_: function(e) { + chrome.send('searchEngineEditCompleted', this.getInputFieldValues_()); + }, + + /** + * Called when cancelling an edit; informs the model and resets the control + * states. + * @param {Event} e The cancel event. + * @private + */ + onEditCancelled_: function() { + chrome.send('searchEngineEditCancelled'); + + // The name field has been automatically set to match the display name, + // but it should use the raw name instead. + this.nameField_.value = this.searchEngine_['name']; + this.currentlyValid_ = !this.isPlaceholder; + }, + + /** + * Returns the input field values as an array suitable for passing to + * chrome.send. The order of the array is important. + * @private + * @return {array} The current input field values. + */ + getInputFieldValues_: function() { + return [this.nameField_.value, + this.keywordField_.value, + this.urlField_.value]; + }, + + /** + * Begins the process of asynchronously validing the input fields. + * @private + */ + startFieldValidation_: function() { + this.waitingForValidation_ = true; + var args = this.getInputFieldValues_(); + args.push(this.searchEngine_['modelIndex']); + chrome.send('checkSearchEngineInfoValidity', args); + }, + + /** + * Callback for the completion of an input validition check. + * @param {Object} validity A dictionary of validitation results. + */ + validationComplete: function(validity) { + this.waitingForValidation_ = false; + // TODO(stuartmorgan): Implement the full validation UI with + // checkmark/exclamation mark icons and tooltips showing the errors. + if (validity.name) { + this.nameField_.setCustomValidity(''); + } else { + this.nameField_.setCustomValidity( + loadTimeData.getString('editSearchEngineInvalidTitleToolTip')); + } + + if (validity.keyword) { + this.keywordField_.setCustomValidity(''); + } else { + this.keywordField_.setCustomValidity( + loadTimeData.getString('editSearchEngineInvalidKeywordToolTip')); + } + + if (validity.url) { + this.urlField_.setCustomValidity(''); + } else { + this.urlField_.setCustomValidity( + loadTimeData.getString('editSearchEngineInvalidURLToolTip')); + } + + this.currentlyValid_ = validity.name && validity.keyword && validity.url; + }, + }; + + var SearchEngineList = cr.ui.define('list'); + + SearchEngineList.prototype = { + __proto__: InlineEditableItemList.prototype, + + /** @inheritDoc */ + createItem: function(searchEngine) { + return new SearchEngineListItem(searchEngine); + }, + + /** @inheritDoc */ + deleteItemAtIndex: function(index) { + var modelIndex = this.dataModel.item(index)['modelIndex']; + chrome.send('removeSearchEngine', [String(modelIndex)]); + }, + + /** + * Passes the results of an input validation check to the requesting row + * if it's still being edited. + * @param {number} modelIndex The model index of the item that was checked. + * @param {Object} validity A dictionary of validitation results. + */ + validationComplete: function(validity, modelIndex) { + // If it's not still being edited, it no longer matters. + var currentSelection = this.selectedItem; + if (!currentSelection) + return; + var listItem = this.getListItem(currentSelection); + if (listItem.editing && currentSelection['modelIndex'] == modelIndex) + listItem.validationComplete(validity); + }, + }; + + // Export + return { + SearchEngineList: SearchEngineList + }; + +}); + diff --git a/chrome/browser/resources/options/search_page.css b/chrome/browser/resources/options/search_page.css new file mode 100644 index 0000000..18b0fb1 --- /dev/null +++ b/chrome/browser/resources/options/search_page.css @@ -0,0 +1,74 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.search-hidden { + display: none !important; +} + +.search-highlighted { + background-color: rgba(255, 240, 120, 0.9); +} + +/* Container for the elements that make up the search bubble. */ +.search-bubble { + left: 0; + margin-top: 5px; + pointer-events: none; + position: absolute; + top: -1000px; /* Minor hack: position off-screen by default. */ + /* Create a z-context for search-bubble-innards, its after and before. */ + z-index: 1; +} + +/* Contains the text content of the bubble. */ +.search-bubble-innards { + background: -webkit-linear-gradient(rgba(255, 248, 172, 0.9), + rgba(255, 243, 128, 0.9)); + border-radius: 2px; + padding: 4px 10px; + text-align: center; + width: 100px; +} + +/* Provides the border around the bubble (has to be behind :after). */ +.search-bubble-innards::before { + border: 1px solid rgb(220, 198, 72); + border-radius: 2px; + bottom: -1px; + content: ''; + left: -1px; + position: absolute; + right: -1px; + top: -1px; + z-index: -2; +} + +/* Provides the arrow which points at the anchor element. */ +.search-bubble-innards::after { + -webkit-transform: rotate(45deg); + background: + -webkit-linear-gradient(-45deg, rgb(251, 255, 181), + rgb(255, 248, 172) 50%, + rgba(255, 248, 172, 0)); + border: 1px solid rgb(220, 198, 72); + border-bottom-color: transparent; + border-right-color: transparent; + content: ''; + height: 12px; + left: 53px; + position: absolute; + top: -7px; + width: 12px; + z-index: -1; +} + +.search-bubble-wrapper { + position: relative; +} + +#searchPage.page { + /* The search page needs no padding as it's provided by showing the results + * from each page. */ + padding: 0; +} diff --git a/chrome/browser/resources/options/search_page.html b/chrome/browser/resources/options/search_page.html new file mode 100644 index 0000000..073c505 --- /dev/null +++ b/chrome/browser/resources/options/search_page.html @@ -0,0 +1,12 @@ + diff --git a/chrome/browser/resources/options/search_page.js b/chrome/browser/resources/options/search_page.js new file mode 100644 index 0000000..ac28df4 --- /dev/null +++ b/chrome/browser/resources/options/search_page.js @@ -0,0 +1,572 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * Encapsulated handling of a search bubble. + * @constructor + */ + function SearchBubble(text) { + var el = cr.doc.createElement('div'); + SearchBubble.decorate(el); + el.content = text; + return el; + } + + SearchBubble.decorate = function(el) { + el.__proto__ = SearchBubble.prototype; + el.decorate(); + }; + + SearchBubble.prototype = { + __proto__: HTMLDivElement.prototype, + + decorate: function() { + this.className = 'search-bubble'; + + this.innards_ = cr.doc.createElement('div'); + this.innards_.className = 'search-bubble-innards'; + this.appendChild(this.innards_); + + // We create a timer to periodically update the position of the bubbles. + // While this isn't all that desirable, it's the only sure-fire way of + // making sure the bubbles stay in the correct location as sections + // may dynamically change size at any time. + this.intervalId = setInterval(this.updatePosition.bind(this), 250); + }, + + /** + * Sets the text message in the bubble. + * @param {string} text The text the bubble will show. + */ + set content(text) { + this.innards_.textContent = text; + }, + + /** + * Attach the bubble to the element. + */ + attachTo: function(element) { + var parent = element.parentElement; + if (!parent) + return; + if (parent.tagName == 'TD') { + // To make absolute positioning work inside a table cell we need + // to wrap the bubble div into another div with position:relative. + // This only works properly if the element is the first child of the + // table cell which is true for all options pages. + this.wrapper = cr.doc.createElement('div'); + this.wrapper.className = 'search-bubble-wrapper'; + this.wrapper.appendChild(this); + parent.insertBefore(this.wrapper, element); + } else { + parent.insertBefore(this, element); + } + }, + + /** + * Clear the interval timer and remove the element from the page. + */ + dispose: function() { + clearInterval(this.intervalId); + + var child = this.wrapper || this; + var parent = child.parentNode; + if (parent) + parent.removeChild(child); + }, + + /** + * Update the position of the bubble. Called at creation time and then + * periodically while the bubble remains visible. + */ + updatePosition: function() { + // This bubble is 'owned' by the next sibling. + var owner = (this.wrapper || this).nextSibling; + + // If there isn't an offset parent, we have nothing to do. + if (!owner.offsetParent) + return; + + // Position the bubble below the location of the owner. + var left = owner.offsetLeft + owner.offsetWidth / 2 - + this.offsetWidth / 2; + var top = owner.offsetTop + owner.offsetHeight; + + // Update the position in the CSS. Cache the last values for + // best performance. + if (left != this.lastLeft) { + this.style.left = left + 'px'; + this.lastLeft = left; + } + if (top != this.lastTop) { + this.style.top = top + 'px'; + this.lastTop = top; + } + }, + }; + + /** + * Encapsulated handling of the search page. + * @constructor + */ + function SearchPage() { + OptionsPage.call(this, 'search', + loadTimeData.getString('searchPageTabTitle'), + 'searchPage'); + } + + cr.addSingletonGetter(SearchPage); + + SearchPage.prototype = { + // Inherit SearchPage from OptionsPage. + __proto__: OptionsPage.prototype, + + /** + * A boolean to prevent recursion. Used by setSearchText_(). + * @type {Boolean} + * @private + */ + insideSetSearchText_: false, + + /** + * Initialize the page. + */ + initializePage: function() { + // Call base class implementation to start preference initialization. + OptionsPage.prototype.initializePage.call(this); + + this.searchField = $('search-field'); + + // Handle search events. (No need to throttle, WebKit's search field + // will do that automatically.) + this.searchField.onsearch = function(e) { + this.setSearchText_(e.currentTarget.value); + }.bind(this); + + // Install handler for key presses. + document.addEventListener('keydown', + this.keyDownEventHandler_.bind(this)); + }, + + /** + * @inheritDoc + */ + get sticky() { + return true; + }, + + /** + * Called after this page has shown. + */ + didShowPage: function() { + // This method is called by the Options page after all pages have + // had their visibilty attribute set. At this point we can perform the + // search specific DOM manipulation. + this.setSearchActive_(true); + }, + + /** + * Called before this page will be hidden. + */ + willHidePage: function() { + // This method is called by the Options page before all pages have + // their visibilty attribute set. Before that happens, we need to + // undo the search specific DOM manipulation that was performed in + // didShowPage. + this.setSearchActive_(false); + }, + + /** + * Update the UI to reflect whether we are in a search state. + * @param {boolean} active True if we are on the search page. + * @private + */ + setSearchActive_: function(active) { + // It's fine to exit if search wasn't active and we're not going to + // activate it now. + if (!this.searchActive_ && !active) + return; + + this.searchActive_ = active; + + if (active) { + var hash = location.hash; + if (hash) { + this.searchField.value = + decodeURIComponent(hash.slice(1).replace(/\+/g, ' ')); + } else if (!this.searchField.value) { + // This should only happen if the user goes directly to + // chrome://settings-frame/search + OptionsPage.showDefaultPage(); + return; + } + + // Move 'advanced' sections into the main settings page to allow + // searching. + if (!this.advancedSections_) { + this.advancedSections_ = + $('advanced-settings-container').querySelectorAll('section'); + for (var i = 0, section; section = this.advancedSections_[i]; i++) + $('settings').appendChild(section); + } + } + + var pagesToSearch = this.getSearchablePages_(); + for (var key in pagesToSearch) { + var page = pagesToSearch[key]; + + if (!active) + page.visible = false; + + // Update the visible state of all top-level elements that are not + // sections (ie titles, button strips). We do this before changing + // the page visibility to avoid excessive re-draw. + for (var i = 0, childDiv; childDiv = page.pageDiv.children[i]; i++) { + if (active) { + if (childDiv.tagName != 'SECTION') + childDiv.classList.add('search-hidden'); + } else { + childDiv.classList.remove('search-hidden'); + } + } + + if (active) { + // When search is active, remove the 'hidden' tag. This tag may have + // been added by the OptionsPage. + page.pageDiv.hidden = false; + } + } + + if (active) { + this.setSearchText_(this.searchField.value); + this.searchField.focus(); + } else { + // After hiding all page content, remove any search results. + this.unhighlightMatches_(); + this.removeSearchBubbles_(); + + // Move 'advanced' sections back into their original container. + if (this.advancedSections_) { + for (var i = 0, section; section = this.advancedSections_[i]; i++) + $('advanced-settings-container').appendChild(section); + this.advancedSections_ = null; + } + } + }, + + /** + * Set the current search criteria. + * @param {string} text Search text. + * @private + */ + setSearchText_: function(text) { + // Prevent recursive execution of this method. + if (this.insideSetSearchText_) return; + this.insideSetSearchText_ = true; + + // Cleanup the search query string. + text = SearchPage.canonicalizeQuery(text); + + // Set the hash on the current page, and the enclosing uber page + var hash = text ? '#' + encodeURIComponent(text) : ''; + var path = text ? this.name : ''; + window.location.hash = hash; + uber.invokeMethodOnParent('setPath', {path: path + hash}); + + // Toggle the search page if necessary. + if (text) { + if (!this.searchActive_) + OptionsPage.showPageByName(this.name, false); + } else { + if (this.searchActive_) + OptionsPage.showPageByName(OptionsPage.getDefaultPage().name, false); + + this.insideSetSearchText_ = false; + return; + } + + var foundMatches = false; + + // Remove any prior search results. + this.unhighlightMatches_(); + this.removeSearchBubbles_(); + + var pagesToSearch = this.getSearchablePages_(); + for (var key in pagesToSearch) { + var page = pagesToSearch[key]; + var elements = page.pageDiv.querySelectorAll('section'); + for (var i = 0, node; node = elements[i]; i++) { + node.classList.add('search-hidden'); + } + } + + var bubbleControls = []; + + // Generate search text by applying lowercase and escaping any characters + // that would be problematic for regular expressions. + var searchText = + text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + // Generate a regular expression for hilighting search terms. + var regExp = new RegExp('(' + searchText + ')', 'ig'); + + if (searchText.length) { + // Search all top-level sections for anchored string matches. + for (var key in pagesToSearch) { + var page = pagesToSearch[key]; + var elements = + page.pageDiv.querySelectorAll('section'); + for (var i = 0, node; node = elements[i]; i++) { + if (this.highlightMatches_(regExp, node)) { + node.classList.remove('search-hidden'); + if (!node.hidden) + foundMatches = true; + } + } + } + + // Search all sub-pages, generating an array of top-level sections that + // we need to make visible. + var subPagesToSearch = this.getSearchableSubPages_(); + var control, node; + for (var key in subPagesToSearch) { + var page = subPagesToSearch[key]; + if (this.highlightMatches_(regExp, page.pageDiv)) { + this.revealAssociatedSections_(page); + + bubbleControls = + bubbleControls.concat(this.getAssociatedControls_(page)); + + foundMatches = true; + } + } + } + + // Configure elements on the search results page based on search results. + $('searchPageNoMatches').hidden = foundMatches; + + // Create search balloons for sub-page results. + length = bubbleControls.length; + for (var i = 0; i < length; i++) + this.createSearchBubble_(bubbleControls[i], text); + + // Cleanup the recursion-prevention variable. + this.insideSetSearchText_ = false; + }, + + /** + * Reveal the associated section for |subpage|, as well as the one for its + * |parentPage|, and its |parentPage|'s |parentPage|, etc. + * @private + */ + revealAssociatedSections_: function(subpage) { + for (var page = subpage; page; page = page.parentPage) { + var section = page.associatedSection; + if (section) + section.classList.remove('search-hidden'); + } + }, + + /** + * @return {!Array.} all the associated controls for |subpage|, + * including |subpage.associatedControls| as well as any controls on parent + * pages that are indirectly necessary to get to the subpage. + * @private + */ + getAssociatedControls_: function(subpage) { + var controls = []; + for (var page = subpage; page; page = page.parentPage) { + if (page.associatedControls) + controls = controls.concat(page.associatedControls); + } + return controls; + }, + + /** + * Wraps matches in spans. + * @param {RegExp} regExp The search query (in regexp form). + * @param {Element} element An HTML container element to recursively search + * within. + * @return {boolean} true if the element was changed. + * @private + */ + highlightMatches_: function(regExp, element) { + var found = false; + var div, child, tmp; + + // Walk the tree, searching each TEXT node. + var walker = document.createTreeWalker(element, + NodeFilter.SHOW_TEXT, + null, + false); + var node = walker.nextNode(); + while (node) { + var textContent = node.nodeValue; + // Perform a search and replace on the text node value. + var split = textContent.split(regExp); + if (split.length > 1) { + found = true; + var nextNode = walker.nextNode(); + var parentNode = node.parentNode; + parentNode.removeChild(node); + node = nextNode; + + for (var i = 0; i < split.length; ++i) { + if (i % 2 == 0) { + parentNode.appendChild(document.createTextNode(split[i])); + } else { + var span = document.createElement('span'); + span.className = 'search-highlighted'; + span.textContent = split[i]; + parentNode.appendChild(span); + } + } + } else { + node = walker.nextNode(); + } + } + + return found; + }, + + /** + * Removes all search highlight tags from the document. + * @private + */ + unhighlightMatches_: function() { + // Find all search highlight elements. + var elements = document.querySelectorAll('.search-highlighted'); + + // For each element, remove the highlighting. + var parent, i; + for (var i = 0, node; node = elements[i]; i++) { + parent = node.parentNode; + + // Replace the highlight element with the first child (the text node). + parent.replaceChild(node.firstChild, node); + + // Normalize the parent so that multiple text nodes will be combined. + parent.normalize(); + } + }, + + /** + * Creates a search result bubble attached to an element. + * @param {Element} element An HTML element, usually a button. + * @param {string} text A string to show in the bubble. + * @private + */ + createSearchBubble_: function(element, text) { + // avoid appending multiple bubbles to a button. + var sibling = element.previousElementSibling; + if (sibling && (sibling.classList.contains('search-bubble') || + sibling.classList.contains('search-bubble-wrapper'))) + return; + + var parent = element.parentElement; + if (parent) { + var bubble = new SearchBubble(text); + bubble.attachTo(element); + bubble.updatePosition(); + } + }, + + /** + * Removes all search match bubbles. + * @private + */ + removeSearchBubbles_: function() { + var elements = document.querySelectorAll('.search-bubble'); + var length = elements.length; + for (var i = 0; i < length; i++) + elements[i].dispose(); + }, + + /** + * Builds a list of top-level pages to search. Omits the search page and + * all sub-pages. + * @return {Array} An array of pages to search. + * @private + */ + getSearchablePages_: function() { + var name, page, pages = []; + for (name in OptionsPage.registeredPages) { + if (name != this.name) { + page = OptionsPage.registeredPages[name]; + if (!page.parentPage) + pages.push(page); + } + } + return pages; + }, + + /** + * Builds a list of sub-pages (and overlay pages) to search. Ignore pages + * that have no associated controls. + * @return {Array} An array of pages to search. + * @private + */ + getSearchableSubPages_: function() { + var name, pageInfo, page, pages = []; + for (name in OptionsPage.registeredPages) { + page = OptionsPage.registeredPages[name]; + if (page.parentPage && page.associatedSection) + pages.push(page); + } + for (name in OptionsPage.registeredOverlayPages) { + page = OptionsPage.registeredOverlayPages[name]; + if (page.associatedSection && page.pageDiv != undefined) + pages.push(page); + } + return pages; + }, + + /** + * A function to handle key press events. + * @return {Event} a keydown event. + * @private + */ + keyDownEventHandler_: function(event) { + /** @const */ var ESCAPE_KEY_CODE = 27; + /** @const */ var FORWARD_SLASH_KEY_CODE = 191; + + switch (event.keyCode) { + case ESCAPE_KEY_CODE: + if (event.target == this.searchField) { + this.setSearchText_(''); + this.searchField.blur(); + event.stopPropagation(); + event.preventDefault(); + } + break; + case FORWARD_SLASH_KEY_CODE: + if (!/INPUT|SELECT|BUTTON|TEXTAREA/.test(event.target.tagName) && + !event.ctrlKey && !event.altKey) { + this.searchField.focus(); + event.stopPropagation(); + event.preventDefault(); + } + break; + } + }, + }; + + /** + * Standardizes a user-entered text query by removing extra whitespace. + * @param {string} The user-entered text. + * @return {string} The trimmed query. + */ + SearchPage.canonicalizeQuery = function(text) { + // Trim beginning and ending whitespace. + return text.replace(/^\s+|\s+$/g, ''); + }; + + // Export + return { + SearchPage: SearchPage + }; + +}); diff --git a/chrome/browser/resources/options/settings_dialog.js b/chrome/browser/resources/options/settings_dialog.js new file mode 100644 index 0000000..609bbaa --- /dev/null +++ b/chrome/browser/resources/options/settings_dialog.js @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Base class for dialogs that require saving preferences on + * confirm and resetting preference inputs on cancel. + */ + +cr.define('options', function() { + /** @const */ var OptionsPage = options.OptionsPage; + + /** + * Base class for settings dialogs. + * @constructor + * @param {string} name See OptionsPage constructor. + * @param {string} title See OptionsPage constructor. + * @param {string} pageDivName See OptionsPage constructor. + * @param {HTMLInputElement} okButton The confirmation button element. + * @param {HTMLInputElement} cancelButton The cancellation button element. + * @extends {OptionsPage} + */ + function SettingsDialog(name, title, pageDivName, okButton, cancelButton) { + OptionsPage.call(this, name, title, pageDivName); + this.okButton = okButton; + this.cancelButton = cancelButton; + } + + SettingsDialog.prototype = { + __proto__: OptionsPage.prototype, + + /** + * @inheritDoc + */ + initializePage: function() { + this.okButton.onclick = this.handleConfirm.bind(this); + this.cancelButton.onclick = this.handleCancel.bind(this); + }, + + /** + * Handles the confirm button by saving the dialog preferences. + */ + handleConfirm: function() { + OptionsPage.closeOverlay(); + + var els = this.pageDiv.querySelectorAll('[dialog-pref]'); + for (var i = 0; i < els.length; i++) { + if (els[i].savePrefState) + els[i].savePrefState(); + } + }, + + /** + * Handles the cancel button by closing the overlay. + */ + handleCancel: function() { + OptionsPage.closeOverlay(); + + var els = this.pageDiv.querySelectorAll('[dialog-pref]'); + for (var i = 0; i < els.length; i++) { + if (els[i].resetPrefState) + els[i].resetPrefState(); + } + }, + }; + + return { + SettingsDialog: SettingsDialog + }; +}); diff --git a/chrome/browser/resources/options/spelling_confirm_overlay.css b/chrome/browser/resources/options/spelling_confirm_overlay.css new file mode 100644 index 0000000..434c26c --- /dev/null +++ b/chrome/browser/resources/options/spelling_confirm_overlay.css @@ -0,0 +1,8 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#spelling-confirm-overlay { + width: 500px; +} diff --git a/chrome/browser/resources/options/spelling_confirm_overlay.html b/chrome/browser/resources/options/spelling_confirm_overlay.html new file mode 100644 index 0000000..d58ce10 --- /dev/null +++ b/chrome/browser/resources/options/spelling_confirm_overlay.html @@ -0,0 +1,18 @@ + diff --git a/chrome/browser/resources/options/spelling_confirm_overlay.js b/chrome/browser/resources/options/spelling_confirm_overlay.js new file mode 100644 index 0000000..86e6598 --- /dev/null +++ b/chrome/browser/resources/options/spelling_confirm_overlay.js @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + var SettingsDialog = options.SettingsDialog; + + /* + * SpellingConfirmOverlay class + * Dialog to confirm that the user really wants to use the Spelling service + * @extends {SettingsDialog} + */ + function SpellingConfirmOverlay() { + SettingsDialog.call(this, + 'spellingConfirm', + loadTimeData.getString('spellingConfirmTitle'), + 'spelling-confirm-overlay', + $('spelling-confirm-ok'), + $('spelling-confirm-cancel')); + }; + + cr.addSingletonGetter(SpellingConfirmOverlay); + + SpellingConfirmOverlay.prototype = { + __proto__: SettingsDialog.prototype, + + /** @inheritDoc */ + initializePage: function() { + SettingsDialog.prototype.initializePage.call(this); + }, + + /** @inheritDoc */ + handleConfirm: function() { + SettingsDialog.prototype.handleConfirm.call(this); + Preferences.setBooleanPref('spellcheck.use_spelling_service', true); + Preferences.setBooleanPref('spellcheck.confirm_dialog_shown', true); + }, + + /** @inheritDoc */ + handleCancel: function() { + SettingsDialog.prototype.handleCancel.call(this); + $('spelling-enabled-control').checked = false; + }, + }; + + // Export + return { + SpellingConfirmOverlay: SpellingConfirmOverlay + }; +}); diff --git a/chrome/browser/resources/options/startup_overlay.css b/chrome/browser/resources/options/startup_overlay.css new file mode 100644 index 0000000..dc74deb --- /dev/null +++ b/chrome/browser/resources/options/startup_overlay.css @@ -0,0 +1,41 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#startup-overlay { + min-width: 500px; +} + +#startupPagesList { + margin-bottom: 20px; + min-height: 64px; +} + +#startupPagesList .title { + width: 40%; +} + +#startupPagesList .url { + -webkit-box-flex: 1; + color: #666; +} + +#startupPagesList > * { + max-width: 700px; +} + +#startupPagesListDropmarker { + background-clip: padding-box; + background-color: hsl(214, 91%, 65%); + border: 2px solid hsl(214, 91%, 65%); + border-bottom-color: transparent; + border-radius: 0; + border-top-color: transparent; + box-sizing: border-box; + display: none; + height: 6px; + overflow: hidden; + pointer-events: none; + position: fixed; + z-index: 10; +} diff --git a/chrome/browser/resources/options/startup_overlay.html b/chrome/browser/resources/options/startup_overlay.html new file mode 100644 index 0000000..1ec343d --- /dev/null +++ b/chrome/browser/resources/options/startup_overlay.html @@ -0,0 +1,23 @@ + diff --git a/chrome/browser/resources/options/startup_overlay.js b/chrome/browser/resources/options/startup_overlay.js new file mode 100644 index 0000000..f452ab2 --- /dev/null +++ b/chrome/browser/resources/options/startup_overlay.js @@ -0,0 +1,174 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('options', function() { + /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; + /** @const */ var OptionsPage = options.OptionsPage; + /** @const */ var SettingsDialog = options.SettingsDialog; + + /** + * StartupOverlay class + * Encapsulated handling of the 'Set Startup pages' overlay page. + * @constructor + * @class + */ + function StartupOverlay() { + SettingsDialog.call(this, 'startup', + loadTimeData.getString('startupPagesOverlayTabTitle'), + 'startup-overlay', + $('startup-overlay-confirm'), + $('startup-overlay-cancel')); + }; + + cr.addSingletonGetter(StartupOverlay); + + StartupOverlay.prototype = { + __proto__: SettingsDialog.prototype, + + /** + * An autocomplete list that can be attached to a text field during editing. + * @type {HTMLElement} + * @private + */ + autocompleteList_: null, + + startup_pages_pref_: { + 'name': 'session.urls_to_restore_on_startup', + 'disabled': false + }, + + /** + * Initialize the page. + */ + initializePage: function() { + SettingsDialog.prototype.initializePage.call(this); + + var self = this; + + var startupPagesList = $('startupPagesList'); + options.browser_options.StartupPageList.decorate(startupPagesList); + startupPagesList.autoExpands = true; + + $('startupUseCurrentButton').onclick = function(event) { + chrome.send('setStartupPagesToCurrentPages'); + }; + + Preferences.getInstance().addEventListener( + this.startup_pages_pref_.name, + this.handleStartupPageListChange_.bind(this)); + + var suggestionList = new cr.ui.AutocompleteList(); + suggestionList.autoExpands = true; + suggestionList.suggestionUpdateRequestCallback = + this.requestAutocompleteSuggestions_.bind(this); + $('startup-overlay').appendChild(suggestionList); + this.autocompleteList_ = suggestionList; + startupPagesList.autocompleteList = suggestionList; + }, + + /** @inheritDoc */ + handleConfirm: function() { + SettingsDialog.prototype.handleConfirm.call(this); + chrome.send('commitStartupPrefChanges'); + }, + + /** @inheritDoc */ + handleCancel: function() { + SettingsDialog.prototype.handleCancel.call(this); + chrome.send('cancelStartupPrefChanges'); + }, + + /** + * Sets the enabled state of the custom startup page list + * @param {boolean} disable True to disable, false to enable + */ + setControlsDisabled: function(disable) { + var startupPagesList = $('startupPagesList'); + startupPagesList.disabled = disable; + startupPagesList.setAttribute('tabindex', disable ? -1 : 0); + // Explicitly set disabled state for input text elements. + var inputs = startupPagesList.querySelectorAll("input[type='text']"); + for (var i = 0; i < inputs.length; i++) + inputs[i].disabled = disable; + $('startupUseCurrentButton').disabled = disable; + }, + + /** + * Enables or disables the the custom startup page list controls + * based on the whether the 'pages to restore on startup' pref is enabled. + */ + updateControlStates: function() { + this.setControlsDisabled( + this.startup_pages_pref_.disabled); + }, + + /** + * Handles change events of the preference + * 'session.urls_to_restore_on_startup'. + * @param {event} preference changed event. + * @private + */ + handleStartupPageListChange_: function(event) { + this.startup_pages_pref_.disabled = event.value['disabled']; + this.updateControlStates(); + }, + + /** + * Updates the startup pages list with the given entries. + * @param {Array} pages List of startup pages. + * @private + */ + updateStartupPages_: function(pages) { + var model = new ArrayDataModel(pages); + // Add a "new page" row. + model.push({ + 'modelIndex': '-1' + }); + $('startupPagesList').dataModel = model; + }, + + /** + * Sends an asynchronous request for new autocompletion suggestions for the + * the given query. When new suggestions are available, the C++ handler will + * call updateAutocompleteSuggestions_. + * @param {string} query List of autocomplete suggestions. + * @private + */ + requestAutocompleteSuggestions_: function(query) { + chrome.send('requestAutocompleteSuggestionsForStartupPages', [query]); + }, + + /** + * Updates the autocomplete suggestion list with the given entries. + * @param {Array} pages List of autocomplete suggestions. + * @private + */ + updateAutocompleteSuggestions_: function(suggestions) { + var list = this.autocompleteList_; + // If the trigger for this update was a value being selected from the + // current list, do nothing. + if (list.targetInput && list.selectedItem && + list.selectedItem['url'] == list.targetInput.value) { + return; + } + list.suggestions = suggestions; + }, + }; + + // Forward public APIs to private implementations. + [ + 'updateStartupPages', + 'updateAutocompleteSuggestions', + ].forEach(function(name) { + StartupOverlay[name] = function() { + var instance = StartupOverlay.getInstance(); + return instance[name + '_'].apply(instance, arguments); + }; + }); + + // Export + return { + StartupOverlay: StartupOverlay + }; +}); diff --git a/chrome/browser/resources/options/startup_section.html b/chrome/browser/resources/options/startup_section.html new file mode 100644 index 0000000..778c870 --- /dev/null +++ b/chrome/browser/resources/options/startup_section.html @@ -0,0 +1,36 @@ +
+

+
+
+ +
+
+ + +
+
+ +
+
+
diff --git a/chrome/browser/resources/options/subpages_tab_controls.css b/chrome/browser/resources/options/subpages_tab_controls.css new file mode 100644 index 0000000..c49c077 --- /dev/null +++ b/chrome/browser/resources/options/subpages_tab_controls.css @@ -0,0 +1,74 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +.subpages-nav-tabs .tab { + padding: 4px 8px; + position: relative; +} + +.subpages-nav-tabs .active-tab { + -webkit-box-shadow: 8px -8px 12px -6px rgb(240, 240, 240); + background: white; + border: 1px solid #ddd; + border-bottom: 2px solid white; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + position: relative; +} + +/* To avoid tabs changing size when they are clicked and their labels become + * bold, we actually put two labels inside each tab: an inactive label and an + * active label. Only one is visible at a time, but the bold label is used to + * size the tab even when it's not visible. This keeps the tab size constant. + */ +.subpages-nav-tabs .active-tab-label, +.subpages-nav-tabs .tab-label:hover { + font-weight: bold; +} + +.subpages-nav-tabs .tab-label { + left: 9px; + position: absolute; + top: 5px; +} + +html[dir=rtl] .subpages-nav-tabs .tab-label { + right: 9px; +} + +.subpages-nav-tabs .active-tab-label, +.subpages-nav-tabs .active-tab .tab-label { + visibility: hidden; +} + +/* .tab is not removed when .active-tab is added, so we must + * override the hidden visibility above in the active tab case. + */ +.subpages-nav-tabs .active-tab .active-tab-label { + visibility: visible; +} + +.subpages-nav-tabs { + background-image: -webkit-gradient( + linear, + left top, + left bottom, + color-stop(0, rgb(255,255,255)), + color-stop(0.6, rgb(255,255,255)), + color-stop(0.8, rgb(250, 250, 250)), + color-stop(1.0, rgb(242,242,242)) + ); + border-bottom: 1px solid #ddd; + padding: 4px 20px; +} + +.subpages-tab-contents { + -webkit-padding-start: 10px; + display: none; + padding-top: 15px; +} + +.active-tab-contents { + display: block; +} diff --git a/chrome/browser/resources/options/sync_section.html b/chrome/browser/resources/options/sync_section.html new file mode 100644 index 0000000..4f7c980 --- /dev/null +++ b/chrome/browser/resources/options/sync_section.html @@ -0,0 +1,62 @@ + +
+

+ + +
+ + + + + +
+ +
+
+
+ + + + + +
+ +
+
+
+ +
+ + + + + + +
+ + +
+
+ + + diff --git a/chrome/browser/resources/options2/OWNERS b/chrome/browser/resources/options2/OWNERS deleted file mode 100644 index 8d7d56d..0000000 --- a/chrome/browser/resources/options2/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -csilv@chromium.org -dbeam@chromium.org -estade@chromium.org -jhawkins@chromium.org diff --git a/chrome/browser/resources/options2/alert_overlay.css b/chrome/browser/resources/options2/alert_overlay.css deleted file mode 100644 index b2d9960..0000000 --- a/chrome/browser/resources/options2/alert_overlay.css +++ /dev/null @@ -1,7 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#alertOverlayMessage { - width: 400px; -} diff --git a/chrome/browser/resources/options2/alert_overlay.html b/chrome/browser/resources/options2/alert_overlay.html deleted file mode 100644 index 94be15c..0000000 --- a/chrome/browser/resources/options2/alert_overlay.html +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/chrome/browser/resources/options2/alert_overlay.js b/chrome/browser/resources/options2/alert_overlay.js deleted file mode 100644 index dbfd409..0000000 --- a/chrome/browser/resources/options2/alert_overlay.js +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - /** - * AlertOverlay class - * Encapsulated handling of a generic alert. - * @class - */ - function AlertOverlay() { - OptionsPage.call(this, 'alertOverlay', '', 'alertOverlay'); - } - - cr.addSingletonGetter(AlertOverlay); - - AlertOverlay.prototype = { - // Inherit AlertOverlay from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Whether the page can be shown. Used to make sure the page is only - * shown via AlertOverlay.Show(), and not via the address bar. - * @private - */ - canShow_: false, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - OptionsPage.prototype.initializePage.call(this); - - var self = this; - $('alertOverlayOk').onclick = function(event) { - self.handleOK_(); - }; - - $('alertOverlayCancel').onclick = function(event) { - self.handleCancel_(); - }; - }, - - /** @inheritDoc */ - get nestingLevel() { - // AlertOverlay is special in that it is not tied to one page or overlay. - // Set the nesting level arbitrarily high so as to always be recognized as - // the top-most visible page. - return 99; - }, - - /** - * Handle the 'ok' button. Clear the overlay and call the ok callback if - * available. - * @private - */ - handleOK_: function() { - OptionsPage.closeOverlay(); - if (this.okCallback != undefined) { - this.okCallback.call(); - } - }, - - /** - * Handle the 'cancel' button. Clear the overlay and call the cancel - * callback if available. - * @private - */ - handleCancel_: function() { - OptionsPage.closeOverlay(); - if (this.cancelCallback != undefined) { - this.cancelCallback.call(); - } - }, - - /** - * The page is getting hidden. Don't let it be shown again. - */ - willHidePage: function() { - canShow_ = false; - }, - - /** @inheritDoc */ - canShowPage: function() { - return this.canShow_; - }, - }; - - /** - * Show an alert overlay with the given message, button titles, and - * callbacks. - * @param {string} title The alert title to display to the user. - * @param {string} message The alert message to display to the user. - * @param {string} okTitle The title of the OK button. If undefined or empty, - * no button is shown. - * @param {string} cancelTitle The title of the cancel button. If undefined or - * empty, no button is shown. - * @param {function} okCallback A function to be called when the user presses - * the ok button. The alert window will be closed automatically. Can be - * undefined. - * @param {function} cancelCallback A function to be called when the user - * presses the cancel button. The alert window will be closed - * automatically. Can be undefined. - */ - AlertOverlay.show = function( - title, message, okTitle, cancelTitle, okCallback, cancelCallback) { - if (title != undefined) { - $('alertOverlayTitle').textContent = title; - $('alertOverlayTitle').style.display = 'block'; - } else { - $('alertOverlayTitle').style.display = 'none'; - } - - if (message != undefined) { - $('alertOverlayMessage').textContent = message; - $('alertOverlayMessage').style.display = 'block'; - } else { - $('alertOverlayMessage').style.display = 'none'; - } - - if (okTitle != undefined && okTitle != '') { - $('alertOverlayOk').textContent = okTitle; - $('alertOverlayOk').style.display = 'block'; - } else { - $('alertOverlayOk').style.display = 'none'; - } - - if (cancelTitle != undefined && cancelTitle != '') { - $('alertOverlayCancel').textContent = cancelTitle; - $('alertOverlayCancel').style.display = 'inline'; - } else { - $('alertOverlayCancel').style.display = 'none'; - } - - var alertOverlay = AlertOverlay.getInstance(); - alertOverlay.okCallback = okCallback; - alertOverlay.cancelCallback = cancelCallback; - alertOverlay.canShow_ = true; - - // Intentionally don't show the URL in the location bar as we don't want - // people trying to navigate here by hand. - OptionsPage.showPageByName('alertOverlay', false); - }; - - // Export - return { - AlertOverlay: AlertOverlay - }; -}); diff --git a/chrome/browser/resources/options2/autofill_edit_address_overlay.html b/chrome/browser/resources/options2/autofill_edit_address_overlay.html deleted file mode 100644 index 361445e..0000000 --- a/chrome/browser/resources/options2/autofill_edit_address_overlay.html +++ /dev/null @@ -1,100 +0,0 @@ - diff --git a/chrome/browser/resources/options2/autofill_edit_address_overlay.js b/chrome/browser/resources/options2/autofill_edit_address_overlay.js deleted file mode 100644 index 8d0059c..0000000 --- a/chrome/browser/resources/options2/autofill_edit_address_overlay.js +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - // The GUID of the loaded address. - var guid; - - /** - * AutofillEditAddressOverlay class - * Encapsulated handling of the 'Add Page' overlay page. - * @class - */ - function AutofillEditAddressOverlay() { - OptionsPage.call(this, 'autofillEditAddress', - loadTimeData.getString('autofillEditAddressTitle'), - 'autofill-edit-address-overlay'); - } - - cr.addSingletonGetter(AutofillEditAddressOverlay); - - AutofillEditAddressOverlay.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initializes the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.createMultiValueLists_(); - - var self = this; - $('autofill-edit-address-cancel-button').onclick = function(event) { - self.dismissOverlay_(); - }; - - // TODO(jhawkins): Investigate other possible solutions. - $('autofill-edit-address-apply-button').onclick = function(event) { - // Blur active element to ensure that pending changes are committed. - if (document.activeElement) - document.activeElement.blur(); - // Blurring is delayed for list elements. Queue save and close to - // ensure that pending changes have been applied. - setTimeout(function() { - self.saveAddress_(); - self.dismissOverlay_(); - }, 0); - }; - - // Prevent 'blur' events on the OK and cancel buttons, which can trigger - // insertion of new placeholder elements. The addition of placeholders - // affects layout, which interferes with being able to click on the - // buttons. - $('autofill-edit-address-apply-button').onmousedown = function(event) { - event.preventDefault(); - }; - $('autofill-edit-address-cancel-button').onmousedown = function(event) { - event.preventDefault(); - }; - - self.guid = ''; - self.populateCountryList_(); - self.clearInputFields_(); - self.connectInputEvents_(); - }, - - /** - * Creates, decorates and initializes the multi-value lists for full name, - * phone, and email. - * @private - */ - createMultiValueLists_: function() { - var list = $('full-name-list'); - options.autofillOptions.AutofillNameValuesList.decorate(list); - list.autoExpands = true; - - list = $('phone-list'); - options.autofillOptions.AutofillPhoneValuesList.decorate(list); - list.autoExpands = true; - - list = $('email-list'); - options.autofillOptions.AutofillValuesList.decorate(list); - list.autoExpands = true; - }, - - /** - * Updates the data model for the list named |listName| with the values from - * |entries|. - * @param {String} listName The id of the list. - * @param {Array} entries The list of items to be added to the list. - */ - setMultiValueList_: function(listName, entries) { - // Add data entries. - var list = $(listName); - - // Add special entry for adding new values. - var augmentedList = entries.slice(); - augmentedList.push(null); - list.dataModel = new ArrayDataModel(augmentedList); - - // Update the status of the 'OK' button. - this.inputFieldChanged_(); - - list.dataModel.addEventListener('splice', - this.inputFieldChanged_.bind(this)); - list.dataModel.addEventListener('change', - this.inputFieldChanged_.bind(this)); - }, - - /** - * Clears any uncommitted input, resets the stored GUID and dismisses the - * overlay. - * @private - */ - dismissOverlay_: function() { - this.clearInputFields_(); - this.guid = ''; - OptionsPage.closeOverlay(); - }, - - /** - * Aggregates the values in the input fields into an array and sends the - * array to the Autofill handler. - * @private - */ - saveAddress_: function() { - var address = new Array(); - address[0] = this.guid; - var list = $('full-name-list'); - address[1] = list.dataModel.slice(0, list.dataModel.length - 1); - address[2] = $('company-name').value; - address[3] = $('addr-line-1').value; - address[4] = $('addr-line-2').value; - address[5] = $('city').value; - address[6] = $('state').value; - address[7] = $('postal-code').value; - address[8] = $('country').value; - list = $('phone-list'); - address[9] = list.dataModel.slice(0, list.dataModel.length - 1); - list = $('email-list'); - address[10] = list.dataModel.slice(0, list.dataModel.length - 1); - - chrome.send('setAddress', address); - }, - - /** - * Connects each input field to the inputFieldChanged_() method that enables - * or disables the 'Ok' button based on whether all the fields are empty or - * not. - * @private - */ - connectInputEvents_: function() { - var self = this; - $('company-name').oninput = $('addr-line-1').oninput = - $('addr-line-2').oninput = $('city').oninput = $('state').oninput = - $('postal-code').oninput = function(event) { - self.inputFieldChanged_(); - }; - - $('country').onchange = function(event) { - self.countryChanged_(); - }; - }, - - /** - * Checks the values of each of the input fields and disables the 'Ok' - * button if all of the fields are empty. - * @private - */ - inputFieldChanged_: function() { - // Length of lists are tested for <= 1 due to the "add" placeholder item - // in the list. - var disabled = - $('full-name-list').items.length <= 1 && - !$('company-name').value && - !$('addr-line-1').value && !$('addr-line-2').value && - !$('city').value && !$('state').value && !$('postal-code').value && - !$('country').value && $('phone-list').items.length <= 1 && - $('email-list').items.length <= 1; - $('autofill-edit-address-apply-button').disabled = disabled; - }, - - /** - * Updates the postal code and state field labels appropriately for the - * selected country. - * @private - */ - countryChanged_: function() { - var countryCode = $('country').value || - loadTimeData.getString('defaultCountryCode'); - - var details = loadTimeData.getValue('autofillCountryData')[countryCode]; - var postal = $('postal-code-label'); - postal.textContent = details['postalCodeLabel']; - $('state-label').textContent = details['stateLabel']; - - // Also update the 'Ok' button as needed. - this.inputFieldChanged_(); - }, - - /** - * Populates the country list. - var countryList = $('country'); - for (var i = 0; i < countries.length; i++) { - var country = new Option(countries[i].name, countries[i].countryCode); - country.disabled = countries[i].disabled; - countryList.appendChild(country); - } - }, - - /** - * Clears the value of each input field. - * @private - */ - clearInputFields_: function() { - this.setMultiValueList_('full-name-list', []); - $('company-name').value = ''; - $('addr-line-1').value = ''; - $('addr-line-2').value = ''; - $('city').value = ''; - $('state').value = ''; - $('postal-code').value = ''; - $('country').value = ''; - this.setMultiValueList_('phone-list', []); - this.setMultiValueList_('email-list', []); - - this.countryChanged_(); - }, - - /** - * Loads the address data from |address|, sets the input fields based on - * this data and stores the GUID of the address. - * @private - */ - loadAddress_: function(address) { - this.setInputFields_(address); - this.inputFieldChanged_(); - this.guid = address['guid']; - }, - - /** - * Sets the value of each input field according to |address| - * @private - */ - setInputFields_: function(address) { - this.setMultiValueList_('full-name-list', address['fullName']); - $('company-name').value = address['companyName']; - $('addr-line-1').value = address['addrLine1']; - $('addr-line-2').value = address['addrLine2']; - $('city').value = address['city']; - $('state').value = address['state']; - $('postal-code').value = address['postalCode']; - $('country').value = address['country']; - this.setMultiValueList_('phone-list', address['phone']); - this.setMultiValueList_('email-list', address['email']); - - this.countryChanged_(); - }, - }; - - AutofillEditAddressOverlay.clearInputFields = function() { - AutofillEditAddressOverlay.getInstance().clearInputFields_(); - }; - - AutofillEditAddressOverlay.loadAddress = function(address) { - AutofillEditAddressOverlay.getInstance().loadAddress_(address); - }; - - AutofillEditAddressOverlay.setTitle = function(title) { - $('autofill-address-title').textContent = title; - }; - - AutofillEditAddressOverlay.setValidatedPhoneNumbers = function(numbers) { - AutofillEditAddressOverlay.getInstance().setMultiValueList_('phone-list', - numbers); - }; - - // Export - return { - AutofillEditAddressOverlay: AutofillEditAddressOverlay - }; -}); diff --git a/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.html b/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.html deleted file mode 100644 index bf3278b..0000000 --- a/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.html +++ /dev/null @@ -1,31 +0,0 @@ - diff --git a/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.js b/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.js deleted file mode 100644 index c9a0b0e..0000000 --- a/chrome/browser/resources/options2/autofill_edit_creditcard_overlay.js +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - // The GUID of the loaded credit card. - var guid_; - - /** - * AutofillEditCreditCardOverlay class - * Encapsulated handling of the 'Add Page' overlay page. - * @class - */ - function AutofillEditCreditCardOverlay() { - OptionsPage.call(this, 'autofillEditCreditCard', - loadTimeData.getString('autofillEditCreditCardTitle'), - 'autofill-edit-credit-card-overlay'); - } - - cr.addSingletonGetter(AutofillEditCreditCardOverlay); - - AutofillEditCreditCardOverlay.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initializes the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var self = this; - $('autofill-edit-credit-card-cancel-button').onclick = function(event) { - self.dismissOverlay_(); - }; - $('autofill-edit-credit-card-apply-button').onclick = function(event) { - self.saveCreditCard_(); - self.dismissOverlay_(); - }; - - self.guid_ = ''; - self.hasEditedNumber_ = false; - self.clearInputFields_(); - self.connectInputEvents_(); - self.setDefaultSelectOptions_(); - }, - - /** - * Clears any uncommitted input, and dismisses the overlay. - * @private - */ - dismissOverlay_: function() { - this.clearInputFields_(); - this.guid_ = ''; - this.hasEditedNumber_ = false; - OptionsPage.closeOverlay(); - }, - - /** - * Aggregates the values in the input fields into an array and sends the - * array to the Autofill handler. - * @private - */ - saveCreditCard_: function() { - var creditCard = new Array(5); - creditCard[0] = this.guid_; - creditCard[1] = $('name-on-card').value; - creditCard[2] = $('credit-card-number').value; - creditCard[3] = $('expiration-month').value; - creditCard[4] = $('expiration-year').value; - chrome.send('setCreditCard', creditCard); - }, - - /** - * Connects each input field to the inputFieldChanged_() method that enables - * or disables the 'Ok' button based on whether all the fields are empty or - * not. - * @private - */ - connectInputEvents_: function() { - var ccNumber = $('credit-card-number'); - $('name-on-card').oninput = ccNumber.oninput = - $('expiration-month').onchange = $('expiration-year').onchange = - this.inputFieldChanged_.bind(this); - }, - - /** - * Checks the values of each of the input fields and disables the 'Ok' - * button if all of the fields are empty. - * @param {Event} opt_event Optional data for the 'input' event. - * @private - */ - inputFieldChanged_: function(opt_event) { - var disabled = !$('name-on-card').value && !$('credit-card-number').value; - $('autofill-edit-credit-card-apply-button').disabled = disabled; - }, - - /** - * Sets the default values of the options in the 'Expiration date' select - * controls. - * @private - */ - setDefaultSelectOptions_: function() { - // Set the 'Expiration month' default options. - var expirationMonth = $('expiration-month'); - expirationMonth.options.length = 0; - for (var i = 1; i <= 12; ++i) { - var text; - if (i < 10) - text = '0' + i; - else - text = i; - - var option = document.createElement('option'); - option.text = text; - option.value = text; - expirationMonth.add(option, null); - } - - // Set the 'Expiration year' default options. - var expirationYear = $('expiration-year'); - expirationYear.options.length = 0; - - var date = new Date(); - var year = parseInt(date.getFullYear()); - for (var i = 0; i < 10; ++i) { - var text = year + i; - var option = document.createElement('option'); - option.text = text; - option.value = text; - expirationYear.add(option, null); - } - }, - - /** - * Clears the value of each input field. - * @private - */ - clearInputFields_: function() { - $('name-on-card').value = ''; - $('credit-card-number').value = ''; - $('expiration-month').selectedIndex = 0; - $('expiration-year').selectedIndex = 0; - - // Reset the enabled status of the 'Ok' button. - this.inputFieldChanged_(); - }, - - /** - * Sets the value of each input field according to |creditCard| - * @private - */ - setInputFields_: function(creditCard) { - $('name-on-card').value = creditCard['nameOnCard']; - $('credit-card-number').value = creditCard['creditCardNumber']; - - // The options for the year select control may be out-dated at this point, - // e.g. the user opened the options page before midnight on New Year's Eve - // and then loaded a credit card profile to edit in the new year, so - // reload the select options just to be safe. - this.setDefaultSelectOptions_(); - - var idx = parseInt(creditCard['expirationMonth'], 10); - $('expiration-month').selectedIndex = idx - 1; - - expYear = creditCard['expirationYear']; - var date = new Date(); - var year = parseInt(date.getFullYear()); - for (var i = 0; i < 10; ++i) { - var text = year + i; - if (expYear == String(text)) - $('expiration-year').selectedIndex = i; - } - }, - - /** - * Loads the credit card data from |creditCard|, sets the input fields based - * on this data and stores the GUID of the credit card. - * @private - */ - loadCreditCard_: function(creditCard) { - this.setInputFields_(creditCard); - this.inputFieldChanged_(); - this.guid_ = creditCard['guid']; - }, - }; - - AutofillEditCreditCardOverlay.clearInputFields = function(title) { - AutofillEditCreditCardOverlay.getInstance().clearInputFields_(); - }; - - AutofillEditCreditCardOverlay.loadCreditCard = function(creditCard) { - AutofillEditCreditCardOverlay.getInstance().loadCreditCard_(creditCard); - }; - - AutofillEditCreditCardOverlay.setTitle = function(title) { - $('autofill-credit-card-title').textContent = title; - }; - - // Export - return { - AutofillEditCreditCardOverlay: AutofillEditCreditCardOverlay - }; -}); diff --git a/chrome/browser/resources/options2/autofill_edit_overlay.css b/chrome/browser/resources/options2/autofill_edit_overlay.css deleted file mode 100644 index 76f56a6..0000000 --- a/chrome/browser/resources/options2/autofill_edit_overlay.css +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#autofill-edit-address-overlay { - min-width: 510px; -} - -#autofill-edit-credit-card-overlay { - min-width: 500px; -} - -div.table { - display: table; -} - -div.cell { - display: table-cell; -} - -div.row { - display: table-row; -} - -div.input { - padding: 2px; -} - -/* Size to match large name fields. */ -#company-name, -#addr-line-1, -#addr-line-2 { - width: 206px; -} - -#country { - max-width: 450px; -} - -#autofill-edit-address-overlay list { - /* Min height is a multiple of the list item height (32) */ - min-height: 32px; - width: 176px; -} - -#autofill-edit-address-overlay list div.static-text { - -webkit-border-radius: 2px; - -webkit-box-flex: 1; - -webkit-padding-end: 4px; - -webkit-padding-start: 4px; - border: 1px solid darkGray; - /* Set the line-height and min-height to match the height of an input element, - * so that even empty cells renderer with the correct height. - */ - line-height: 1.75em; - min-height: 1.75em; - width: 141px; -} - -#autofill-edit-address-overlay list input { - width: 151px; -} - -#autofill-name-labels { - -webkit-box-orient: horizontal; - /* Set the margin to compensate for each list item's close button and - * padding. - */ - -webkit-margin-end: 25px; - display: -webkit-box; -} - -#autofill-name-labels label { - -webkit-box-flex: 1; - display: block; - /* Set the minimum width to the size of an input element, so that all boxes - * have an equal amount of flex space to work with. - */ - min-width: 141px; -} - -#autofill-edit-address-overlay list#full-name-list div.static-text { - width: 131px; -} - -#autofill-edit-address-overlay list#full-name-list input { - width: 141px; -} - -#autofill-edit-address-overlay list#full-name-list { - width: 100%; -} - -#full-name-list div[role='listitem'] > div { - -webkit-box-orient: horizontal; - display: -webkit-box; -} - -#full-name-list div[role='listitem'] > div > div { - -webkit-box-flex: 1; -} diff --git a/chrome/browser/resources/options2/autofill_options.css b/chrome/browser/resources/options2/autofill_options.css deleted file mode 100644 index 488197c..0000000 --- a/chrome/browser/resources/options2/autofill_options.css +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#autofill-options { - min-width: 550px; -} - -#autofill-help { - bottom: 18px; - position: absolute; -} - -#autofill-options list { - min-height: 172px; -} - -.autofill-list-item { - -webkit-box-flex: 1; - -webkit-padding-start: 8px; - overflow: hidden; - text-overflow: ellipsis; -} - -.autofill-list-item + img { - -webkit-padding-end: 20px; - vertical-align: top; -} - -#autofill-options > div:last-child { - margin-top: 15px; -} - -#autofill-options > div.settings-list > div:last-child { - border-top: 1px solid #d9d9d9; - padding: 5px 10px; -} - -#autofill-add-address, -#autofill-add-creditcard { - margin: 5px 5px; -} - -#autofill-options .list-inline-button { - margin-top: 0; - vertical-align: top; -} - -#autofill-options div[role='listitem']:not(:hover):not([selected]) - .list-inline-button { - display: none; -} diff --git a/chrome/browser/resources/options2/autofill_options.html b/chrome/browser/resources/options2/autofill_options.html deleted file mode 100644 index a1c4481..0000000 --- a/chrome/browser/resources/options2/autofill_options.html +++ /dev/null @@ -1,39 +0,0 @@ - diff --git a/chrome/browser/resources/options2/autofill_options.js b/chrome/browser/resources/options2/autofill_options.js deleted file mode 100644 index 82d8c24..0000000 --- a/chrome/browser/resources/options2/autofill_options.js +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - var ArrayDataModel = cr.ui.ArrayDataModel; - - ///////////////////////////////////////////////////////////////////////////// - // AutofillOptions class: - - /** - * Encapsulated handling of Autofill options page. - * @constructor - */ - function AutofillOptions() { - OptionsPage.call(this, - 'autofill', - loadTimeData.getString('autofillOptionsPageTabTitle'), - 'autofill-options'); - } - - cr.addSingletonGetter(AutofillOptions); - - AutofillOptions.prototype = { - __proto__: OptionsPage.prototype, - - /** - * The address list. - * @type {DeletableItemList} - * @private - */ - addressList_: null, - - /** - * The credit card list. - * @type {DeletableItemList} - * @private - */ - creditCardList_: null, - - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.createAddressList_(); - this.createCreditCardList_(); - - var self = this; - $('autofill-add-address').onclick = function(event) { - self.showAddAddressOverlay_(); - }; - $('autofill-add-creditcard').onclick = function(event) { - self.showAddCreditCardOverlay_(); - }; - $('autofill-options-confirm').onclick = function(event) { - OptionsPage.closeOverlay(); - }; - - // TODO(jhawkins): What happens when Autofill is disabled whilst on the - // Autofill options page? - }, - - /** - * Creates, decorates and initializes the address list. - * @private - */ - createAddressList_: function() { - this.addressList_ = $('address-list'); - options.autofillOptions.AutofillAddressList.decorate(this.addressList_); - this.addressList_.autoExpands = true; - }, - - /** - * Creates, decorates and initializes the credit card list. - * @private - */ - createCreditCardList_: function() { - this.creditCardList_ = $('creditcard-list'); - options.autofillOptions.AutofillCreditCardList.decorate( - this.creditCardList_); - this.creditCardList_.autoExpands = true; - }, - - /** - * Shows the 'Add address' overlay, specifically by loading the - * 'Edit address' overlay, emptying the input fields and modifying the - * overlay title. - * @private - */ - showAddAddressOverlay_: function() { - var title = loadTimeData.getString('addAddressTitle'); - AutofillEditAddressOverlay.setTitle(title); - AutofillEditAddressOverlay.clearInputFields(); - OptionsPage.navigateToPage('autofillEditAddress'); - }, - - /** - * Shows the 'Add credit card' overlay, specifically by loading the - * 'Edit credit card' overlay, emptying the input fields and modifying the - * overlay title. - * @private - */ - showAddCreditCardOverlay_: function() { - var title = loadTimeData.getString('addCreditCardTitle'); - AutofillEditCreditCardOverlay.setTitle(title); - AutofillEditCreditCardOverlay.clearInputFields(); - OptionsPage.navigateToPage('autofillEditCreditCard'); - }, - - /** - * Updates the data model for the address list with the values from - * |entries|. - * @param {Array} entries The list of addresses. - */ - setAddressList_: function(entries) { - this.addressList_.dataModel = new ArrayDataModel(entries); - }, - - /** - * Updates the data model for the credit card list with the values from - * |entries|. - * @param {Array} entries The list of credit cards. - */ - setCreditCardList_: function(entries) { - this.creditCardList_.dataModel = new ArrayDataModel(entries); - }, - - /** - * Removes the Autofill address represented by |guid|. - * @param {String} guid The GUID of the address to remove. - * @private - */ - removeAddress_: function(guid) { - chrome.send('removeAddress', [guid]); - }, - - /** - * Removes the Autofill credit card represented by |guid|. - * @param {String} guid The GUID of the credit card to remove. - * @private - */ - removeCreditCard_: function(guid) { - chrome.send('removeCreditCard', [guid]); - }, - - /** - * Requests profile data for the address represented by |guid| from the - * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler - * calls showEditAddressOverlay(). - * @param {String} guid The GUID of the address to edit. - * @private - */ - loadAddressEditor_: function(guid) { - chrome.send('loadAddressEditor', [guid]); - }, - - /** - * Requests profile data for the credit card represented by |guid| from the - * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler - * calls showEditCreditCardOverlay(). - * @param {String} guid The GUID of the credit card to edit. - * @private - */ - loadCreditCardEditor_: function(guid) { - chrome.send('loadCreditCardEditor', [guid]); - }, - - /** - * Shows the 'Edit address' overlay, using the data in |address| to fill the - * input fields. |address| is a list with one item, an associative array - * that contains the address data. - * @private - */ - showEditAddressOverlay_: function(address) { - var title = loadTimeData.getString('editAddressTitle'); - AutofillEditAddressOverlay.setTitle(title); - AutofillEditAddressOverlay.loadAddress(address); - OptionsPage.navigateToPage('autofillEditAddress'); - }, - - /** - * Shows the 'Edit credit card' overlay, using the data in |credit_card| to - * fill the input fields. |address| is a list with one item, an associative - * array that contains the credit card data. - * @private - */ - showEditCreditCardOverlay_: function(creditCard) { - var title = loadTimeData.getString('editCreditCardTitle'); - AutofillEditCreditCardOverlay.setTitle(title); - AutofillEditCreditCardOverlay.loadCreditCard(creditCard); - OptionsPage.navigateToPage('autofillEditCreditCard'); - }, - }; - - AutofillOptions.setAddressList = function(entries) { - AutofillOptions.getInstance().setAddressList_(entries); - }; - - AutofillOptions.setCreditCardList = function(entries) { - AutofillOptions.getInstance().setCreditCardList_(entries); - }; - - AutofillOptions.removeAddress = function(guid) { - AutofillOptions.getInstance().removeAddress_(guid); - }; - - AutofillOptions.removeCreditCard = function(guid) { - AutofillOptions.getInstance().removeCreditCard_(guid); - }; - - AutofillOptions.loadAddressEditor = function(guid) { - AutofillOptions.getInstance().loadAddressEditor_(guid); - }; - - AutofillOptions.loadCreditCardEditor = function(guid) { - AutofillOptions.getInstance().loadCreditCardEditor_(guid); - }; - - AutofillOptions.editAddress = function(address) { - AutofillOptions.getInstance().showEditAddressOverlay_(address); - }; - - AutofillOptions.editCreditCard = function(creditCard) { - AutofillOptions.getInstance().showEditCreditCardOverlay_(creditCard); - }; - - // Export - return { - AutofillOptions: AutofillOptions - }; - -}); - diff --git a/chrome/browser/resources/options2/autofill_options_list.js b/chrome/browser/resources/options2/autofill_options_list.js deleted file mode 100644 index 7bb9c7b..0000000 --- a/chrome/browser/resources/options2/autofill_options_list.js +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.autofillOptions', function() { - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - /** @const */ var InlineEditableItem = options.InlineEditableItem; - /** @const */ var InlineEditableItemList = options.InlineEditableItemList; - - function AutofillEditProfileButton(guid, edit) { - var editButtonEl = document.createElement('button'); - editButtonEl.className = 'list-inline-button custom-appearance'; - editButtonEl.textContent = - loadTimeData.getString('autofillEditProfileButton'); - editButtonEl.onclick = function(e) { edit(guid); }; - - // Don't select the row when clicking the button. - editButtonEl.onmousedown = function(e) { - e.stopPropagation(); - }; - - return editButtonEl; - } - - /** - * Creates a new address list item. - * @param {Array} entry An array of the form [guid, label]. - * @constructor - * @extends {options.DeletableItem} - */ - function AddressListItem(entry) { - var el = cr.doc.createElement('div'); - el.guid = entry[0]; - el.label = entry[1]; - el.__proto__ = AddressListItem.prototype; - el.decorate(); - - return el; - } - - AddressListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - // The stored label. - var label = this.ownerDocument.createElement('div'); - label.className = 'autofill-list-item'; - label.textContent = this.label; - this.contentElement.appendChild(label); - - // The 'Edit' button. - var editButtonEl = new AutofillEditProfileButton( - this.guid, - AutofillOptions.loadAddressEditor); - this.contentElement.appendChild(editButtonEl); - }, - }; - - /** - * Creates a new credit card list item. - * @param {Array} entry An array of the form [guid, label, icon]. - * @constructor - * @extends {options.DeletableItem} - */ - function CreditCardListItem(entry) { - var el = cr.doc.createElement('div'); - el.guid = entry[0]; - el.label = entry[1]; - el.icon = entry[2]; - el.description = entry[3]; - el.__proto__ = CreditCardListItem.prototype; - el.decorate(); - - return el; - } - - CreditCardListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - // The stored label. - var label = this.ownerDocument.createElement('div'); - label.className = 'autofill-list-item'; - label.textContent = this.label; - this.contentElement.appendChild(label); - - // The credit card icon. - var icon = this.ownerDocument.createElement('image'); - icon.src = this.icon; - icon.alt = this.description; - this.contentElement.appendChild(icon); - - // The 'Edit' button. - var editButtonEl = new AutofillEditProfileButton( - this.guid, - AutofillOptions.loadCreditCardEditor); - this.contentElement.appendChild(editButtonEl); - }, - }; - - /** - * Creates a new value list item. - * @param {AutofillValuesList} list The parent list of this item. - * @param {String} entry A string value. - * @constructor - * @extends {options.InlineEditableItem} - */ - function ValuesListItem(list, entry) { - var el = cr.doc.createElement('div'); - el.list = list; - el.value = entry ? entry : ''; - el.__proto__ = ValuesListItem.prototype; - el.decorate(); - - return el; - } - - ValuesListItem.prototype = { - __proto__: InlineEditableItem.prototype, - - /** @inheritDoc */ - decorate: function() { - InlineEditableItem.prototype.decorate.call(this); - - // Note: This must be set prior to calling |createEditableTextCell|. - this.isPlaceholder = !this.value; - - // The stored value. - var cell = this.createEditableTextCell(this.value); - this.contentElement.appendChild(cell); - this.input = cell.querySelector('input'); - - if (this.isPlaceholder) { - this.input.placeholder = this.list.getAttribute('placeholder'); - this.deletable = false; - } - - this.addEventListener('commitedit', this.onEditCommitted_); - }, - - /** - * @return {string} This item's value. - * @protected - */ - value_: function() { - return this.input.value; - }, - - /** - * @param {Object} value The value to test. - * @return {boolean} True if the given value is non-empty. - * @protected - */ - valueIsNonEmpty_: function(value) { - return !!value; - }, - - /** - * @return {boolean} True if value1 is logically equal to value2. - */ - valuesAreEqual_: function(value1, value2) { - return value1 === value2; - }, - - /** - * Clears the item's value. - * @protected - */ - clearValue_: function() { - this.input.value = ''; - }, - - /** - * Called when committing an edit. - * If this is an "Add ..." item, committing a non-empty value adds that - * value to the end of the values list, but also leaves this "Add ..." item - * in place. - * @param {Event} e The end event. - * @private - */ - onEditCommitted_: function(e) { - var value = this.value_(); - var i = this.list.items.indexOf(this); - if (i < this.list.dataModel.length && - this.valuesAreEqual_(value, this.list.dataModel.item(i))) { - return; - } - - var entries = this.list.dataModel.slice(); - if (this.valueIsNonEmpty_(value) && - !entries.some(this.valuesAreEqual_.bind(this, value))) { - // Update with new value. - if (this.isPlaceholder) { - // It is important that updateIndex is done before validateAndSave. - // Otherwise we can not be sure about AddRow index. - this.list.dataModel.updateIndex(i); - this.list.validateAndSave(i, 0, value); - } else { - this.list.validateAndSave(i, 1, value); - } - } else { - // Reject empty values and duplicates. - if (!this.isPlaceholder) - this.list.dataModel.splice(i, 1); - else - this.clearValue_(); - } - }, - }; - - /** - * Creates a new name value list item. - * @param {AutofillNameValuesList} list The parent list of this item. - * @param {array} entry An array of [first, middle, last] names. - * @constructor - * @extends {options.ValuesListItem} - */ - function NameListItem(list, entry) { - var el = cr.doc.createElement('div'); - el.list = list; - el.first = entry ? entry[0] : ''; - el.middle = entry ? entry[1] : ''; - el.last = entry ? entry[2] : ''; - el.__proto__ = NameListItem.prototype; - el.decorate(); - - return el; - } - - NameListItem.prototype = { - __proto__: ValuesListItem.prototype, - - /** @inheritDoc */ - decorate: function() { - InlineEditableItem.prototype.decorate.call(this); - - // Note: This must be set prior to calling |createEditableTextCell|. - this.isPlaceholder = !this.first && !this.middle && !this.last; - - // The stored value. - // For the simulated static "input element" to display correctly, the - // value must not be empty. We use a space to force the UI to render - // correctly when the value is logically empty. - var cell = this.createEditableTextCell(this.first); - this.contentElement.appendChild(cell); - this.firstNameInput = cell.querySelector('input'); - - cell = this.createEditableTextCell(this.middle); - this.contentElement.appendChild(cell); - this.middleNameInput = cell.querySelector('input'); - - cell = this.createEditableTextCell(this.last); - this.contentElement.appendChild(cell); - this.lastNameInput = cell.querySelector('input'); - - if (this.isPlaceholder) { - this.firstNameInput.placeholder = - loadTimeData.getString('autofillAddFirstNamePlaceholder'); - this.middleNameInput.placeholder = - loadTimeData.getString('autofillAddMiddleNamePlaceholder'); - this.lastNameInput.placeholder = - loadTimeData.getString('autofillAddLastNamePlaceholder'); - this.deletable = false; - } - - this.addEventListener('commitedit', this.onEditCommitted_); - }, - - /** @inheritDoc */ - value_: function() { - return [this.firstNameInput.value, - this.middleNameInput.value, - this.lastNameInput.value]; - }, - - /** @inheritDoc */ - valueIsNonEmpty_: function(value) { - return value[0] || value[1] || value[2]; - }, - - /** @inheritDoc */ - valuesAreEqual_: function(value1, value2) { - // First, check for null values. - if (!value1 || !value2) - return value1 == value2; - - return value1[0] === value2[0] && - value1[1] === value2[1] && - value1[2] === value2[2]; - }, - - /** @inheritDoc */ - clearValue_: function() { - this.firstNameInput.value = ''; - this.middleNameInput.value = ''; - this.lastNameInput.value = ''; - }, - }; - - /** - * Base class for shared implementation between address and credit card lists. - * @constructor - * @extends {options.DeletableItemList} - */ - var AutofillProfileList = cr.ui.define('list'); - - AutofillProfileList.prototype = { - __proto__: DeletableItemList.prototype, - - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - - this.addEventListener('blur', this.onBlur_); - }, - - /** - * When the list loses focus, unselect all items in the list. - * @private - */ - onBlur_: function() { - this.selectionModel.unselectAll(); - }, - }; - - /** - * Create a new address list. - * @constructor - * @extends {options.AutofillProfileList} - */ - var AutofillAddressList = cr.ui.define('list'); - - AutofillAddressList.prototype = { - __proto__: AutofillProfileList.prototype, - - decorate: function() { - AutofillProfileList.prototype.decorate.call(this); - }, - - /** @inheritDoc */ - activateItemAtIndex: function(index) { - AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]); - }, - - /** @inheritDoc */ - createItem: function(entry) { - return new AddressListItem(entry); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - AutofillOptions.removeAddress(this.dataModel.item(index)[0]); - }, - }; - - /** - * Create a new credit card list. - * @constructor - * @extends {options.DeletableItemList} - */ - var AutofillCreditCardList = cr.ui.define('list'); - - AutofillCreditCardList.prototype = { - __proto__: AutofillProfileList.prototype, - - decorate: function() { - AutofillProfileList.prototype.decorate.call(this); - }, - - /** @inheritDoc */ - activateItemAtIndex: function(index) { - AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]); - }, - - /** @inheritDoc */ - createItem: function(entry) { - return new CreditCardListItem(entry); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]); - }, - }; - - /** - * Create a new value list. - * @constructor - * @extends {options.InlineEditableItemList} - */ - var AutofillValuesList = cr.ui.define('list'); - - AutofillValuesList.prototype = { - __proto__: InlineEditableItemList.prototype, - - /** @inheritDoc */ - createItem: function(entry) { - return new ValuesListItem(this, entry); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - this.dataModel.splice(index, 1); - }, - - /** @inheritDoc */ - shouldFocusPlaceholder: function() { - return false; - }, - - /** - * Called when the list hierarchy as a whole loses or gains focus. - * If the list was focused in response to a mouse click, call into the - * superclass's implementation. If the list was focused in response to a - * keyboard navigation, focus the first item. - * If the list loses focus, unselect all the elements. - * @param {Event} e The change event. - * @private - */ - handleListFocusChange_: function(e) { - // We check to see whether there is a selected item as a proxy for - // distinguishing between mouse- and keyboard-originated focus events. - var selectedItem = this.selectedItem; - if (selectedItem) - InlineEditableItemList.prototype.handleListFocusChange_.call(this, e); - - if (!e.newValue) { - // When the list loses focus, unselect all the elements. - this.selectionModel.unselectAll(); - } else { - // When the list gains focus, select the first item if nothing else is - // selected. - var firstItem = this.getListItemByIndex(0); - if (!selectedItem && firstItem && e.newValue) - firstItem.handleFocus_(); - } - }, - - /** - * Called when a new list item should be validated; subclasses are - * responsible for implementing if validation is required. - * @param {number} index The index of the item that was inserted or changed. - * @param {number} remove The number items to remove. - * @param {string} value The value of the item to insert. - */ - validateAndSave: function(index, remove, value) { - this.dataModel.splice(index, remove, value); - }, - }; - - /** - * Create a new value list for phone number validation. - * @constructor - * @extends {options.AutofillValuesList} - */ - var AutofillNameValuesList = cr.ui.define('list'); - - AutofillNameValuesList.prototype = { - __proto__: AutofillValuesList.prototype, - - /** @inheritDoc */ - createItem: function(entry) { - return new NameListItem(this, entry); - }, - }; - - /** - * Create a new value list for phone number validation. - * @constructor - * @extends {options.AutofillValuesList} - */ - var AutofillPhoneValuesList = cr.ui.define('list'); - - AutofillPhoneValuesList.prototype = { - __proto__: AutofillValuesList.prototype, - - /** @inheritDoc */ - validateAndSave: function(index, remove, value) { - var numbers = this.dataModel.slice(0, this.dataModel.length - 1); - numbers.splice(index, remove, value); - var info = new Array(); - info[0] = index; - info[1] = numbers; - info[2] = $('country').value; - chrome.send('validatePhoneNumbers', info); - }, - }; - - return { - AddressListItem: AddressListItem, - CreditCardListItem: CreditCardListItem, - ValuesListItem: ValuesListItem, - NameListItem: NameListItem, - AutofillAddressList: AutofillAddressList, - AutofillCreditCardList: AutofillCreditCardList, - AutofillValuesList: AutofillValuesList, - AutofillNameValuesList: AutofillNameValuesList, - AutofillPhoneValuesList: AutofillPhoneValuesList, - }; -}); diff --git a/chrome/browser/resources/options2/browser_options.css b/chrome/browser/resources/options2/browser_options.css deleted file mode 100644 index 7e20964..0000000 --- a/chrome/browser/resources/options2/browser_options.css +++ /dev/null @@ -1,425 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#change-home-page-section { - margin-left: 30px; -} - -#home-page-url { - display: inline-block; - max-width: 400px; - overflow: hidden; - text-overflow: ellipsis; -} - -#default-browser-state { - margin-top: 6px; -} - -#sync-overview p { - display: inline; -} - -#account-picture-wrapper { - border: 1px solid rgba(0, 0, 0, 0.3); - border-radius: 4px; - cursor: pointer; - display: inline-block; - float: left; - margin: 0 2px 10px 0; - padding: 3px; - position: relative; -} - -html[dir=rtl] #account-picture-wrapper { - float: right; -} - -#account-picture { - height: 56px; - vertical-align: middle; - width: 56px; -} - -#change-picture-caption { - background: rgba(0, 0, 0, 0.5); - bottom: 0; - color: white; - font-size: small; - margin: 3px 0; - position: absolute; - text-align: center; - /* Width of #account-picture. */ - width: 56px; -} - -#account-picture-wrapper:not(:hover) #change-picture-caption { - visibility: hidden; -} - -#sync-general { - -webkit-margin-start: 76px; - margin-bottom: 10px; -} - -#sync-buttons { - clear: both; -} - -#profiles-list { - margin-bottom: 10px; - min-height: 0; -} - -#profiles-list > * { - height: 40px; -} - -#profiles-list:focus { - border-color: rgb(77, 144, 254); -} - -.profile-img { - height: 31px; - padding: 3px; - vertical-align: middle; - width: 38px; -} - -.profile-item-current { - font-weight: bold; -} - -#profiles-buttons { - white-space: nowrap; -} - -.sync-error { - background: rgb(255, 219, 219); - border: 1px solid rgb(206, 76, 76); - border-radius: 2px; - padding: 10px; -} - -.sync-error .link-button { - margin: 0 1ex; - padding: 0; -} - -#mac-passwords-warning { - margin-top: 10px; -} - -input[type='range'] { - vertical-align: middle; -} - -/* Internet settings */ - -#network-settings { - position: relative; -} - -#network-list { - min-height: 0; - width: 320px; -} - -.network-group { - -webkit-box-orient: horizontal; - display: -webkit-box; - height: 42px; - vertical-align: middle; -} - -list:not([disabled]) > .network-group:hover, -list:not([disabled]) > .network-group[selected] { - background-color: #f8f8f8 !important; - background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), - rgba(255, 255, 255, 0)) !important; - box-shadow: inset 0 0 1px 1px #f0f0f0; -} - -.network-group-labels { - -webkit-box-flex: 1; - -webkit-box-orient: vertical; - display: -webkit-box; - padding-top: 3px; -} - -.network-icon { - -webkit-margin-end: 8px; - background-position: left top; - background-size: 25px; - display: -webkit-box; - height: 25px; - width: 25px; -} - -@-webkit-keyframes connecting-animation { - 0% { - background-position: 0 25%; - } - 12.5% { - background-position: 0 50%; - } - 25% { - background-position: 0 75%; - } - 37.5% { - background-position: 0 100%; - } - 50% { - background-position: 0 100%; - } - 62.5% { - background-position: 0 75%; - } - 75% { - background-position: 0 50%; - } - 87.5% { - background-position: 0 25%; - } -} - -.network-wifi { - background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_ARCS_DARK'); - background-size: 25px; -} - -.network-wimax { - background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_BARS_DARK'); - background-size: 25px; -} - -.network-cellular { - background-image: url('chrome://theme/IDR_STATUSBAR_NETWORK_BARS_DARK'); - background-size: 25px; -} - -.network-vpn { - background-image: url('chrome://theme/IDR_STATUSBAR_VPN'); - background-size: 25px; -} - -.network-add-connection, -.network-control-active, -.network-control-inactive { - background-position: center center !important; - background-repeat: no-repeat; -} - -.network-add-connection { - background-image: url('chrome://theme/IDR_SIDETABS_NEW_TAB'); - background-size: 16px; -} - -.network-control-inactive { - background-image: none; -} - -.network-control-active { - background-image: url('chrome://theme/IDR_PROFILE_SELECTED'); - background-size: 16px; -} - -.network-options-button { - -webkit-box-flex: 0; - -webkit-transform: scale(0.6); - background-image: none; - background-position: center center; - display: block; - opacity: 0.5; - vertical-align: middle; - width: 19px; -} - -.network-group > .controlled-setting-indicator, -.network-menu-item > .controlled-setting-indicator { - -webkit-box-flex: 0; - -webkit-margin-end: 5px; - display: block; - height: 16px; - margin-top: 9px; - width: 16px; -} - -.network-group > .controlled-setting-indicator { - margin-top: 0; -} - -.network-options-button:hover { - opacity: 1; -} - -@-webkit-keyframes vpn-connecting-animation { - from { - opacity: 1; - } - to { - opacity: 0.2; - } -} - -.network-connecting { - -webkit-animation: connecting-animation 1s step-end infinite; -} - -.network-vpn.network-connecting { - -webkit-animation: vpn-connecting-animation 500ms alternate infinite; -} - -.network-title { - font-weight: 600; - line-height: 120%; -} - -.network-subtitle { - color: #333; - display: inline-block; - line-height: 100%; - max-width: 260px; - opacity: 0.4; - overflow: hidden; - padding-bottom: 3px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.network-selector { - background: right center no-repeat; - background-image: url('../shared/images/select.png'); - padding-right: 20px; -} - -.network-menu { - -webkit-box-shadow: - 0 0 0 1px rgba(0,0,0,0.1), - 0 5px 1px 1px rgba(0,0,0,0.1), - 0 5px 2px 1px rgba(0,0,0,0.1), - 0 5px 12px 1px rgba(0,0,0,0.5); - background: #fff; - display: block; - position: absolute; - width: 320px; - z-index: 1; -} - -.network-menu-item { - -webkit-padding-start: 32px; - background: left center no-repeat; - background-size: 25px; - display: -webkit-box; - height: 32px; - margin-left: 4px; - margin-right: 4px; - vertical-align: middle; -} - -@media (pointer:coarse) { - network-menu-item { - height: 40px; - } -} - -.network-menu-item-label { - -webkit-box-flex: 1; - color: #555; - display: block; - overflow-x: hidden; - padding-top: 8px; - text-overflow: ellipsis; - vertical-align: middle; - white-space: nowrap; -} - -.active-network { - color: black; - font-weight: bold; -} - -@media (pointer:coarse) { - .network-menu-item-label { - padding-top: 14px; - } -} - -.network-disabled-control { - color: #999; -} - -/* Restrict the size of the networks menu, by limiting the number of - visible networks. */ -.network-menu-group { - max-height: 192px; - overflow-x: hidden; - overflow-y: auto; - text-overflow: ellipsis; -} - -@media (pointer:coarse) { - .network-menu-group { - max-height: 200px; - } -} - -html[dir=rtl] .network-menu-item { - background: right center no-repeat; -} - -.network-menu-item:hover { - background-color: #eee; -} - -.network-menu > hr { - opacity: 0.4; -} - -#shared-proxies { - margin-top: 12px; -} - -#web-content-section select, -.web-content-select-label { - min-width: 145px; -} - -.web-content-select-label > span:only-of-type { - display: inline-block; - min-width: 100px; -} - -#timezone-value { - display: inline-block; - vertical-align: baseline; -} - -#privacy-explanation { - line-height: 1.8em; -} - -#advanced-settings { - height: 0; - margin-top: 8px; - overflow: hidden; -} - -#auto-open-file-types-label { - padding: 0.45em 0 -} - -.sliding { - -webkit-transition: height 200ms; - overflow-y: hidden; -} - -#keyboard-overlay .option-value > select { - width: 100%; -} - -#keyboard-overlay table { - /* Same as .settings-row {margin}. */ - -webkit-border-vertical-spacing: 0.65em; -} diff --git a/chrome/browser/resources/options2/browser_options.html b/chrome/browser/resources/options2/browser_options.html deleted file mode 100644 index 77c4215..0000000 --- a/chrome/browser/resources/options2/browser_options.html +++ /dev/null @@ -1,541 +0,0 @@ - diff --git a/chrome/browser/resources/options2/browser_options.js b/chrome/browser/resources/options2/browser_options.js deleted file mode 100644 index e0acdb7..0000000 --- a/chrome/browser/resources/options2/browser_options.js +++ /dev/null @@ -1,1350 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - var ArrayDataModel = cr.ui.ArrayDataModel; - var RepeatingButton = cr.ui.RepeatingButton; - - // - // BrowserOptions class - // Encapsulated handling of browser options page. - // - function BrowserOptions() { - OptionsPage.call(this, 'settings', loadTimeData.getString('settingsTitle'), - 'settings'); - } - - cr.addSingletonGetter(BrowserOptions); - - BrowserOptions.prototype = { - __proto__: options.OptionsPage.prototype, - - // State variables. - syncSetupCompleted: false, - - /** - * The cached value of the instant.confirm_dialog_shown preference. - * @type {bool} - * @private - */ - instantConfirmDialogShown_: false, - - /** - * The cached value of the spellcheck.confirm_dialog_shown preference. - * @type {bool} - * @private - */ - spellcheckConfirmDialogShown_: false, - - /** - * Keeps track of whether |onShowHomeButtonChanged_| has been called. See - * |onShowHomeButtonChanged_|. - * @type {bool} - * @private - */ - onShowHomeButtonChangedCalled_: false, - - /** - * @inheritDoc - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - var self = this; - - // Ensure that navigation events are unblocked on uber page. A reload of - // the settings page while an overlay is open would otherwise leave uber - // page in a blocked state, where tab switching is not possible. - uber.invokeMethodOnParent('stopInterceptingEvents'); - - window.addEventListener('message', this.handleWindowMessage_.bind(this)); - - $('advanced-settings-expander').onclick = function() { - self.toggleSectionWithAnimation_( - $('advanced-settings'), - $('advanced-settings-container')); - - // If the link was focused (i.e., it was activated using the keyboard) - // and it was used to show the section (rather than hiding it), focus - // the first element in the container. - if (document.activeElement === $('advanced-settings-expander') && - $('advanced-settings').style.height === '') { - var focusElement = $('advanced-settings-container').querySelector( - 'button, input, list, select, a[href]'); - if (focusElement) - focusElement.focus(); - } - } - - $('advanced-settings').addEventListener('webkitTransitionEnd', - this.updateAdvancedSettingsExpander_.bind(this)); - - if (cr.isChromeOS) - UIAccountTweaks.applyGuestModeVisibility(document); - - // Sync (Sign in) section. - this.updateSyncState_(loadTimeData.getValue('syncData')); - - $('sync-action-link').onclick = function(event) { - if (cr.isChromeOS) { - // On Chrome OS, sign out the user and sign in again to get fresh - // credentials on auth errors. - SyncSetupOverlay.doSignOutOnAuthError(); - } else { - SyncSetupOverlay.showErrorUI(); - } - }; - $('start-stop-sync').onclick = function(event) { - if (self.syncSetupCompleted) - SyncSetupOverlay.showStopSyncingUI(); - else if (cr.isChromeOS) - SyncSetupOverlay.showSetupUIWithoutLogin(); - else - SyncSetupOverlay.showSetupUI(); - }; - $('customize-sync').onclick = function(event) { - if (cr.isChromeOS) - SyncSetupOverlay.showSetupUIWithoutLogin(); - else - SyncSetupOverlay.showSetupUI(); - }; - - // Internet connection section (ChromeOS only). - if (cr.isChromeOS) { - options.network.NetworkList.decorate($('network-list')); - options.network.NetworkList.refreshNetworkData( - loadTimeData.getValue('networkData')); - } - - // On Startup section. - Preferences.getInstance().addEventListener('session.restore_on_startup', - this.onRestoreOnStartupChanged_.bind(this)); - - $('startup-set-pages').onclick = function() { - OptionsPage.navigateToPage('startup'); - }; - - // Appearance section. - Preferences.getInstance().addEventListener('browser.show_home_button', - this.onShowHomeButtonChanged_.bind(this)); - - Preferences.getInstance().addEventListener('homepage', - this.onHomePageChanged_.bind(this)); - Preferences.getInstance().addEventListener('homepage_is_newtabpage', - this.onHomePageIsNtpChanged_.bind(this)); - - $('change-home-page').onclick = function(event) { - OptionsPage.navigateToPage('homePageOverlay'); - }; - - if ($('set-wallpaper')) { - $('set-wallpaper').onclick = function(event) { - chrome.send('openWallpaperManager'); - }; - } - - $('themes-gallery').onclick = function(event) { - window.open(loadTimeData.getString('themesGalleryURL')); - }; - $('themes-reset').onclick = function(event) { - chrome.send('themesReset'); - }; - - // Device section (ChromeOS only). - if (cr.isChromeOS) { - $('keyboard-settings-button').onclick = function(evt) { - OptionsPage.navigateToPage('keyboard-overlay'); - }; - $('pointer-settings-button').onclick = function(evt) { - OptionsPage.navigateToPage('pointer-overlay'); - }; - } - - // Search section. - $('manage-default-search-engines').onclick = function(event) { - OptionsPage.navigateToPage('searchEngines'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ManageSearchEngines']); - }; - $('default-search-engine').addEventListener('change', - this.setDefaultSearchEngine_); - $('instant-enabled-control').customChangeHandler = function(event) { - if (this.checked && !self.instantConfirmDialogShown_) { - OptionsPage.showPageByName('instantConfirm', false); - return true; // Stop default preference processing. - } - return false; // Allow default preference processing. - }; - Preferences.getInstance().addEventListener('instant.confirm_dialog_shown', - this.onInstantConfirmDialogShownChanged_.bind(this)); - - // Users section. - if (loadTimeData.valueExists('profilesInfo')) { - $('profiles-section').hidden = false; - - var profilesList = $('profiles-list'); - options.browser_options.ProfileList.decorate(profilesList); - profilesList.autoExpands = true; - - this.setProfilesInfo_(loadTimeData.getValue('profilesInfo')); - - profilesList.addEventListener('change', - this.setProfileViewButtonsStatus_); - $('profiles-create').onclick = function(event) { - chrome.send('createProfileInfo'); - }; - $('profiles-manage').onclick = function(event) { - ManageProfileOverlay.showManageDialog(); - }; - $('profiles-delete').onclick = function(event) { - var selectedProfile = self.getSelectedProfileItem_(); - if (selectedProfile) - ManageProfileOverlay.showDeleteDialog(selectedProfile); - }; - } - - if (cr.isChromeOS) { - if (!UIAccountTweaks.loggedInAsGuest()) { - $('account-picture-wrapper').onclick = function(event) { - OptionsPage.navigateToPage('changePicture'); - }; - } - - // Username (canonical email) of the currently logged in user or - // |kGuestUser| if a guest session is active. - this.username_ = loadTimeData.getString('username'); - - this.updateAccountPicture_(); - - $('manage-accounts-button').onclick = function(event) { - OptionsPage.navigateToPage('accounts'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ManageAccounts']); - }; - } else { - $('import-data').onclick = function(event) { - ImportDataOverlay.show(); - chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']); - }; - - if ($('themes-GTK-button')) { - $('themes-GTK-button').onclick = function(event) { - chrome.send('themesSetGTK'); - }; - } - } - - // Default browser section. - if (!cr.isChromeOS) { - $('set-as-default-browser').onclick = function(event) { - chrome.send('becomeDefaultBrowser'); - }; - - $('auto-launch').onclick = this.handleAutoLaunchChanged_; - } - - // Privacy section. - $('privacyContentSettingsButton').onclick = function(event) { - OptionsPage.navigateToPage('content'); - OptionsPage.showTab($('cookies-nav-tab')); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ContentSettings']); - }; - $('privacyClearDataButton').onclick = function(event) { - OptionsPage.navigateToPage('clearBrowserData'); - chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']); - }; - // 'spelling-enabled-control' element is only present on Chrome branded - // builds. - if ($('spelling-enabled-control')) { - $('spelling-enabled-control').customChangeHandler = function(event) { - if (this.checked && !self.spellcheckConfirmDialogShown_) { - OptionsPage.showPageByName('spellingConfirm', false); - return true; - } - return false; - }; - Preferences.getInstance().addEventListener( - 'spellcheck.confirm_dialog_shown', - this.onSpellcheckConfirmDialogShownChanged_.bind(this)); - } - // 'metricsReportingEnabled' element is only present on Chrome branded - // builds. - if ($('metricsReportingEnabled')) { - $('metricsReportingEnabled').onclick = function(event) { - chrome.send('metricsReportingCheckboxAction', - [String(event.target.checked)]); - }; - } - - // Bluetooth (CrOS only). - if (cr.isChromeOS) { - options.system.bluetooth.BluetoothDeviceList.decorate( - $('bluetooth-paired-devices-list')); - - $('bluetooth-add-device').onclick = - this.handleAddBluetoothDevice_.bind(this); - - $('enable-bluetooth').onchange = function(event) { - var state = $('enable-bluetooth').checked; - chrome.send('bluetoothEnableChange', [Boolean(state)]); - }; - - $('bluetooth-reconnect-device').onclick = function(event) { - var device = $('bluetooth-paired-devices-list').selectedItem; - var address = device.address; - chrome.send('updateBluetoothDevice', [address, 'connect']); - OptionsPage.closeOverlay(); - }; - - $('bluetooth-reconnect-device').onmousedown = function(event) { - // Prevent 'blur' event, which would reset the list selection, - // thereby disabling the apply button. - event.preventDefault(); - }; - - $('bluetooth-paired-devices-list').addEventListener('change', - function() { - var item = $('bluetooth-paired-devices-list').selectedItem; - var disabled = !item || item.connected; - $('bluetooth-reconnect-device').disabled = disabled; - }); - } - - // Passwords and Forms section. - $('autofill-settings').onclick = function(event) { - OptionsPage.navigateToPage('autofill'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ShowAutofillSettings']); - }; - $('manage-passwords').onclick = function(event) { - OptionsPage.navigateToPage('passwords'); - OptionsPage.showTab($('passwords-nav-tab')); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ShowPasswordManager']); - }; - if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { - // Disable and turn off Autofill in guest mode. - var autofillEnabled = $('autofill-enabled'); - autofillEnabled.disabled = true; - autofillEnabled.checked = false; - cr.dispatchSimpleEvent(autofillEnabled, 'change'); - $('autofill-settings').disabled = true; - - // Disable and turn off Password Manager in guest mode. - var passwordManagerEnabled = $('password-manager-enabled'); - passwordManagerEnabled.disabled = true; - passwordManagerEnabled.checked = false; - cr.dispatchSimpleEvent(passwordManagerEnabled, 'change'); - $('manage-passwords').disabled = true; - } - - if (cr.isMac) { - $('mac-passwords-warning').hidden = - !loadTimeData.getBoolean('multiple_profiles'); - } - - // Network section. - if (!cr.isChromeOS) { - $('proxiesConfigureButton').onclick = function(event) { - chrome.send('showNetworkProxySettings'); - }; - } - - // Web Content section. - $('fontSettingsCustomizeFontsButton').onclick = function(event) { - OptionsPage.navigateToPage('fonts'); - chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']); - }; - $('defaultFontSize').onchange = function(event) { - var value = event.target.options[event.target.selectedIndex].value; - Preferences.setIntegerPref( - 'webkit.webprefs.default_fixed_font_size', - value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, ''); - chrome.send('defaultFontSizeAction', [String(value)]); - }; - $('defaultZoomFactor').onchange = function(event) { - chrome.send('defaultZoomFactorAction', - [String(event.target.options[event.target.selectedIndex].value)]); - }; - - // Languages section. - $('language-button').onclick = function(event) { - OptionsPage.navigateToPage('languages'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_LanuageAndSpellCheckSettings']); - }; - - // Downloads section. - Preferences.getInstance().addEventListener('download.default_directory', - this.onDefaultDownloadDirectoryChanged_.bind(this)); - $('downloadLocationChangeButton').onclick = function(event) { - chrome.send('selectDownloadLocation'); - }; - if (!cr.isChromeOS) { - $('autoOpenFileTypesResetToDefault').onclick = function(event) { - chrome.send('autoOpenFileTypesAction'); - }; - } - - // HTTPS/SSL section. - if (cr.isWindows || cr.isMac) { - $('certificatesManageButton').onclick = function(event) { - chrome.send('showManageSSLCertificates'); - }; - } else { - $('certificatesManageButton').onclick = function(event) { - OptionsPage.navigateToPage('certificates'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_ManageSSLCertificates']); - }; - } - $('sslCheckRevocation').onclick = function(event) { - chrome.send('checkRevocationCheckboxAction', - [String($('sslCheckRevocation').checked)]); - }; - - // Cloud Print section. - // 'cloudPrintProxyEnabled' is true for Chrome branded builds on - // certain platforms, or could be enabled by a lab. - if (!cr.isChromeOS) { - $('cloudPrintConnectorSetupButton').onclick = function(event) { - if ($('cloudPrintManageButton').style.display == 'none') { - // Disable the button, set its text to the intermediate state. - $('cloudPrintConnectorSetupButton').textContent = - loadTimeData.getString('cloudPrintConnectorEnablingButton'); - $('cloudPrintConnectorSetupButton').disabled = true; - chrome.send('showCloudPrintSetupDialog'); - } else { - chrome.send('disableCloudPrintConnector'); - } - }; - } - $('cloudPrintManageButton').onclick = function(event) { - chrome.send('showCloudPrintManagePage'); - }; - - // Accessibility section (CrOS only). - if (cr.isChromeOS) { - $('accessibility-spoken-feedback-check').onchange = function(event) { - chrome.send('spokenFeedbackChange', - [$('accessibility-spoken-feedback-check').checked]); - }; - - $('accessibility-high-contrast-check').onchange = function(event) { - chrome.send('highContrastChange', - [$('accessibility-high-contrast-check').checked]); - }; - - $('accessibility-screen-magnifier-check').onchange = function(event) { - chrome.send('screenMagnifierChange', - [$('accessibility-screen-magnifier-check').checked]); - }; - } - - // Display management section (CrOS only). - if (cr.isChromeOS) { - $('display-options-button').onclick = function(event) { - OptionsPage.navigateToPage('display'); - chrome.send('coreOptionsUserMetricsAction', - ['Options_Display']); - } - } - - // Background mode section. - if ($('backgroundModeCheckbox')) { - cr.defineProperty($('backgroundModeCheckbox'), - 'controlledBy', - cr.PropertyKind.ATTR); - $('backgroundModeCheckbox').onclick = function(event) { - chrome.send('backgroundModeAction', - [String($('backgroundModeCheckbox').checked)]); - }; - } - }, - - /** - * @inheritDoc - */ - didShowPage: function() { - $('search-field').focus(); - }, - - /** - * Event listener for the 'session.restore_on_startup' pref. - * @param {Event} event The preference change event. - * @private - */ - onRestoreOnStartupChanged_: function(event) { - /** @const */ var showPagesValue = Number($('startup-show-pages').value); - /** @const */ var showHomePageValue = 0; - - $('startup-set-pages').disabled = event.value.disabled && - event.value.value != showPagesValue; - - if (event.value.value == showHomePageValue) { - // If the user previously selected "Show the homepage", the - // preference will already be migrated to "Open a specific page". So - // the only way to reach this code is if the 'restore on startup' - // preference is managed. - assert(event.value.controlledBy); - - // Select "open the following pages" and lock down the list of URLs - // to reflect the intention of the policy. - $('startup-show-pages').checked = true; - StartupOverlay.getInstance().setControlsDisabled(true); - } else { - // Re-enable the controls in the startup overlay if necessary. - StartupOverlay.getInstance().updateControlStates(); - } - }, - - /** - * Handler for messages sent from the main uber page. - * @param {Event} e The 'message' event from the uber page. - * @private - */ - handleWindowMessage_: function(e) { - if (e.data.method == 'frameSelected') - $('search-field').focus(); - }, - - /** - * Shows the given section, with animation. - * @param {HTMLElement} section The section to be shown. - * @param {HTMLElement} container The container for the section. Must be - * inside of |section|. - * @private - */ - showSectionWithAnimation_: function(section, container) { - this.addTransitionEndListener_(section); - - // Unhide - section.hidden = false; - - // Delay starting the transition so that hidden change will be - // processed. - setTimeout(function() { - // Reveal the section using a WebKit transition. - section.classList.add('sliding'); - section.style.height = - container.offsetHeight + 'px'; - }, 0); - }, - - /** - * See showSectionWithAnimation_. - */ - hideSectionWithAnimation_: function(section, container) { - this.addTransitionEndListener_(section); - - // Before we start hiding the section, we need to set - // the height to a pixel value. - section.style.height = container.offsetHeight + 'px'; - - // Delay starting the transition so that the height change will be - // processed. - setTimeout(function() { - // Hide the section using a WebKit transition. - section.classList.add('sliding'); - section.style.height = ''; - }, 0); - }, - - /** - * See showSectionWithAnimation_. - */ - toggleSectionWithAnimation_: function(section, container) { - if (section.style.height == '') - this.showSectionWithAnimation_(section, container); - else - this.hideSectionWithAnimation_(section, container); - }, - - /** - * Adds a |webkitTransitionEnd| listener to the given section so that - * it can be animated. The listener will only be added to a given section - * once, so this can be called as multiple times. - * @param {HTMLElement} section The section to be animated. - * @private - */ - addTransitionEndListener_: function(section) { - if (section.hasTransitionEndListener_) - return; - - section.addEventListener('webkitTransitionEnd', - this.onTransitionEnd_.bind(this)); - section.hasTransitionEndListener_ = true; - }, - - /** - * Called after an animation transition has ended. - * @private - */ - onTransitionEnd_: function(event) { - if (event.propertyName != 'height') - return; - - var section = event.target; - - // Disable WebKit transitions. - section.classList.remove('sliding'); - - if (section.style.height == '') { - // Hide the content so it can't get tab focus. - section.hidden = true; - } else { - // Set the section height to 'auto' to allow for size changes - // (due to font change or dynamic content). - section.style.height = 'auto'; - } - }, - - updateAdvancedSettingsExpander_: function() { - var expander = $('advanced-settings-expander'); - if ($('advanced-settings').style.height == '') - expander.textContent = loadTimeData.getString('showAdvancedSettings'); - else - expander.textContent = loadTimeData.getString('hideAdvancedSettings'); - }, - - /** - * Updates the sync section with the given state. - * @param {Object} syncData A bunch of data records that describe the status - * of the sync system. - * @private - */ - updateSyncState_: function(syncData) { - if (!syncData.syncSystemEnabled) { - $('sync-section').hidden = true; - return; - } - - $('sync-section').hidden = false; - this.syncSetupCompleted = syncData.setupCompleted; - $('customize-sync').hidden = !syncData.setupCompleted; - - var startStopButton = $('start-stop-sync'); - startStopButton.disabled = syncData.managed || - syncData.setupInProgress; - startStopButton.hidden = - syncData.setupCompleted && cr.isChromeOS; - startStopButton.textContent = - syncData.setupCompleted ? - loadTimeData.getString('syncButtonTextStop') : - syncData.setupInProgress ? - loadTimeData.getString('syncButtonTextInProgress') : - loadTimeData.getString('syncButtonTextStart'); - - - // TODO(estade): can this just be textContent? - $('sync-status-text').innerHTML = syncData.statusText; - var statusSet = syncData.statusText.length != 0; - $('sync-overview').hidden = statusSet; - $('sync-status').hidden = !statusSet; - - $('sync-action-link').textContent = syncData.actionLinkText; - $('sync-action-link').hidden = syncData.actionLinkText.length == 0; - $('sync-action-link').disabled = syncData.managed; - - if (syncData.hasError) - $('sync-status').classList.add('sync-error'); - else - $('sync-status').classList.remove('sync-error'); - - $('customize-sync').disabled = syncData.hasUnrecoverableError; - // Move #enable-auto-login-checkbox to a different location on CrOS. - if (cr.isChromeOs) { - $('sync-general').insertBefore($('sync-status').nextSibling, - $('enable-auto-login-checkbox')); - } - $('enable-auto-login-checkbox').hidden = !syncData.autoLoginVisible; - }, - - /** - * Get the start/stop sync button DOM element. Used for testing. - * @return {DOMElement} The start/stop sync button. - * @private - */ - getStartStopSyncButton_: function() { - return $('start-stop-sync'); - }, - - /** - * Event listener for the 'show home button' preference. Shows/hides the - * UI for changing the home page with animation, unless this is the first - * time this function is called, in which case there is no animation. - * @param {Event} event The preference change event. - */ - onShowHomeButtonChanged_: function(event) { - var section = $('change-home-page-section'); - if (this.onShowHomeButtonChangedCalled_) { - var container = $('change-home-page-section-container'); - if (event.value.value) - this.showSectionWithAnimation_(section, container); - else - this.hideSectionWithAnimation_(section, container); - } else { - section.hidden = !event.value.value; - this.onShowHomeButtonChangedCalled_ = true; - } - }, - - /** - * Event listener for the 'homepage is NTP' preference. Updates the label - * next to the 'Change' button. - * @param {Event} event The preference change event. - */ - onHomePageIsNtpChanged_: function(event) { - $('home-page-url').hidden = event.value.value; - $('home-page-ntp').hidden = !event.value.value; - }, - - /** - * Event listener for changes to the homepage preference. Updates the label - * next to the 'Change' button. - * @param {Event} event The preference change event. - */ - onHomePageChanged_: function(event) { - $('home-page-url').textContent = this.stripHttp_(event.value.value); - }, - - /** - * Removes the 'http://' from a URL, like the omnibox does. If the string - * doesn't start with 'http://' it is returned unchanged. - * @param {string} url The url to be processed - * @return {string} The url with the 'http://' removed. - */ - stripHttp_: function(url) { - return url.replace(/^http:\/\//, ''); - }, - - /** - * Shows the autoLaunch preference and initializes its checkbox value. - * @param {bool} enabled Whether autolaunch is enabled or or not. - * @private - */ - updateAutoLaunchState_: function(enabled) { - $('auto-launch-option').hidden = false; - $('auto-launch').checked = enabled; - }, - - /** - * Called when the value of the instant.confirm_dialog_shown preference - * changes. Cache this value. - * @param {Event} event Change event. - * @private - */ - onInstantConfirmDialogShownChanged_: function(event) { - this.instantConfirmDialogShown_ = event.value.value; - }, - - /** - * Called when the value of the spellcheck.confirm_dialog_shown preference - * changes. Cache this value. - * @param {Event} event Change event. - * @private - */ - onSpellcheckConfirmDialogShownChanged_: function(event) { - this.spellcheckConfirmDialogShown_ = event.value.value; - }, - - /** - * Called when the value of the download.default_directory preference - * changes. - * @param {Event} event Change event. - * @private - */ - onDefaultDownloadDirectoryChanged_: function(event) { - $('downloadLocationPath').value = event.value.value; - if (cr.isChromeOS) { - // On ChromeOS, strip out /special for drive paths, and - // /home/chronos/user for local files. - $('downloadLocationPath').value = $('downloadLocationPath').value. - replace(/^\/(special|home\/chronos\/user)/, ''); - } - }, - - /** - * Update the Default Browsers section based on the current state. - * @param {string} statusString Description of the current default state. - * @param {boolean} isDefault Whether or not the browser is currently - * default. - * @param {boolean} canBeDefault Whether or not the browser can be default. - * @private - */ - updateDefaultBrowserState_: function(statusString, isDefault, - canBeDefault) { - if (!cr.isChromeOS) { - var label = $('default-browser-state'); - label.textContent = statusString; - - $('set-as-default-browser').hidden = !canBeDefault || isDefault; - } - }, - - /** - * Clears the search engine popup. - * @private - */ - clearSearchEngines_: function() { - $('default-search-engine').textContent = ''; - }, - - /** - * Updates the search engine popup with the given entries. - * @param {Array} engines List of available search engines. - * @param {number} defaultValue The value of the current default engine. - * @param {boolean} defaultManaged Whether the default search provider is - * managed. If true, the default search provider can't be changed. - * @private - */ - updateSearchEngines_: function(engines, defaultValue, defaultManaged) { - this.clearSearchEngines_(); - engineSelect = $('default-search-engine'); - engineSelect.disabled = defaultManaged; - engineCount = engines.length; - var defaultIndex = -1; - for (var i = 0; i < engineCount; i++) { - var engine = engines[i]; - var option = new Option(engine['name'], engine['index']); - if (defaultValue == option.value) - defaultIndex = i; - engineSelect.appendChild(option); - } - if (defaultIndex >= 0) - engineSelect.selectedIndex = defaultIndex; - }, - - /** - * Set the default search engine based on the popup selection. - * @private - */ - setDefaultSearchEngine_: function() { - var engineSelect = $('default-search-engine'); - var selectedIndex = engineSelect.selectedIndex; - if (selectedIndex >= 0) { - var selection = engineSelect.options[selectedIndex]; - chrome.send('setDefaultSearchEngine', [String(selection.value)]); - } - }, - - /** - * Sets or clear whether Chrome should Auto-launch on computer startup. - * @private - */ - handleAutoLaunchChanged_: function() { - chrome.send('toggleAutoLaunch', [$('auto-launch').checked]); - }, - - /** - * Get the selected profile item from the profile list. This also works - * correctly if the list is not displayed. - * @return {Object} the profile item object, or null if nothing is selected. - * @private - */ - getSelectedProfileItem_: function() { - var profilesList = $('profiles-list'); - if (profilesList.hidden) { - if (profilesList.dataModel.length > 0) - return profilesList.dataModel.item(0); - } else { - return profilesList.selectedItem; - } - return null; - }, - - /** - * Helper function to set the status of profile view buttons to disabled or - * enabled, depending on the number of profiles and selection status of the - * profiles list. - * @private - */ - setProfileViewButtonsStatus_: function() { - var profilesList = $('profiles-list'); - var selectedProfile = profilesList.selectedItem; - var hasSelection = selectedProfile != null; - var hasSingleProfile = profilesList.dataModel.length == 1; - $('profiles-manage').disabled = !hasSelection || - !selectedProfile.isCurrentProfile; - if (hasSelection && !selectedProfile.isCurrentProfile) - $('profiles-manage').title = loadTimeData.getString('currentUserOnly'); - else - $('profiles-manage').title = ''; - $('profiles-delete').disabled = !hasSelection && !hasSingleProfile; - var importData = $('import-data'); - if (importData) { - importData.disabled = $('import-data').disabled = hasSelection && - !selectedProfile.isCurrentProfile; - } - }, - - /** - * Display the correct dialog layout, depending on how many profiles are - * available. - * @param {number} numProfiles The number of profiles to display. - * @private - */ - setProfileViewSingle_: function(numProfiles) { - var hasSingleProfile = numProfiles == 1; - $('profiles-list').hidden = hasSingleProfile; - $('profiles-single-message').hidden = !hasSingleProfile; - $('profiles-manage').hidden = hasSingleProfile; - $('profiles-delete').textContent = hasSingleProfile ? - loadTimeData.getString('profilesDeleteSingle') : - loadTimeData.getString('profilesDelete'); - }, - - /** - * Adds all |profiles| to the list. - * @param {Array.} profiles An array of profile info objects. - * each object is of the form: - * profileInfo = { - * name: "Profile Name", - * iconURL: "chrome://path/to/icon/image", - * filePath: "/path/to/profile/data/on/disk", - * isCurrentProfile: false - * }; - * @private - */ - setProfilesInfo_: function(profiles) { - this.setProfileViewSingle_(profiles.length); - // add it to the list, even if the list is hidden so we can access it - // later. - $('profiles-list').dataModel = new ArrayDataModel(profiles); - - // Received new data. If showing the "manage" overlay, keep it up to - // date. If showing the "delete" overlay, close it. - if (ManageProfileOverlay.getInstance().visible && - !$('manage-profile-overlay-manage').hidden) { - ManageProfileOverlay.showManageDialog(); - } else { - ManageProfileOverlay.getInstance().visible = false; - } - - this.setProfileViewButtonsStatus_(); - }, - - /** - * Returns the currently active profile for this browser window. - * @return {Object} A profile info object. - * @private - */ - getCurrentProfile_: function() { - for (var i = 0; i < $('profiles-list').dataModel.length; i++) { - var profile = $('profiles-list').dataModel.item(i); - if (profile.isCurrentProfile) - return profile; - } - - assert(false, - 'There should always be a current profile, but none found.'); - }, - - setGtkThemeButtonEnabled_: function(enabled) { - if (!cr.isChromeOS && navigator.platform.match(/linux|BSD/i)) - $('themes-GTK-button').disabled = !enabled; - }, - - setThemesResetButtonEnabled_: function(enabled) { - $('themes-reset').disabled = !enabled; - }, - - /** - * (Re)loads IMG element with current user account picture. - * @private - */ - updateAccountPicture_: function() { - var picture = $('account-picture'); - if (picture) { - picture.src = 'chrome://userimage/' + this.username_ + '?id=' + - Date.now(); - } - }, - - /** - * Handle the 'add device' button click. - * @private - */ - handleAddBluetoothDevice_: function() { - $('bluetooth-unpaired-devices-list').clear(); - chrome.send('findBluetoothDevices'); - OptionsPage.showPageByName('bluetooth', false); - }, - - /** - * Set the checked state of the metrics reporting checkbox. - * @private - */ - setMetricsReportingCheckboxState_: function(checked, disabled) { - $('metricsReportingEnabled').checked = checked; - $('metricsReportingEnabled').disabled = disabled; - }, - - /** - * @private - */ - setMetricsReportingSettingVisibility_: function(visible) { - if (visible) - $('metricsReportingSetting').style.display = 'block'; - else - $('metricsReportingSetting').style.display = 'none'; - }, - - /** - * Set the visibility of the password generation checkbox. - * @private - */ - setPasswordGenerationSettingVisibility_: function(visible) { - if (visible) - $('password-generation-checkbox').style.display = 'block'; - else - $('password-generation-checkbox').style.display = 'none'; - }, - - /** - * Set the font size selected item. - * @private - */ - setFontSize_: function(font_size_value) { - var selectCtl = $('defaultFontSize'); - for (var i = 0; i < selectCtl.options.length; i++) { - if (selectCtl.options[i].value == font_size_value) { - selectCtl.selectedIndex = i; - if ($('Custom')) - selectCtl.remove($('Custom').index); - return; - } - } - - // Add/Select Custom Option in the font size label list. - if (!$('Custom')) { - var option = new Option(loadTimeData.getString('fontSizeLabelCustom'), - -1, false, true); - option.setAttribute('id', 'Custom'); - selectCtl.add(option); - } - $('Custom').selected = true; - }, - - /** - * Populate the page zoom selector with values received from the caller. - * @param {Array} items An array of items to populate the selector. - * each object is an array with three elements as follows: - * 0: The title of the item (string). - * 1: The value of the item (number). - * 2: Whether the item should be selected (boolean). - * @private - */ - setupPageZoomSelector_: function(items) { - var element = $('defaultZoomFactor'); - - // Remove any existing content. - element.textContent = ''; - - // Insert new child nodes into select element. - var value, title, selected; - for (var i = 0; i < items.length; i++) { - title = items[i][0]; - value = items[i][1]; - selected = items[i][2]; - element.appendChild(new Option(title, value, false, selected)); - } - }, - - /** - * Shows/hides the autoOpenFileTypesResetToDefault button and label, with - * animation. - * @param {boolean} display Whether to show the button and label or not. - * @private - */ - setAutoOpenFileTypesDisplayed_: function(display) { - if (cr.isChromeOS) - return; - - if ($('advanced-settings').hidden) { - // If the Advanced section is hidden, don't animate the transition. - $('auto-open-file-types-section').hidden = !display; - } else { - if (display) { - this.showSectionWithAnimation_( - $('auto-open-file-types-section'), - $('auto-open-file-types-container')); - } else { - this.hideSectionWithAnimation_( - $('auto-open-file-types-section'), - $('auto-open-file-types-container')); - } - } - }, - - /** - * Set the enabled state for the proxy settings button. - * @private - */ - setupProxySettingsSection_: function(disabled, label) { - if (!cr.isChromeOS) { - $('proxiesConfigureButton').disabled = disabled; - $('proxiesLabel').textContent = label; - } - }, - - /** - * Set the checked state for the sslCheckRevocation checkbox. - * @private - */ - setCheckRevocationCheckboxState_: function(checked, disabled) { - $('sslCheckRevocation').checked = checked; - $('sslCheckRevocation').disabled = disabled; - }, - - /** - * Set the checked state for the backgroundModeCheckbox element. - * @private - */ - setBackgroundModeCheckboxState_: function( - checked, disabled, controlled_by) { - $('backgroundModeCheckbox').checked = checked; - $('backgroundModeCheckbox').disabled = disabled; - $('backgroundModeCheckbox').controlledBy = controlled_by; - OptionsPage.updateManagedBannerVisibility(); - }, - - /** - * Set the Cloud Print proxy UI to enabled, disabled, or processing. - * @private - */ - setupCloudPrintConnectorSection_: function(disabled, label, allowed) { - if (!cr.isChromeOS) { - $('cloudPrintConnectorLabel').textContent = label; - if (disabled || !allowed) { - $('cloudPrintConnectorSetupButton').textContent = - loadTimeData.getString('cloudPrintConnectorDisabledButton'); - $('cloudPrintManageButton').style.display = 'none'; - } else { - $('cloudPrintConnectorSetupButton').textContent = - loadTimeData.getString('cloudPrintConnectorEnabledButton'); - $('cloudPrintManageButton').style.display = 'inline'; - } - $('cloudPrintConnectorSetupButton').disabled = !allowed; - } - }, - - /** - * @private - */ - removeCloudPrintConnectorSection_: function() { - if (!cr.isChromeOS) { - var connectorSectionElm = $('cloud-print-connector-section'); - if (connectorSectionElm) - connectorSectionElm.parentNode.removeChild(connectorSectionElm); - } - }, - - /** - * Set the initial state of the spoken feedback checkbox. - * @private - */ - setSpokenFeedbackCheckboxState_: function(checked) { - $('accessibility-spoken-feedback-check').checked = checked; - }, - - /** - * Set the initial state of the high contrast checkbox. - * @private - */ - setHighContrastCheckboxState_: function(checked) { - $('accessibility-high-contrast-check').checked = checked; - }, - - /** - * Set the initial state of the screen magnifier checkbox. - * @private - */ - setScreenMagnifierCheckboxState_: function(checked) { - $('accessibility-screen-magnifier-check').checked = checked; - }, - - /** - * Set the initial state of the virtual keyboard checkbox. - * @private - */ - setVirtualKeyboardCheckboxState_: function(checked) { - // TODO(zork): Update UI - }, - - /** - * Show/hide mouse settings slider. - * @private - */ - showMouseControls_: function(show) { - $('mouse-settings').hidden = !show; - }, - - /** - * Show/hide touchpad settings slider. - * @private - */ - showTouchpadControls_: function(show) { - $('touchpad-settings').hidden = !show; - }, - - /** - * Show/hide the display options button on the System settings page. - * @private - */ - showDisplayOptions_: function(show) { - $('display-options-section').hidden = !show; - }, - - /** - * Activate the bluetooth settings section on the System settings page. - * @private - */ - showBluetoothSettings_: function() { - $('bluetooth-devices').hidden = false; - }, - - /** - * Dectivates the bluetooth settings section from the System settings page. - * @private - */ - hideBluetoothSettings_: function() { - $('bluetooth-devices').hidden = true; - }, - - /** - * Sets the state of the checkbox indicating if bluetooth is turned on. The - * state of the "Find devices" button and the list of discovered devices may - * also be affected by a change to the state. - * @param {boolean} checked Flag Indicating if Bluetooth is turned on. - * @private - */ - setBluetoothState_: function(checked) { - $('enable-bluetooth').checked = checked; - $('bluetooth-paired-devices-list').parentNode.hidden = !checked; - $('bluetooth-add-device').hidden = !checked; - $('bluetooth-reconnect-device').hidden = !checked; - // Flush list of previously discovered devices if bluetooth is turned off. - if (!checked) { - $('bluetooth-paired-devices-list').clear(); - $('bluetooth-unpaired-devices-list').clear(); - } else { - chrome.send('getPairedBluetoothDevices'); - } - }, - - /** - * Adds an element to the list of available bluetooth devices. If an element - * with a matching address is found, the existing element is updated. - * @param {{name: string, - * address: string, - * paired: boolean, - * bonded: boolean, - * connected: boolean}} device - * Decription of the bluetooth device. - * @private - */ - addBluetoothDevice_: function(device) { - var list = $('bluetooth-unpaired-devices-list'); - if (device.paired) { - // Test to see if the device is currently in the unpaired list, in which - // case it should be removed from that list. - var index = $('bluetooth-unpaired-devices-list').find(device.address); - if (index != undefined) - $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index); - list = $('bluetooth-paired-devices-list'); - } else { - // Test to see if the device is currently in the paired list, in which - // case it should be removed from that list. - var index = $('bluetooth-paired-devices-list').find(device.address); - if (index != undefined) - $('bluetooth-paired-devices-list').deleteItemAtIndex(index); - } - list.appendDevice(device); - - // One device can be in the process of pairing. If found, display - // the Bluetooth pairing overlay. - if (device.pairing) - BluetoothPairing.showDialog(device); - }, - - /** - * Removes an element from the list of available devices. - * @param {string} address Unique address of the device. - * @private - */ - removeBluetoothDevice_: function(address) { - var index = $('bluetooth-unpaired-devices-list').find(address); - if (index != undefined) { - $('bluetooth-unpaired-devices-list').deleteItemAtIndex(index); - } else { - index = $('bluetooth-paired-devices-list').find(address); - if (index != undefined) - $('bluetooth-paired-devices-list').deleteItemAtIndex(index); - } - } - - }; - - //Forward public APIs to private implementations. - [ - 'addBluetoothDevice', - 'getCurrentProfile', - 'getStartStopSyncButton', - 'hideBluetoothSettings', - 'removeCloudPrintConnectorSection', - 'removeBluetoothDevice', - 'setAutoOpenFileTypesDisplayed', - 'setBackgroundModeCheckboxState', - 'setBluetoothState', - 'setCheckRevocationCheckboxState', - 'setFontSize', - 'setGtkThemeButtonEnabled', - 'setHighContrastCheckboxState', - 'setMetricsReportingCheckboxState', - 'setMetricsReportingSettingVisibility', - 'setPasswordGenerationSettingVisibility', - 'setProfilesInfo', - 'setScreenMagnifierCheckboxState', - 'setSpokenFeedbackCheckboxState', - 'setThemesResetButtonEnabled', - 'setupCloudPrintConnectorSection', - 'setupPageZoomSelector', - 'setupProxySettingsSection', - 'setVirtualKeyboardCheckboxState', - 'showBluetoothSettings', - 'showDisplayOptions', - 'showMouseControls', - 'showTouchpadControls', - 'updateAccountPicture', - 'updateAutoLaunchState', - 'updateDefaultBrowserState', - 'updateManagedBannerVisibility', - 'updateSearchEngines', - 'updateSyncState', - 'updateStartupPages', - ].forEach(function(name) { - BrowserOptions[name] = function() { - var instance = BrowserOptions.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - if (cr.isChromeOS) { - /** - * Returns username (canonical email) of the user logged in (ChromeOS only). - * @return {string} user email. - */ - // TODO(jhawkins): Investigate the use case for this method. - BrowserOptions.getLoggedInUsername = function() { - return BrowserOptions.getInstance().username_; - }; - } - - // Export - return { - BrowserOptions: BrowserOptions - }; -}); diff --git a/chrome/browser/resources/options2/browser_options_profile_list.js b/chrome/browser/resources/options2/browser_options_profile_list.js deleted file mode 100644 index 822a379..0000000 --- a/chrome/browser/resources/options2/browser_options_profile_list.js +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.browser_options', function() { - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; - - /** - * Creates a new profile list item. - * @param {Object} profileInfo The profile this item respresents. - * @constructor - * @extends {cr.ui.DeletableItem} - */ - function ProfileListItem(profileInfo) { - var el = cr.doc.createElement('div'); - el.profileInfo_ = profileInfo; - ProfileListItem.decorate(el); - return el; - } - - /** - * Decorates an element as a profile list item. - * @param {!HTMLElement} el The element to decorate. - */ - ProfileListItem.decorate = function(el) { - el.__proto__ = ProfileListItem.prototype; - el.decorate(); - }; - - ProfileListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * @type {string} the file path of this profile list item. - */ - get profilePath() { - return this.profileInfo_.filePath; - }, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - var profileInfo = this.profileInfo_; - - var iconEl = this.ownerDocument.createElement('img'); - iconEl.className = 'profile-img'; - iconEl.src = profileInfo.iconURL; - this.contentElement.appendChild(iconEl); - - var nameEl = this.ownerDocument.createElement('div'); - if (profileInfo.isCurrentProfile) - nameEl.classList.add('profile-item-current'); - this.contentElement.appendChild(nameEl); - - var displayName = profileInfo.name; - if (profileInfo.isCurrentProfile) { - displayName = loadTimeData.getStringF('profilesListItemCurrent', - profileInfo.name); - } - nameEl.textContent = displayName; - - // Ensure that the button cannot be tabbed to for accessibility reasons. - this.closeButtonElement.tabIndex = -1; - }, - }; - - var ProfileList = cr.ui.define('list'); - - ProfileList.prototype = { - __proto__: DeletableItemList.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.selectionModel = new ListSingleSelectionModel(); - }, - - /** @inheritDoc */ - createItem: function(pageInfo) { - var item = new ProfileListItem(pageInfo); - return item; - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - ManageProfileOverlay.showDeleteDialog(this.dataModel.item(index)); - }, - - /** @inheritDoc */ - activateItemAtIndex: function(index) { - // Don't allow the user to edit a profile that is not current. - var profileInfo = this.dataModel.item(index); - if (profileInfo.isCurrentProfile) - ManageProfileOverlay.showManageDialog(profileInfo); - }, - }; - - return { - ProfileList: ProfileList - }; -}); - diff --git a/chrome/browser/resources/options2/browser_options_startup_page_list.js b/chrome/browser/resources/options2/browser_options_startup_page_list.js deleted file mode 100644 index f88e5d4..0000000 --- a/chrome/browser/resources/options2/browser_options_startup_page_list.js +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.browser_options', function() { - /** @const */ var AutocompleteList = cr.ui.AutocompleteList; - /** @const */ var InlineEditableItem = options.InlineEditableItem; - /** @const */ var InlineEditableItemList = options.InlineEditableItemList; - - /** - * Creates a new startup page list item. - * @param {Object} pageInfo The page this item represents. - * @constructor - * @extends {cr.ui.ListItem} - */ - function StartupPageListItem(pageInfo) { - var el = cr.doc.createElement('div'); - el.pageInfo_ = pageInfo; - StartupPageListItem.decorate(el); - return el; - } - - /** - * Decorates an element as a startup page list item. - * @param {!HTMLElement} el The element to decorate. - */ - StartupPageListItem.decorate = function(el) { - el.__proto__ = StartupPageListItem.prototype; - el.decorate(); - }; - - StartupPageListItem.prototype = { - __proto__: InlineEditableItem.prototype, - - /** - * Input field for editing the page url. - * @type {HTMLElement} - * @private - */ - urlField_: null, - - /** @inheritDoc */ - decorate: function() { - InlineEditableItem.prototype.decorate.call(this); - - var pageInfo = this.pageInfo_; - - if (pageInfo['modelIndex'] == '-1') { - this.isPlaceholder = true; - pageInfo['title'] = loadTimeData.getString('startupAddLabel'); - pageInfo['url'] = ''; - } - - var titleEl = this.ownerDocument.createElement('div'); - titleEl.className = 'title'; - titleEl.classList.add('favicon-cell'); - titleEl.classList.add('weakrtl'); - titleEl.textContent = pageInfo['title']; - if (!this.isPlaceholder) { - titleEl.style.backgroundImage = url('chrome://favicon/' + - pageInfo['url']); - titleEl.title = pageInfo['tooltip']; - } - - this.contentElement.appendChild(titleEl); - - var urlEl = this.createEditableTextCell(pageInfo['url']); - urlEl.className = 'url'; - urlEl.classList.add('weakrtl'); - this.contentElement.appendChild(urlEl); - - var urlField = urlEl.querySelector('input'); - urlField.className = 'weakrtl'; - urlField.placeholder = loadTimeData.getString('startupPagesPlaceholder'); - this.urlField_ = urlField; - - this.addEventListener('commitedit', this.onEditCommitted_); - - var self = this; - urlField.addEventListener('focus', function(event) { - self.parentNode.autocompleteList.attachToInput(urlField); - }); - urlField.addEventListener('blur', function(event) { - self.parentNode.autocompleteList.detach(); - }); - - if (!this.isPlaceholder) - this.draggable = true; - }, - - /** @inheritDoc */ - get currentInputIsValid() { - return this.urlField_.validity.valid; - }, - - /** @inheritDoc */ - get hasBeenEdited() { - return this.urlField_.value != this.pageInfo_['url']; - }, - - /** - * Called when committing an edit; updates the model. - * @param {Event} e The end event. - * @private - */ - onEditCommitted_: function(e) { - var url = this.urlField_.value; - if (this.isPlaceholder) - chrome.send('addStartupPage', [url]); - else - chrome.send('editStartupPage', [this.pageInfo_['modelIndex'], url]); - }, - }; - - var StartupPageList = cr.ui.define('list'); - - StartupPageList.prototype = { - __proto__: InlineEditableItemList.prototype, - - /** - * An autocomplete suggestion list for URL editing. - * @type {AutocompleteList} - */ - autocompleteList: null, - - /** - * The drop position information: "below" or "above". - */ - dropPos: null, - - /** @inheritDoc */ - decorate: function() { - InlineEditableItemList.prototype.decorate.call(this); - - // Listen to drag and drop events. - this.addEventListener('dragstart', this.handleDragStart_.bind(this)); - this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); - this.addEventListener('dragover', this.handleDragOver_.bind(this)); - this.addEventListener('drop', this.handleDrop_.bind(this)); - this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); - this.addEventListener('dragend', this.handleDragEnd_.bind(this)); - }, - - /** @inheritDoc */ - createItem: function(pageInfo) { - var item = new StartupPageListItem(pageInfo); - item.urlField_.disabled = this.disabled; - return item; - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - chrome.send('removeStartupPages', [String(index)]); - }, - - /** - * Computes the target item of drop event. - * @param {Event} e The drop or dragover event. - * @private - */ - getTargetFromDropEvent_: function(e) { - var target = e.target; - // e.target may be an inner element of the list item - while (target != null && !(target instanceof StartupPageListItem)) { - target = target.parentNode; - } - return target; - }, - - /** - * Handles the dragstart event. - * @param {Event} e The dragstart event. - * @private - */ - handleDragStart_: function(e) { - // Prevent dragging if the list is disabled. - if (this.disabled) { - e.preventDefault(); - return false; - } - - var target = e.target; - // StartupPageListItem should be the only draggable element type in the - // page but let's make sure. - if (target instanceof StartupPageListItem) { - this.draggedItem = target; - this.draggedItem.editable = false; - e.dataTransfer.effectAllowed = 'move'; - // We need to put some kind of data in the drag or it will be - // ignored. Use the URL in case the user drags to a text field or the - // desktop. - e.dataTransfer.setData('text/plain', target.urlField_.value); - } - }, - - /* - * Handles the dragenter event. - * @param {Event} e The dragenter event. - * @private - */ - handleDragEnter_: function(e) { - e.preventDefault(); - }, - - /* - * Handles the dragover event. - * @param {Event} e The dragover event. - * @private - */ - handleDragOver_: function(e) { - var dropTarget = this.getTargetFromDropEvent_(e); - // Determines whether the drop target is to accept the drop. - // The drop is only successful on another StartupPageListItem. - if (!(dropTarget instanceof StartupPageListItem) || - dropTarget == this.draggedItem || dropTarget.isPlaceholder) { - this.hideDropMarker_(); - return; - } - // Compute the drop postion. Should we move the dragged item to - // below or above the drop target? - var rect = dropTarget.getBoundingClientRect(); - var dy = e.clientY - rect.top; - var yRatio = dy / rect.height; - var dropPos = yRatio <= .5 ? 'above' : 'below'; - this.dropPos = dropPos; - this.showDropMarker_(dropTarget, dropPos); - e.preventDefault(); - }, - - /* - * Handles the drop event. - * @param {Event} e The drop event. - * @private - */ - handleDrop_: function(e) { - var dropTarget = this.getTargetFromDropEvent_(e); - this.hideDropMarker_(); - - // Insert the selection at the new position. - var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_); - if (this.dropPos == 'below') - newIndex += 1; - - var selected = this.selectionModel.selectedIndexes; - var stringized_selected = []; - for (var j = 0; j < selected.length; j++) - stringized_selected.push(String(selected[j])); - - chrome.send('dragDropStartupPage', - [String(newIndex), stringized_selected]); - }, - - /** - * Handles the dragleave event. - * @param {Event} e The dragleave event. - * @private - */ - handleDragLeave_: function(e) { - this.hideDropMarker_(); - }, - - /** - * Handles the dragend event. - * @param {Event} e The dragend event. - * @private - */ - handleDragEnd_: function(e) { - this.draggedItem.editable = true; - this.draggedItem.updateEditState(); - }, - - /** - * Shows and positions the marker to indicate the drop target. - * @param {HTMLElement} target The current target list item of drop. - * @param {string} pos 'below' or 'above'. - * @private - */ - showDropMarker_: function(target, pos) { - window.clearTimeout(this.hideDropMarkerTimer_); - var marker = $('startupPagesListDropmarker'); - var rect = target.getBoundingClientRect(); - var markerHeight = 6; - if (pos == 'above') { - marker.style.top = (rect.top - markerHeight / 2) + 'px'; - } else { - marker.style.top = (rect.bottom - markerHeight / 2) + 'px'; - } - marker.style.width = rect.width + 'px'; - marker.style.left = rect.left + 'px'; - marker.style.display = 'block'; - }, - - /** - * Hides the drop marker. - * @private - */ - hideDropMarker_: function() { - // Hide the marker in a timeout to reduce flickering as we move between - // valid drop targets. - window.clearTimeout(this.hideDropMarkerTimer_); - this.hideDropMarkerTimer_ = window.setTimeout(function() { - $('startupPagesListDropmarker').style.display = ''; - }, 100); - }, - }; - - return { - StartupPageList: StartupPageList - }; -}); diff --git a/chrome/browser/resources/options2/certificate_backup_overlay.html b/chrome/browser/resources/options2/certificate_backup_overlay.html deleted file mode 100644 index 146b38f..0000000 --- a/chrome/browser/resources/options2/certificate_backup_overlay.html +++ /dev/null @@ -1,39 +0,0 @@ - diff --git a/chrome/browser/resources/options2/certificate_backup_overlay.js b/chrome/browser/resources/options2/certificate_backup_overlay.js deleted file mode 100644 index c904a47..0000000 --- a/chrome/browser/resources/options2/certificate_backup_overlay.js +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * CertificateBackupOverlay class - * Encapsulated handling of the 'enter backup password' overlay page. - * @class - */ - function CertificateBackupOverlay() { - OptionsPage.call(this, 'certificateBackupOverlay', - '', - 'certificateBackupOverlay'); - } - - cr.addSingletonGetter(CertificateBackupOverlay); - - CertificateBackupOverlay.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initializes the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var self = this; - $('certificateBackupCancelButton').onclick = function(event) { - self.cancelBackup_(); - }; - $('certificateBackupOkButton').onclick = function(event) { - self.finishBackup_(); - }; - var onBackupPasswordInput = function(event) { - self.comparePasswords_(); - }; - $('certificateBackupPassword').oninput = onBackupPasswordInput; - $('certificateBackupPassword2').oninput = onBackupPasswordInput; - - self.clearInputFields_(); - }, - - /** - * Clears any uncommitted input, and dismisses the overlay. - * @private - */ - dismissOverlay_: function() { - this.clearInputFields_(); - OptionsPage.closeOverlay(); - }, - - /** - * Attempt the Backup operation. - * The overlay will be left up with inputs disabled until the backend - * finishes and dismisses it. - * @private - */ - finishBackup_: function() { - chrome.send('exportPersonalCertificatePasswordSelected', - [$('certificateBackupPassword').value]); - $('certificateBackupCancelButton').disabled = true; - $('certificateBackupOkButton').disabled = true; - $('certificateBackupPassword').disabled = true; - $('certificateBackupPassword2').disabled = true; - }, - - /** - * Cancel the Backup operation. - * @private - */ - cancelBackup_: function() { - chrome.send('cancelImportExportCertificate'); - this.dismissOverlay_(); - }, - - /** - * Compares the password fields and sets the button state appropriately. - * @private - */ - comparePasswords_: function() { - var password1 = $('certificateBackupPassword').value; - var password2 = $('certificateBackupPassword2').value; - $('certificateBackupOkButton').disabled = - !password1 || password1 != password2; - }, - - /** - * Clears the value of each input field. - * @private - */ - clearInputFields_: function() { - $('certificateBackupPassword').value = ''; - $('certificateBackupPassword2').value = ''; - $('certificateBackupPassword').disabled = false; - $('certificateBackupPassword2').disabled = false; - $('certificateBackupCancelButton').disabled = false; - $('certificateBackupOkButton').disabled = true; - }, - }; - - CertificateBackupOverlay.show = function() { - CertificateBackupOverlay.getInstance().clearInputFields_(); - OptionsPage.navigateToPage('certificateBackupOverlay'); - }; - - CertificateBackupOverlay.dismiss = function() { - CertificateBackupOverlay.getInstance().dismissOverlay_(); - }; - - // Export - return { - CertificateBackupOverlay: CertificateBackupOverlay - }; -}); diff --git a/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.html b/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.html deleted file mode 100644 index d875c27..0000000 --- a/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.html +++ /dev/null @@ -1,38 +0,0 @@ - diff --git a/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.js b/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.js deleted file mode 100644 index 5f017c8..0000000 --- a/chrome/browser/resources/options2/certificate_edit_ca_trust_overlay.js +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * CertificateEditCaTrustOverlay class - * Encapsulated handling of the 'edit ca trust' and 'import ca' overlay pages. - * @class - */ - function CertificateEditCaTrustOverlay() { - OptionsPage.call(this, 'certificateEditCaTrustOverlay', - '', - 'certificateEditCaTrustOverlay'); - } - - cr.addSingletonGetter(CertificateEditCaTrustOverlay); - - CertificateEditCaTrustOverlay.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Dismisses the overlay. - * @private - */ - dismissOverlay_: function() { - OptionsPage.closeOverlay(); - }, - - /** - * Enables or disables input fields. - * @private - */ - enableInputs_: function(enabled) { - $('certificateCaTrustSSLCheckbox').disabled = - $('certificateCaTrustEmailCheckbox').disabled = - $('certificateCaTrustObjSignCheckbox').disabled = - $('certificateEditCaTrustCancelButton').disabled = - $('certificateEditCaTrustOkButton').disabled = !enabled; - }, - - /** - * Attempt the Edit operation. - * The overlay will be left up with inputs disabled until the backend - * finishes and dismisses it. - * @private - */ - finishEdit_: function() { - // TODO(mattm): Send checked values as booleans. For now send them as - // strings, since WebUIBindings::send does not support any other types :( - chrome.send('editCaCertificateTrust', - [this.certId, - $('certificateCaTrustSSLCheckbox').checked.toString(), - $('certificateCaTrustEmailCheckbox').checked.toString(), - $('certificateCaTrustObjSignCheckbox').checked.toString()]); - this.enableInputs_(false); - }, - - /** - * Cancel the Edit operation. - * @private - */ - cancelEdit_: function() { - this.dismissOverlay_(); - }, - - /** - * Attempt the Import operation. - * The overlay will be left up with inputs disabled until the backend - * finishes and dismisses it. - * @private - */ - finishImport_: function() { - // TODO(mattm): Send checked values as booleans. For now send them as - // strings, since WebUIBindings::send does not support any other types :( - chrome.send('importCaCertificateTrustSelected', - [$('certificateCaTrustSSLCheckbox').checked.toString(), - $('certificateCaTrustEmailCheckbox').checked.toString(), - $('certificateCaTrustObjSignCheckbox').checked.toString()]); - this.enableInputs_(false); - }, - - /** - * Cancel the Import operation. - * @private - */ - cancelImport_: function() { - chrome.send('cancelImportExportCertificate'); - this.dismissOverlay_(); - }, - }; - - /** - * Callback from CertificateManagerHandler with the trust values. - * @param {boolean} trustSSL The initial value of SSL trust checkbox. - * @param {boolean} trustEmail The initial value of Email trust checkbox. - * @param {boolean} trustObjSign The initial value of Object Signing trust. - */ - CertificateEditCaTrustOverlay.populateTrust = function( - trustSSL, trustEmail, trustObjSign) { - $('certificateCaTrustSSLCheckbox').checked = trustSSL; - $('certificateCaTrustEmailCheckbox').checked = trustEmail; - $('certificateCaTrustObjSignCheckbox').checked = trustObjSign; - CertificateEditCaTrustOverlay.getInstance().enableInputs_(true); - } - - /** - * Show the Edit CA Trust overlay. - * @param {string} certId The id of the certificate to be passed to the - * certificate manager model. - * @param {string} certName The display name of the certificate. - * checkbox. - */ - CertificateEditCaTrustOverlay.show = function(certId, certName) { - var self = CertificateEditCaTrustOverlay.getInstance(); - self.certId = certId; - $('certificateEditCaTrustCancelButton').onclick = function(event) { - self.cancelEdit_(); - } - $('certificateEditCaTrustOkButton').onclick = function(event) { - self.finishEdit_(); - } - $('certificateEditCaTrustDescription').textContent = - loadTimeData.getStringF('certificateEditCaTrustDescriptionFormat', - certName); - self.enableInputs_(false); - OptionsPage.navigateToPage('certificateEditCaTrustOverlay'); - chrome.send('getCaCertificateTrust', [certId]); - } - - /** - * Show the Import CA overlay. - * @param {string} certId The id of the certificate to be passed to the - * certificate manager model. - * @param {string} certName The display name of the certificate. - * checkbox. - */ - CertificateEditCaTrustOverlay.showImport = function(certName) { - var self = CertificateEditCaTrustOverlay.getInstance(); - // TODO(mattm): do we want a view certificate button here like firefox has? - $('certificateEditCaTrustCancelButton').onclick = function(event) { - self.cancelImport_(); - } - $('certificateEditCaTrustOkButton').onclick = function(event) { - self.finishImport_(); - } - $('certificateEditCaTrustDescription').textContent = - loadTimeData.getStringF('certificateImportCaDescriptionFormat', - certName); - CertificateEditCaTrustOverlay.populateTrust(false, false, false); - OptionsPage.navigateToPage('certificateEditCaTrustOverlay'); - } - - CertificateEditCaTrustOverlay.dismiss = function() { - CertificateEditCaTrustOverlay.getInstance().dismissOverlay_(); - }; - - // Export - return { - CertificateEditCaTrustOverlay: CertificateEditCaTrustOverlay - }; -}); diff --git a/chrome/browser/resources/options2/certificate_import_error_overlay.html b/chrome/browser/resources/options2/certificate_import_error_overlay.html deleted file mode 100644 index d3946b4..0000000 --- a/chrome/browser/resources/options2/certificate_import_error_overlay.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/chrome/browser/resources/options2/certificate_import_error_overlay.js b/chrome/browser/resources/options2/certificate_import_error_overlay.js deleted file mode 100644 index 4eed1d2..0000000 --- a/chrome/browser/resources/options2/certificate_import_error_overlay.js +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - - /** - * CertificateImportErrorOverlay class - * Displays a list of certificates and errors. - * @class - */ - function CertificateImportErrorOverlay() { - OptionsPage.call(this, 'certificateImportErrorOverlay', '', - 'certificateImportErrorOverlay'); - } - - cr.addSingletonGetter(CertificateImportErrorOverlay); - - CertificateImportErrorOverlay.prototype = { - // Inherit CertificateImportErrorOverlay from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - OptionsPage.prototype.initializePage.call(this); - - $('certificateImportErrorOverlayOk').onclick = function(event) { - OptionsPage.closeOverlay(); - }; - }, - }; - - /** - * Show an alert overlay with the given message, button titles, and - * callbacks. - * @param {string} title The alert title to display to the user. - * @param {string} message The alert message to display to the user. - * @param {Array} certErrors The list of cert errors. Each error should have - * a .name and .error attribute. - */ - CertificateImportErrorOverlay.show = function(title, message, certErrors) { - $('certificateImportErrorOverlayTitle').textContent = title; - $('certificateImportErrorOverlayMessage').textContent = message; - - ul = $('certificateImportErrorOverlayCertErrors'); - ul.innerHTML = ''; - for (var i = 0; i < certErrors.length; ++i) { - li = document.createElement('li'); - li.textContent = loadTimeData.getStringF('certificateImportErrorFormat', - certErrors[i].name, - certErrors[i].error); - ul.appendChild(li); - } - - OptionsPage.navigateToPage('certificateImportErrorOverlay'); - } - - // Export - return { - CertificateImportErrorOverlay: CertificateImportErrorOverlay - }; - -}); diff --git a/chrome/browser/resources/options2/certificate_manager.css b/chrome/browser/resources/options2/certificate_manager.css deleted file mode 100644 index 0516e5b..0000000 --- a/chrome/browser/resources/options2/certificate_manager.css +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#certificateManagerPage { - min-width: 700px; -} - -#certificateRestoreOverlay h1 { - /* Leave enough space for the close-button. */ - /* TODO(KGR): make dialogs to use flex-boxes instead of relying on padding. */ - padding-right: 35px; -} - -/* Force tab strip to extend to the left and right edges of the window. */ -#certificate-manager-content-area { - padding: 6px 0 6px 0; -} - -#certificate-manager-content-area .subpages-tab-contents { - padding-left: 28px; - padding-right: 14px; -} - -.certificate-tree-table { - width: 100%; -} - -.certificate-tree { - /* TODO(mattm): BLAH. Make this not statically sized. */ - height: 300px; -} diff --git a/chrome/browser/resources/options2/certificate_manager.html b/chrome/browser/resources/options2/certificate_manager.html deleted file mode 100644 index 47d59d0..0000000 --- a/chrome/browser/resources/options2/certificate_manager.html +++ /dev/null @@ -1,142 +0,0 @@ - diff --git a/chrome/browser/resources/options2/certificate_manager.js b/chrome/browser/resources/options2/certificate_manager.js deleted file mode 100644 index 480b8e1..0000000 --- a/chrome/browser/resources/options2/certificate_manager.js +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - ///////////////////////////////////////////////////////////////////////////// - // CertificateManagerTab class: - - /** - * blah - * @param {!string} id The id of this tab. - */ - function CertificateManagerTab(id) { - this.tree = $(id + '-tree'); - - options.CertificatesTree.decorate(this.tree); - this.tree.addEventListener('change', - this.handleCertificatesTreeChange_.bind(this)); - - var tree = this.tree; - - this.viewButton = $(id + '-view'); - this.viewButton.onclick = function(e) { - var selected = tree.selectedItem; - chrome.send('viewCertificate', [selected.data.id]); - } - - this.editButton = $(id + '-edit'); - if (this.editButton !== null) { - if (id == 'serverCertsTab') { - this.editButton.onclick = function(e) { - var selected = tree.selectedItem; - chrome.send('editServerCertificate', [selected.data.id]); - } - } else if (id == 'caCertsTab') { - this.editButton.onclick = function(e) { - var data = tree.selectedItem.data; - CertificateEditCaTrustOverlay.show(data.id, data.name); - } - } - } - - this.backupButton = $(id + '-backup'); - if (this.backupButton !== null) { - this.backupButton.onclick = function(e) { - var selected = tree.selectedItem; - chrome.send('exportPersonalCertificate', [selected.data.id]); - } - } - - this.backupAllButton = $(id + '-backup-all'); - if (this.backupAllButton !== null) { - this.backupAllButton.onclick = function(e) { - chrome.send('exportAllPersonalCertificates'); - } - } - - this.importButton = $(id + '-import'); - if (this.importButton !== null) { - if (id == 'personalCertsTab') { - this.importButton.onclick = function(e) { - chrome.send('importPersonalCertificate', [false]); - } - } else if (id == 'serverCertsTab') { - this.importButton.onclick = function(e) { - chrome.send('importServerCertificate'); - } - } else if (id == 'caCertsTab') { - this.importButton.onclick = function(e) { - chrome.send('importCaCertificate'); - } - } - } - - this.importAndBindButton = $(id + '-import-and-bind'); - if (this.importAndBindButton !== null) { - if (id == 'personalCertsTab') { - this.importAndBindButton.onclick = function(e) { - chrome.send('importPersonalCertificate', [true]); - } - } - } - - this.exportButton = $(id + '-export'); - if (this.exportButton !== null) { - this.exportButton.onclick = function(e) { - var selected = tree.selectedItem; - chrome.send('exportCertificate', [selected.data.id]); - } - } - - this.deleteButton = $(id + '-delete'); - this.deleteButton.onclick = function(e) { - var data = tree.selectedItem.data; - AlertOverlay.show( - loadTimeData.getStringF(id + 'DeleteConfirm', data.name), - loadTimeData.getString(id + 'DeleteImpact'), - loadTimeData.getString('ok'), - loadTimeData.getString('cancel'), - function() { - tree.selectedItem = null; - chrome.send('deleteCertificate', [data.id]); - }); - } - } - - CertificateManagerTab.prototype = { - - /** - * Update button state. - * @private - * @param {!Object} data The data of the selected item. - */ - updateButtonState: function(data) { - var isCert = !!data && data.isCert; - var readOnly = !!data && data.readonly; - var extractable = !!data && data.extractable; - var hasChildren = this.tree.items.length > 0; - this.viewButton.disabled = !isCert; - if (this.editButton !== null) - this.editButton.disabled = !isCert; - if (this.backupButton !== null) - this.backupButton.disabled = !isCert || !extractable; - if (this.backupAllButton !== null) - this.backupAllButton.disabled = !hasChildren; - if (this.exportButton !== null) - this.exportButton.disabled = !isCert; - this.deleteButton.disabled = !isCert || readOnly; - }, - - /** - * Handles certificate tree selection change. - * @private - * @param {!Event} e The change event object. - */ - handleCertificatesTreeChange_: function(e) { - var data = null; - if (this.tree.selectedItem) { - data = this.tree.selectedItem.data; - } - - this.updateButtonState(data); - }, - }; - - // TODO(xiyuan): Use notification from backend instead of polling. - // TPM token check polling timer. - var tpmPollingTimer; - - // Initiate tpm token check if needed. - function checkTpmToken() { - var importAndBindButton = $('personalCertsTab-import-and-bind'); - - if (importAndBindButton && importAndBindButton.disabled) - chrome.send('checkTpmTokenReady'); - } - - // Stop tpm polling timer. - function stopTpmTokenCheckPolling() { - if (tpmPollingTimer) { - window.clearTimeout(tpmPollingTimer); - tpmPollingTimer = undefined; - } - } - - ///////////////////////////////////////////////////////////////////////////// - // CertificateManager class: - - /** - * Encapsulated handling of ChromeOS accounts options page. - * @constructor - */ - function CertificateManager(model) { - OptionsPage.call(this, 'certificates', - loadTimeData.getString('certificateManagerPageTabTitle'), - 'certificateManagerPage'); - } - - cr.addSingletonGetter(CertificateManager); - - CertificateManager.prototype = { - __proto__: OptionsPage.prototype, - - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.personalTab = new CertificateManagerTab('personalCertsTab'); - this.serverTab = new CertificateManagerTab('serverCertsTab'); - this.caTab = new CertificateManagerTab('caCertsTab'); - this.otherTab = new CertificateManagerTab('otherCertsTab'); - - this.addEventListener('visibleChange', this.handleVisibleChange_); - - $('certificate-confirm').onclick = function() { - OptionsPage.closeOverlay(); - }; - }, - - initalized_: false, - - /** - * Handler for OptionsPage's visible property change event. - * @private - * @param {Event} e Property change event. - */ - handleVisibleChange_: function(e) { - if (!this.initalized_ && this.visible) { - this.initalized_ = true; - OptionsPage.showTab($('personal-certs-nav-tab')); - chrome.send('populateCertificateManager'); - } - - if (cr.isChromeOS) { - // Ensure TPM token check on visible and stop polling when hidden. - if (this.visible) - checkTpmToken(); - else - stopTpmTokenCheckPolling(); - } - } - }; - - // CertificateManagerHandler callbacks. - CertificateManager.onPopulateTree = function(args) { - $(args[0]).populate(args[1]); - }; - - CertificateManager.exportPersonalAskPassword = function(args) { - CertificateBackupOverlay.show(); - }; - - CertificateManager.importPersonalAskPassword = function(args) { - CertificateRestoreOverlay.show(); - }; - - CertificateManager.onCheckTpmTokenReady = function(ready) { - var importAndBindButton = $('personalCertsTab-import-and-bind'); - if (importAndBindButton) { - importAndBindButton.disabled = !ready; - - // Check again after 5 seconds if Tpm is not ready and certificate manager - // is still visible. - if (!ready && CertificateManager.getInstance().visible) - tpmPollingTimer = window.setTimeout(checkTpmToken, 5000); - } - }; - - // Export - return { - CertificateManagerTab: CertificateManagerTab, - CertificateManager: CertificateManager - }; -}); diff --git a/chrome/browser/resources/options2/certificate_restore_overlay.html b/chrome/browser/resources/options2/certificate_restore_overlay.html deleted file mode 100644 index 82cc202..0000000 --- a/chrome/browser/resources/options2/certificate_restore_overlay.html +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/chrome/browser/resources/options2/certificate_restore_overlay.js b/chrome/browser/resources/options2/certificate_restore_overlay.js deleted file mode 100644 index 2e16fbf..0000000 --- a/chrome/browser/resources/options2/certificate_restore_overlay.js +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * CertificateRestoreOverlay class - * Encapsulated handling of the 'enter restore password' overlay page. - * @class - */ - function CertificateRestoreOverlay() { - OptionsPage.call(this, 'certificateRestore', '', - 'certificateRestoreOverlay'); - } - - cr.addSingletonGetter(CertificateRestoreOverlay); - - CertificateRestoreOverlay.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initializes the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var self = this; - $('certificateRestoreCancelButton').onclick = function(event) { - self.cancelRestore_(); - }; - $('certificateRestoreOkButton').onclick = function(event) { - self.finishRestore_(); - }; - - self.clearInputFields_(); - }, - - /** @inheritDoc */ - didShowPage: function() { - $('certificateRestorePassword').focus(); - }, - - /** - * Clears any uncommitted input, and dismisses the overlay. - * @private - */ - dismissOverlay_: function() { - this.clearInputFields_(); - OptionsPage.closeOverlay(); - }, - - /** - * Attempt the restore operation. - * The overlay will be left up with inputs disabled until the backend - * finishes and dismisses it. - * @private - */ - finishRestore_: function() { - chrome.send('importPersonalCertificatePasswordSelected', - [$('certificateRestorePassword').value]); - $('certificateRestoreCancelButton').disabled = true; - $('certificateRestoreOkButton').disabled = true; - }, - - /** - * Cancel the restore operation. - * @private - */ - cancelRestore_: function() { - chrome.send('cancelImportExportCertificate'); - this.dismissOverlay_(); - }, - - /** - * Clears the value of each input field. - * @private - */ - clearInputFields_: function() { - $('certificateRestorePassword').value = ''; - $('certificateRestoreCancelButton').disabled = false; - $('certificateRestoreOkButton').disabled = false; - }, - }; - - CertificateRestoreOverlay.show = function() { - CertificateRestoreOverlay.getInstance().clearInputFields_(); - OptionsPage.navigateToPage('certificateRestore'); - }; - - CertificateRestoreOverlay.dismiss = function() { - CertificateRestoreOverlay.getInstance().dismissOverlay_(); - }; - - // Export - return { - CertificateRestoreOverlay: CertificateRestoreOverlay - }; - -}); diff --git a/chrome/browser/resources/options2/certificate_tree.css b/chrome/browser/resources/options2/certificate_tree.css deleted file mode 100644 index d8f5350..0000000 --- a/chrome/browser/resources/options2/certificate_tree.css +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -span.cert-untrusted { - background-color: pink; - border: 1px solid red; - border-radius: 3px; - margin-right: 3px; - padding-left: 1px; - padding-right: 1px; -} diff --git a/chrome/browser/resources/options2/certificate_tree.js b/chrome/browser/resources/options2/certificate_tree.js deleted file mode 100644 index 4e44bc1..0000000 --- a/chrome/browser/resources/options2/certificate_tree.js +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var Tree = cr.ui.Tree; - /** @const */ var TreeItem = cr.ui.TreeItem; - - /** - * Creates a new tree folder for certificate data. - * @param {Object=} data Data used to create a certificate tree folder. - * @constructor - * @extends {TreeItem} - */ - function CertificateTreeFolder(data) { - data.isCert = false; - var treeFolder = new TreeItem({ - label: data.name, - data: data - }); - treeFolder.__proto__ = CertificateTreeFolder.prototype; - - if (data.icon) - treeFolder.icon = data.icon; - - return treeFolder; - } - - CertificateTreeFolder.prototype = { - __proto__: TreeItem.prototype, - - /** - * The tree path id/. - * @type {string} - */ - get pathId() { - return this.data.id; - } - }; - - /** - * Creates a new tree item for certificate data. - * @param {Object=} data Data used to create a certificate tree item. - * @constructor - * @extends {TreeItem} - */ - function CertificateTreeItem(data) { - data.isCert = true; - // TODO(mattm): other columns - var treeItem = new TreeItem({ - label: data.name, - data: data - }); - treeItem.__proto__ = CertificateTreeItem.prototype; - - if (data.icon) - treeItem.icon = data.icon; - - if (data.untrusted) { - var badge = document.createElement('span'); - badge.classList.add('cert-untrusted'); - badge.textContent = loadTimeData.getString('badgeCertUntrusted'); - treeItem.labelElement.insertBefore( - badge, treeItem.labelElement.firstChild); - } - - return treeItem; - } - - CertificateTreeItem.prototype = { - __proto__: TreeItem.prototype, - - /** - * The tree path id/. - * @type {string} - */ - get pathId() { - return this.parentItem.pathId + ',' + this.data.id; - } - }; - - /** - * Creates a new cookies tree. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {Tree} - */ - var CertificatesTree = cr.ui.define('tree'); - - CertificatesTree.prototype = { - __proto__: Tree.prototype, - - /** @inheritDoc */ - decorate: function() { - Tree.prototype.decorate.call(this); - this.treeLookup_ = {}; - }, - - /** @inheritDoc */ - addAt: function(child, index) { - Tree.prototype.addAt.call(this, child, index); - if (child.data && child.data.id) - this.treeLookup_[child.data.id] = child; - }, - - /** @inheritDoc */ - remove: function(child) { - Tree.prototype.remove.call(this, child); - if (child.data && child.data.id) - delete this.treeLookup_[child.data.id]; - }, - - /** - * Clears the tree. - */ - clear: function() { - // Remove all fields without recreating the object since other code - // references it. - for (var id in this.treeLookup_) - delete this.treeLookup_[id]; - this.textContent = ''; - }, - - /** - * Populate the tree. - * @param {Array} nodesData Nodes data array. - */ - populate: function(nodesData) { - this.clear(); - - for (var i = 0; i < nodesData.length; ++i) { - var subnodes = nodesData[i]['subnodes']; - delete nodesData[i]['subnodes']; - - var item = new CertificateTreeFolder(nodesData[i]); - this.addAt(item, i); - - for (var j = 0; j < subnodes.length; ++j) { - var subitem = new CertificateTreeItem(subnodes[j]); - item.addAt(subitem, j); - } - // Make tree expanded by default. - item.expanded = true; - } - - cr.dispatchSimpleEvent(this, 'change'); - }, - }; - - return { - CertificatesTree: CertificatesTree - }; -}); - diff --git a/chrome/browser/resources/options2/chromeos/OWNERS b/chrome/browser/resources/options2/chromeos/OWNERS deleted file mode 100644 index 1bb1ea6..0000000 --- a/chrome/browser/resources/options2/chromeos/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -dpolukhin@chromium.org -glotov@chromium.org -nkostylev@chromium.org - -# Change Picture and Accounts pages. -ivankr@chromium.org diff --git a/chrome/browser/resources/options2/chromeos/accounts_options.html b/chrome/browser/resources/options2/chromeos/accounts_options.html deleted file mode 100644 index adb451a..0000000 --- a/chrome/browser/resources/options2/chromeos/accounts_options.html +++ /dev/null @@ -1,67 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/accounts_options.js b/chrome/browser/resources/options2/chromeos/accounts_options.js deleted file mode 100644 index a4faaa6..0000000 --- a/chrome/browser/resources/options2/chromeos/accounts_options.js +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - ///////////////////////////////////////////////////////////////////////////// - // AccountsOptions class: - - /** - * Encapsulated handling of ChromeOS accounts options page. - * @constructor - */ - function AccountsOptions(model) { - OptionsPage.call(this, 'accounts', - loadTimeData.getString('accountsPageTabTitle'), - 'accountsPage'); - // Whether to show the whitelist. - this.showWhitelist_ = false; - } - - cr.addSingletonGetter(AccountsOptions); - - AccountsOptions.prototype = { - // Inherit AccountsOptions from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Initializes AccountsOptions page. - */ - initializePage: function() { - // Call base class implementation to starts preference initialization. - OptionsPage.prototype.initializePage.call(this); - - // Set up accounts page. - var userList = $('userList'); - userList.addEventListener('remove', this.handleRemoveUser_); - - var userNameEdit = $('userNameEdit'); - options.accounts.UserNameEdit.decorate(userNameEdit); - userNameEdit.addEventListener('add', this.handleAddUser_); - - // If the current user is not the owner, show some warning, - // and do not show the user list. - this.showWhitelist_ = UIAccountTweaks.currentUserIsOwner(); - if (this.showWhitelist_) { - options.accounts.UserList.decorate(userList); - } else { - if (!AccountsOptions.whitelistIsManaged()) { - $('ownerOnlyWarning').hidden = false; - } else { - this.managed = true; - } - } - - this.addEventListener('visibleChange', this.handleVisibleChange_); - - $('useWhitelistCheck').addEventListener('change', - this.handleUseWhitelistCheckChange_.bind(this)); - - Preferences.getInstance().addEventListener( - $('useWhitelistCheck').pref, - this.handleUseWhitelistPrefChange_.bind(this)); - - $('accounts-options-overlay-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - /** - * Update user list control state. - * @private - */ - updateControls_: function() { - $('userList').disabled = - $('userNameEdit').disabled = !this.showWhitelist_ || - AccountsOptions.whitelistIsManaged() || - !$('useWhitelistCheck').checked; - }, - - /** - * Handler for OptionsPage's visible property change event. - * @private - * @param {Event} e Property change event. - */ - handleVisibleChange_: function(e) { - if (this.visible) { - this.updateControls_(); - if (this.showWhitelist_) - $('userList').redraw(); - } - }, - - /** - * Handler for allow guest check change. - * @private - */ - handleUseWhitelistCheckChange_: function(e) { - // Whitelist existing users when guest login is being disabled. - if ($('useWhitelistCheck').checked) { - chrome.send('whitelistExistingUsers'); - } - - this.updateControls_(); - }, - - /** - * handler for allow guest pref change. - * @private - */ - handleUseWhitelistPrefChange_: function(e) { - this.updateControls_(); - }, - - /** - * Handler for "add" event fired from userNameEdit. - * @private - * @param {Event} e Add event fired from userNameEdit. - */ - handleAddUser_: function(e) { - chrome.send('whitelistUser', [e.user.email, e.user.name]); - }, - - /** - * Handler for "remove" event fired from userList. - * @private - * @param {Event} e Remove event fired from userList. - */ - handleRemoveUser_: function(e) { - chrome.send('unwhitelistUser', [e.user.username]); - } - }; - - - /** - * Returns whether the whitelist is managed by policy or not. - */ - AccountsOptions.whitelistIsManaged = function() { - return loadTimeData.getBoolean('whitelist_is_managed'); - }; - - /** - * Update account picture. - * @param {string} username User for which to update the image. - */ - AccountsOptions.updateAccountPicture = function(username) { - if (this.showWhitelist_) - $('userList').updateAccountPicture(username); - }; - - // Export - return { - AccountsOptions: AccountsOptions - }; - -}); diff --git a/chrome/browser/resources/options2/chromeos/accounts_options_page.css b/chrome/browser/resources/options2/chromeos/accounts_options_page.css deleted file mode 100644 index 0a81c08..0000000 --- a/chrome/browser/resources/options2/chromeos/accounts_options_page.css +++ /dev/null @@ -1,97 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.user-list-table { - border: 1px solid lightgrey; - border-collapse: collapse; - border-spacing: 0; -} - -.user-name-edit-row { - background-color: rgb(235, 239, 250); - border: 1px solid lightgrey; - padding: 5px; -} - -.user-list-item { - padding: 2px; -} - -.user-icon { - border: 1px solid black; - height: 26px; - width: 26px; -} - -.user-email-label { - -webkit-margin-start: 10px; -} - -.user-name-label { - -webkit-margin-start: 10px; - color: darkgray; -} - -.user-email-name-block { - -webkit-box-flex: 1; - max-width: 318px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.remove-user-button { - background-image: -webkit-image-set( - url('../../../../../ui/resources/default_100_percent/close_bar.png') 1x, - url('../../../../../ui/resources/default_200_percent/close_bar.png') 2x); - height: 16px; - width: 16px; -} - -.remove-user-button:hover { - background-image: -webkit-image-set( - url('../../../../../ui/resources/default_100_percent/close_bar_hover.png') - 1x, - url('../../../../../ui/resources/default_200_percent/close_bar_hover.png') - 2x); -} - -#userList { - height: 166px; - padding: 5px; - width: 366px; -} - -#userList[disabled], -#userList[disabled] > [selected], -#userList[disabled] > :hover { - border-color: hsl(0, 0%, 85%); -} - -#userList[disabled] > [selected], -#userList[disabled] > :hover { - background-color: hsl(0, 0%, 90%); -} - -#userList[disabled] .remove-user-button { - visibility: hidden; -} - -#userNameEdit { - border: 1px solid lightgrey; - width: 366px; -} - -#ownerOnlyWarning { - -webkit-padding-start: 20px; - background-image: url('../../options/chromeos/warning.png'); - background-repeat: no-repeat; - margin-bottom: 10px; - margin-top: 10px; - padding-bottom: 1px; -} - -input#userNameEdit:invalid { - background-color: rgb(255, 102, 102); -} diff --git a/chrome/browser/resources/options2/chromeos/accounts_user_list.js b/chrome/browser/resources/options2/chromeos/accounts_user_list.js deleted file mode 100644 index 7a2eda5..0000000 --- a/chrome/browser/resources/options2/chromeos/accounts_user_list.js +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.accounts', function() { - /** @const */ var List = cr.ui.List; - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - /** - * Creates a new user list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {cr.ui.List} - */ - var UserList = cr.ui.define('list'); - - UserList.prototype = { - __proto__: List.prototype, - - pref: 'cros.accounts.users', - - /** @inheritDoc */ - decorate: function() { - List.prototype.decorate.call(this); - - // HACK(arv): http://crbug.com/40902 - window.addEventListener('resize', this.redraw.bind(this)); - - var self = this; - - // Listens to pref changes. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - self.load_(event.value.value); - }); - }, - - createItem: function(user) { - return new UserListItem(user); - }, - - /** - * Finds the index of user by given username (canonicalized email). - * @private - * @param {string} username The username to look for. - * @return {number} The index of the found user or -1 if not found. - */ - indexOf_: function(username) { - var dataModel = this.dataModel; - if (!dataModel) - return -1; - - var length = dataModel.length; - for (var i = 0; i < length; ++i) { - var user = dataModel.item(i); - if (user.username == username) { - return i; - } - } - - return -1; - }, - - /** - * Update given user's account picture. - * @param {string} username User for which to update the image. - */ - updateAccountPicture: function(username) { - var index = this.indexOf_(username); - if (index >= 0) { - var item = this.getListItemByIndex(index); - if (item) - item.updatePicture(); - } - }, - - /** - * Loads given user list. - * @param {Array.} users An array of user info objects. - * @private - */ - load_: function(users) { - this.dataModel = new ArrayDataModel(users); - }, - - /** - * Removes given user from the list. - * @param {Object} user User info object to be removed from user list. - * @private - */ - removeUser_: function(user) { - var e = new Event('remove'); - e.user = user; - this.dispatchEvent(e); - } - }; - - /** - * Whether the user list is disabled. Only used for display purpose. - * @type {boolean} - */ - cr.defineProperty(UserList, 'disabled', cr.PropertyKind.BOOL_ATTR); - - /** - * Creates a new user list item. - * @param {Object} user The user account this represents. - * @constructor - * @extends {cr.ui.ListItem} - */ - function UserListItem(user) { - var el = cr.doc.createElement('div'); - el.user = user; - UserListItem.decorate(el); - return el; - } - - /** - * Decorates an element as a user account item. - * @param {!HTMLElement} el The element to decorate. - */ - UserListItem.decorate = function(el) { - el.__proto__ = UserListItem.prototype; - el.decorate(); - }; - - UserListItem.prototype = { - __proto__: ListItem.prototype, - - /** @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - - this.className = 'user-list-item'; - - this.icon_ = this.ownerDocument.createElement('img'); - this.icon_.className = 'user-icon'; - this.updatePicture(); - - var labelEmail = this.ownerDocument.createElement('span'); - labelEmail.className = 'user-email-label'; - labelEmail.textContent = this.user.email; - - var labelName = this.ownerDocument.createElement('span'); - labelName.className = 'user-name-label'; - labelName.textContent = this.user.owner ? - loadTimeData.getStringF('username_format', this.user.name) : - this.user.name; - - var emailNameBlock = this.ownerDocument.createElement('div'); - emailNameBlock.className = 'user-email-name-block'; - emailNameBlock.appendChild(labelEmail); - emailNameBlock.appendChild(labelName); - emailNameBlock.title = this.user.owner ? - loadTimeData.getStringF('username_format', this.user.email) : - this.user.email; - - this.appendChild(this.icon_); - this.appendChild(emailNameBlock); - - if (!this.user.owner) { - var removeButton = this.ownerDocument.createElement('button'); - removeButton.className = - 'raw-button remove-user-button custom-appearance'; - removeButton.addEventListener( - 'click', this.handleRemoveButtonClick_.bind(this)); - this.appendChild(removeButton); - } - }, - - /** - * Handles click on the remove button. - * @param {Event} e Click event. - * @private - */ - handleRemoveButtonClick_: function(e) { - // Handle left button click - if (e.button == 0) - this.parentNode.removeUser_(this.user); - }, - - /** - * Reloads user picture. - */ - updatePicture: function() { - this.icon_.src = 'chrome://userimage/' + this.user.username + - '?id=' + (new Date()).getTime(); - } - }; - - return { - UserList: UserList - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/accounts_user_name_edit.js b/chrome/browser/resources/options2/chromeos/accounts_user_name_edit.js deleted file mode 100644 index e6e5276..0000000 --- a/chrome/browser/resources/options2/chromeos/accounts_user_name_edit.js +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.accounts', function() { - /** @const */ var Event = cr.Event; - - /** - * Email alias only, assuming it's a gmail address. - * e.g. 'john' - * {name: 'john', email: 'john@gmail.com'} - * @const - */ - var format1String = - '^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)\\s*$'; - /** - * Email address only. - * e.g. 'john@chromium.org' - * {name: 'john', email: 'john@chromium.org'} - * @const - */ - var format2String = - '^\\s*([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+)@' + - '([A-Za-z0-9\-]{2,63}\\..+)\\s*$'; - /** - * Full format. - * e.g. '"John Doe" ' - * {name: 'John doe', email: 'john@chromium.org'} - * @const - */ - var format3String = - '^\\s*"{0,1}([^"]+)"{0,1}\\s*' + - '<([\\w\\.!#\\$%&\'\\*\\+-\\/=\\?\\^`\\{\\|\\}~]+@' + - '[A-Za-z0-9\-]{2,63}\\..+)>\\s*$'; - - /** - * Creates a new user name edit element. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {HTMLInputElement} - */ - var UserNameEdit = cr.ui.define('input'); - - UserNameEdit.prototype = { - __proto__: HTMLInputElement.prototype, - - /** - * Called when an element is decorated as a user name edit. - */ - decorate: function() { - this.pattern = format1String + '|' + format2String + '|' + - format3String; - - this.onkeypress = this.handleKeyPress_.bind(this); - }, - - - /** - * Parses given str for user info. - * - * Note that the email parsing is based on RFC 5322 and does not support - * IMA (Internationalized Email Address). We take only the following chars - * as valid for an email alias (aka local-part): - * - Letters: a–z, A–Z - * - Digits: 0-9 - * - Characters: ! # $ % & ' * + - / = ? ^ _ ` { | } ~ - * - Dot: . (Note that we did not cover the cases that dot should not - * appear as first or last character and should not appear two or - * more times in a row.) - * - * @param {string} str A string to parse. - * @return {{name: string, email: string}} User info parsed from the string. - */ - parse: function(str) { - /** @const */ var format1 = new RegExp(format1String); - /** @const */ var format2 = new RegExp(format2String); - /** @const */ var format3 = new RegExp(format3String); - - var matches = format1.exec(str); - if (matches) { - return { - name: matches[1], - email: matches[1] + '@gmail.com' - }; - } - - matches = format2.exec(str); - if (matches) { - return { - name: matches[1], - email: matches[1] + '@' + matches[2] - }; - } - - matches = format3.exec(str); - if (matches) { - return { - name: matches[1], - email: matches[2] - }; - } - - return null; - }, - - /** - * Handler for key press event. - * @private - * @param {!Event} e The keypress event object. - */ - handleKeyPress_: function(e) { - // Enter - if (e.keyCode == 13) { - var user = this.parse(this.value); - if (user) { - var e = new Event('add'); - e.user = user; - this.dispatchEvent(e); - } - - this.select(); - } - } - }; - - return { - UserNameEdit: UserNameEdit - }; -}); - diff --git a/chrome/browser/resources/options2/chromeos/bluetooth.css b/chrome/browser/resources/options2/chromeos/bluetooth.css deleted file mode 100644 index f8b1fd0..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth.css +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.bluetooth-device-list { - margin: 10px; - padding: 5px 10px; -} - -.bluetooth-device[paired] { - color: gray; -} - -#bluetooth-scanning { - -webkit-box-orient: horizontal; - display: -webkit-inline-box; - margin-bottom: 5px; -} - -#bluetooth-scanning-label { - -webkit-margin-start: 5px; - color: #999; -} - -#bluetooth-scanning-icon { - -webkit-margin-start: 10px; - height: 20px; - opacity: 0.66; - vertical-align: middle; - width: 20px; -} - -#bluetooth-paired-devices-list { - min-height: 96px !important; -} - -#bluetooth-paired-devices-list, -#bluetooth-unpaired-devices-list { - /* Prevent dialog from expanding if many devices are found. */ - max-height: 192px; - overflow-x: hidden; - overflow-y: auto; -} - -.bluetooth-empty-list-label { - box-sizing: border-box; - color: #999; - padding-top: 32px; - text-align: center; -} - -#bluetooth-paired-devices-list-empty-placeholder { - height: 96px; -} - -#bluetooth-unpaired-devices-list-empty-placeholder { - height: 192px; -} - -/* Fix the dimensions of the message area so that the dialog does not change - change size during the pairing process as the message changes. Sized - generously to accomodate the longest of the messages. */ -#bluetooth-pairing-message-area { - display: table; - height: 160px; - padding: 6px 0 !important; - width: 420px; -} - -/* Force the message to be vertical centered so that a shorter message does not - look out of place when there is room for a much longer message. */ -#bluetooth-pairing-message-contents { - display: table-cell; - vertical-align: middle; -} - -#bluetooth-pairing-instructions, -#bluetooth-pairing-passkey-display, -#bluetooth-pairing-passkey-entry, -#bluetooth-pairing-pincode-entry, -#bluetooth-passkey, -#bluetooth-pincode { - text-align: center; -} - -#bluetooth-pairing-instructions { - margin: 10px; -} - -#bluetooth-pairing-passkey-display, -#bluetooth-pairing-passkey-entry, -#bluetooth-pairing-pincode-entry { - margin: 40px 0; -} - -.bluetooth-keyboard-button { - -webkit-padding-end: 15px; - -webkit-padding-start: 15px; - background-image: -webkit-gradient(linear, - left top, - left bottom, - color-stop(0, #e9e9e9), - color-stop(1, #f5f5f5)); - border: 1px solid #d4d4d4; - border-radius: 4px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.07), - inset 0 1px 1px 1px #fff, - inset 0 -1px 1px 1px #ddd; - color: #666; - display: inline-block; - font-size: 14px; - font-weight: 600; - height: 38px; - line-height: 38px; - margin: 0 10px 0 0; - position: relative; - text-align: center; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - vertical-align: baseline; -} - -.bluetooth-keyboard-button:last-child { - margin: 0; -} - -#bluetooth-enter-key { - min-width: 54px; -} - -.bluetooth-passkey-char { - -webkit-margin-start: 45px; - color: #999; - font-size: 20px; - font-weight: 600; /* semibold */ - padding-bottom: 5px; -} - -.bluetooth-passkey-char:first-child { - -webkit-margin-start: 0; -} - -.bluetooth-keyboard-button.key-typed { - border: 1px solid #ccc; - box-shadow: 0 0 0 1px #888, - inset 0 1px 1px 1px #fff, - inset 0 -1px 1px 1px #eee; - color: #222; -} - -.bluetooth-keyboard-button.key-pin { - color: #222; -} diff --git a/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.html b/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.html deleted file mode 100644 index a09222f..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.js b/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.js deleted file mode 100644 index e5713001..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth_add_device_overlay.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * Encapsulated handling of the Bluetooth options page. - * @constructor - */ - function BluetoothOptions() { - OptionsPage.call(this, - 'bluetooth', - loadTimeData.getString('bluetoothOptionsPageTabTitle'), - 'bluetooth-options'); - } - - cr.addSingletonGetter(BluetoothOptions); - - BluetoothOptions.prototype = { - __proto__: OptionsPage.prototype, - - /** - * The list of available (unpaired) bluetooth devices. - * @type {DeletableItemList} - * @private - */ - deviceList_: null, - - /** @inheritDoc */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - this.createDeviceList_(); - - $('bluetooth-add-device-cancel-button').onclick = function(event) { - chrome.send('stopBluetoothDeviceDiscovery'); - OptionsPage.closeOverlay(); - }; - - var self = this; - $('bluetooth-add-device-apply-button').onclick = function(event) { - var device = self.deviceList_.selectedItem; - var address = device.address; - chrome.send('stopBluetoothDeviceDiscovery'); - OptionsPage.closeOverlay(); - device.pairing = 'bluetoothStartConnecting'; - options.BluetoothPairing.showDialog(device); - chrome.send('updateBluetoothDevice', [address, 'connect']); - }; - - $('bluetooth-add-device-apply-button').onmousedown = function(event) { - // Prevent 'blur' event, which would reset the list selection, - // thereby disabling the apply button. - event.preventDefault(); - }; - - $('bluetooth-unpaired-devices-list').addEventListener('change', - function() { - var item = $('bluetooth-unpaired-devices-list').selectedItem; - var disabled = !item || item.paired || item.connected; - $('bluetooth-add-device-apply-button').disabled = disabled; - }); - }, - - /** - * Creates, decorates and initializes the bluetooth device list. - * @private - */ - createDeviceList_: function() { - this.deviceList_ = $('bluetooth-unpaired-devices-list'); - options.system.bluetooth.BluetoothDeviceList.decorate(this.deviceList_); - this.deviceList_.autoExpands = true; - } - }; - - /** - * Automatically start the device discovery process if the - * "Add device" dialog is visible. - */ - BluetoothOptions.updateDiscovery = function() { - var page = BluetoothOptions.getInstance(); - if (page && page.visible) - chrome.send('findBluetoothDevices'); - } - - // Export - return { - BluetoothOptions: BluetoothOptions - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/bluetooth_device_list.js b/chrome/browser/resources/options2/chromeos/bluetooth_device_list.js deleted file mode 100644 index eb715d7..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth_device_list.js +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.system.bluetooth', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - - /** - * Bluetooth settings constants. - */ - function Constants() {} - - /** - * Creates a new bluetooth list item. - * @param {{name: string, - * address: string, - * paired: boolean, - * bonded: boolean, - * connected: boolean, - * pairing: string|undefined, - * passkey: number|undefined, - * entered: number|undefined}} device - * Description of the Bluetooth device. - * @constructor - * @extends {options.DeletableItem} - */ - function BluetoothListItem(device) { - var el = cr.doc.createElement('div'); - el.__proto__ = BluetoothListItem.prototype; - el.data = {}; - for (var key in device) - el.data[key] = device[key]; - el.decorate(); - // Only show the close button for paired devices. - el.deletable = device.paired; - return el; - } - - BluetoothListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * Description of the Bluetooth device. - * @type {{name: string, - * address: string, - * paired: boolean, - * bonded: boolean, - * connected: boolean, - * pairing: string|undefined, - * passkey: number|undefined, - * entered: number|undefined}} - */ - data: null, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - var label = this.ownerDocument.createElement('div'); - label.className = 'bluetooth-device-label'; - this.classList.add('bluetooth-device'); - this.connected = this.data.connected; - // Though strictly speaking, a connected device will also be paired, we - // are interested in tracking paired devices that are not connected. - this.paired = this.data.paired && !this.data.connected; - this.connecting = !!this.data.pairing; - var content = this.data.name; - // Update label for devices that are paired but not connected. - if (this.paired) { - content = content + ' (' + - loadTimeData.getString('bluetoothDeviceNotConnected') + ')'; - } - label.textContent = content; - this.contentElement.appendChild(label); - }, - }; - - /** - * Class for displaying a list of Bluetooth devices. - * @constructor - * @extends {options.DeletableItemList} - */ - var BluetoothDeviceList = cr.ui.define('list'); - - BluetoothDeviceList.prototype = { - __proto__: DeletableItemList.prototype, - - /** - * Height of a list entry in px. - * @type {number} - * @private - */ - itemHeight_: 32, - - /** - * Width of a list entry in px. - * @type {number} - * @private. - */ - itemWidth_: 400, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - // Force layout of all items even if not in the viewport to address - // calculation errors when the list is hidden. The impact on performance - // should be minimal given that the list is not expected to grow very - // large. - this.autoExpand = true; - this.addEventListener('blur', this.onBlur_); - this.clear(); - }, - - /** - * When the list loses focus, unselect all items in the list. - * @private - */ - onBlur_: function() { - // TODO(kevers): Should this be pushed up to the list class? - this.selectionModel.unselectAll(); - }, - - /** - * Adds a bluetooth device to the list of available devices. A check is - * made to see if the device is already in the list, in which case the - * existing device is updated. - * @param {{name: string, - * address: string, - * paired: boolean, - * bonded: boolean, - * connected: boolean, - * pairing: string|undefined, - * passkey: number|undefined, - * entered: number|undefined}} device - * Description of the bluetooth device. - * @return {boolean} True if the devies was successfully added or updated. - */ - appendDevice: function(device) { - var selectedDevice = this.getSelectedDevice_(); - var index = this.find(device.address); - if (index == undefined) { - this.dataModel.push(device); - this.redraw(); - } else { - this.dataModel.splice(index, 1, device); - this.redrawItem(index); - } - this.updateListVisibility_(); - if (selectedDevice) - this.setSelectedDevice_(selectedDevice); - return true; - }, - - /** - * Forces a revailidation of the list content. Content added while the list - * is hidden is not properly rendered when the list becomes visible. In - * addition, deleting a single item from the list results in a stale cache - * requiring an invalidation. - * @param {String=} opt_selection Optional address of device to select - * after refreshing the list. - */ - refresh: function(opt_selection) { - // TODO(kevers): Investigate if the root source of the problems can be - // fixed in cr.ui.list. - var selectedDevice = opt_selection ? opt_selection : - this.getSelectedDevice_(); - this.invalidate(); - this.redraw(); - if (selectedDevice) - this.setSelectedDevice_(selectedDevice); - }, - - /** - * Retrieves the address of the selected device, or null if no device is - * selected. - * @return {?String} Address of selected device or null. - * @private - */ - getSelectedDevice_: function() { - var selection = this.selectedItem; - if (selection) - return selection.address; - return null; - }, - - /** - * Selects the device with the matching address. - * @param {String} address The unique address of the device. - * @private - */ - setSelectedDevice_: function(address) { - var index = this.find(address); - if (index != undefined) - this.selectionModel.selectRange(index, index); - }, - - /** - * Perges all devices from the list. - */ - clear: function() { - this.dataModel = new ArrayDataModel([]); - this.redraw(); - this.updateListVisibility_(); - }, - - /** - * Returns the index of the list entry with the matching address. - * @param {string} address Unique address of the Bluetooth device. - * @return {number|undefined} Index of the matching entry or - * undefined if no match found. - */ - find: function(address) { - var size = this.dataModel.length; - for (var i = 0; i < size; i++) { - var entry = this.dataModel.item(i); - if (entry.address == address) - return i; - } - }, - - /** @inheritDoc */ - createItem: function(entry) { - return new BluetoothListItem(entry); - }, - - /** - * Overrides the default implementation, which is used to compute the - * size of an element in the list. The default implementation relies - * on adding a placeholder item to the list and fetching its size and - * position. This strategy does not work if an item is added to the list - * while it is hidden, as the computed metrics will all be zero in that - * case. - * @return {{height: number, marginTop: number, marginBottom: number, - * width: number, marginLeft: number, marginRight: number}} - * The height and width of the item, taking margins into account, - * and the margins themselves. - */ - measureItem: function() { - return { - height: this.itemHeight_, - marginTop: 0, - marginBotton: 0, - width: this.itemWidth_, - marginLeft: 0, - marginRight: 0 - }; - }, - - /** - * Override the default implementation to return a predetermined size, - * which in turns allows proper layout of items even if the list is hidden. - * @return {height: number, width: number} Dimensions of a single item in - * the list of bluetooth device. - * @private. - */ - getDefaultItemSize_: function() { - return { - height: this.itemHeight_, - width: this.itemWidth_ - }; - }, - - /** - * Override base implementation of handleClick_, which unconditionally - * removes the item. In this case, removal of the element is deferred - * pending confirmation from the Bluetooth adapter. - * @param {Event} e The click event object. - * @private - */ - handleClick_: function(e) { - if (this.disabled) - return; - - var target = e.target; - if (!target.classList.contains('row-delete-button')) - return; - - var listItem = this.getListItemAncestor(target); - var selected = this.selectionModel.selectedIndexes; - var index = this.getIndexOfListItem(listItem); - if (selected.indexOf(index) == -1) - selected = [index]; - for (var j = selected.length - 1; j >= 0; j--) { - var index = selected[j]; - var item = this.getListItemByIndex(index); - if (item && item.deletable) { - // Device is busy until we hear back from the Bluetooth adapter. - // Prevent double removal request. - item.deletable = false; - // TODO(kevers): Provide visual feedback that the device is busy. - - // Inform the bluetooth adapter that we are disconnecting or - // forgetting the device. - chrome.send('updateBluetoothDevice', - [item.data.address, item.connected ? 'disconnect' : 'forget']); - } - } - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var selectedDevice = this.getSelectedDevice_(); - this.dataModel.splice(index, 1); - this.refresh(selectedDevice); - this.updateListVisibility_(); - }, - - /** - * If the list has an associated empty list placholder then update the - * visibility of the list and placeholder. - * @private - */ - updateListVisibility_: function() { - var empty = this.dataModel.length == 0; - var listPlaceHolderID = this.id + '-empty-placeholder'; - if ($(listPlaceHolderID)) { - if (this.hidden != empty) { - this.hidden = empty; - $(listPlaceHolderID).hidden = !empty; - this.refresh(); - } - } - }, - }; - - cr.defineProperty(BluetoothListItem, 'connected', cr.PropertyKind.BOOL_ATTR); - - cr.defineProperty(BluetoothListItem, 'paired', cr.PropertyKind.BOOL_ATTR); - - cr.defineProperty(BluetoothListItem, 'connecting', cr.PropertyKind.BOOL_ATTR); - - return { - BluetoothListItem: BluetoothListItem, - BluetoothDeviceList: BluetoothDeviceList, - Constants: Constants - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.html b/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.html deleted file mode 100644 index 95b9c03..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.html +++ /dev/null @@ -1,28 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.js b/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.js deleted file mode 100644 index be86dfc..0000000 --- a/chrome/browser/resources/options2/chromeos/bluetooth_pair_device_overlay.js +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * Enumeration of possible states during pairing. The value associated with - * each state maps to a localized string in the global variable - * |loadTimeData|. - * @enum {string} - */ - var PAIRING = { - STARTUP: 'bluetoothStartConnecting', - ENTER_PIN_CODE: 'bluetoothEnterPinCode', - ENTER_PASSKEY: 'bluetoothEnterPasskey', - REMOTE_PIN_CODE: 'bluetoothRemotePinCode', - REMOTE_PASSKEY: 'bluetoothRemotePasskey', - CONFIRM_PASSKEY: 'bluetoothConfirmPasskey', - }; - - /** - * List of IDs for conditionally visible elements in the dialog. - * @type {Array.} - * @const - */ - var ELEMENTS = ['bluetooth-pairing-passkey-display', - 'bluetooth-pairing-passkey-entry', - 'bluetooth-pairing-pincode-entry', - 'bluetooth-pair-device-connect-button', - 'bluetooth-pair-device-cancel-button', - 'bluetooth-pair-device-accept-button', - 'bluetooth-pair-device-reject-button', - 'bluetooth-pair-device-dismiss-button']; - - /** - * Encapsulated handling of the Bluetooth device pairing page. - * @constructor - */ - function BluetoothPairing() { - OptionsPage.call(this, - 'bluetoothPairing', - loadTimeData.getString('bluetoothOptionsPageTabTitle'), - 'bluetooth-pairing'); - } - - cr.addSingletonGetter(BluetoothPairing); - - BluetoothPairing.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Description of the bluetooth device. - * @type {{name: string, - * address: string, - * icon: Constants.DEVICE_TYPE, - * paired: boolean, - * bonded: boolean, - * connected: boolean, - * pairing: string|undefined, - * passkey: number|undefined, - * pincode: string|undefined, - * entered: number|undefined}} - * @private. - */ - device_: null, - - /** - * Can the dialog be programmatically dismissed. - * @type {boolean} - */ - dismissible_: true, - - /** @inheritDoc */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - var self = this; - $('bluetooth-pair-device-cancel-button').onclick = function() { - chrome.send('updateBluetoothDevice', - [self.device_.address, 'cancel']); - OptionsPage.closeOverlay(); - }; - $('bluetooth-pair-device-reject-button').onclick = function() { - chrome.send('updateBluetoothDevice', - [self.device_.address, 'reject']); - OptionsPage.closeOverlay(); - }; - $('bluetooth-pair-device-connect-button').onclick = function() { - var args = [self.device_.address, 'connect']; - var passkey = self.device_.passkey; - if (passkey) - args.push(String(passkey)); - else if (!$('bluetooth-pairing-passkey-entry').hidden) - args.push($('bluetooth-passkey').value); - else if (!$('bluetooth-pairing-pincode-entry').hidden) - args.push($('bluetooth-pincode').value); - chrome.send('updateBluetoothDevice', args); - OptionsPage.closeOverlay(); - }; - $('bluetooth-pair-device-accept-button').onclick = function() { - chrome.send('updateBluetoothDevice', - [self.device_.address, 'accept']); - OptionsPage.closeOverlay(); - }; - $('bluetooth-pair-device-dismiss-button').onclick = function() { - OptionsPage.closeOverlay(); - }; - $('bluetooth-passkey').oninput = function() { - var inputField = $('bluetooth-passkey'); - var value = inputField.value; - // Note that using is insufficient to restrict - // the input as it allows negative numbers and does not limit the - // number of charactes typed even if a range is set. Furthermore, - // it sometimes produces strange repaint artifacts. - var filtered = value.replace(/[^0-9]/g, ''); - if (filtered != value) - inputField.value = filtered; - $('bluetooth-pair-device-connect-button').disabled = - inputField.value.length == 0; - } - $('bluetooth-pincode').oninput = function() { - $('bluetooth-pair-device-connect-button').disabled = - $('bluetooth-pincode').value.length == 0; - } - $('bluetooth-passkey').addEventListener('keydown', - this.keyDownEventHandler_.bind(this)); - $('bluetooth-pincode').addEventListener('keydown', - this.keyDownEventHandler_.bind(this)); - }, - - /** - * Override to prevent showing the overlay if the Bluetooth device details - * have not been specified. Prevents showing an empty dialog if the user - * quits and restarts Chrome while in the process of pairing with a device. - * @return {boolean} True if the overlay can be displayed. - */ - canShowPage: function() { - return this.device_ && this.device_.address && this.device_.pairing; - }, - - /** - * Sets input focus on the passkey or pincode field if appropriate. - */ - didShowPage: function() { - if (!$('bluetooth-pincode').hidden) - $('bluetooth-pincode').focus(); - else if (!$('bluetooth-passkey').hidden) - $('bluetooth-passkey').focus(); - }, - - /** - * Configures the overlay for pairing a device. - * @param {Object} device Description of the bluetooth device. - */ - update: function(device) { - this.device_ = {}; - for (key in device) - this.device_[key] = device[key]; - // Update the pairing instructions. - var instructionsEl = $('bluetooth-pairing-instructions'); - this.clearElement_(instructionsEl); - this.dismissible_ = ('dismissible' in device) ? - device.dismissible : true; - - var message = loadTimeData.getString(device.pairing); - message = message.replace('%1', this.device_.name); - instructionsEl.textContent = message; - - // Update visibility of dialog elements. - if (this.device_.passkey) { - this.updatePasskey_(); - if (this.device_.pairing == PAIRING.CONFIRM_PASSKEY) { - // Confirming a match between displayed passkeys. - this.displayElements_(['bluetooth-pairing-passkey-display', - 'bluetooth-pair-device-accept-button', - 'bluetooth-pair-device-reject-button']); - } else { - // Remote entering a passkey. - this.displayElements_(['bluetooth-pairing-passkey-display', - 'bluetooth-pair-device-cancel-button']); - } - } else if (this.device_.pincode) { - this.updatePinCode_(); - this.displayElements_(['bluetooth-pairing-passkey-display', - 'bluetooth-pair-device-cancel-button']); - } else if (this.device_.pairing == PAIRING.ENTER_PIN_CODE) { - // Prompting the user to enter a PIN code. - this.displayElements_(['bluetooth-pairing-pincode-entry', - 'bluetooth-pair-device-connect-button', - 'bluetooth-pair-device-cancel-button']); - $('bluetooth-pincode').value = ''; - } else if (this.device_.pairing == PAIRING.ENTER_PASSKEY) { - // Prompting the user to enter a passkey. - this.displayElements_(['bluetooth-pairing-passkey-entry', - 'bluetooth-pair-device-connect-button', - 'bluetooth-pair-device-cancel-button']); - $('bluetooth-passkey').value = ''; - } else if (this.device_.pairing == PAIRING.STARTUP) { - // Starting the pairing process. - this.displayElements_(['bluetooth-pair-device-cancel-button']); - } else { - // Displaying an error message. - this.displayElements_(['bluetooth-pair-device-dismiss-button']); - } - // User is required to enter a passkey or pincode before the connect - // button can be enabled. The 'oninput' methods for the input fields - // determine when the connect button becomes active. - $('bluetooth-pair-device-connect-button').disabled = true; - }, - - /** - * Handles the ENTER key for the passkey or pincode entry field. - * @return {Event} a keydown event. - * @private - */ - keyDownEventHandler_: function(event) { - /** @const */ var ENTER_KEY_CODE = 13; - if (event.keyCode == ENTER_KEY_CODE) { - var button = $('bluetooth-pair-device-connect-button'); - if (!button.hidden) - button.click(); - } - }, - - /** - * Updates the visibility of elements in the dialog. - * @param {Array.} list List of conditionally visible elements that - * are to be made visible. - * @private - */ - displayElements_: function(list) { - var enabled = {}; - for (var i = 0; i < list.length; i++) { - var key = list[i]; - enabled[key] = true; - } - for (var i = 0; i < ELEMENTS.length; i++) { - var key = ELEMENTS[i]; - $(key).hidden = !enabled[key]; - } - }, - - /** - * Removes all children from an element. - * @param {!Element} element Target element to clear. - */ - clearElement_: function(element) { - var child = element.firstChild; - while (child) { - element.removeChild(child); - child = element.firstChild; - } - }, - - /** - * Formats an element for displaying the passkey. - */ - updatePasskey_: function() { - var passkeyEl = $('bluetooth-pairing-passkey-display'); - var keyClass = this.device_.pairing == PAIRING.REMOTE_PASSKEY ? - 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; - this.clearElement_(passkeyEl); - var key = String(this.device_.passkey); - var progress = this.device_.entered | 0; - for (var i = 0; i < key.length; i++) { - var keyEl = document.createElement('span'); - keyEl.textContent = key.charAt(i); - keyEl.className = keyClass; - if (i < progress) - keyEl.classList.add('key-typed'); - passkeyEl.appendChild(keyEl); - } - if (this.device_.pairing == PAIRING.REMOTE_PASSKEY) { - // Add enter key. - var label = loadTimeData.getString('bluetoothEnterKey'); - var keyEl = document.createElement('span'); - keyEl.textContent = label; - keyEl.className = keyClass; - keyEl.id = 'bluetooth-enter-key'; - passkeyEl.appendChild(keyEl); - } - passkeyEl.hidden = false; - }, - - /** - * Formats an element for displaying the PIN code. - */ - updatePinCode_: function() { - var passkeyEl = $('bluetooth-pairing-passkey-display'); - var keyClass = this.device_.pairing == PAIRING.REMOTE_PIN_CODE ? - 'bluetooth-keyboard-button' : 'bluetooth-passkey-char'; - this.clearElement_(passkeyEl); - var key = String(this.device_.pincode); - for (var i = 0; i < key.length; i++) { - var keyEl = document.createElement('span'); - keyEl.textContent = key.charAt(i); - keyEl.className = keyClass; - keyEl.classList.add('key-pin'); - passkeyEl.appendChild(keyEl); - } - if (this.device_.pairing == PAIRING.REMOTE_PIN_CODE) { - // Add enter key. - var label = loadTimeData.getString('bluetoothEnterKey'); - var keyEl = document.createElement('span'); - keyEl.textContent = label; - keyEl.className = keyClass; - keyEl.classList.add('key-pin'); - keyEl.id = 'bluetooth-enter-key'; - passkeyEl.appendChild(keyEl); - } - passkeyEl.hidden = false; - }, - }; - - /** - * Configures the device pairing instructions and displays the pairing - * overlay. - * @param {Object} device Description of the Bluetooth device. - */ - BluetoothPairing.showDialog = function(device) { - BluetoothPairing.getInstance().update(device); - OptionsPage.showPageByName('bluetoothPairing', false); - }; - - /** - * Displays a message from the Bluetooth adapter. - * @param {{string: label, - * string: address} data Data for constructing the message. - */ - BluetoothPairing.showMessage = function(data) { - var name = ''; - if (data.address.length > 0) { - name = data.address; - var list = $('bluetooth-paired-devices-list'); - var index = list.find(name); - if (index == undefined) { - list = $('bluetooth-unpaired-devices-list'); - index = list.find(name); - } - if (index != undefined) { - var entry = list.dataModel.item(index); - if (entry && entry.name) - name = entry.name; - } - } - BluetoothPairing.showDialog({name: name, - address: data.address, - pairing: data.label, - dismissible: false}); - }; - - /** - * Closes the Bluetooth pairing dialog. - */ - BluetoothPairing.dismissDialog = function() { - var overlay = OptionsPage.getTopmostVisiblePage(); - var dialog = BluetoothPairing.getInstance(); - if (overlay == dialog && dialog.dismissible_) - OptionsPage.closeOverlay(); - }; - - // Export - return { - BluetoothPairing: BluetoothPairing - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/cellular_plan_element.js b/chrome/browser/resources/options2/chromeos/cellular_plan_element.js deleted file mode 100644 index 2ec59f0..0000000 --- a/chrome/browser/resources/options2/chromeos/cellular_plan_element.js +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.internet', function() { - /** - * Creates a new network list div. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {HTMLDivElement} - */ - var CellularPlanElement = cr.ui.define('div'); - - CellularPlanElement.prototype = { - __proto__: HTMLDivElement.prototype, - - /** @inheritDoc */ - decorate: function() { - }, - - /** - * Loads given network list. - * @param {Array} networks An array of network object. - */ - load: function(plans) { - this.textContent = ''; - if (!plans || !plans.length) { - var noplansDiv = this.ownerDocument.createElement('div'); - noplansDiv.textContent = loadTimeData.getString('noPlansFound'); - this.appendChild(detailsTable); - } else { - for (var i = 0; i < plans.length; ++i) { - this.appendChild(new CellularPlanItem(i, plans[i])); - } - } - } - }; - - /** - * Creates a new network item. - * @param {Object} network The network this represents. - * @constructor - * @extends {HTMLDivElement} - */ - function CellularPlanItem(idx, plan) { - var el = cr.doc.createElement('div'); - el.data = { - idx: idx, - planType: plan.planType, - name: plan.name, - planSummary: plan.planSummary, - dataRemaining: plan.dataRemaining, - planExpires: plan.planExpires, - warning: plan.warning - }; - CellularPlanItem.decorate(el); - return el; - } - - - /** - * Decorates an element as a network item. - * @param {!HTMLElement} el The element to decorate. - */ - CellularPlanItem.decorate = function(el) { - el.__proto__ = CellularPlanItem.prototype; - el.decorate(); - }; - - CellularPlanItem.prototype = { - __proto__: HTMLDivElement.prototype, - - /** @inheritDoc */ - decorate: function() { - this.className = 'cellular-plan'; - var detailsTable = this.createTable_('details-plan-table', - 'option-control-table'); - this.addRow_(detailsTable, 'plan-details-info', - 'option-name', 'planSummary', this.data.planSummary); - this.addRow_(detailsTable, 'plan-details-info', - 'option-name', null, loadTimeData.getString('planName'), - 'option-value', 'planName', this.data.name); - this.addRow_(detailsTable, 'plan-details-info', - 'option-name', null, loadTimeData.getString('dataRemaining'), - 'option-value', 'dataRemaining', this.data.dataRemaining); - this.addRow_(detailsTable, 'plan-details-info', - 'option-name', null, loadTimeData.getString('planExpires'), - 'option-value', 'dataRemaining', this.data.planExpires); - if (this.data.warning && this.data.warning != '') { - this.addRow_(detailsTable, 'plan-details-info', - 'option-name', 'planWarning', this.data.warning); - } - this.appendChild(detailsTable); - this.appendChild(this.ownerDocument.createElement('hr')); - }, - - createTable_: function(tableId, tableClass) { - var table = this.ownerDocument.createElement('table'); - table.id = tableId; - table.className = tableClass; - return table; - }, - - addRow_: function(table, rowClass, col1Class, col1Id, col1Value, - col2Class, col2Id, col2Value) { - var row = this.ownerDocument.createElement('tr'); - if (rowClass) - row.className = rowClass; - var col1 = this.ownerDocument.createElement('td'); - col1.className = col1Class; - if (col1Id) - col1.id = col1Id; - col1.textContent = col1Value; - if (!col2Class) - col1.setAttribute('colspan', '2'); - row.appendChild(col1); - if (col2Class) { - var col2 = this.ownerDocument.createElement('td'); - col2.className = col2Class; - if (col2Id) - col2.id = col2Id; - col2.textContent = col2Value; - row.appendChild(col2); - } - table.appendChild(row); - } - }; - - return { - CellularPlanElement: CellularPlanElement - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/change_picture_options.css b/chrome/browser/resources/options2/chromeos/change_picture_options.css deleted file mode 100644 index 098b3ab..0000000 --- a/chrome/browser/resources/options2/chromeos/change_picture_options.css +++ /dev/null @@ -1,193 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#change-picture-page .content-area { - min-width: 800px; -} - -#user-image-grid { - -webkit-user-drag: none; - -webkit-user-select: none; - display: inline-block; - margin: 10px; - outline: none; - /* Necessary for correct metrics calculation by grid.js. */ - overflow: hidden; - padding: 0; - width: 538px; -} - -#user-image-grid * { - margin: 0; - padding: 0; -} - -#user-image-grid img { - background-color: white; - height: 64px; - vertical-align: middle; - width: 64px; -} - -#user-image-grid > li { - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 4px; - display: inline-block; - margin: 8px; - padding: 3px; -} - -#user-image-grid [selected] { - border: 2px solid rgb(0, 102, 204); - padding: 2px; -} - -/** - * #user-image-preview can have the following classes: - * .default-image: one of the default images is selected (including the grey - * silhouette); - * .profile-image: profile image is selected; - * .online: camera is streaming video; - * .camera: camera (live or photo) is selected; - * .live: camera is in live mode (no photo taken yet/last photo removed). - */ - -#user-image-preview { - display: inline-block; - margin-right: 10px; - margin-top: 20px; - max-width: 220px; - vertical-align: top; -} - -html[dir=rtl] #user-image-preview { - float: left; -} - -#user-image-preview .perspective-box { - -webkit-perspective: 600px; -} - -.user-image-preview-img { - display: block; - max-height: 220px; - max-width: 220px; -} - -[camera=webrtc] .camera.live .user-image-preview-img { - display: none; -} - -.default-image .user-image-preview-img { - background: white; - border: solid 1px #cacaca; - border-radius: 4px; - padding: 2px; -} - -.user-image-stream-area { - display: none; - position: relative; -} - -[camera=webrtc] .camera.live .user-image-stream-area { - display: block; -} - -#user-image-stream-crop { - /* TODO(ivankr): temporary workaround for crbug.com/142347. */ - -webkit-transform: rotateY(360deg); - -webkit-transition: -webkit-transform 200ms linear; - height: 220px; - overflow: hidden; - position: relative; - width: 220px; -} - -.flip-x #user-image-stream-crop { - -webkit-transform: rotateY(180deg); -} - -/* TODO(ivankr): specify dimensions from real capture size. */ -.user-image-stream { - border: solid 1px #cacaca; - height: 220px; - /* Center image for 4:3 aspect ratio. */ - left: -16.6%; - position: absolute; - visibility: hidden; -} - -.online .user-image-stream { - visibility: visible; -} - -.user-image-stream-area .spinner { - display: none; - left: 14px; - position: absolute; - top: 14px; -} - -html[dir=rtl] .user-image-stream-area .spinner { - left: auto; - right: 14px; -} - -.camera.live:not(.online) .user-image-stream-area .spinner { - display: block; -} - -#flip-photo { - -webkit-transition: opacity 75ms linear; - background: url('chrome://theme/IDR_MIRROR_FLIP') no-repeat; - border: none; - bottom: 8px; - display: block; - height: 32px; - opacity: 0; - position: absolute; - right: 8px; - width: 32px; -} - -html[dir=rtl] #flip-photo { - left: 8px; - right: auto; -} - -/* "Flip photo" button is hidden during flip animation. */ -.online:not(.animation) .user-image-stream-area:hover #flip-photo { - opacity: 0.75; -} - -#discard-photo, -#take-photo { - display: none; - height: 25px; - margin: 4px 1px; - padding: 0; - width: 220px; -} - -[camera=webrtc] .camera:not(.live) #discard-photo { - background: url('chrome://theme/IDR_USER_IMAGE_RECYCLE') - no-repeat center 0; - display: block; -} - -[camera=webrtc] .camera.live.online #take-photo { - background: url('chrome://theme/IDR_USER_IMAGE_CAPTURE') - no-repeat center -1px; - display: block; -} - -#user-image-attribution { - -webkit-padding-start: 34px; - line-height: 26px; -} - -#user-image-author-website { - -webkit-padding-start: 5px; -} diff --git a/chrome/browser/resources/options2/chromeos/change_picture_options.html b/chrome/browser/resources/options2/chromeos/change_picture_options.html deleted file mode 100644 index 0bfa12c..0000000 --- a/chrome/browser/resources/options2/chromeos/change_picture_options.html +++ /dev/null @@ -1,32 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/change_picture_options.js b/chrome/browser/resources/options2/chromeos/change_picture_options.js deleted file mode 100644 index 2404337..0000000 --- a/chrome/browser/resources/options2/chromeos/change_picture_options.js +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - var UserImagesGrid = options.UserImagesGrid; - var ButtonImages = UserImagesGrid.ButtonImages; - - /** - * Array of button URLs used on this page. - * @type {Array.} - * @const - */ - var ButtonImageUrls = [ - ButtonImages.TAKE_PHOTO, - ButtonImages.CHOOSE_FILE - ]; - - ///////////////////////////////////////////////////////////////////////////// - // ChangePictureOptions class: - - /** - * Encapsulated handling of ChromeOS change picture options page. - * @constructor - */ - function ChangePictureOptions() { - var isWebRTC = $('change-picture-page').getAttribute('camera') == 'webrtc'; - ChangePictureOptions.prototype = isWebRTC ? - ChangePictureOptionsWebRTCProto : ChangePictureOptionsOldProto; - // |this| has been already created so it's |__proto__| has to be reset. - this.__proto__ = ChangePictureOptions.prototype; - OptionsPage.call( - this, - 'changePicture', - loadTimeData.getString('changePicturePage'), - 'change-picture-page'); - } - - cr.addSingletonGetter(ChangePictureOptions); - - var ChangePictureOptionsOldProto = { - // Inherit ChangePictureOptions from OptionsPage. - __proto__: options.OptionsPage.prototype, - - /** - * Initializes ChangePictureOptions page. - */ - initializePage: function() { - // Call base class implementation to start preferences initialization. - OptionsPage.prototype.initializePage.call(this); - - var imageGrid = $('user-image-grid'); - UserImagesGrid.decorate(imageGrid); - - imageGrid.previewElement = $('user-image-preview'); - - imageGrid.addEventListener('select', - this.handleImageSelected_.bind(this)); - imageGrid.addEventListener('activate', - this.handleImageActivated_.bind(this)); - - // Add the "Choose file" button. - imageGrid.addItem(ButtonImages.CHOOSE_FILE, - loadTimeData.getString('chooseFile'), - this.handleChooseFile_.bind(this)); - - // Profile image data. - this.profileImage_ = imageGrid.addItem( - ButtonImages.PROFILE_PICTURE, - loadTimeData.getString('profilePhotoLoading')); - - // Old user image data (if present). - this.oldImage_ = null; - - $('change-picture-overlay-confirm').onclick = this.closePage_; - - chrome.send('onChangePicturePageInitialized'); - }, - - /** - * Called right after the page has been shown to user. - */ - didShowPage: function() { - $('user-image-grid').updateAndFocus(); - chrome.send('onChangePicturePageShown'); - }, - - /** - * Called right before the page is hidden. - */ - willHidePage: function() { - var imageGrid = $('user-image-grid'); - imageGrid.blur(); // Make sure the image grid is not active. - if (this.oldImage_) { - imageGrid.removeItem(this.oldImage_); - this.oldImage_ = null; - } - }, - - /** - * Called right after the page has been hidden. - */ - // TODO(ivankr): both callbacks are required as only one of them is called - // depending on the way the page was closed, see http://crbug.com/118923. - didClosePage: function() { - this.willHidePage(); - }, - - /** - * Closes current page, returning back to Personal Stuff page. - * @private - */ - closePage_: function() { - OptionsPage.closeOverlay(); - }, - - /** - * Handles "Take photo" button activation. - * @private - */ - handleTakePhoto_: function() { - chrome.send('takePhoto'); - this.closePage_(); - }, - - /** - * Handles "Choose a file" button activation. - * @private - */ - handleChooseFile_: function() { - chrome.send('chooseFile'); - this.closePage_(); - }, - - /** - * Handles image selection change. - * @private - */ - handleImageSelected_: function() { - var imageGrid = $('user-image-grid'); - var url = imageGrid.selectedItemUrl; - // Ignore deselection, selection change caused by program itself and - // selection of one of the action buttons. - if (url && - !imageGrid.inProgramSelection && - ButtonImageUrls.indexOf(url) == -1) { - chrome.send('selectImage', [url]); - } - }, - - /** - * Handles image activation (by pressing Enter). - * @private - */ - handleImageActivated_: function() { - switch ($('user-image-grid').selectedItemUrl) { - case ButtonImages.TAKE_PHOTO: - this.handleTakePhoto_(); - break; - case ButtonImages.CHOOSE_FILE: - this.handleChooseFile_(); - break; - default: - this.closePage_(); - break; - } - }, - - /** - * URL of the current user image. - * @type {string} - */ - get currentUserImageUrl() { - return 'chrome://userimage/' + BrowserOptions.getLoggedInUsername() + - '?id=' + (new Date()).getTime() + '&animated'; - }, - - /** - * Notifies about camera presence change. - * @param {boolean} present Whether a camera is present or not. - * @private - */ - setCameraPresent_: function(present) { - var imageGrid = $('user-image-grid'); - var showTakePhotoButton = present; - if (showTakePhotoButton && !this.takePhotoButton_) { - this.takePhotoButton_ = imageGrid.addItem( - ButtonImages.TAKE_PHOTO, - loadTimeData.getString('takePhoto'), - this.handleTakePhoto_.bind(this), - 1); - } else if (!showTakePhotoButton && this.takePhotoButton_) { - imageGrid.removeItem(this.takePhotoButton_); - this.takePhotoButton_ = null; - } - }, - - /** - * Adds or updates old user image taken from file/camera (neither a profile - * image nor a default one). - * @private - */ - setOldImage_: function() { - var imageGrid = $('user-image-grid'); - var url = this.currentUserImageUrl; - if (this.oldImage_) { - this.oldImage_ = imageGrid.updateItem(this.oldImage_, url); - } else { - // Insert next to the profile image. - var pos = imageGrid.indexOf(this.profileImage_) + 1; - this.oldImage_ = imageGrid.addItem(url, undefined, undefined, pos); - imageGrid.selectedItem = this.oldImage_; - } - }, - - /** - * Updates user's profile image. - * @param {string} imageUrl Profile image, encoded as data URL. - * @param {boolean} select If true, profile image should be selected. - * @private - */ - setProfileImage_: function(imageUrl, select) { - var imageGrid = $('user-image-grid'); - this.profileImage_ = imageGrid.updateItem( - this.profileImage_, imageUrl, loadTimeData.getString('profilePhoto')); - if (select) - imageGrid.selectedItem = this.profileImage_; - }, - - /** - * Selects user image with the given URL. - * @param {string} url URL of the image to select. - * @private - */ - setSelectedImage_: function(url) { - $('user-image-grid').selectedItemUrl = url; - }, - - /** - * Appends default images to the image grid. Should only be called once. - * @param {Array.<{url: string, author: string, website: string}>} images - * An array of default images data, including URL, author and website. - * @private - */ - setDefaultImages_: function(images) { - var imageGrid = $('user-image-grid'); - for (var i = 0, data; data = imagesData[i]; i++) { - imageGrid.addItem(data.url); - } - }, - }; - - var ChangePictureOptionsWebRTCProto = { - // Inherit ChangePictureOptions from OptionsPage. - __proto__: options.OptionsPage.prototype, - - /** - * Initializes ChangePictureOptions page. - */ - initializePage: function() { - // Call base class implementation to start preferences initialization. - OptionsPage.prototype.initializePage.call(this); - - var imageGrid = $('user-image-grid'); - UserImagesGrid.decorate(imageGrid); - - // Preview image will track the selected item's URL. - var previewElement = $('user-image-preview'); - imageGrid.previewElement = previewElement; - imageGrid.selectionType = 'default'; - - imageGrid.addEventListener('select', - this.handleImageSelected_.bind(this)); - imageGrid.addEventListener('activate', - this.handleImageActivated_.bind(this)); - - // Set the title for "Take Photo" button. - imageGrid.cameraTitle = loadTimeData.getString('takePhoto'); - - // Add the "Choose file" button. - imageGrid.addItem(ButtonImages.CHOOSE_FILE, - loadTimeData.getString('chooseFile'), - this.handleChooseFile_.bind(this)).type = 'file'; - - // Profile image data. - this.profileImage_ = imageGrid.addItem( - ButtonImages.PROFILE_PICTURE, - loadTimeData.getString('profilePhotoLoading')); - this.profileImage_.type = 'profile'; - - $('take-photo').addEventListener( - 'click', this.handleTakePhoto_.bind(this)); - $('discard-photo').addEventListener( - 'click', imageGrid.discardPhoto.bind(imageGrid)); - - // Toggle 'animation' class for the duration of WebKit transition. - $('flip-photo').addEventListener( - 'click', function(e) { - previewElement.classList.add('animation'); - imageGrid.flipPhoto = !imageGrid.flipPhoto; - }); - $('user-image-stream-crop').addEventListener( - 'webkitTransitionEnd', function(e) { - previewElement.classList.remove('animation'); - }); - - // Old user image data (if present). - this.oldImage_ = null; - - $('change-picture-overlay-confirm').addEventListener( - 'click', this.closePage_.bind(this)); - - chrome.send('onChangePicturePageInitialized'); - }, - - /** - * Called right after the page has been shown to user. - */ - didShowPage: function() { - var imageGrid = $('user-image-grid'); - imageGrid.updateAndFocus(); - // Reset camera element. - imageGrid.cameraImage = null; - // Autoplay but do not preselect. - imageGrid.checkCameraPresence(true, false); - chrome.send('onChangePicturePageShown'); - }, - - /** - * Called right before the page is hidden. - */ - willHidePage: function() { - var imageGrid = $('user-image-grid'); - imageGrid.blur(); // Make sure the image grid is not active. - imageGrid.stopCamera(); - if (this.oldImage_) { - imageGrid.removeItem(this.oldImage_); - this.oldImage_ = null; - } - }, - - /** - * Called right after the page has been hidden. - */ - // TODO(ivankr): both callbacks are required as only one of them is called - // depending on the way the page was closed, see http://crbug.com/118923. - didClosePage: function() { - this.willHidePage(); - }, - - /** - * Closes current page, returning back to Personal Stuff page. - * @private - */ - closePage_: function() { - OptionsPage.closeOverlay(); - }, - - /** - * Handles "Take photo" button click. - * @private - */ - handleTakePhoto_: function() { - $('user-image-grid').takePhoto(function(photoURL) { - chrome.send('photoTaken', [photoURL]); - }); - }, - - /** - * Handles "Choose a file" button activation. - * @private - */ - handleChooseFile_: function() { - chrome.send('chooseFile'); - this.closePage_(); - }, - - /** - * Handles image selection change. - * @private - */ - handleImageSelected_: function() { - var imageGrid = $('user-image-grid'); - var url = imageGrid.selectedItemUrl; - // Ignore selection change caused by program itself and selection of one - // of the action buttons. - if (!imageGrid.inProgramSelection && - url != ButtonImages.TAKE_PHOTO && url != ButtonImages.CHOOSE_FILE) { - chrome.send('selectImage', [url]); - } - // Update image attribution text. - var image = imageGrid.selectedItem; - $('user-image-author-name').textContent = image.author; - $('user-image-author-website').textContent = image.website; - $('user-image-author-website').href = image.website; - $('user-image-attribution').style.visibility = - (image.author || image.website) ? 'visible' : 'hidden'; - }, - - /** - * Handles image activation (by pressing Enter). - * @private - */ - handleImageActivated_: function() { - switch ($('user-image-grid').selectedItemUrl) { - case ButtonImages.TAKE_PHOTO: - this.handleTakePhoto_(); - break; - case ButtonImages.CHOOSE_FILE: - this.handleChooseFile_(); - break; - default: - this.closePage_(); - break; - } - }, - - /** - * URL of the current user image. - * @type {string} - */ - get currentUserImageUrl() { - return 'chrome://userimage/' + BrowserOptions.getLoggedInUsername() + - '?id=' + new Date().getTime(); - }, - - /** - * Adds or updates old user image taken from file/camera (neither a profile - * image nor a default one). - * @private - */ - setOldImage_: function() { - var imageGrid = $('user-image-grid'); - var url = this.currentUserImageUrl; - if (this.oldImage_) { - this.oldImage_ = imageGrid.updateItem(this.oldImage_, url); - } else { - // Insert next to the profile image. - var pos = imageGrid.indexOf(this.profileImage_) + 1; - this.oldImage_ = imageGrid.addItem(url, undefined, undefined, pos); - imageGrid.selectedItem = this.oldImage_; - } - }, - - /** - * Updates user's profile image. - * @param {string} imageUrl Profile image, encoded as data URL. - * @param {boolean} select If true, profile image should be selected. - * @private - */ - setProfileImage_: function(imageUrl, select) { - var imageGrid = $('user-image-grid'); - this.profileImage_ = imageGrid.updateItem( - this.profileImage_, imageUrl, loadTimeData.getString('profilePhoto')); - if (select) - imageGrid.selectedItem = this.profileImage_; - }, - - /** - * Selects user image with the given URL. - * @param {string} url URL of the image to select. - * @private - */ - setSelectedImage_: function(url) { - $('user-image-grid').selectedItemUrl = url; - }, - - /** - * Appends default images to the image grid. Should only be called once. - * @param {Array.<{url: string, author: string, website: string}>} images - * An array of default images data, including URL, author and website. - * @private - */ - setDefaultImages_: function(imagesData) { - var imageGrid = $('user-image-grid'); - for (var i = 0, data; data = imagesData[i]; i++) { - var item = imageGrid.addItem(data.url); - item.type = 'default'; - item.author = data.author || ''; - item.website = data.website || ''; - } - }, - }; - - // Forward public APIs to private implementations. - [ - 'setCameraPresent', - 'setDefaultImages', - 'setOldImage', - 'setProfileImage', - 'setSelectedImage', - ].forEach(function(name) { - ChangePictureOptions[name] = function() { - var instance = ChangePictureOptions.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - // Export - return { - ChangePictureOptions: ChangePictureOptions - }; - -}); diff --git a/chrome/browser/resources/options2/chromeos/display_options.css b/chrome/browser/resources/options2/chromeos/display_options.css deleted file mode 100644 index 7d32475..0000000 --- a/chrome/browser/resources/options2/chromeos/display_options.css +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#display-options { - background-color: rgb(240, 240, 240); -} - -#display-options-content-area { - padding: 0; -} - -#display-options-displays-view-host { - padding: 20px 0 20px 0; -} - -#display-options-displays-view { - position: relative; - width: 100%; -} - -#display-options-displays-view-mirroring { - margin: 20px 0 20px 0; -} - -#display-configurations { - background-color: white; - border-top: 1px solid lightgrey; - height: 65px; - padding: 15px; -} - -/* The arrow at the border #display-configurations to point the focused display. - * This is achieved by a square rotated by 45-deg, and it has border at the - * upper-half, which were left/top before the rotation. */ -#display-configuration-arrow { - -webkit-transform: rotate(45deg); - background-color: white; - border-left: 1px solid lightgrey; - border-top: 1px solid lightgrey; - height: 20px; - position: absolute; - width: 20px; - z-index: 1; -} - -#display-options-buttons-container { - float: right; - z-index: 2; -} - -html[dir=rtl] #display-options-buttons-container { - float: left; -} - - -#selected-display-data-container { - float: left; - line-height: 200%; - z-index: 2; -} - -html[dir=rtl] #selected-display-data-container { - float: right; -} - -#selected-display-name { - font-weight: bold; -} - -#display-launcher { - background-color: lightgrey; - bottom: 0; - height: 10px; - position: absolute; -} - -.displays-display { - background: rgb(240, 240, 240); - border: solid 1px; - font-weight: bold; - position: absolute; - text-align: center; - vertical-align: middle; - z-index: 2; -} - -.display-mirrored { - border: dashed 1px; -} - -.displays-focused { - border: solid 2px rgb(0, 138, 255); - color: rgb(0, 138, 255); -} - -.display-options-single-button-container { - margin: 5px 0 5px 0; -} - -.display-options-button { - width: 120px; -} diff --git a/chrome/browser/resources/options2/chromeos/display_options.html b/chrome/browser/resources/options2/chromeos/display_options.html deleted file mode 100644 index 3b865c8..0000000 --- a/chrome/browser/resources/options2/chromeos/display_options.html +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/display_options.js b/chrome/browser/resources/options2/chromeos/display_options.js deleted file mode 100644 index b663d94..0000000 --- a/chrome/browser/resources/options2/chromeos/display_options.js +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - // The scale ratio of the display rectangle to its original size. - /** @const */ var VISUAL_SCALE = 1 / 10; - - // The number of pixels to share the edges between displays. - /** @const */ var MIN_OFFSET_OVERLAP = 5; - - /** - * Enumeration of secondary display layout. The value has to be same as the - * values in ash/monitor/monitor_controller.cc. - * @enum {number} - */ - var SecondaryDisplayLayout = { - TOP: 0, - RIGHT: 1, - BOTTOM: 2, - LEFT: 3 - }; - - /** - * Encapsulated handling of the 'Display' page. - * @constructor - */ - function DisplayOptions() { - OptionsPage.call(this, 'display', - loadTimeData.getString('displayOptionsPageTabTitle'), - 'display-options'); - this.mirroring_ = false; - this.focusedIndex_ = null; - this.displays_ = []; - } - - cr.addSingletonGetter(DisplayOptions); - - DisplayOptions.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - $('display-options-toggle-mirroring').onclick = (function() { - this.mirroring_ = !this.mirroring_; - chrome.send('setMirroring', [this.mirroring_]); - }).bind(this); - - $('display-options-apply').onclick = this.applyResult_.bind(this); - chrome.send('getDisplayInfo'); - }, - - /** @override */ - onVisibilityChanged_: function() { - OptionsPage.prototype.onVisibilityChanged_(this); - if (this.visible) - chrome.send('getDisplayInfo'); - }, - - /** - * Collects the current data and sends it to Chrome. - * @private - */ - applyResult_: function() { - // Offset is calculated from top or left edge. - var primary = this.displays_[0]; - var secondary = this.displays_[1]; - var offset; - if (this.layout_ == SecondaryDisplayLayout.LEFT || - this.layout_ == SecondaryDisplayLayout.RIGHT) { - offset = secondary.div.offsetTop - primary.div.offsetTop; - } else { - offset = secondary.div.offsetLeft - primary.div.offsetLeft; - } - chrome.send('setDisplayLayout', [this.layout_, offset / VISUAL_SCALE]); - }, - - /** - * Mouse move handler for dragging display rectangle. - * @private - * @param {Event} e The mouse move event. - */ - onMouseMove_: function(e) { - if (!this.dragging_) - return true; - - var index = -1; - for (var i = 0; i < this.displays_.length; i++) { - if (this.displays_[i] == this.dragging_.display) { - index = i; - break; - } - } - if (index < 0) - return true; - - // Note that current code of moving display-rectangles doesn't work - // if there are >=3 displays. This is our assumption for M21. - // TODO(mukai): Fix the code to allow >=3 displays. - var mousePosition = { - x: e.pageX - this.dragging_.offset.x, - y: e.pageY - this.dragging_.offset.y - }; - var newPosition = { - x: mousePosition.x - this.dragging_.clickLocation.x, - y: mousePosition.y - this.dragging_.clickLocation.y - }; - - var primaryDiv = this.displays_[0].div; - var display = this.dragging_.display; - - // Separate the area into four (LEFT/RIGHT/TOP/BOTTOM) by the diagonals of - // the primary display, and decide which area the display should reside. - var diagonalSlope = primaryDiv.offsetHeight / primaryDiv.offsetWidth; - var topDownIntercept = - primaryDiv.offsetTop - primaryDiv.offsetLeft * diagonalSlope; - var bottomUpIntercept = primaryDiv.offsetTop + - primaryDiv.offsetHeight + primaryDiv.offsetLeft * diagonalSlope; - - if (mousePosition.y > - topDownIntercept + mousePosition.x * diagonalSlope) { - if (mousePosition.y > - bottomUpIntercept - mousePosition.x * diagonalSlope) - this.layout_ = SecondaryDisplayLayout.BOTTOM; - else - this.layout_ = SecondaryDisplayLayout.LEFT; - } else { - if (mousePosition.y > - bottomUpIntercept - mousePosition.x * diagonalSlope) - this.layout_ = SecondaryDisplayLayout.RIGHT; - else - this.layout_ = SecondaryDisplayLayout.TOP; - } - - if (this.layout_ == SecondaryDisplayLayout.LEFT || - this.layout_ == SecondaryDisplayLayout.RIGHT) { - if (newPosition.y > primaryDiv.offsetTop + primaryDiv.offsetHeight) - this.layout_ = SecondaryDisplayLayout.BOTTOM; - else if (newPosition.y + display.div.offsetHeight < - primaryDiv.offsetTop) - this.layout_ = SecondaryDisplayLayout.TOP; - } else { - if (newPosition.y > primaryDiv.offsetLeft + primaryDiv.offsetWidth) - this.layout_ = SecondaryDisplayLayout.RIGHT; - else if (newPosition.y + display.div.offsetWidth < - primaryDiv.offstLeft) - this.layout_ = SecondaryDisplayLayout.LEFT; - } - - switch (this.layout_) { - case SecondaryDisplayLayout.RIGHT: - display.div.style.left = - primaryDiv.offsetLeft + primaryDiv.offsetWidth + 'px'; - display.div.style.top = newPosition.y + 'px'; - break; - case SecondaryDisplayLayout.LEFT: - display.div.style.left = - primaryDiv.offsetLeft - display.div.offsetWidth + 'px'; - display.div.style.top = newPosition.y + 'px'; - break; - case SecondaryDisplayLayout.TOP: - display.div.style.top = - primaryDiv.offsetTop - display.div.offsetHeight + 'px'; - display.div.style.left = newPosition.x + 'px'; - break; - case SecondaryDisplayLayout.BOTTOM: - display.div.style.top = - primaryDiv.offsetTop + primaryDiv.offsetHeight + 'px'; - display.div.style.left = newPosition.x + 'px'; - break; - } - - return false; - }, - - /** - * Mouse down handler for dragging display rectangle. - * @private - * @param {Event} e The mouse down event. - */ - onMouseDown_: function(e) { - if (this.mirroring_) - return true; - - if (e.button != 0) - return true; - - this.focusedIndex_ = null; - for (var i = 0; i < this.displays_.length; i++) { - var display = this.displays_[i]; - if (this.displays_[i].div == e.target || - (i == 0 && $('display-launcher') == e.target)) { - this.focusedIndex_ = i; - break; - } - } - - for (var i = 0; i < this.displays_.length; i++) { - var display = this.displays_[i]; - display.div.className = 'displays-display'; - if (i != this.focusedIndex_) - continue; - - display.div.classList.add('displays-focused'); - // Do not drag the primary monitor. - if (i == 0) - continue; - - this.dragging_ = { - display: display, - clickLocation: {x: e.offsetX, y: e.offsetY}, - offset: {x: e.pageX - e.offsetX - display.div.offsetLeft, - y: e.pageY - e.offsetY - display.div.offsetTop} - }; - } - this.updateSelectedDisplayDescription_(); - return false; - }, - - /** - * Mouse up handler for dragging display rectangle. - * @private - * @param {Event} e The mouse up event. - */ - onMouseUp_: function(e) { - if (this.dragging_) { - // Make sure the dragging location is connected. - var primaryDiv = this.displays_[0].div; - var draggingDiv = this.dragging_.display.div; - if (this.layout_ == SecondaryDisplayLayout.LEFT || - this.layout_ == SecondaryDisplayLayout.RIGHT) { - var top = Math.max(draggingDiv.offsetTop, - primaryDiv.offsetTop - draggingDiv.offsetHeight + - MIN_OFFSET_OVERLAP); - top = Math.min(top, - primaryDiv.offsetTop + primaryDiv.offsetHeight - - MIN_OFFSET_OVERLAP); - draggingDiv.style.top = top + 'px'; - } else { - var left = Math.max(draggingDiv.offsetLeft, - primaryDiv.offsetLeft - draggingDiv.offsetWidth + - MIN_OFFSET_OVERLAP); - left = Math.min(left, - primaryDiv.offsetLeft + primaryDiv.offsetWidth - - MIN_OFFSET_OVERLAP); - draggingDiv.style.left = left + 'px'; - } - this.dragging_ = null; - } - this.updateSelectedDisplayDescription_(); - return false; - }, - - /** - * Updates the description of the selected display section. - * @private - */ - updateSelectedDisplayDescription_: function() { - if (this.focusedIndex_ == null || - this.displays_[this.focusedIndex_] == null) { - $('selected-display-data-container').hidden = true; - $('display-configuration-arrow').hidden = true; - return; - } - - $('selected-display-data-container').hidden = false; - var display = this.displays_[this.focusedIndex_]; - var nameElement = $('selected-display-name'); - while (nameElement.childNodes.length > 0) - nameElement.removeChild(nameElement.firstChild); - nameElement.appendChild(document.createTextNode(display.name)); - - var resolutionData = display.width + 'x' + display.height; - var resolutionElement = $('selected-display-resolution'); - while (resolutionElement.childNodes.length > 0) - resolutionElement.removeChild(resolutionElement.firstChild); - resolutionElement.appendChild(document.createTextNode(resolutionData)); - - var arrow = $('display-configuration-arrow'); - arrow.hidden = false; - arrow.style.top = - $('display-configurations').offsetTop - arrow.offsetHeight / 2 + 'px'; - arrow.style.left = display.div.offsetLeft + display.div.offsetWidth / 2 - - arrow.offsetWidth / 2 + 'px'; - }, - - /** - * Clears the drawing area for display rectangles. - * @private - */ - resetDisplaysView_: function() { - var displaysViewHost = $('display-options-displays-view-host'); - displaysViewHost.removeChild(displaysViewHost.firstChild); - this.displaysView_ = document.createElement('div'); - this.displaysView_.id = 'display-options-displays-view'; - this.displaysView_.onmousemove = this.onMouseMove_.bind(this); - this.displaysView_.onmousedown = this.onMouseDown_.bind(this); - this.displaysView_.onmouseup = this.onMouseUp_.bind(this); - displaysViewHost.appendChild(this.displaysView_); - }, - - /** - * Lays out the display rectangles for mirroring. - * @private - */ - layoutMirroringDisplays_: function() { - // Offset pixels for secondary display rectangles. - /** @const */ var MIRRORING_OFFSET_PIXELS = 2; - // Always show two displays because there must be two displays when - // the display_options is enabled. Don't rely on displays_.length because - // there is only one display from chrome's perspective in mirror mode. - /** @const */ var MIN_NUM_DISPLAYS = 2; - /** @const */ var MIRRORING_VERTICAL_MARGIN = 20; - - // The width/height should be same as the primary display: - var width = this.displays_[0].width * VISUAL_SCALE; - var height = this.displays_[0].height * VISUAL_SCALE; - - var numDisplays = Math.max(MIN_NUM_DISPLAYS, this.displays_.length); - - var totalWidth = width + numDisplays * MIRRORING_OFFSET_PIXELS; - var totalHeight = height + numDisplays * MIRRORING_OFFSET_PIXELS; - - this.displaysView_.style.height = totalHeight + 'px'; - this.displaysView_.classList.add( - 'display-options-displays-view-mirroring'); - - // The displays should be centered. - var offsetX = - $('display-options-displays-view').offsetWidth / 2 - totalWidth / 2; - - for (var i = 0; i < numDisplays; i++) { - var div = document.createElement('div'); - div.className = 'displays-display'; - div.style.top = i * MIRRORING_OFFSET_PIXELS + 'px'; - div.style.left = i * MIRRORING_OFFSET_PIXELS + offsetX + 'px'; - div.style.width = width + 'px'; - div.style.height = height + 'px'; - div.style.zIndex = i; - // set 'display-mirrored' class for the background display rectangles. - if (i != numDisplays - 1) - div.classList.add('display-mirrored'); - this.displaysView_.appendChild(div); - } - }, - - /** - * Layouts the display rectangles according to the current layout_. - * @private - */ - layoutDisplays_: function() { - var totalHeight = 0; - var boundingBox = {left: 0, right: 0, top: 0, bottom: 0}; - for (var i = 0; i < this.displays_.length; i++) { - var display = this.displays_[i]; - totalHeight += display.height * VISUAL_SCALE; - boundingBox.left = Math.min(boundingBox.left, display.x * VISUAL_SCALE); - boundingBox.right = Math.max( - boundingBox.right, (display.x + display.width) * VISUAL_SCALE); - boundingBox.top = Math.min(boundingBox.top, display.y * VISUAL_SCALE); - boundingBox.bottom = Math.max( - boundingBox.bottom, (display.y + display.height) * VISUAL_SCALE); - } - - // Prepare enough area for thisplays_view by adding the maximum height. - this.displaysView_.style.height = totalHeight + 'px'; - - // Centering the bounding box of the display rectangles. - var offset = {x: $('display-options-displays-view').offsetWidth / 2 - - (boundingBox.left + boundingBox.right) / 2, - y: totalHeight / 2 - - (boundingBox.top + boundingBox.bottom) / 2}; - - - for (var i = 0; i < this.displays_.length; i++) { - var display = this.displays_[i]; - var div = document.createElement('div'); - display.div = div; - - div.className = 'displays-display'; - if (i == this.focusedIndex_) - div.classList.add('displays-focused'); - div.style.width = display.width * VISUAL_SCALE + 'px'; - div.style.height = display.height * VISUAL_SCALE + 'px'; - div.style.lineHeight = div.style.height; - if (i == 0) { - // Assumes that first display is primary and put a grey rectangle to - // denote launcher below. - var launcher = document.createElement('div'); - launcher.id = 'display-launcher'; - launcher.style.width = display.div.style.width; - div.appendChild(launcher); - } - div.style.left = display.x * VISUAL_SCALE + offset.x + 'px'; - div.style.top = display.y * VISUAL_SCALE + offset.y + 'px'; - - div.appendChild(document.createTextNode(display.name)); - - this.displaysView_.appendChild(div); - } - }, - - /** - * Called when the display arrangement has changed. - * @private - * @param {boolean} mirroring Whether current mode is mirroring or not. - * @param {Array} displays The list of the display information. - * @param {SecondaryDisplayLayout} layout The layout strategy. - * @param {number} offset The offset of the secondary display. - */ - onDisplayChanged_: function(mirroring, displays, layout, offset) { - this.mirroring_ = mirroring; - this.layout_ = layout; - this.offset_ = offset; - - $('display-options-toggle-mirroring').textContent = - loadTimeData.getString( - this.mirroring_ ? 'stopMirroring' : 'startMirroring'); - - // Focus to the first display next to the primary one when |displays| list - // is updated. - if (this.mirroring_) - this.focusedIndex_ = null; - else if (this.displays_.length != displays.length) - this.focusedIndex_ = 1; - - this.displays_ = displays; - - this.resetDisplaysView_(); - if (this.mirroring_) - this.layoutMirroringDisplays_(); - else - this.layoutDisplays_(); - this.updateSelectedDisplayDescription_(); - }, - }; - - DisplayOptions.setDisplayInfo = function( - mirroring, displays, layout, offset) { - DisplayOptions.getInstance().onDisplayChanged_( - mirroring, displays, layout, offset); - }; - - // Export - return { - DisplayOptions: DisplayOptions - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/internet_detail.css b/chrome/browser/resources/options2/chromeos/internet_detail.css deleted file mode 100644 index b92a4f5..0000000 --- a/chrome/browser/resources/options2/chromeos/internet_detail.css +++ /dev/null @@ -1,83 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -/* Force tab strip to extend to the left and right edges of the window. */ -#internet-details-content-area { - -webkit-box-orient: vertical; - display: -webkit-box; - padding: 6px 0 0 0; -} - -#network-details-header { - -webkit-padding-start: 20px; - margin: 0; - padding-bottom: 12px; - padding-top: 32px; -} - -#network-details-title { - font-size: 18px; -} - -#network-details-subtitle-status { - color: rgb(53, 174, 71); -} - - -/* Fix the height of the subpages so that the dialog does not resize when the - user switches tabs. */ -#internet-details-content-area > .subpages-tab-contents { - -webkit-box-flex: 1; - -webkit-box-sizing: border-box; - -webkit-padding-end: 10px; - height: 380px; - min-width: 480px; - overflow-y: auto; -} - -#ip-config-list { - min-height: 96px !important; -} - -/* Minimum and maximum height are integer multiples of the height of a list - entry. */ -#ignored-host-list { - -webkit-margin-start: 0; - border: solid 1px #999; - max-height: 128px; - min-height: 64px; - width: 400px; -} - -#new-host { - -webkit-margin-start: 0; - margin-top: 8px; -} - -#ipconfig-section { - border-top: 1px solid #eee; - margin-bottom: 10px; - padding-top: 10px; -} - -#ipconfig-dns-section { - border-top: 1px solid #eee; - padding-top: 10px; -} - -#user-dns-settings:not([selected]) { - display: none; -} - -.dns-display { - -webkit-margin-start: 24px; - -webkit-transition: opacity 150ms ease-in-out; - color: #bbb; - font-style: italic; -} - -.dns-display:not([selected]) { - -webkit-transition: opacity 150ms ease-in-out; - display: none; -} \ No newline at end of file diff --git a/chrome/browser/resources/options2/chromeos/internet_detail.html b/chrome/browser/resources/options2/chromeos/internet_detail.html deleted file mode 100644 index 7069d27..0000000 --- a/chrome/browser/resources/options2/chromeos/internet_detail.html +++ /dev/null @@ -1,667 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/internet_detail.js b/chrome/browser/resources/options2/chromeos/internet_detail.js deleted file mode 100644 index 51c28e7..0000000 --- a/chrome/browser/resources/options2/chromeos/internet_detail.js +++ /dev/null @@ -1,1018 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.internet', function() { - var OptionsPage = options.OptionsPage; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var IPAddressField = options.internet.IPAddressField; - - /** - * Network settings constants. These enums must match their C++ - * counterparts. - */ - function Constants() {} - - // Network types: - Constants.TYPE_UNKNOWN = 0; - Constants.TYPE_ETHERNET = 1; - Constants.TYPE_WIFI = 2; - Constants.TYPE_WIMAX = 3; - Constants.TYPE_BLUETOOTH = 4; - Constants.TYPE_CELLULAR = 5; - Constants.TYPE_VPN = 6; - - /* - * Helper function to set hidden attribute for elements matching a selector. - * @param {string} selector CSS selector for extracting a list of elements. - * @param {bool} hidden New hidden value. - */ - function updateHidden(selector, hidden) { - var elements = cr.doc.querySelectorAll(selector); - for (var i = 0, el; el = elements[i]; i++) { - el.hidden = hidden; - } - } - - /** - * Monitor pref change of given element. - * @param {Element} el Target element. - */ - function observePrefsUI(el) { - Preferences.getInstance().addEventListener(el.pref, handlePrefUpdate); - } - - /** - * UI pref change handler. - * @param {Event} e The update event. - */ - function handlePrefUpdate(e) { - DetailsInternetPage.getInstance().updateControls(); - } - - ///////////////////////////////////////////////////////////////////////////// - // DetailsInternetPage class: - - /** - * Encapsulated handling of ChromeOS internet details overlay page. - * @constructor - */ - function DetailsInternetPage() { - OptionsPage.call(this, - 'detailsInternetPage', - null, - 'details-internet-page'); - } - - cr.addSingletonGetter(DetailsInternetPage); - - DetailsInternetPage.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Indicates if the list of proxy exceptions has been initialized. - * @type {boolean} - */ - proxyListInitialized_: false, - - /** - * Initializes DetailsInternetPage page. - * Calls base class implementation to starts preference initialization. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - options.internet.CellularPlanElement.decorate($('plan-list')); - var params = parseQueryParams(window.location); - this.initializePageContents_(params); - this.showNetworkDetails_(params); - }, - - /** - * Auto-activates the network details dialog if network information - * is included in the URL. - */ - showNetworkDetails_: function(params) { - var servicePath = params.servicePath; - var networkType = params.networkType; - if (!servicePath || !servicePath.length || - !networkType || !networkType.length) - return; - chrome.send('networkCommand', - [networkType, servicePath, 'options']); - }, - - /** - * Initializes the contents of the page. - */ - initializePageContents_: function(params) { - $('details-internet-dismiss').addEventListener('click', function(event) { - DetailsInternetPage.setDetails(); - }); - - $('details-internet-login').addEventListener('click', function(event) { - DetailsInternetPage.setDetails(); - DetailsInternetPage.loginFromDetails(); - }); - - $('details-internet-disconnect').addEventListener('click', - function(event) { - DetailsInternetPage.setDetails(); - DetailsInternetPage.disconnectNetwork(); - }); - - $('activate-details').addEventListener('click', function(event) { - DetailsInternetPage.activateFromDetails(); - }); - - $('buyplan-details').addEventListener('click', function(event) { - var data = $('connection-state').data; - chrome.send('buyDataPlan', [String(data.servicePath)]); - OptionsPage.closeOverlay(); - }); - - $('view-account-details').addEventListener('click', function(event) { - chrome.send('showMorePlanInfo'); - OptionsPage.closeOverlay(); - }); - - $('cellular-apn-use-default').addEventListener('click', function(event) { - var data = $('connection-state').data; - var apnSelector = $('select-apn'); - - if (data.userApnIndex != -1) { - apnSelector.remove(data.userApnIndex); - data.userApnIndex = -1; - } - - if (data.providerApnList.value.length > 0) { - var iApn = 0; - data.apn.apn = data.providerApnList.value[iApn].apn; - data.apn.username = data.providerApnList.value[iApn].username; - data.apn.password = data.providerApnList.value[iApn].password; - chrome.send('setApn', [String(data.servicePath), - String(data.apn.apn), - String(data.apn.username), - String(data.apn.password)]); - apnSelector.selectedIndex = iApn; - data.selectedApn = iApn; - } else { - data.apn.apn = ''; - data.apn.username = ''; - data.apn.password = ''; - apnSelector.selectedIndex = -1; - data.selectedApn = -1; - } - updateHidden('.apn-list-view', false); - updateHidden('.apn-details-view', true); - }); - - $('cellular-apn-set').addEventListener('click', function(event) { - if ($('cellular-apn').value == '') - return; - - var data = $('connection-state').data; - var apnSelector = $('select-apn'); - - data.apn.apn = String($('cellular-apn').value); - data.apn.username = String($('cellular-apn-username').value); - data.apn.password = String($('cellular-apn-password').value); - chrome.send('setApn', [String(data.servicePath), - String(data.apn.apn), - String(data.apn.username), - String(data.apn.password)]); - - if (data.userApnIndex != -1) { - apnSelector.remove(data.userApnIndex); - data.userApnIndex = -1; - } - - var option = document.createElement('option'); - option.textContent = data.apn.apn; - option.value = -1; - option.selected = true; - apnSelector.add(option, apnSelector[apnSelector.length - 1]); - data.userApnIndex = apnSelector.length - 2; - data.selectedApn = data.userApnIndex; - - updateHidden('.apn-list-view', false); - updateHidden('.apn-details-view', true); - }); - - $('cellular-apn-cancel').addEventListener('click', function(event) { - $('select-apn').selectedIndex = $('connection-state').data.selectedApn; - updateHidden('.apn-list-view', false); - updateHidden('.apn-details-view', true); - }); - - $('select-apn').addEventListener('change', function(event) { - var data = $('connection-state').data; - var apnSelector = $('select-apn'); - if (apnSelector[apnSelector.selectedIndex].value != -1) { - var apnList = data.providerApnList.value; - chrome.send('setApn', [String(data.servicePath), - String(apnList[apnSelector.selectedIndex].apn), - String(apnList[apnSelector.selectedIndex].username), - String(apnList[apnSelector.selectedIndex].password) - ]); - data.selectedApn = apnSelector.selectedIndex; - } else if (apnSelector.selectedIndex == data.userApnIndex) { - chrome.send('setApn', [String(data.servicePath), - String(data.apn.apn), - String(data.apn.username), - String(data.apn.password)]); - data.selectedApn = apnSelector.selectedIndex; - } else { - $('cellular-apn').value = data.apn.apn; - $('cellular-apn-username').value = data.apn.username; - $('cellular-apn-password').value = data.apn.password; - - updateHidden('.apn-list-view', true); - updateHidden('.apn-details-view', false); - } - }); - - $('sim-card-lock-enabled').addEventListener('click', function(event) { - var newValue = $('sim-card-lock-enabled').checked; - // Leave value as is because user needs to enter PIN code first. - // When PIN will be entered and value changed, - // we'll update UI to reflect that change. - $('sim-card-lock-enabled').checked = !newValue; - chrome.send('setSimCardLock', [newValue]); - }); - $('change-pin').addEventListener('click', function(event) { - chrome.send('changePin'); - }); - - // Proxy - options.proxyexceptions.ProxyExceptions.decorate($('ignored-host-list')); - $('remove-host').addEventListener('click', - this.handleRemoveProxyExceptions_); - $('add-host').addEventListener('click', this.handleAddProxyException_); - $('direct-proxy').addEventListener('click', this.disableManualProxy_); - $('manual-proxy').addEventListener('click', this.enableManualProxy_); - $('auto-proxy').addEventListener('click', this.disableManualProxy_); - $('proxy-all-protocols').addEventListener('click', - this.toggleSingleProxy_); - - observePrefsUI($('direct-proxy')); - observePrefsUI($('manual-proxy')); - observePrefsUI($('auto-proxy')); - observePrefsUI($('proxy-all-protocols')); - - $('ip-automatic-configuration-checkbox').addEventListener('click', - this.handleIpAutoConfig_); - $('automatic-dns-radio').addEventListener('click', - this.handleNameServerTypeChange_); - $('google-dns-radio').addEventListener('click', - this.handleNameServerTypeChange_); - $('user-dns-radio').addEventListener('click', - this.handleNameServerTypeChange_); - }, - - /** - * Handler for "add" event fired from userNameEdit. - * @param {Event} e Add event fired from userNameEdit. - * @private - */ - handleAddProxyException_: function(e) { - var exception = $('new-host').value; - $('new-host').value = ''; - - exception = exception.trim(); - if (exception) - $('ignored-host-list').addException(exception); - }, - - /** - * Handler for when the remove button is clicked - * @param {Event} e The click event. - * @private - */ - handleRemoveProxyExceptions_: function(e) { - var selectedItems = $('ignored-host-list').selectedItems; - for (var x = 0; x < selectedItems.length; x++) { - $('ignored-host-list').removeException(selectedItems[x]); - } - }, - - /** - * Handler for when the IP automatic configuration checkbox is clicked. - * @param {Event} e The click event. - * @private - */ - handleIpAutoConfig_: function(e) { - var checked = $('ip-automatic-configuration-checkbox').checked; - var fields = [$('ip-address'), $('ip-netmask'), $('ip-gateway')]; - for (var i = 0; i < fields.length; ++i) { - fields[i].editable = !checked; - if (checked) { - var model = fields[i].model; - model.value = model.automatic; - fields[i].model = model; - } - } - if (!checked) - $('ip-address').focus(); - }, - - /** - * Handler for when the name server selection changes. - * @param {Event} e The click event. - * @private - */ - handleNameServerTypeChange_: function(event) { - var type = event.target.value; - DetailsInternetPage.updateNameServerDisplay(type); - }, - - /** - * Update details page controls. - * @private - */ - updateControls: function() { - // Only show ipconfig section if network is connected OR if nothing on - // this device is connected. This is so that you can fix the ip configs - // if you can't connect to any network. - // TODO(chocobo): Once ipconfig is moved to flimflam service objects, - // we need to redo this logic to allow configuration of all networks. - $('ipconfig-section').hidden = !this.connected && this.deviceConnected; - $('ipconfig-dns-section').hidden = - !this.connected && this.deviceConnected; - - // Network type related. - updateHidden('#details-internet-page .cellular-details', !this.cellular); - updateHidden('#details-internet-page .wifi-details', !this.wireless); - updateHidden('#details-internet-page .wimax-details', !this.wimax); - updateHidden('#details-internet-page .vpn-details', !this.vpn); - updateHidden('#details-internet-page .proxy-details', !this.showProxy); - /* Network information merged into the Wifi tab for wireless networks - unless the option is set for enabling a static IP configuration. */ - updateHidden('#details-internet-page .network-details', - (this.wireless && !this.showStaticIPConfig) || this.vpn); - updateHidden('#details-internet-page .wifi-network-setting', - this.showStaticIPConfig); - - // Cell plan related. - $('plan-list').hidden = this.cellplanloading; - updateHidden('#details-internet-page .no-plan-info', - !this.cellular || this.cellplanloading || this.hascellplan); - updateHidden('#details-internet-page .plan-loading-info', - !this.cellular || this.nocellplan || this.hascellplan); - updateHidden('#details-internet-page .plan-details-info', - !this.cellular || this.nocellplan || this.cellplanloading); - updateHidden('#details-internet-page .gsm-only', - !this.cellular || !this.gsm); - updateHidden('#details-internet-page .cdma-only', - !this.cellular || this.gsm); - updateHidden('#details-internet-page .apn-list-view', - !this.cellular || !this.gsm); - updateHidden('#details-internet-page .apn-details-view', true); - - // Wifi - Password and shared. - updateHidden('#details-internet-page #password-details', - !this.wireless || !this.password); - updateHidden('#details-internet-page #wifi-shared-network', - !this.shared); - updateHidden('#details-internet-page #prefer-network', - !this.showPreferred); - - // WiMAX. - updateHidden('#details-internet-page #wimax-shared-network', - !this.shared); - - // Proxy - this.updateProxyBannerVisibility_(); - this.toggleSingleProxy_(); - if ($('manual-proxy').checked) - this.enableManualProxy_(); - else - this.disableManualProxy_(); - if (!this.proxyListInitialized_ && this.visible) { - this.proxyListInitialized_ = true; - $('ignored-host-list').redraw(); - } - }, - - /** - * Updates info banner visibility state. This function shows the banner - * if proxy is managed or shared-proxies is off for shared network. - * @private - */ - updateProxyBannerVisibility_: function() { - var bannerDiv = $('info-banner'); - // Show banner and determine its message if necessary. - var controlledBy = $('direct-proxy').controlledBy; - if (!controlledBy || controlledBy == '') { - bannerDiv.hidden = true; - } else { - bannerDiv.hidden = false; - // controlledBy must match strings loaded in proxy_handler.cc and - // set in proxy_cros_settings_provider.cc. - $('banner-text').textContent = loadTimeData.getString(controlledBy); - } - }, - - /** - * Handler for when the user clicks on the checkbox to allow a - * single proxy usage. - * @private - * @param {Event} e Click Event. - */ - toggleSingleProxy_: function(e) { - if ($('proxy-all-protocols').checked) { - $('multi-proxy').hidden = true; - $('single-proxy').hidden = false; - } else { - $('multi-proxy').hidden = false; - $('single-proxy').hidden = true; - } - }, - - /** - * Handler for selecting a radio button that will disable the manual - * controls. - * @private - * @param {Event} e Click event. - */ - disableManualProxy_: function(e) { - $('advanced-config').hidden = true; - $('proxy-all-protocols').disabled = true; - $('proxy-host-name').disabled = true; - $('proxy-host-port').disabled = true; - $('proxy-host-single-name').disabled = true; - $('proxy-host-single-port').disabled = true; - $('secure-proxy-host-name').disabled = true; - $('secure-proxy-port').disabled = true; - $('ftp-proxy').disabled = true; - $('ftp-proxy-port').disabled = true; - $('socks-host').disabled = true; - $('socks-port').disabled = true; - $('proxy-config').disabled = $('auto-proxy').disabled || - !$('auto-proxy').checked; - }, - - /** - * Handler for selecting a radio button that will enable the manual - * controls. - * @private - * @param {Event} e Click event. - */ - enableManualProxy_: function(e) { - $('advanced-config').hidden = false; - $('ignored-host-list').redraw(); - var all_disabled = $('manual-proxy').disabled; - $('new-host').disabled = all_disabled; - $('remove-host').disabled = all_disabled; - $('add-host').disabled = all_disabled; - $('proxy-all-protocols').disabled = all_disabled; - $('proxy-host-name').disabled = all_disabled; - $('proxy-host-port').disabled = all_disabled; - $('proxy-host-single-name').disabled = all_disabled; - $('proxy-host-single-port').disabled = all_disabled; - $('secure-proxy-host-name').disabled = all_disabled; - $('secure-proxy-port').disabled = all_disabled; - $('ftp-proxy').disabled = all_disabled; - $('ftp-proxy-port').disabled = all_disabled; - $('socks-host').disabled = all_disabled; - $('socks-port').disabled = all_disabled; - $('proxy-config').disabled = true; - }, - }; - - /** - * Performs minimal initialization of the InternetDetails dialog in - * preparation for showing proxy-setttings. - */ - DetailsInternetPage.initializeProxySettings = function() { - var detailsPage = DetailsInternetPage.getInstance(); - detailsPage.initializePageContents_(); - }; - - /** - * Displays the InternetDetails dialog with only the proxy settings visible. - */ - DetailsInternetPage.showProxySettings = function() { - var detailsPage = DetailsInternetPage.getInstance(); - $('network-details-header').hidden = true; - $('buyplan-details').hidden = true; - $('activate-details').hidden = true; - $('view-account-details').hidden = true; - detailsPage.cellular = false; - detailsPage.wireless = false; - detailsPage.vpn = false; - detailsPage.showProxy = true; - updateHidden('#internet-tab', true); - updateHidden('#details-tab-strip', true); - updateHidden('#details-internet-page .action-area', true); - detailsPage.updateControls(); - detailsPage.visible = true; - }; - - DetailsInternetPage.updateCellularPlans = function(data) { - var detailsPage = DetailsInternetPage.getInstance(); - detailsPage.cellplanloading = false; - if (data.plans && data.plans.length) { - detailsPage.nocellplan = false; - detailsPage.hascellplan = true; - $('plan-list').load(data.plans); - } else { - detailsPage.nocellplan = true; - detailsPage.hascellplan = false; - } - - detailsPage.hasactiveplan = !data.needsPlan; - detailsPage.activated = data.activated; - if (!data.activated) - $('details-internet-login').hidden = true; - - $('buyplan-details').hidden = !data.showBuyButton; - $('activate-details').hidden = !data.showActivateButton; - $('view-account-details').hidden = !data.showViewAccountButton; - }; - - DetailsInternetPage.updateSecurityTab = function(requirePin) { - $('sim-card-lock-enabled').checked = requirePin; - $('change-pin').hidden = !requirePin; - }; - - - DetailsInternetPage.loginFromDetails = function() { - var data = $('connection-state').data; - var servicePath = data.servicePath; - chrome.send('networkCommand', [String(data.type), - servicePath, - 'connect']); - OptionsPage.closeOverlay(); - }; - - DetailsInternetPage.disconnectNetwork = function() { - var data = $('connection-state').data; - var servicePath = data.servicePath; - chrome.send('networkCommand', [String(data.type), - servicePath, - 'disconnect']); - OptionsPage.closeOverlay(); - }; - - DetailsInternetPage.activateFromDetails = function() { - var data = $('connection-state').data; - var servicePath = data.servicePath; - if (data.type == Constants.TYPE_CELLULAR) { - chrome.send('networkCommand', [String(data.type), - String(servicePath), - 'activate']); - } - OptionsPage.closeOverlay(); - }; - - DetailsInternetPage.setDetails = function() { - var data = $('connection-state').data; - var servicePath = data.servicePath; - if (data.type == Constants.TYPE_WIFI) { - chrome.send('setPreferNetwork', - [String(servicePath), - $('prefer-network-wifi').checked ? 'true' : 'false']); - chrome.send('setAutoConnect', - [String(servicePath), - $('auto-connect-network-wifi').checked ? 'true' : 'false']); - } else if (data.type == Constants.TYPE_WIMAX) { - chrome.send('setAutoConnect', - [String(servicePath), - $('auto-connect-network-wimax').checked ? 'true' : 'false']); - } else if (data.type == Constants.TYPE_CELLULAR) { - chrome.send('setAutoConnect', - [String(servicePath), - $('auto-connect-network-cellular').checked ? 'true' : - 'false']); - } - - var nameServerTypes = ['automatic', 'google', 'user']; - var nameServerType = 'automatic'; - for (var i = 0; i < nameServerTypes.length; ++i) { - if ($(nameServerTypes[i] + '-dns-radio').checked) { - nameServerType = nameServerTypes[i]; - break; - } - } - - // Skip any empty values. - var userNameServers = []; - for (var i = 1; i <= 4; ++i) { - var nameServerField = $('ipconfig-dns' + i); - if (nameServerField && nameServerField.model && - nameServerField.model.value) { - userNameServers.push(nameServerField.model.value); - } - } - - userNameServers = userNameServers.join(','); - - chrome.send('setIPConfig', - [servicePath, - Boolean($('ip-automatic-configuration-checkbox').checked), - $('ip-address').model.value || '', - $('ip-netmask').model.value || '', - $('ip-gateway').model.value || '', - nameServerType, - userNameServers]); - OptionsPage.closeOverlay(); - }; - - DetailsInternetPage.updateNameServerDisplay = function(type) { - var editable = type == 'user'; - var fields = [$('ipconfig-dns1'), $('ipconfig-dns2'), - $('ipconfig-dns3'), $('ipconfig-dns4')]; - for (var i = 0; i < fields.length; ++i) { - fields[i].editable = editable; - } - if (editable) - $('ipconfig-dns1').focus(); - - var automaticDns = $('automatic-dns-display'); - var googleDns = $('google-dns-display'); - var userDns = $('user-dns-settings'); - switch (type) { - case 'automatic': - automaticDns.setAttribute('selected', ''); - googleDns.removeAttribute('selected'); - userDns.removeAttribute('selected'); - break; - case 'google': - automaticDns.removeAttribute('selected'); - googleDns.setAttribute('selected', ''); - userDns.removeAttribute('selected'); - break; - case 'user': - automaticDns.removeAttribute('selected'); - googleDns.removeAttribute('selected'); - userDns.setAttribute('selected', ''); - break; - } - }; - - DetailsInternetPage.showDetailedInfo = function(data) { - var detailsPage = DetailsInternetPage.getInstance(); - - // Populate header - $('network-details-title').textContent = data.networkName; - var statusKey = data.connected ? 'networkConnected' : - 'networkNotConnected'; - $('network-details-subtitle-status').textContent = - loadTimeData.getString(statusKey); - var typeKey = null; - switch (data.type) { - case Constants.TYPE_ETHERNET: - typeKey = 'ethernetTitle'; - break; - case Constants.TYPE_WIFI: - typeKey = 'wifiTitle'; - break; - case Constants.TYPE_WIMAX: - typeKey = 'wimaxTitle'; - break; - case Constants.TYPE_CELLULAR: - typeKey = 'cellularTitle'; - break; - case Constants.TYPE_VPN: - typeKey = 'vpnTitle'; - break; - } - var typeLabel = $('network-details-subtitle-type'); - var typeSeparator = $('network-details-subtitle-separator'); - if (typeKey) { - typeLabel.textContent = loadTimeData.getString(typeKey); - typeLabel.hidden = false; - typeSeparator.hidden = false; - } else { - typeLabel.hidden = true; - typeSeparator.hidden = true; - } - - // TODO(chocobo): Is this hack to cache the data here reasonable? - // TODO(kevers): Find more appropriate place to cache data. - $('connection-state').data = data; - - $('buyplan-details').hidden = true; - $('activate-details').hidden = true; - $('view-account-details').hidden = true; - $('details-internet-login').hidden = data.connected; - if (data.type == Constants.TYPE_ETHERNET) - $('details-internet-disconnect').hidden = true; - else - $('details-internet-disconnect').hidden = !data.connected; - - detailsPage.deviceConnected = data.deviceConnected; - detailsPage.connecting = data.connecting; - detailsPage.connected = data.connected; - detailsPage.showProxy = data.showProxy; - detailsPage.showStaticIPConfig = data.showStaticIPConfig; - $('connection-state').textContent = data.connectionState; - - var ipAutoConfig = data.ipAutoConfig ? 'automatic' : 'user'; - $('ip-automatic-configuration-checkbox').checked = data.ipAutoConfig; - var inetAddress = {autoConfig: ipAutoConfig}; - var inetNetmask = {autoConfig: ipAutoConfig}; - var inetGateway = {autoConfig: ipAutoConfig}; - - if (data.ipconfig.value) { - inetAddress.automatic = data.ipconfig.value.address; - inetAddress.value = data.ipconfig.value.address; - inetNetmask.automatic = data.ipconfig.value.netmask; - inetNetmask.value = data.ipconfig.value.netmask; - inetGateway.automatic = data.ipconfig.value.gateway; - inetGateway.value = data.ipconfig.value.gateway; - } - - // Override the "automatic" values with the real saved DHCP values, - // if they are set. - if (data.savedIP.address) { - inetAddress.automatic = data.savedIP.address; - inetAddress.value = data.savedIP.address; - } - if (data.savedIP.netmask) { - inetNetmask.automatic = data.savedIP.netmask; - inetNetmask.value = data.savedIP.netmask; - } - if (data.savedIP.gateway) { - inetGateway.automatic = data.savedIP.gateway; - inetGateway.value = data.savedIP.gateway; - } - - if (ipAutoConfig == 'user') { - if (data.staticIP.value.address) { - inetAddress.value = data.staticIP.value.address; - inetAddress.user = data.staticIP.value.address; - } - if (data.staticIP.value.netmask) { - inetNetmask.value = data.staticIP.value.netmask; - inetNetmask.user = data.staticIP.value.netmask; - } - if (data.staticIP.value.gateway) { - inetGateway.value = data.staticIP.value.gateway; - inetGateway.user = data.staticIP.value.gateway; - } - } - - var configureAddressField = function(field, model) { - IPAddressField.decorate(field); - field.model = model; - field.editable = model.autoConfig == 'user'; - }; - - configureAddressField($('ip-address'), inetAddress); - configureAddressField($('ip-netmask'), inetNetmask); - configureAddressField($('ip-gateway'), inetGateway); - - var inetNameServers = ''; - if (data.ipconfig.value && data.ipconfig.value.nameServers) { - inetNameServers = data.ipconfig.value.nameServers; - $('automatic-dns-display').textContent = inetNameServers; - } - - if (data.savedIP && data.savedIP.nameServers) - $('automatic-dns-display').textContent = data.savedIP.nameServers; - - if (data.nameServersGoogle) - $('google-dns-display').textContent = data.nameServersGoogle; - - var nameServersUser = []; - if (data.staticIP.value.nameServers) - nameServersUser = data.staticIP.value.nameServers.split(','); - - var nameServerModels = []; - for (var i = 0; i < 4; ++i) - nameServerModels.push({value: nameServersUser[i] || ''}); - - $(data.nameServerType + '-dns-radio').checked = true; - configureAddressField($('ipconfig-dns1'), nameServerModels[0]); - configureAddressField($('ipconfig-dns2'), nameServerModels[1]); - configureAddressField($('ipconfig-dns3'), nameServerModels[2]); - configureAddressField($('ipconfig-dns4'), nameServerModels[3]); - - DetailsInternetPage.updateNameServerDisplay(data.nameServerType); - - if (data.hardwareAddress) { - $('hardware-address').textContent = data.hardwareAddress; - $('hardware-address-row').style.display = 'table-row'; - } else { - // This is most likely a device without a hardware address. - $('hardware-address-row').style.display = 'none'; - } - if (data.type == Constants.TYPE_WIFI) { - OptionsPage.showTab($('wifi-network-nav-tab')); - detailsPage.wireless = true; - detailsPage.vpn = false; - detailsPage.ethernet = false; - detailsPage.cellular = false; - detailsPage.gsm = false; - detailsPage.wimax = false; - detailsPage.shared = data.shared; - $('wifi-connection-state').textContent = data.connectionState; - $('wifi-ssid').textContent = data.ssid; - if (data.bssid && data.bssid.length > 0) { - $('wifi-bssid').textContent = data.bssid; - $('wifi-bssid-entry').hidden = false; - } else { - $('wifi-bssid-entry').hidden = true; - } - $('wifi-ip-address').textContent = inetAddress.value; - $('wifi-netmask').textContent = inetNetmask.value; - $('wifi-gateway').textContent = inetGateway.value; - $('wifi-name-servers').textContent = inetNameServers; - if (data.encryption && data.encryption.length > 0) { - $('wifi-security').textContent = data.encryption; - $('wifi-security-entry').hidden = false; - } else { - $('wifi-security-entry').hidden = true; - } - // Frequency is in MHz. - var frequency = loadTimeData.getString('inetFrequencyFormat'); - frequency = frequency.replace('$1', data.frequency); - $('wifi-frequency').textContent = frequency; - // Signal strength as percentage. - var signalStrength = loadTimeData.getString('inetSignalStrengthFormat'); - signalStrength = signalStrength.replace('$1', data.strength); - $('wifi-signal-strength').textContent = signalStrength; - if (data.hardwareAddress) { - $('wifi-hardware-address').textContent = data.hardwareAddress; - $('wifi-hardware-address-entry').hidden = false; - } else { - $('wifi-hardware-address-entry').hidden = true; - } - detailsPage.showPreferred = data.showPreferred; - $('prefer-network-wifi').checked = data.preferred.value; - $('prefer-network-wifi').disabled = !data.remembered; - $('auto-connect-network-wifi').checked = data.autoConnect.value; - $('auto-connect-network-wifi').disabled = !data.remembered; - detailsPage.password = data.encrypted; - } else if (data.type == Constants.TYPE_WIMAX) { - OptionsPage.showTab($('wimax-network-nav-tab')); - detailsPage.wimax = true; - detailsPage.wireless = false; - detailsPage.vpn = false; - detailsPage.ethernet = false; - detailsPage.cellular = false; - detailsPage.gsm = false; - detailsPage.shared = data.shared; - detailsPage.showPreferred = data.showPreferred; - $('wimax-connection-state').textContent = data.connectionState; - $('auto-connect-network-wimax').checked = data.autoConnect.value; - $('auto-connect-network-wimax').disabled = !data.remembered; - if (data.identity) { - $('wimax-eap-identity').textContent = data.identity; - $('wimax-eap-identity-entry').hidden = false; - } else { - $('wimax-eap-identity-entry').hidden = true; - } - // Signal strength as percentage. - var signalStrength = loadTimeData.getString('inetSignalStrengthFormat'); - signalStrength = signalStrength.replace('$1', data.strength); - $('wimax-signal-strength').textContent = signalStrength; - } else if (data.type == Constants.TYPE_CELLULAR) { - if (!data.gsm) - OptionsPage.showTab($('cellular-plan-nav-tab')); - else - OptionsPage.showTab($('cellular-conn-nav-tab')); - detailsPage.ethernet = false; - detailsPage.wireless = false; - detailsPage.wimax = false; - detailsPage.vpn = false; - detailsPage.cellular = true; - $('service-name').textContent = data.serviceName; - $('network-technology').textContent = data.networkTechnology; - $('activation-state').textContent = data.activationState; - $('roaming-state').textContent = data.roamingState; - $('restricted-pool').textContent = data.restrictedPool; - $('error-state').textContent = data.errorState; - $('manufacturer').textContent = data.manufacturer; - $('model-id').textContent = data.modelId; - $('firmware-revision').textContent = data.firmwareRevision; - $('hardware-revision').textContent = data.hardwareRevision; - $('prl-version').textContent = data.prlVersion; - $('meid').textContent = data.meid; - $('imei').textContent = data.imei; - $('mdn').textContent = data.mdn; - $('esn').textContent = data.esn; - $('min').textContent = data.min; - detailsPage.gsm = data.gsm; - if (data.gsm) { - $('operator-name').textContent = data.operatorName; - $('operator-code').textContent = data.operatorCode; - $('imsi').textContent = data.imsi; - - var apnSelector = $('select-apn'); - // Clear APN lists, keep only last element that "other". - while (apnSelector.length != 1) - apnSelector.remove(0); - var otherOption = apnSelector[0]; - data.selectedApn = -1; - data.userApnIndex = -1; - var apnList = data.providerApnList.value; - for (var i = 0; i < apnList.length; i++) { - var option = document.createElement('option'); - var name = apnList[i].localizedName; - if (name == '' && apnList[i].name != '') - name = apnList[i].name; - if (name == '') - name = apnList[i].apn; - else - name = name + ' (' + apnList[i].apn + ')'; - option.textContent = name; - option.value = i; - if ((data.apn.apn == apnList[i].apn && - data.apn.username == apnList[i].username && - data.apn.password == apnList[i].password) || - (data.apn.apn == '' && - data.lastGoodApn.apn == apnList[i].apn && - data.lastGoodApn.username == apnList[i].username && - data.lastGoodApn.password == apnList[i].password)) { - data.selectedApn = i; - } - // Insert new option before "other" option. - apnSelector.add(option, otherOption); - } - if (data.selectedApn == -1 && data.apn.apn != '') { - var option = document.createElement('option'); - option.textContent = data.apn.apn; - option.value = -1; - apnSelector.add(option, otherOption); - data.selectedApn = apnSelector.length - 2; - data.userApnIndex = data.selectedApn; - } - apnSelector.selectedIndex = data.selectedApn; - updateHidden('.apn-list-view', false); - updateHidden('.apn-details-view', true); - DetailsInternetPage.updateSecurityTab(data.simCardLockEnabled.value); - } - $('auto-connect-network-cellular').checked = data.autoConnect.value; - $('auto-connect-network-cellular').disabled = false; - - $('buyplan-details').hidden = !data.showBuyButton; - $('view-account-details').hidden = !data.showViewAccountButton; - $('activate-details').hidden = !data.showActivateButton; - if (data.showActivateButton) { - $('details-internet-login').hidden = true; - } - - detailsPage.hascellplan = false; - if (data.connected) { - detailsPage.nocellplan = false; - detailsPage.cellplanloading = true; - chrome.send('refreshCellularPlan', [data.servicePath]); - } else { - detailsPage.nocellplan = true; - detailsPage.cellplanloading = false; - } - } else if (data.type == Constants.TYPE_VPN) { - OptionsPage.showTab($('vpn-nav-tab')); - detailsPage.wireless = false; - detailsPage.wimax = false; - detailsPage.vpn = true; - detailsPage.ethernet = false; - detailsPage.cellular = false; - detailsPage.gsm = false; - $('inet-service-name').textContent = data.service_name; - $('inet-server-hostname').textContent = data.server_hostname; - $('inet-provider-type').textContent = data.provider_type; - $('inet-username').textContent = data.username; - } else { - OptionsPage.showTab($('internet-nav-tab')); - detailsPage.ethernet = true; - detailsPage.wireless = false; - detailsPage.wimax = false; - detailsPage.vpn = false; - detailsPage.cellular = false; - detailsPage.gsm = false; - } - - // Update controlled option indicators. - indicators = cr.doc.querySelectorAll( - '#details-internet-page .controlled-setting-indicator'); - for (var i = 0; i < indicators.length; i++) { - var dataProperty = indicators[i].getAttribute('data'); - if (dataProperty && data[dataProperty]) { - var controlledBy = data[dataProperty].controlledBy; - if (controlledBy) { - indicators[i].controlledBy = controlledBy; - var forElement = $(indicators[i].getAttribute('for')); - if (forElement) - forElement.disabled = controlledBy != 'recommended'; - if (forElement.type == 'radio' && !forElement.checked) - indicators[i].hidden = true; - } else { - indicators[i].controlledBy = null; - } - } - } - - detailsPage.updateControls(); - - // Don't show page name in address bar and in history to prevent people - // navigate here by hand and solve issue with page session restore. - OptionsPage.showPageByName('detailsInternetPage', false); - }; - - return { - DetailsInternetPage: DetailsInternetPage - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/internet_detail_ip_address_field.js b/chrome/browser/resources/options2/chromeos/internet_detail_ip_address_field.js deleted file mode 100644 index 5155ecd..0000000 --- a/chrome/browser/resources/options2/chromeos/internet_detail_ip_address_field.js +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.internet', function() { - /** @const */ var EditableTextField = options.EditableTextField; - - /** - * The regular expression that matches an IP address. String to match against - * should have all whitespace stripped already. - * @const - * @type {RegExp} - */ - var singleIp_ = /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/; - - /** - * Creates a new field specifically for entering IP addresses. - * @constructor - */ - function IPAddressField() { - var el = cr.doc.createElement('div'); - IPAddressField.decorate(el); - return el; - } - - /** - * Decorates an element as a inline-editable list item. Note that this is - * a subclass of IPAddressField. - * @param {!HTMLElement} el The element to decorate. - */ - IPAddressField.decorate = function(el) { - el.__proto__ = IPAddressField.prototype; - el.decorate(); - }; - - IPAddressField.prototype = { - __proto__: EditableTextField.prototype, - - /** @override */ - decorate: function() { - EditableTextField.prototype.decorate.call(this); - }, - - /** - * Indicates whether or not empty values are allowed. - * @type {boolean} - */ - get allowEmpty() { - return this.hasAttribute('allow-empty'); - }, - - /** @override */ - get currentInputIsValid() { - if (!this.editField.value && this.allowEmpty) - return true; - - // Make sure it's only got numbers and ".", there are the correct - // count of them, and they are all within the correct range. - var fieldValue = this.editField.value.replace(/\s/g, ''); - var matches = singleIp_.exec(fieldValue); - var rangeCorrect = true; - if (matches != null) { - for (var i = 1; i < matches.length; ++i) { - var value = parseInt(matches[i], 10); - if (value < 0 || value > 255) { - rangeCorrect = false; - break; - } - } - } - return this.editField.validity.valid && matches != null && - rangeCorrect && matches.length == 5; - }, - - /** @override */ - get hasBeenEdited() { - return this.editField.value != this.model.value; - }, - - /** - * Overrides superclass to mutate the input during a successful commit. For - * the purposes of entering IP addresses, this just means stripping off - * whitespace and leading zeros from each of the octets so that they conform - * to the normal format for IP addresses. - * @override - * @param {String} value Input IP address to be mutated. - * @return {String} mutated IP address. - */ - mutateInput: function(value) { - if (!value) - return value; - - var fieldValue = value.replace(/\s/g, ''); - var matches = singleIp_.exec(fieldValue); - var result = []; - - // If we got this far, matches shouldn't be null, but make sure. - if (matches != null) { - // starting at one because the first match element contains the entire - // match, and we don't care about that. - for (var i = 1; i < matches.length; ++i) - result.push(parseInt(matches[i], 10)); - } - return result.join('.'); - }, - }; - - return { - IPAddressField: IPAddressField, - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/keyboard_overlay.html b/chrome/browser/resources/options2/chromeos/keyboard_overlay.html deleted file mode 100644 index 7e6c047..0000000 --- a/chrome/browser/resources/options2/chromeos/keyboard_overlay.html +++ /dev/null @@ -1,52 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/keyboard_overlay.js b/chrome/browser/resources/options2/chromeos/keyboard_overlay.js deleted file mode 100644 index 45b6859..0000000 --- a/chrome/browser/resources/options2/chromeos/keyboard_overlay.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - /** - * Encapsulated handling of the keyboard overlay. - * @constructor - */ - function KeyboardOverlay() { - options.SettingsDialog.call(this, 'keyboard-overlay', - loadTimeData.getString('keyboardOverlayTitle'), - 'keyboard-overlay', - $('keyboard-confirm'), $('keyboard-cancel')); - } - - cr.addSingletonGetter(KeyboardOverlay); - - KeyboardOverlay.prototype = { - __proto__: options.SettingsDialog.prototype, - }; - - // Export - return { - KeyboardOverlay: KeyboardOverlay - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/language_chewing_options.html b/chrome/browser/resources/options2/chromeos/language_chewing_options.html deleted file mode 100644 index f5cd74c..0000000 --- a/chrome/browser/resources/options2/chromeos/language_chewing_options.html +++ /dev/null @@ -1,147 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/language_hangul_options.html b/chrome/browser/resources/options2/chromeos/language_hangul_options.html deleted file mode 100644 index 1a8d094..0000000 --- a/chrome/browser/resources/options2/chromeos/language_hangul_options.html +++ /dev/null @@ -1,24 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/language_mozc_options.html b/chrome/browser/resources/options2/chromeos/language_mozc_options.html deleted file mode 100644 index 6f3aec2..0000000 --- a/chrome/browser/resources/options2/chromeos/language_mozc_options.html +++ /dev/null @@ -1,141 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/language_pinyin_options.html b/chrome/browser/resources/options2/chromeos/language_pinyin_options.html deleted file mode 100644 index 68d5013..0000000 --- a/chrome/browser/resources/options2/chromeos/language_pinyin_options.html +++ /dev/null @@ -1,154 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/network_list.js b/chrome/browser/resources/options2/chromeos/network_list.js deleted file mode 100644 index 4408828..0000000 --- a/chrome/browser/resources/options2/chromeos/network_list.js +++ /dev/null @@ -1,1098 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.network', function() { - - var ArrayDataModel = cr.ui.ArrayDataModel; - var List = cr.ui.List; - var ListItem = cr.ui.ListItem; - var Menu = cr.ui.Menu; - var MenuItem = cr.ui.MenuItem; - var ControlledSettingIndicator = options.ControlledSettingIndicator; - - /** - * Network settings constants. These enums usually match their C++ - * counterparts. - */ - function Constants() {} - - // Network types: - Constants.TYPE_UNKNOWN = 0; - Constants.TYPE_ETHERNET = 1; - Constants.TYPE_WIFI = 2; - Constants.TYPE_WIMAX = 3; - Constants.TYPE_BLUETOOTH = 4; - Constants.TYPE_CELLULAR = 5; - Constants.TYPE_VPN = 6; - - // Cellular activation states: - Constants.ACTIVATION_STATE_UNKNOWN = 0; - Constants.ACTIVATION_STATE_ACTIVATED = 1; - Constants.ACTIVATION_STATE_ACTIVATING = 2; - Constants.ACTIVATION_STATE_NOT_ACTIVATED = 3; - Constants.ACTIVATION_STATE_PARTIALLY_ACTIVATED = 4; - - /** - * Order in which controls are to appear in the network list sorted by key. - */ - Constants.NETWORK_ORDER = ['ethernet', - 'wifi', - 'wimax', - 'cellular', - 'vpn', - 'airplaneMode', - 'addConnection']; - - /** - * Mapping of network category titles to the network type. - */ - var categoryMap = { - 'cellular': Constants.TYPE_CELLULAR, - 'ethernet': Constants.TYPE_ETHERNET, - 'wimax': Constants.TYPE_WIMAX, - 'wifi': Constants.TYPE_WIFI, - 'vpn': Constants.TYPE_VPN - }; - - /** - * ID of the menu that is currently visible. - * @type {?string} - * @private - */ - var activeMenu_ = null; - - /** - * Indicates if cellular networks are available. - * @type {boolean} - * @private - */ - var cellularAvailable_ = false; - - /** - * Indicates if cellular networks are enabled. - * @type {boolean} - * @private - */ - var cellularEnabled_ = false; - - /** - * Indicates if WiMAX networks are available. - * @type {boolean} - * @private - */ - var wimaxAvailable_ = false; - - /** - * Indicates if WiMAX networks are enabled. - * @type {boolean} - * @private - */ - var wimaxEnabled_ = false; - - /** - * Indicates if mobile data roaming is enabled. - * @type {boolean} - * @private - */ - var enableDataRoaming_ = false; - - /** - * Create an element in the network list for controlling network - * connectivity. - * @param {Object} data Description of the network list or command. - * @constructor - */ - function NetworkListItem(data) { - var el = cr.doc.createElement('li'); - el.data_ = {}; - for (var key in data) - el.data_[key] = data[key]; - NetworkListItem.decorate(el); - return el; - } - - /** - * Decorate an element as a NetworkListItem. - * @param {!Element} el The element to decorate. - */ - NetworkListItem.decorate = function(el) { - el.__proto__ = NetworkListItem.prototype; - el.decorate(); - }; - - NetworkListItem.prototype = { - __proto__: ListItem.prototype, - - /** - * Description of the network group or control. - * @type {Object.} - * @private - */ - data_: null, - - /** - * Element for the control's subtitle. - * @type {?Element} - * @private - */ - subtitle_: null, - - /** - * Icon for the network control. - * @type {?Element} - * @private - */ - icon_: null, - - /** - * Indicates if in the process of connecting to a network. - * @type {boolean} - * @private - */ - connecting_: false, - - /** - * Description of the network control. - * @type {Object} - */ - get data() { - return this.data_; - }, - - /** - * Text label for the subtitle. - * @type {string} - */ - set subtitle(text) { - if (text) - this.subtitle_.textContent = text; - this.subtitle_.hidden = !text; - }, - - /** - * URL for the network icon. - * @type {string} - */ - set iconURL(iconURL) { - this.icon_.style.backgroundImage = url(iconURL); - }, - - /** - * Type of network icon. Each type corresponds to a CSS rule. - * @type {string} - */ - set iconType(type) { - this.icon_.classList.add('network-' + type); - }, - - /** - * Indicates if the network is in the process of being connected. - * @type {boolean} - */ - set connecting(state) { - this.connecting_ = state; - if (state) - this.icon_.classList.add('network-connecting'); - else - this.icon_.classList.remove('network-connecting'); - }, - - /** - * Indicates if the network is in the process of being connected. - * @type {boolean} - */ - get connecting() { - return this.connecting_; - }, - - /** - * Set the direction of the text. - * @param {string} direction The direction of the text, e.g. 'ltr'. - */ - setSubtitleDirection: function(direction) { - this.subtitle_.dir = direction; - }, - - /** - * Indicate that the selector arrow should be shown. - */ - showSelector: function() { - this.subtitle_.classList.add('network-selector'); - }, - - /** - * Adds an indicator to show that the network is policy managed. - */ - showManagedNetworkIndicator: function() { - this.appendChild(new ManagedNetworkIndicator()); - }, - - /* @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - this.className = 'network-group'; - this.icon_ = this.ownerDocument.createElement('div'); - this.icon_.className = 'network-icon'; - this.appendChild(this.icon_); - var textContent = this.ownerDocument.createElement('div'); - textContent.className = 'network-group-labels'; - this.appendChild(textContent); - var categoryLabel = this.ownerDocument.createElement('div'); - var title = this.data_.key + 'Title'; - categoryLabel.className = 'network-title'; - categoryLabel.textContent = loadTimeData.getString(title); - textContent.appendChild(categoryLabel); - this.subtitle_ = this.ownerDocument.createElement('div'); - this.subtitle_.className = 'network-subtitle'; - textContent.appendChild(this.subtitle_); - }, - }; - - /** - * Creates a control that displays a popup menu when clicked. - * @param {Object} data Description of the control. - */ - function NetworkMenuItem(data) { - var el = new NetworkListItem(data); - el.__proto__ = NetworkMenuItem.prototype; - el.decorate(); - return el; - } - - NetworkMenuItem.prototype = { - __proto__: NetworkListItem.prototype, - - /** - * Popup menu element. - * @type {?Element} - * @private - */ - menu_: null, - - /* @inheritDoc */ - decorate: function() { - this.subtitle = null; - if (this.data.iconType) - this.iconType = this.data.iconType; - this.addEventListener('click', function() { - this.showMenu(); - }); - }, - - /** - * Retrieves the ID for the menu. - * @private - */ - getMenuName_: function() { - return this.data_.key + '-network-menu'; - }, - - /** - * Creates a popup menu for the control. - * @return {Element} The newly created menu. - */ - createMenu: function() { - if (this.data.menu) { - var menu = this.ownerDocument.createElement('div'); - menu.id = this.getMenuName_(); - menu.className = 'network-menu'; - menu.hidden = true; - Menu.decorate(menu); - for (var i = 0; i < this.data.menu.length; i++) { - var entry = this.data.menu[i]; - var button = this.ownerDocument.createElement('div'); - button.className = 'network-menu-item'; - var buttonLabel = this.ownerDocument.createElement('div'); - buttonLabel.className = 'network-menu-item-label'; - buttonLabel.textContent = entry.label; - button.appendChild(buttonLabel); - button.addEventListener('click', entry.command); - MenuItem.decorate(button); - menu.appendChild(button); - } - return menu; - } - return null; - }, - - canUpdateMenu: function() { - return false; - }, - - /** - * Displays a popup menu. - */ - showMenu: function() { - var rebuild = false; - // Force a rescan if opening the menu for WiFi networks to ensure the - // list is up to date. Networks are periodically rescanned, but depending - // on timing, there could be an excessive delay before the first rescan - // unless forced. - var rescan = !activeMenu_ && this.data_.key == 'wifi'; - if (!this.menu_) { - rebuild = true; - var existing = $(this.getMenuName_()); - if (existing) { - if (this.updateMenu()) - return; - closeMenu_(); - } - this.menu_ = this.createMenu(); - this.menu_.addEventListener('mousedown', function(e) { - // Prevent blurring of list, which would close the menu. - e.preventDefault(); - }, true); - var parent = $('network-menus'); - if (existing) - parent.replaceChild(this.menu_, existing); - else - parent.appendChild(this.menu_); - } - var top = this.offsetTop + this.clientHeight; - var menuId = this.getMenuName_(); - if (menuId != activeMenu_ || rebuild) { - closeMenu_(); - activeMenu_ = menuId; - this.menu_.style.setProperty('top', top + 'px'); - this.menu_.hidden = false; - } - if (rescan) - chrome.send('refreshNetworks'); - }, - }; - - /** - * Creates a control for selecting or configuring a network connection based - * on the type of connection (e.g. wifi versus vpn). - * @param {{key: string, - * networkList: Array.} data Description of the network. - * @constructor - */ - function NetworkSelectorItem(data) { - var el = new NetworkMenuItem(data); - el.__proto__ = NetworkSelectorItem.prototype; - el.decorate(); - return el; - } - - NetworkSelectorItem.prototype = { - __proto__: NetworkMenuItem.prototype, - - /* @inheritDoc */ - decorate: function() { - // TODO(kevers): Generalize method of setting default label. - var policyManaged = false; - var defaultMessage = this.data_.key == 'wifi' ? - 'networkOffline' : 'networkNotConnected'; - this.subtitle = loadTimeData.getString(defaultMessage); - var list = this.data_.networkList; - var candidateURL = null; - for (var i = 0; i < list.length; i++) { - var networkDetails = list[i]; - if (networkDetails.connecting || networkDetails.connected) { - this.subtitle = networkDetails.networkName; - this.setSubtitleDirection('ltr'); - policyManaged = networkDetails.policyManaged; - candidateURL = networkDetails.iconURL; - // Only break when we see a connecting network as it is possible to - // have a connected network and a connecting network at the same - // time. - if (networkDetails.connecting) { - this.connecting = true; - candidateURL = null; - break; - } - } - } - if (candidateURL) - this.iconURL = candidateURL; - else - this.iconType = this.data.key; - - this.showSelector(); - - if (policyManaged) - this.showManagedNetworkIndicator(); - - if (activeMenu_ == this.getMenuName_()) { - // Menu is already showing and needs to be updated. Explicitly calling - // show menu will force the existing menu to be replaced. The call - // is deferred in order to ensure that position of this element has - // beem properly updated. - var self = this; - setTimeout(function() {self.showMenu();}, 0); - } - }, - - /** - * Creates a menu for selecting, configuring or disconnecting from a - * network. - * @return {Element} The newly created menu. - */ - createMenu: function() { - var menu = this.ownerDocument.createElement('div'); - menu.id = this.getMenuName_(); - menu.className = 'network-menu'; - menu.hidden = true; - Menu.decorate(menu); - var addendum = []; - if (this.data_.key == 'wifi') { - addendum.push({label: loadTimeData.getString('joinOtherNetwork'), - command: 'connect', - data: {networkType: Constants.TYPE_WIFI, - servicePath: '?'}}); - } else if (this.data_.key == 'cellular') { - var label = enableDataRoaming_ ? 'disableDataRoaming' : - 'enableDataRoaming'; - var disabled = !UIAccountTweaks.currentUserIsOwner(); - var entry = {label: loadTimeData.getString(label), - data: {}}; - if (disabled) { - entry.command = null; - entry.tooltip = - loadTimeData.getString('dataRoamingDisableToggleTooltip'); - } else { - entry.command = function() { - options.Preferences.setBooleanPref( - 'cros.signed.data_roaming_enabled', - !enableDataRoaming_); - // Force revalidation of the menu the next time it is displayed. - this.menu_ = null; - }; - } - addendum.push(entry); - } - var list = this.data.rememberedNetworks; - if (list && list.length > 0) { - var callback = function(list) { - $('remembered-network-list').clear(); - var dialog = options.PreferredNetworks.getInstance(); - OptionsPage.showPageByName('preferredNetworksPage', false); - dialog.update(list); - }; - addendum.push({label: loadTimeData.getString('preferredNetworks'), - command: callback, - data: list}); - } - - var networkGroup = this.ownerDocument.createElement('div'); - networkGroup.className = 'network-menu-group'; - list = this.data.networkList; - var empty = !list || list.length == 0; - if (list) { - for (var i = 0; i < list.length; i++) { - var data = list[i]; - this.createNetworkOptionsCallback_(networkGroup, data); - if (data.connected) { - if (data.networkType == Constants.TYPE_VPN) { - // Add separator - addendum.push({}); - var i18nKey = 'disconnectNetwork'; - addendum.push({label: loadTimeData.getString(i18nKey), - command: 'disconnect', - data: data}); - } - } - } - } - if (this.data_.key == 'wifi' || this.data_.key == 'wimax' || - this.data_.key == 'cellular') { - addendum.push({}); - if (this.data_.key == 'wifi') { - addendum.push({label: loadTimeData.getString('turnOffWifi'), - command: function() { - chrome.send('disableWifi'); - }, - data: {}}); - } else if (this.data_.key == 'wimax') { - // TODO(zelidrag): Add proper strings for wimax. - addendum.push({label: loadTimeData.getString('turnOffCellular'), - command: function() { - chrome.send('disableWimax'); - }, - data: {}}); - } else if (this.data_.key == 'cellular') { - addendum.push({label: loadTimeData.getString('turnOffCellular'), - command: function() { - chrome.send('disableCellular'); - }, - data: {}}); - } - } - if (!empty) - menu.appendChild(networkGroup); - if (addendum.length > 0) { - var separator = false; - if (!empty) { - menu.appendChild(MenuItem.createSeparator()); - separator = true; - } - for (var i = 0; i < addendum.length; i++) { - var value = addendum[i]; - if (value.data) { - var item = this.createCallback_(menu, value.data, value.label, - value.command); - if (value.tooltip) - item.title = value.tooltip; - separator = false; - } else if (!separator) { - menu.appendChild(MenuItem.createSeparator()); - separator = true; - } - } - } - return menu; - }, - - /** - * Determines if a menu can be updated on the fly. Menus that cannot be - * updated are fully regenerated using createMenu. The advantage of - * updating a menu is that it can preserve ordering of networks avoiding - * entries from jumping around after an update. - */ - canUpdateMenu: function() { - return this.data_.key == 'wifi' && activeMenu_ == this.getMenuName_(); - }, - - /** - * Updates an existing menu. Updated menus preserve ordering of prior - * entries. During the update process, the ordering may differ from the - * preferred ordering as determined by the network library. If the - * ordering becomes potentially out of sync, then the updated menu is - * marked for disposal on close. Reopening the menu will force a - * regeneration, which will in turn fix the ordering. - * @return {boolean} True if successfully updated. - */ - updateMenu: function() { - if (!this.canUpdateMenu()) - return false; - var oldMenu = $(this.getMenuName_()); - var group = oldMenu.getElementsByClassName('network-menu-group')[0]; - if (!group) - return false; - var newMenu = this.createMenu(); - var discardOnClose = false; - var oldNetworkButtons = this.extractNetworkConnectButtons_(oldMenu); - var newNetworkButtons = this.extractNetworkConnectButtons_(newMenu); - for (var key in oldNetworkButtons) { - if (newNetworkButtons[key]) { - group.replaceChild(newNetworkButtons[key].button, - oldNetworkButtons[key].button); - if (newNetworkButtons[key].index != oldNetworkButtons[key].index) - discardOnClose = true; - newNetworkButtons[key] = null; - } else { - // Leave item in list to prevent network items from jumping due to - // deletions. - oldNetworkButtons[key].disabled = true; - discardOnClose = true; - } - } - for (var key in newNetworkButtons) { - var entry = newNetworkButtons[key]; - if (entry) { - group.appendChild(entry.button); - discardOnClose = true; - } - } - oldMenu.data = {discardOnClose: discardOnClose}; - return true; - }, - - /** - * Extracts a mapping of network names to menu element and position. - * @param {!Element} menu The menu to process. - * @return {Object.} Network mapping. - * @private - */ - extractNetworkConnectButtons_: function(menu) { - var group = menu.getElementsByClassName('network-menu-group')[0]; - var networkButtons = {}; - if (!group) - return networkButtons; - var buttons = group.getElementsByClassName('network-menu-item'); - for (var i = 0; i < buttons.length; i++) { - var label = buttons[i].data.label; - networkButtons[label] = {index: i, button: buttons[i]}; - } - return networkButtons; - }, - - /** - * Adds a command to a menu for modifying network settings. - * @param {!Element} menu Parent menu. - * @param {Object} data Description of the network. - * @param {string} label Display name for the menu item. - * @param {string|function} command Callback function or name - * of the command for |networkCommand|. - * @return {!Element} The created menu item. - * @private - */ - createCallback_: function(menu, data, label, command) { - var button = this.ownerDocument.createElement('div'); - button.className = 'network-menu-item'; - var buttonLabel = this.ownerDocument.createElement('span'); - buttonLabel.className = 'network-menu-item-label'; - buttonLabel.textContent = label; - button.appendChild(buttonLabel); - var callback = null; - if (typeof command == 'string') { - var type = String(data.networkType); - var path = data.servicePath; - callback = function() { - chrome.send('networkCommand', - [type, path, command]); - closeMenu_(); - }; - } else if (command != null) { - callback = function() { - command(data); - closeMenu_(); - }; - } - if (callback != null) - button.addEventListener('click', callback); - else - buttonLabel.classList.add('network-disabled-control'); - - button.data = {label: label}; - MenuItem.decorate(button); - menu.appendChild(button); - return button; - }, - - /** - * Adds a menu item for showing network details. - * @param {!Element} parent The parent element. - * @param {Object} data Description of the network. - * @private - */ - createNetworkOptionsCallback_: function(parent, data) { - var menuItem = this.createCallback_(parent, - data, - data.networkName, - 'options'); - menuItem.style.backgroundImage = url(data.iconURL); - if (data.policyManaged) - menuItem.appendChild(new ManagedNetworkIndicator()); - if (data.connected || data.connecting) { - var label = menuItem.getElementsByClassName( - 'network-menu-item-label')[0]; - label.classList.add('active-network'); - } - } - }; - - /** - * Creates a button-like control for configurating internet connectivity. - * @param {{key: string, - * subtitle: string, - * command: function} data Description of the network control. - * @constructor - */ - function NetworkButtonItem(data) { - var el = new NetworkListItem(data); - el.__proto__ = NetworkButtonItem.prototype; - el.decorate(); - return el; - } - - NetworkButtonItem.prototype = { - __proto__: NetworkListItem.prototype, - - /** @inheritDoc */ - decorate: function() { - if (this.data.subtitle) - this.subtitle = this.data.subtitle; - else - this.subtitle = null; - if (this.data.command) - this.addEventListener('click', this.data.command); - if (this.data.iconURL) - this.iconURL = this.data.iconURL; - else if (this.data.iconType) - this.iconType = this.data.iconType; - }, - }; - - /** - * A list of controls for manipulating network connectivity. - * @constructor - */ - var NetworkList = cr.ui.define('list'); - - NetworkList.prototype = { - __proto__: List.prototype, - - /** @inheritDoc */ - decorate: function() { - List.prototype.decorate.call(this); - this.startBatchUpdates(); - this.autoExpands = true; - this.addEventListener('blur', this.onBlur_); - this.dataModel = new ArrayDataModel([]); - - // Wi-Fi control is always visible. - this.update({key: 'wifi', networkList: []}); - - if (airplaneModeAvailable_()) { - this.update({key: 'airplaneMode', - subtitle: loadTimeData.getString('airplaneModeLabel'), - command: function() { - chrome.send('toggleAirplaneMode'); - }}); - } - - // Add connection control. - var addConnection = function(type) { - var callback = function() { - chrome.send('networkCommand', - [String(type), '?', 'connect']); - } - return callback; - } - this.update({key: 'addConnection', - iconType: 'add-connection', - menu: [{label: loadTimeData.getString('addConnectionWifi'), - command: addConnection(Constants.TYPE_WIFI)}, - {label: loadTimeData.getString('addConnectionVPN'), - command: addConnection(Constants.TYPE_VPN)}] - }); - - var prefs = options.Preferences.getInstance(); - prefs.addEventListener('cros.signed.data_roaming_enabled', - function(event) { - enableDataRoaming_ = event.value.value; - }); - this.endBatchUpdates(); - }, - - /** - * When the list loses focus, unselect all items in the list and close the - * active menu. - * @private - */ - onBlur_: function() { - this.selectionModel.unselectAll(); - closeMenu_(); - }, - - /** - * Finds the index of a network item within the data model based on - * category. - * @param {string} key Unique key for the item in the list. - * @return {number} The index of the network item, or |undefined| if it is - * not found. - */ - indexOf: function(key) { - var size = this.dataModel.length; - for (var i = 0; i < size; i++) { - var entry = this.dataModel.item(i); - if (entry.key == key) - return i; - } - }, - - /** - * Updates a network control. - * @param {Object.} data Description of the entry. - */ - update: function(data) { - this.startBatchUpdates(); - var index = this.indexOf(data.key); - if (index == undefined) { - // Find reference position for adding the element. We cannot hide - // individual list elements, thus we need to conditionally add or - // remove elements and cannot rely on any element having a fixed index. - for (var i = 0; i < Constants.NETWORK_ORDER.length; i++) { - if (data.key == Constants.NETWORK_ORDER[i]) { - data.sortIndex = i; - break; - } - } - var referenceIndex = -1; - for (var i = 0; i < this.dataModel.length; i++) { - var entry = this.dataModel.item(i); - if (entry.sortIndex < data.sortIndex) - referenceIndex = i; - else - break; - } - if (referenceIndex == -1) { - // Prepend to the start of the list. - this.dataModel.splice(0, 0, data); - } else if (referenceIndex == this.dataModel.length) { - // Append to the end of the list. - this.dataModel.push(data); - } else { - // Insert after the reference element. - this.dataModel.splice(referenceIndex + 1, 0, data); - } - } else { - var entry = this.dataModel.item(index); - data.sortIndex = entry.sortIndex; - this.dataModel.splice(index, 1, data); - } - this.endBatchUpdates(); - }, - - /** @inheritDoc */ - createItem: function(entry) { - if (entry.networkList) - return new NetworkSelectorItem(entry); - if (entry.command) - return new NetworkButtonItem(entry); - if (entry.menu) - return new NetworkMenuItem(entry); - }, - - /** - * Deletes an element from the list. - * @param {string} key Unique identifier for the element. - */ - deleteItem: function(key) { - var index = this.indexOf(key); - if (index != undefined) - this.dataModel.splice(index, 1); - }, - - /** - * Updates the state of a toggle button. - * @param {string} key Unique identifier for the element. - * @param {boolean} active Whether the control is active. - */ - updateToggleControl: function(key, active) { - var index = this.indexOf(key); - if (index != undefined) { - var entry = this.dataModel.item(index); - entry.iconType = active ? 'control-active' : - 'control-inactive'; - this.update(entry); - } - } - }; - - /** - * Chrome callback for updating network controls. - * @param {Object} data Description of available network devices and their - * corresponding state. - */ - NetworkList.refreshNetworkData = function(data) { - var networkList = $('network-list'); - networkList.startBatchUpdates(); - cellularAvailable_ = data.cellularAvailable; - cellularEnabled_ = data.cellularEnabled; - wimaxAvailable_ = data.wimaxAvailable; - wimaxEnabled_ = data.wimaxEnabled; - - if (data.accessLocked) { - $('network-locked-message').hidden = false; - networkList.disabled = true; - $('use-shared-proxies').disabled = true; - } else { - $('network-locked-message').hidden = true; - networkList.disabled = false; - $('use-shared-proxies').disabled = false; - } - - // Only show Ethernet control if connected. - var ethernetConnection = getConnection_(data.wiredList); - if (ethernetConnection) { - var type = String(Constants.TYPE_ETHERNET); - var path = ethernetConnection.servicePath; - var ethernetOptions = function() { - chrome.send('networkCommand', - [type, path, 'options']); - }; - networkList.update({key: 'ethernet', - subtitle: loadTimeData.getString('networkConnected'), - iconURL: ethernetConnection.iconURL, - command: ethernetOptions}); - } else { - networkList.deleteItem('ethernet'); - } - - if (data.wifiEnabled) { - loadData_('wifi', data.wirelessList, data.rememberedList); - } else { - var enableWifi = function() { - chrome.send('enableWifi'); - }; - networkList.update({key: 'wifi', - subtitle: loadTimeData.getString('networkDisabled'), - iconType: 'wifi', - command: enableWifi}); - } - - // Only show cellular control if available and not in airplane mode. - if (data.cellularAvailable && !data.airplaneMode) { - if (data.cellularEnabled) { - loadData_('cellular', data.wirelessList, data.rememberedList); - } else { - var subtitle = loadTimeData.getString('networkDisabled'); - var enableCellular = function() { - chrome.send('enableCellular'); - }; - networkList.update({key: 'cellular', - subtitle: subtitle, - iconType: 'cellular', - command: enableCellular}); - } - } else { - networkList.deleteItem('cellular'); - } - - // Only show cellular control if available and not in airplane mode. - if (data.wimaxAvailable && !data.airplaneMode) { - if (data.wimaxEnabled) { - loadData_('wimax', data.wirelessList, data.rememberedList); - } else { - var subtitle = loadTimeData.getString('networkDisabled'); - var enableWimax = function() { - chrome.send('enableWimax'); - }; - networkList.update({key: 'wimax', - subtitle: subtitle, - iconType: 'cellular', - command: enableWimax}); - } - } else { - networkList.deleteItem('wimax'); - } - - // Only show VPN control if there is an available network and an internet - // connection. - if (data.vpnList.length > 0 && (ethernetConnection || - isConnected_(data.wirelessList))) - loadData_('vpn', data.vpnList, data.rememberedList); - else - networkList.deleteItem('vpn'); - networkList.updateToggleControl('airplaneMode', data.airplaneMode); - networkList.endBatchUpdates(); - }; - - /** - * Element for indicating a policy managed network. - * @constructor - */ - function ManagedNetworkIndicator() { - var el = cr.doc.createElement('div'); - el.__proto__ = ManagedNetworkIndicator.prototype; - el.decorate(); - return el; - } - - ManagedNetworkIndicator.prototype = { - __proto__: ControlledSettingIndicator.prototype, - - /** @inheritDoc */ - decorate: function() { - ControlledSettingIndicator.prototype.decorate.call(this); - this.controlledBy = 'policy'; - var policyLabel = loadTimeData.getString('managedNetwork'); - this.setAttribute('textPolicy', policyLabel); - this.className = 'controlled-setting-indicator'; - // The default popup clips to the bounds of the list of networks in the - // drop-down because it has enforced size constraints with auto- - // scrolling. Use a tooltip in place of the bubble popup until the - // clipping issues are resolved. - this.setAttribute('title', policyLabel); - this.addEventListener('click', function(e) { - e.preventDefault(); - e.stopPropagation(); - }); - } - }; - - /** - * Updates the list of available networks and their status, filtered by - * network type. - * @param {string} category The type of network. - * @param {Array} available The list of available networks and their status. - * @param {Array} remembered The list of remmebered networks. - */ - function loadData_(category, available, remembered) { - var data = {key: category}; - var type = categoryMap[category]; - var availableNetworks = []; - for (var i = 0; i < available.length; i++) { - if (available[i].networkType == type) - availableNetworks.push(available[i]); - } - data.networkList = availableNetworks; - if (remembered) { - var rememberedNetworks = []; - for (var i = 0; i < remembered.length; i++) { - if (remembered[i].networkType == type) - rememberedNetworks.push(remembered[i]); - } - data.rememberedNetworks = rememberedNetworks; - } - $('network-list').update(data); - } - - /** - * Hides the currently visible menu. - * @private - */ - function closeMenu_() { - if (activeMenu_) { - var menu = $(activeMenu_); - menu.hidden = true; - if (menu.data && menu.data.discardOnClose) - menu.parentNode.removeChild(menu); - activeMenu_ = null; - } - } - - /** - * Determines if the user is connected to or in the process of connecting to - * a wireless network. - * @param {Array.} networkList List of networks. - * @return {boolean} True if connected or connecting to a network. - * @private - */ - function isConnected_(networkList) { - return getConnection_(networkList) != null; - } - - /** - * Fetches the active connection. - * @param {Array.} networkList List of networks. - * @return {boolean} True if connected or connecting to a network. - * @private - */ - function getConnection_(networkList) { - if (!networkList) - return null; - for (var i = 0; i < networkList.length; i++) { - var entry = networkList[i]; - if (entry.connected || entry.connecting) - return entry; - } - return null; - } - - /** - * Queries if airplane mode is available. - * @return {boolean} Indicates if airplane mode is available. - * @private - */ - function airplaneModeAvailable_() { - // TODO(kevers): Use library callback to determine if airplane mode is - // available once back-end suport is in place. - return false; - } - - /** - * Whether the Network list is disabled. Only used for display purpose. - * @type {boolean} - */ - cr.defineProperty(NetworkList, 'disabled', cr.PropertyKind.BOOL_ATTR); - - // Export - return { - NetworkList: NetworkList - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/pointer_overlay.css b/chrome/browser/resources/options2/chromeos/pointer_overlay.css deleted file mode 100644 index 66924c2..0000000 --- a/chrome/browser/resources/options2/chromeos/pointer_overlay.css +++ /dev/null @@ -1,7 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#pointer-overlay > .content-area > :not([hidden]) + :nth-child(2) { - margin-top: 20px; -} diff --git a/chrome/browser/resources/options2/chromeos/pointer_overlay.html b/chrome/browser/resources/options2/chromeos/pointer_overlay.html deleted file mode 100644 index 706ca9e..0000000 --- a/chrome/browser/resources/options2/chromeos/pointer_overlay.html +++ /dev/null @@ -1,41 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/pointer_overlay.js b/chrome/browser/resources/options2/chromeos/pointer_overlay.js deleted file mode 100644 index eaeaf94..0000000 --- a/chrome/browser/resources/options2/chromeos/pointer_overlay.js +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var SettingsDialog = options.SettingsDialog; - - /** - * PointerOverlay class - * Dialog that allows users to set pointer settings (touchpad/mouse). - * @extends {SettingsDialog} - */ - function PointerOverlay() { - // The title is updated dynamically in the setTitle method as pointer - // devices are discovered or removed. - SettingsDialog.call(this, 'pointer-overlay', - '', 'pointer-overlay', - $('pointer-overlay-confirm'), - $('pointer-overlay-cancel')); - } - - cr.addSingletonGetter(PointerOverlay); - - PointerOverlay.prototype = { - __proto__: SettingsDialog.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - SettingsDialog.prototype.initializePage.call(this); - }, - }; - - /** - * Sets the visibility state of the touchpad group. - * @param {boolean} show True to show, false to hide. - */ - PointerOverlay.showTouchpadControls = function(show) { - $('pointer-section-touchpad').hidden = !show; - }; - - /** - * Sets the visibility state of the mouse group. - * @param {boolean} show True to show, false to hide. - */ - PointerOverlay.showMouseControls = function(show) { - $('pointer-section-mouse').hidden = !show; - }; - - /** - * Updates the title of the pointer dialog. The title is set dynamically - * based on whether a touchpad, mouse or both are present. The label on the - * button that activates the overlay is also updated to stay in sync. A - * message is displayed in the main settings page if no pointer devices are - * available. - * @param {String} label i18n key for the overlay title. - */ - PointerOverlay.setTitle = function(label) { - var header = $('pointer-overlay-title'); - var button = $('pointer-settings-button'); - var noPointersLabel = $('no-pointing-devices'); - if (label.length > 0) { - var title = loadTimeData.getString(label); - header.textContent = title; - button.textContent = title; - button.hidden = false; - noPointersLabel.hidden = true; - } else { - header.textContent = ''; - button.hidden = true; - noPointersLabel.hidden = false; - } - }; - - // Export - return { - PointerOverlay: PointerOverlay - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/preferred_networks.html b/chrome/browser/resources/options2/chromeos/preferred_networks.html deleted file mode 100644 index d7f112e..0000000 --- a/chrome/browser/resources/options2/chromeos/preferred_networks.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/preferred_networks.js b/chrome/browser/resources/options2/chromeos/preferred_networks.js deleted file mode 100644 index 6c875d7..0000000 --- a/chrome/browser/resources/options2/chromeos/preferred_networks.js +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - var ArrayDataModel = cr.ui.ArrayDataModel; - var DeletableItem = options.DeletableItem; - var DeletableItemList = options.DeletableItemList; - - ///////////////////////////////////////////////////////////////////////////// - // NetworkPreferences class: - - /** - * Encapsulated handling of ChromeOS network preferences page. - * @constructor - */ - function PreferredNetworks(model) { - OptionsPage.call(this, - 'preferredNetworksPage', - null, - 'preferredNetworksPage'); - } - - cr.addSingletonGetter(PreferredNetworks); - - PreferredNetworks.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initializes the preferred networks page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - PreferredNetworkList.decorate($('remembered-network-list')); - $('preferred-networks-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - update: function(rememberedNetworks) { - var list = $('remembered-network-list'); - list.clear(); - for (var i = 0; i < rememberedNetworks.length; i++) { - list.append(rememberedNetworks[i]); - } - list.redraw(); - } - - }; - - /** - * Creates a list entry for a remembered network. - * @param{{networkName: string, - networkType: string, - servicePath: string}} data - * Description of the network. - * @constructor - */ - function PreferredNetworkListItem(data) { - var el = cr.doc.createElement('div'); - el.__proto__ = PreferredNetworkListItem.prototype; - el.data = {}; - for (var key in data) - el.data[key] = data[key]; - el.decorate(); - return el; - } - - PreferredNetworkListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * Description of the network. - * @type {{networkName: string, - * networkType: string, - * servicePath: string}} - */ - data: null, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - var label = this.ownerDocument.createElement('div'); - label.textContent = this.data.networkName; - if (this.data.policyManaged) - this.deletable = false; - this.contentElement.appendChild(label); - } - }; - - /** - * Class for displaying a list of preferred networks. - * @constructor - * @extends {options.DeletableItemList} - */ - var PreferredNetworkList = cr.ui.define('list'); - - PreferredNetworkList.prototype = { - __proto__: DeletableItemList.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.addEventListener('blur', this.onBlur_); - this.clear(); - }, - - /** - * When the list loses focus, unselect all items in the list. - * @private - */ - onBlur_: function() { - this.selectionModel.unselectAll(); - }, - - /** @inheritDoc */ - createItem: function(entry) { - return new PreferredNetworkListItem(entry); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var item = this.dataModel.item(index); - if (item) { - // Inform the network library that we are forgetting this network. - chrome.send('networkCommand', - [String(item.networkType), - item.servicePath, - 'forget']); - } - this.dataModel.splice(index, 1); - // Invalidate the list since it has a stale cache after a splice - // involving a deletion. - this.invalidate(); - this.redraw(); - }, - - /** - * Purges all networks from the list. - */ - clear: function() { - this.dataModel = new ArrayDataModel([]); - this.redraw(); - }, - - /** - * Adds a remembered network to the list. - * @param {{networkName: string, - networkType: string, - servicePath: string} data - * Description of the network. - */ - append: function(data) { - this.dataModel.push(data); - } - }; - - // Export - return { - PreferredNetworks: PreferredNetworks - }; - -}); - diff --git a/chrome/browser/resources/options2/chromeos/proxy_rules_list.js b/chrome/browser/resources/options2/chromeos/proxy_rules_list.js deleted file mode 100644 index fa7c2ec..0000000 --- a/chrome/browser/resources/options2/chromeos/proxy_rules_list.js +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.proxyexceptions', function() { - /** @const */ var List = cr.ui.List; - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - /** - * Creates a new exception list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {cr.ui.List} - */ - var ProxyExceptions = cr.ui.define('list'); - - ProxyExceptions.prototype = { - __proto__: List.prototype, - - pref: 'cros.session.proxy.ignorelist', - - /** @inheritDoc */ - decorate: function() { - List.prototype.decorate.call(this); - - // HACK(arv): http://crbug.com/40902 - window.addEventListener('resize', this.redraw.bind(this)); - - this.addEventListener('click', this.handleClick_); - - var self = this; - - // Listens to pref changes. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - self.load_(event.value.value); - }); - }, - - createItem: function(exception) { - return new ProxyExceptionsItem(exception); - }, - - /** - * Adds given exception to model and update backend. - * @param {Object} exception A exception to be added to exception list. - */ - addException: function(exception) { - this.dataModel.push(exception); - this.updateBackend_(); - }, - - /** - * Removes given exception from model and update backend. - */ - removeException: function(exception) { - var dataModel = this.dataModel; - - var index = dataModel.indexOf(exception); - if (index >= 0) { - dataModel.splice(index, 1); - this.updateBackend_(); - } - }, - - /** - * Handles the clicks on the list and triggers exception removal if the - * click is on the remove exception button. - * @private - * @param {!Event} e The click event object. - */ - handleClick_: function(e) { - // Handle left button click - if (e.button == 0) { - var el = e.target; - if (el.className == 'remove-exception-button') { - this.removeException(el.parentNode.exception); - } - } - }, - - /** - * Loads given exception list. - * @param {Array} exceptions An array of exception object. - */ - load_: function(exceptions) { - this.dataModel = new ArrayDataModel(exceptions); - }, - - /** - * Updates backend. - */ - updateBackend_: function() { - Preferences.setListPref(this.pref, this.dataModel.slice()); - } - }; - - /** - * Creates a new exception list item. - * @param {Object} exception The exception account this represents. - * @constructor - * @extends {cr.ui.ListItem} - */ - function ProxyExceptionsItem(exception) { - var el = cr.doc.createElement('div'); - el.exception = exception; - ProxyExceptionsItem.decorate(el); - return el; - } - - /** - * Decorates an element as a exception account item. - * @param {!HTMLElement} el The element to decorate. - */ - ProxyExceptionsItem.decorate = function(el) { - el.__proto__ = ProxyExceptionsItem.prototype; - el.decorate(); - }; - - ProxyExceptionsItem.prototype = { - __proto__: ListItem.prototype, - - /** @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - this.className = 'exception-list-item'; - - var labelException = this.ownerDocument.createElement('span'); - labelException.className = ''; - labelException.textContent = this.exception; - this.appendChild(labelException); - } - }; - - return { - ProxyExceptions: ProxyExceptions - }; -}); diff --git a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.css b/chrome/browser/resources/options2/chromeos/set_wallpaper_options.css deleted file mode 100644 index 2732dea..0000000 --- a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.css +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#wallpaper-title span { - -webkit-padding-start: 17px; - padding-top: 6px; -} - -#set-wallpaper-page .content-area { - height: 400px; -} - -#wallpaper-grid { - margin: 0; - overflow: hidden; - width: 580px; -} - -#wallpaper-grid img { - height: 80px; - width: 128px; -} - -.grayout img { - -webkit-filter: opacity(50%); -} - -.bottom-bar { - -webkit-box-orient: vertical; - display: -webkit-box; -} - -.bottom-bar .spacer { - -webkit-box-flex: 1; -} - -.wallpaper-image-attr { - -webkit-padding-start: 34px; - line-height: 26px; - padding-top: 14px; -} - -#wallpaper-author-website { - -webkit-padding-start: 5px; -} - -#wallpaper-action-area { - -webkit-padding-start: 34px; - padding-top: 0; -} diff --git a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.html b/chrome/browser/resources/options2/chromeos/set_wallpaper_options.html deleted file mode 100644 index e47da5b..0000000 --- a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.html +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.js b/chrome/browser/resources/options2/chromeos/set_wallpaper_options.js deleted file mode 100644 index 4f01abb..0000000 --- a/chrome/browser/resources/options2/chromeos/set_wallpaper_options.js +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - var UserImagesGrid = options.UserImagesGrid; - - /** @const */ var CUSTOM_WALLPAPER_PREFIX = - 'chrome://wallpaper-thumb/custom_'; - - ///////////////////////////////////////////////////////////////////////////// - // SetWallpaperOptions class: - - /** - * Encapsulated handling of ChromeOS set wallpaper options page. - * @constructor - */ - function SetWallpaperOptions() { - OptionsPage.call( - this, - 'setWallpaper', - loadTimeData.getString('setWallpaper'), - 'set-wallpaper-page'); - } - - cr.addSingletonGetter(SetWallpaperOptions); - - SetWallpaperOptions.prototype = { - // Inherit SetWallpaperOptions from OptionsPage. - __proto__: options.OptionsPage.prototype, - - /** - * Initializes SetWallpaperOptions page. - */ - initializePage: function() { - // Call base class implementation to start preferences initialization. - OptionsPage.prototype.initializePage.call(this); - - var wallpaperGrid = $('wallpaper-grid'); - UserImagesGrid.decorate(wallpaperGrid); - - wallpaperGrid.addEventListener('change', - this.handleImageSelected_.bind(this)); - wallpaperGrid.addEventListener('dblclick', - this.handleImageDblClick_.bind(this)); - wallpaperGrid.addEventListener('activate', - function() { OptionsPage.closeOverlay() }); - - $('set-wallpaper-layout').addEventListener('change', - this.handleLayoutChange_); - $('set-custom-wallpaper').onclick = this.handleChooseFile_; - $('use-daily-wallpaper').onclick = this.handleCheckboxClick_.bind(this); - $('set-wallpaper-overlay-confirm').onclick = function() { - OptionsPage.closeOverlay(); - }; - - // @type {Array.} - this.wallpapers_ = []; - - // @type {Object} Old user custom wallpaper thumbnail. - this.oldImage_ = null; - - chrome.send('onSetWallpaperPageInitialized'); - }, - - /** - * Called right after the page has been shown to user. - */ - didShowPage: function() { - $('wallpaper-grid').updateAndFocus(); - // A quick hack to fix issue 118472. This is a general problem of list - // control and options overlay. - // TODO(bshe): Remove this hack when we fixed the general problem which - // tracked in issue 118829. - $('wallpaper-grid').redraw(); - chrome.send('onSetWallpaperPageShown'); - }, - - /** - * Called right before the page is hidden. - */ - willHidePage: function() { - var wallpaperGrid = $('wallpaper-grid'); - wallpaperGrid.blur(); - if (this.oldImage_) { - wallpaperGrid.removeItem(this.oldImage_); - this.oldImage_ = null; - } - $('set-wallpaper-layout').innerText = ''; - }, - - /** - * Set attributions of wallpaper with given URL. - * @param {string} url URL of the selected wallpaper. - * @private - */ - setWallpaperAttribution_: function(url) { - for (var i = 0; i < this.wallpapers_.length; i++) { - if (this.wallpapers_[i].url == url) { - $('wallpaper-author-name').textContent = this.wallpapers_[i].author; - $('wallpaper-author-website').textContent = - this.wallpapers_[i].website; - return; - } - } - $('wallpaper-author-name').textContent = ''; - $('wallpaper-author-website').textContent = ''; - }, - - /** - * Populates the drop down box for custom wallpaper layouts. - * param {string} layouts Available wallpaper layouts. - * param {number} selectedLayout The value of selected/default layout. - * @private - */ - populateWallpaperLayouts_: function(layouts, selectedLayout) { - var wallpaperLayout = $('set-wallpaper-layout'); - var selectedIndex = -1; - for (var i = 0; i < layouts.length; i++) { - var option = new Option(layouts[i]['name'], layouts[i]['index']); - if (selectedLayout == option.value) - selectedIndex = i; - wallpaperLayout.appendChild(option); - } - if (selectedIndex >= 0) - wallpaperLayout.selectedIndex = selectedIndex; - }, - - /** - * Handles "Custom..." button activation. - * @private - */ - handleChooseFile_: function() { - chrome.send('chooseWallpaper'); - }, - - /** - * Handle the wallpaper layout setting change. - * @private - */ - handleLayoutChange_: function() { - var setWallpaperLayout = $('set-wallpaper-layout'); - var layout = setWallpaperLayout.options[ - setWallpaperLayout.selectedIndex].value; - chrome.send('changeWallpaperLayout', [layout]); - }, - - /** - * Handles image selection change. - * @private - */ - handleImageSelected_: function() { - var wallpaperGrid = $('wallpaper-grid'); - var url = wallpaperGrid.selectedItemUrl; - if (url && - !wallpaperGrid.inProgramSelection) { - if (url.indexOf(CUSTOM_WALLPAPER_PREFIX) == 0) { - // User custom wallpaper is selected - this.isCustom = true; - // When users select the custom wallpaper thumbnail from picker UI, - // use the saved layout value and redraw the wallpaper. - this.handleLayoutChange_(); - } else { - this.isCustom = false; - chrome.send('selectDefaultWallpaper', [url]); - } - this.setWallpaperAttribution_(url); - } - }, - - /** - * Handles double click on the image grid. - * @param {Event} e Double click Event. - */ - handleImageDblClick_: function(e) { - var wallpaperGrid = $('wallpaper-grid'); - if (wallpaperGrid.disabled) - return; - // Close page unless the click target is the grid itself. - if (e.target instanceof HTMLImageElement) - OptionsPage.closeOverlay(); - }, - - /** - * Handles click on the "I'm feeling lucky" checkbox. - * @private - */ - handleCheckboxClick_: function() { - var wallpaperGrid = $('wallpaper-grid'); - if ($('use-daily-wallpaper').checked) { - wallpaperGrid.disabled = true; - $('wallpaper-attribution-label').hidden = false; - chrome.send('selectDailyWallpaper'); - wallpaperGrid.classList.add('grayout'); - $('set-wallpaper-layout').hidden = true; - } else { - wallpaperGrid.disabled = false; - wallpaperGrid.classList.remove('grayout'); - // Set the wallpaper type to User::DEFAULT. - this.handleImageSelected_(); - } - }, - - /** - * Selects corresponding wallpaper thumbnail with the given URL and toggle - * the "Change wallpaper daily..." checkbox. - * @param {string} url URL of the wallpaper thumbnail to select. - * @param {boolean} isDaily True if user checked "Change wallpaper daily..." - * checkbox. - * @private - */ - setSelectedImage_: function(url, isDaily) { - var wallpaperGrid = $('wallpaper-grid'); - wallpaperGrid.selectedItemUrl = url; - this.setWallpaperAttribution_(url); - if (isDaily) { - // Do not call chrome.send('selectDailyWallpaper'). - $('use-daily-wallpaper').checked = true; - wallpaperGrid.disabled = true; - wallpaperGrid.classList.add('grayout'); - } - }, - - /** - * Appends default images to the image grid. Should only be called once. - * @param {Array.<{author: string, url: string, website: string}>} - * wallpapers An array of wallpaper objects. - * @private - */ - setDefaultImages_: function(wallpapers) { - var wallpaperGrid = $('wallpaper-grid'); - // TODO(bshe): Ideally we should save author and website with the actual - // image (URL) and not use index related storage. This way this data is - // stored in one place rather than depending on the index to be - // consistent. - for (var i = 0, wallpaper; wallpaper = wallpapers[i]; i++) { - this.wallpapers_.push(wallpaper); - wallpaperGrid.addItem(wallpaper.url); - } - }, - - /** - * Display layout drop down box and disable daily mode if enabled. Called - * when user select a valid file from file system. - */ - didSelectFile_: function() { - $('set-wallpaper-layout').hidden = false; - var wallpaperGrid = $('wallpaper-grid'); - if ($('use-daily-wallpaper').checked) { - $('use-daily-wallpaper').checked = false; - wallpaperGrid.disabled = false; - wallpaperGrid.classList.remove('grayout'); - } - }, - - /** - * Returns url of current user's custom wallpaper thumbnail. - * @private - */ - currentWallpaperImageUrl_: function() { - return CUSTOM_WALLPAPER_PREFIX + BrowserOptions.getLoggedInUsername() + - '?id=' + (new Date()).getTime(); - }, - - /** - * Updates the visibility of attribution-label and set-wallpaper-layout. - * @param {boolean} isCustom True if users select custom wallpaper. - */ - set isCustom(isCustom) { - if (isCustom) { - // Clear attributions for custom wallpaper. - $('wallpaper-attribution-label').hidden = true; - // Enable the layout drop down box when custom wallpaper is selected. - $('set-wallpaper-layout').hidden = false; - } else { - $('wallpaper-attribution-label').hidden = false; - $('set-wallpaper-layout').hidden = true; - } - }, - - /** - * Adds or updates custom user wallpaper thumbnail from file. - * @private - */ - setCustomImage_: function() { - var wallpaperGrid = $('wallpaper-grid'); - var url = this.currentWallpaperImageUrl_(); - if (this.oldImage_) { - this.oldImage_ = wallpaperGrid.updateItem(this.oldImage_, url); - } else { - // Insert to the end of wallpaper list. - var pos = wallpaperGrid.length; - this.oldImage_ = wallpaperGrid.addItem(url, undefined, undefined, pos); - } - - this.isCustom = true; - this.setWallpaperAttribution_(''); - wallpaperGrid.selectedItem = this.oldImage_; - }, - }; - - // Forward public APIs to private implementations. - [ - 'setDefaultImages', - 'setSelectedImage', - 'populateWallpaperLayouts', - 'didSelectFile', - 'setCustomImage' - ].forEach(function(name) { - SetWallpaperOptions[name] = function() { - var instance = SetWallpaperOptions.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - // Export - return { - SetWallpaperOptions: SetWallpaperOptions - }; - -}); - diff --git a/chrome/browser/resources/options2/clear_browser_data_overlay.css b/chrome/browser/resources/options2/clear_browser_data_overlay.css deleted file mode 100644 index 0c70109..0000000 --- a/chrome/browser/resources/options2/clear_browser_data_overlay.css +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#clear-browser-data-overlay { - min-width: 500px; -} - -#clear-data-checkboxes { - -webkit-padding-start: 8px; - margin: 5px 0; -} - -#cbd-throbber { - margin: 4px 10px; - vertical-align: middle; - visibility: hidden; -} - -#flash-storage-settings { - padding-top: 5px; -} diff --git a/chrome/browser/resources/options2/clear_browser_data_overlay.html b/chrome/browser/resources/options2/clear_browser_data_overlay.html deleted file mode 100644 index 24a43545..0000000 --- a/chrome/browser/resources/options2/clear_browser_data_overlay.html +++ /dev/null @@ -1,92 +0,0 @@ - diff --git a/chrome/browser/resources/options2/clear_browser_data_overlay.js b/chrome/browser/resources/options2/clear_browser_data_overlay.js deleted file mode 100644 index fa7aadd..0000000 --- a/chrome/browser/resources/options2/clear_browser_data_overlay.js +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - /** - * ClearBrowserDataOverlay class - * Encapsulated handling of the 'Clear Browser Data' overlay page. - * @class - */ - function ClearBrowserDataOverlay() { - OptionsPage.call(this, 'clearBrowserData', - loadTimeData.getString('clearBrowserDataOverlayTabTitle'), - 'clear-browser-data-overlay'); - } - - cr.addSingletonGetter(ClearBrowserDataOverlay); - - ClearBrowserDataOverlay.prototype = { - // Inherit ClearBrowserDataOverlay from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to starts preference initialization. - OptionsPage.prototype.initializePage.call(this); - - var f = this.updateCommitButtonState_.bind(this); - var types = ['browser.clear_data.browsing_history', - 'browser.clear_data.download_history', - 'browser.clear_data.cache', - 'browser.clear_data.cookies', - 'browser.clear_data.passwords', - 'browser.clear_data.form_data', - 'browser.clear_data.hosted_apps_data', - 'browser.clear_data.content_licenses']; - types.forEach(function(type) { - Preferences.getInstance().addEventListener(type, f); - }); - - var checkboxes = document.querySelectorAll( - '#cbd-content-area input[type=checkbox]'); - for (var i = 0; i < checkboxes.length; i++) { - checkboxes[i].onclick = f; - } - this.updateCommitButtonState_(); - - $('clear-browser-data-dismiss').onclick = function(event) { - ClearBrowserDataOverlay.dismiss(); - }; - $('clear-browser-data-commit').onclick = function(event) { - ClearBrowserDataOverlay.setClearingState(true); - chrome.send('performClearBrowserData'); - }; - }, - - // Set the enabled state of the commit button. - updateCommitButtonState_: function() { - var checkboxes = document.querySelectorAll( - '#cbd-content-area input[type=checkbox]'); - var isChecked = false; - for (var i = 0; i < checkboxes.length; i++) { - if (checkboxes[i].checked) { - isChecked = true; - break; - } - } - $('clear-browser-data-commit').disabled = !isChecked; - }, - }; - - // - // Chrome callbacks - // - ClearBrowserDataOverlay.setClearingState = function(state) { - $('delete-browsing-history-checkbox').disabled = state; - $('delete-download-history-checkbox').disabled = state; - $('delete-cache-checkbox').disabled = state; - $('delete-cookies-checkbox').disabled = state; - $('delete-passwords-checkbox').disabled = state; - $('delete-form-data-checkbox').disabled = state; - $('delete-hosted-apps-data-checkbox').disabled = state; - $('deauthorize-content-licenses-checkbox').disabled = state; - $('clear-browser-data-time-period').disabled = state; - $('cbd-throbber').style.visibility = state ? 'visible' : 'hidden'; - $('clear-browser-data-dismiss').disabled = state; - - if (state) - $('clear-browser-data-commit').disabled = true; - else - ClearBrowserDataOverlay.getInstance().updateCommitButtonState_(); - }; - - ClearBrowserDataOverlay.doneClearing = function() { - // The delay gives the user some feedback that the clearing - // actually worked. Otherwise the dialog just vanishes instantly in most - // cases. - window.setTimeout(function() { - ClearBrowserDataOverlay.dismiss(); - }, 200); - }; - - ClearBrowserDataOverlay.dismiss = function() { - OptionsPage.closeOverlay(); - this.setClearingState(false); - }; - - // Export - return { - ClearBrowserDataOverlay: ClearBrowserDataOverlay - }; -}); diff --git a/chrome/browser/resources/options2/content_settings.css b/chrome/browser/resources/options2/content_settings.css deleted file mode 100644 index 53b7ca6..0000000 --- a/chrome/browser/resources/options2/content_settings.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#content-settings-page { - min-width: 600px; -} - -#content-settings-exceptions-area { - min-width: 540px; -} - -.exception-pattern { - -webkit-box-flex: 1; - -webkit-margin-end: 10px; - -webkit-margin-start: 14px; -} - -.exception-setting { - display: inline-block; - width: 120px; -} - -select.exception-setting { - vertical-align: middle; -} - -#exception-column-headers { - -webkit-margin-start: 17px; - display: -webkit-box; - margin-top: 17px; -} - -#exception-column-headers > div { - font-weight: bold; -} - -#exception-pattern-column { - -webkit-box-flex: 1; -} - -#exception-behavior-column { - width: 145px; -} - -.otr-explanation { - font-style: italic; -} - -#content-settings-exceptions-area list { - margin-bottom: 10px; - margin-top: 4px; -} - -#disable-plugins-container { - /* Same as .checkbox and .radio padding. Using padding instead of margin - * to ensure minimum height for tap target. */ - padding: 7px 0; -} - -div[role='listitem'][managedby] { - color: #666; - font-style: italic; - position: relative; -} - -.settings-list div[role='listitem'][managedby='policy'], -.settings-list div[role='listitem'][managedby='extension'] { - background: -webkit-linear-gradient(rgb(250, 230, 146), rgb(250, 230, 146)); - border-bottom: 1px solid rgb(201, 189, 141); - border-top: 0; -} - -list div[role='listitem'][managedby='policy'] .close-button { - background-image: url('chrome://theme/IDR_MANAGED'); - background-size: 16px; - opacity: 1; -} - -list div[role='listitem'][managedby='extension'] .close-button { - background-image: url('chrome://theme/IDR_EXTENSIONS_SECTION_SMALL'); - background-size: 16px; - opacity: 1; -} diff --git a/chrome/browser/resources/options2/content_settings.html b/chrome/browser/resources/options2/content_settings.html deleted file mode 100644 index 03c25e0a..0000000 --- a/chrome/browser/resources/options2/content_settings.html +++ /dev/null @@ -1,356 +0,0 @@ - diff --git a/chrome/browser/resources/options2/content_settings.js b/chrome/browser/resources/options2/content_settings.js deleted file mode 100644 index fd45aca..0000000 --- a/chrome/browser/resources/options2/content_settings.js +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - ////////////////////////////////////////////////////////////////////////////// - // ContentSettings class: - - /** - * Encapsulated handling of content settings page. - * @constructor - */ - function ContentSettings() { - this.activeNavTab = null; - OptionsPage.call(this, 'content', - loadTimeData.getString('contentSettingsPageTabTitle'), - 'content-settings-page'); - } - - cr.addSingletonGetter(ContentSettings); - - ContentSettings.prototype = { - __proto__: OptionsPage.prototype, - - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - chrome.send('getContentFilterSettings'); - - var exceptionsButtons = - this.pageDiv.querySelectorAll('.exceptions-list-button'); - for (var i = 0; i < exceptionsButtons.length; i++) { - exceptionsButtons[i].onclick = function(event) { - var page = ContentSettingsExceptionsArea.getInstance(); - - // Add on the proper hash for the content type, and store that in the - // history so back/forward and tab restore works. - var hash = event.target.getAttribute('contentType'); - var url = page.name + '#' + hash; - window.history.replaceState({pageName: page.name}, - page.title, - '/' + url); - - // Navigate after the history has been replaced in order to have the - // correct hash loaded. - OptionsPage.navigateToPage('contentExceptions'); - - uber.invokeMethodOnParent('setPath', {path: url}); - uber.invokeMethodOnParent('setTitle', - {title: loadTimeData.getString(hash + 'TabTitle')}); - }; - } - - var manageHandlersButton = $('manage-handlers-button'); - if (manageHandlersButton) { - manageHandlersButton.onclick = function(event) { - OptionsPage.navigateToPage('handlers'); - }; - } - - $('manage-galleries-button').onclick = function(event) { - OptionsPage.navigateToPage('manageGalleries'); - }; - - if (cr.isChromeOS) - UIAccountTweaks.applyGuestModeVisibility(document); - - // Cookies filter page --------------------------------------------------- - $('show-cookies-button').onclick = function(event) { - chrome.send('coreOptionsUserMetricsAction', ['Options_ShowCookies']); - OptionsPage.navigateToPage('cookies'); - }; - $('show-app-cookies-button').onclick = function(event) { - OptionsPage.navigateToPage('app-cookies'); - }; - - var intentsSection = $('intents-section'); - if (!loadTimeData.getBoolean('enable_web_intents') && intentsSection) - intentsSection.parentNode.removeChild(intentsSection); - - $('content-settings-overlay-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - - $('pepper-flash-cameramic-section').style.display = 'none'; - $('pepper-flash-cameramic-exceptions-div').style.display = 'none'; - }, - }; - - ContentSettings.updateHandlersEnabledRadios = function(enabled) { - var selector = '#content-settings-page input[type=radio][value=' + - (enabled ? 'allow' : 'block') + '].handler-radio'; - document.querySelector(selector).checked = true; - }; - - /** - * Sets the values for all the content settings radios. - * @param {Object} dict A mapping from radio groups to the checked value for - * that group. - */ - ContentSettings.setContentFilterSettingsValue = function(dict) { - for (var group in dict) { - document.querySelector('input[type=radio][name=' + group + '][value=' + - dict[group]['value'] + ']').checked = true; - var radios = document.querySelectorAll('input[type=radio][name=' + - group + ']'); - var managedBy = dict[group]['managedBy']; - for (var i = 0, len = radios.length; i < len; i++) { - radios[i].disabled = (managedBy != 'default'); - radios[i].controlledBy = managedBy; - } - } - OptionsPage.updateManagedBannerVisibility(); - }; - - /** - * Initializes an exceptions list. - * @param {string} type The content type that we are setting exceptions for. - * @param {Array} list An array of pairs, where the first element of each pair - * is the filter string, and the second is the setting (allow/block). - */ - ContentSettings.setExceptions = function(type, list) { - var exceptionsList = - document.querySelector('div[contentType=' + type + ']' + - ' list[mode=normal]'); - exceptionsList.setExceptions(list); - }; - - ContentSettings.setHandlers = function(list) { - $('handlers-list').setHandlers(list); - }; - - ContentSettings.setIgnoredHandlers = function(list) { - $('ignored-handlers-list').setHandlers(list); - }; - - ContentSettings.setOTRExceptions = function(type, list) { - var exceptionsList = - document.querySelector('div[contentType=' + type + ']' + - ' list[mode=otr]'); - - exceptionsList.parentNode.hidden = false; - exceptionsList.setExceptions(list); - }; - - /** - * The browser's response to a request to check the validity of a given URL - * pattern. - * @param {string} type The content type. - * @param {string} mode The browser mode. - * @param {string} pattern The pattern. - * @param {bool} valid Whether said pattern is valid in the context of - * a content exception setting. - */ - ContentSettings.patternValidityCheckComplete = - function(type, mode, pattern, valid) { - var exceptionsList = - document.querySelector('div[contentType=' + type + '] ' + - 'list[mode=' + mode + ']'); - exceptionsList.patternValidityCheckComplete(pattern, valid); - }; - - /** - * Enables the Pepper Flash camera and microphone settings. - * Please note that whether the settings are actually showed or not is also - * affected by the style class pepper-flash-settings. - */ - ContentSettings.enablePepperFlashCameraMicSettings = function() { - $('pepper-flash-cameramic-section').style.display = ''; - $('pepper-flash-cameramic-exceptions-div').style.display = ''; - } - - // Export - return { - ContentSettings: ContentSettings - }; - -}); diff --git a/chrome/browser/resources/options2/content_settings_exceptions_area.html b/chrome/browser/resources/options2/content_settings_exceptions_area.html deleted file mode 100644 index cbcfd77..0000000 --- a/chrome/browser/resources/options2/content_settings_exceptions_area.html +++ /dev/null @@ -1,99 +0,0 @@ - diff --git a/chrome/browser/resources/options2/content_settings_exceptions_area.js b/chrome/browser/resources/options2/content_settings_exceptions_area.js deleted file mode 100644 index b9bb96d..0000000 --- a/chrome/browser/resources/options2/content_settings_exceptions_area.js +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.contentSettings', function() { - /** @const */ var InlineEditableItemList = options.InlineEditableItemList; - /** @const */ var InlineEditableItem = options.InlineEditableItem; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - /** - * Creates a new exceptions list item. - * - * @param {string} contentType The type of the list. - * @param {string} mode The browser mode, 'otr' or 'normal'. - * @param {boolean} enableAskOption Whether to show an 'ask every time' - * option in the select. - * @param {Object} exception A dictionary that contains the data of the - * exception. - * @constructor - * @extends {options.InlineEditableItem} - */ - function ExceptionsListItem(contentType, mode, enableAskOption, exception) { - var el = cr.doc.createElement('div'); - el.mode = mode; - el.contentType = contentType; - el.enableAskOption = enableAskOption; - el.dataItem = exception; - el.__proto__ = ExceptionsListItem.prototype; - el.decorate(); - - return el; - } - - ExceptionsListItem.prototype = { - __proto__: InlineEditableItem.prototype, - - /** - * Called when an element is decorated as a list item. - */ - decorate: function() { - InlineEditableItem.prototype.decorate.call(this); - - this.isPlaceholder = !this.pattern; - var patternCell = this.createEditableTextCell(this.pattern); - patternCell.className = 'exception-pattern'; - patternCell.classList.add('weakrtl'); - this.contentElement.appendChild(patternCell); - if (this.pattern) - this.patternLabel = patternCell.querySelector('.static-text'); - var input = patternCell.querySelector('input'); - - // TODO(stuartmorgan): Create an createEditableSelectCell abstracting - // this code. - // Setting label for display mode. |pattern| will be null for the 'add new - // exception' row. - if (this.pattern) { - var settingLabel = cr.doc.createElement('span'); - settingLabel.textContent = this.settingForDisplay(); - settingLabel.className = 'exception-setting'; - settingLabel.setAttribute('displaymode', 'static'); - this.contentElement.appendChild(settingLabel); - this.settingLabel = settingLabel; - } - - // Setting select element for edit mode. - var select = cr.doc.createElement('select'); - var optionAllow = cr.doc.createElement('option'); - optionAllow.textContent = loadTimeData.getString('allowException'); - optionAllow.value = 'allow'; - select.appendChild(optionAllow); - - if (this.enableAskOption) { - var optionAsk = cr.doc.createElement('option'); - optionAsk.textContent = loadTimeData.getString('askException'); - optionAsk.value = 'ask'; - select.appendChild(optionAsk); - } - - if (this.contentType == 'cookies') { - var optionSession = cr.doc.createElement('option'); - optionSession.textContent = loadTimeData.getString('sessionException'); - optionSession.value = 'session'; - select.appendChild(optionSession); - } - - if (this.contentType != 'fullscreen') { - var optionBlock = cr.doc.createElement('option'); - optionBlock.textContent = loadTimeData.getString('blockException'); - optionBlock.value = 'block'; - select.appendChild(optionBlock); - } - - this.contentElement.appendChild(select); - select.className = 'exception-setting'; - if (this.pattern) - select.setAttribute('displaymode', 'edit'); - - // Used to track whether the URL pattern in the input is valid. - // This will be true if the browser process has informed us that the - // current text in the input is valid. Changing the text resets this to - // false, and getting a response from the browser sets it back to true. - // It starts off as false for empty string (new exceptions) or true for - // already-existing exceptions (which we assume are valid). - this.inputValidityKnown = this.pattern; - // This one tracks the actual validity of the pattern in the input. This - // starts off as true so as not to annoy the user when he adds a new and - // empty input. - this.inputIsValid = true; - - this.input = input; - this.select = select; - - this.updateEditables(); - - // Editing notifications, geolocation and media-stream is disabled for - // now. - if (this.contentType == 'notifications' || - this.contentType == 'location' || - this.contentType == 'media-stream') { - this.editable = false; - } - - // If the source of the content setting exception is not the user - // preference, then the content settings exception is managed and the user - // can't edit it. - if (this.dataItem.source && - this.dataItem.source != 'preference') { - this.setAttribute('managedby', this.dataItem.source); - this.deletable = false; - this.editable = false; - } - - // If the exception comes from a hosted app, display the name and the - // icon of the app. - if (this.dataItem.source == 'HostedApp') { - this.title = - loadTimeData.getString('set_by') + ' ' + this.dataItem.appName; - var button = this.querySelector('.row-delete-button'); - // Use the host app's favicon (16px, match bigger size). - // See c/b/ui/webui/extensions/extension_icon_source.h - // for a description of the chrome://extension-icon URL. - button.style.backgroundImage = - 'url(\'chrome://extension-icon/' + this.dataItem.appId + '/16/1\')'; - } - - var listItem = this; - // Handle events on the editable nodes. - input.oninput = function(event) { - listItem.inputValidityKnown = false; - chrome.send('checkExceptionPatternValidity', - [listItem.contentType, listItem.mode, input.value]); - }; - - // Listen for edit events. - this.addEventListener('canceledit', this.onEditCancelled_); - this.addEventListener('commitedit', this.onEditCommitted_); - }, - - /** - * The pattern (e.g., a URL) for the exception. - * - * @type {string} - */ - get pattern() { - return this.dataItem['displayPattern']; - }, - set pattern(pattern) { - this.dataItem['displayPattern'] = pattern; - }, - - /** - * The setting (allow/block) for the exception. - * - * @type {string} - */ - get setting() { - return this.dataItem['setting']; - }, - set setting(setting) { - this.dataItem['setting'] = setting; - }, - - /** - * Gets a human-readable setting string. - * - * @type {string} - */ - settingForDisplay: function() { - var setting = this.setting; - if (setting == 'allow') - return loadTimeData.getString('allowException'); - else if (setting == 'block') - return loadTimeData.getString('blockException'); - else if (setting == 'ask') - return loadTimeData.getString('askException'); - else if (setting == 'session') - return loadTimeData.getString('sessionException'); - }, - - /** - * Update this list item to reflect whether the input is a valid pattern. - * - * @param {boolean} valid Whether said pattern is valid in the context of a - * content exception setting. - */ - setPatternValid: function(valid) { - if (valid || !this.input.value) - this.input.setCustomValidity(''); - else - this.input.setCustomValidity(' '); - this.inputIsValid = valid; - this.inputValidityKnown = true; - }, - - /** - * Set the to its original contents. Used when the user quits - * editing. - */ - resetInput: function() { - this.input.value = this.pattern; - }, - - /** - * Copy the data model values to the editable nodes. - */ - updateEditables: function() { - this.resetInput(); - - var settingOption = - this.select.querySelector('[value=\'' + this.setting + '\']'); - if (settingOption) - settingOption.selected = true; - }, - - /** @inheritDoc */ - get currentInputIsValid() { - return this.inputValidityKnown && this.inputIsValid; - }, - - /** @inheritDoc */ - get hasBeenEdited() { - var livePattern = this.input.value; - var liveSetting = this.select.value; - return livePattern != this.pattern || liveSetting != this.setting; - }, - - /** - * Called when committing an edit. - * - * @param {Event} e The end event. - * @private - */ - onEditCommitted_: function(e) { - var newPattern = this.input.value; - var newSetting = this.select.value; - - this.finishEdit(newPattern, newSetting); - }, - - /** - * Called when cancelling an edit; resets the control states. - * - * @param {Event} e The cancel event. - * @private - */ - onEditCancelled_: function() { - this.updateEditables(); - this.setPatternValid(true); - }, - - /** - * Editing is complete; update the model. - * - * @param {string} newPattern The pattern that the user entered. - * @param {string} newSetting The setting the user chose. - */ - finishEdit: function(newPattern, newSetting) { - this.patternLabel.textContent = newPattern; - this.settingLabel.textContent = this.settingForDisplay(); - var oldPattern = this.pattern; - this.pattern = newPattern; - this.setting = newSetting; - - // TODO(estade): this will need to be updated if geolocation/notifications - // become editable. - if (oldPattern != newPattern) { - chrome.send('removeException', - [this.contentType, this.mode, oldPattern]); - } - - chrome.send('setException', - [this.contentType, this.mode, newPattern, newSetting]); - } - }; - - /** - * Creates a new list item for the Add New Item row, which doesn't represent - * an actual entry in the exceptions list but allows the user to add new - * exceptions. - * - * @param {string} contentType The type of the list. - * @param {string} mode The browser mode, 'otr' or 'normal'. - * @param {boolean} enableAskOption Whether to show an 'ask every time' option - * in the select. - * @constructor - * @extends {cr.ui.ExceptionsListItem} - */ - function ExceptionsAddRowListItem(contentType, mode, enableAskOption) { - var el = cr.doc.createElement('div'); - el.mode = mode; - el.contentType = contentType; - el.enableAskOption = enableAskOption; - el.dataItem = []; - el.__proto__ = ExceptionsAddRowListItem.prototype; - el.decorate(); - - return el; - } - - ExceptionsAddRowListItem.prototype = { - __proto__: ExceptionsListItem.prototype, - - decorate: function() { - ExceptionsListItem.prototype.decorate.call(this); - - this.input.placeholder = - loadTimeData.getString('addNewExceptionInstructions'); - - // Do we always want a default of allow? - this.setting = 'allow'; - }, - - /** - * Clear the and let the placeholder text show again. - */ - resetInput: function() { - this.input.value = ''; - }, - - /** @inheritDoc */ - get hasBeenEdited() { - return this.input.value != ''; - }, - - /** - * Editing is complete; update the model. As long as the pattern isn't - * empty, we'll just add it. - * - * @param {string} newPattern The pattern that the user entered. - * @param {string} newSetting The setting the user chose. - */ - finishEdit: function(newPattern, newSetting) { - this.resetInput(); - chrome.send('setException', - [this.contentType, this.mode, newPattern, newSetting]); - }, - }; - - /** - * Creates a new exceptions list. - * - * @constructor - * @extends {cr.ui.List} - */ - var ExceptionsList = cr.ui.define('list'); - - ExceptionsList.prototype = { - __proto__: InlineEditableItemList.prototype, - - /** - * Called when an element is decorated as a list. - */ - decorate: function() { - InlineEditableItemList.prototype.decorate.call(this); - - this.classList.add('settings-list'); - - for (var parentNode = this.parentNode; parentNode; - parentNode = parentNode.parentNode) { - if (parentNode.hasAttribute('contentType')) { - this.contentType = parentNode.getAttribute('contentType'); - break; - } - } - - this.mode = this.getAttribute('mode'); - - var exceptionList = this; - - // Whether the exceptions in this list allow an 'Ask every time' option. - this.enableAskOption = this.contentType == 'plugins' || - this.contentType == 'pepper-flash-cameramic'; - - this.autoExpands = true; - this.reset(); - }, - - /** - * Creates an item to go in the list. - * - * @param {Object} entry The element from the data model for this row. - */ - createItem: function(entry) { - if (entry) { - return new ExceptionsListItem(this.contentType, - this.mode, - this.enableAskOption, - entry); - } else { - var addRowItem = new ExceptionsAddRowListItem(this.contentType, - this.mode, - this.enableAskOption); - addRowItem.deletable = false; - return addRowItem; - } - }, - - /** - * Sets the exceptions in the js model. - * - * @param {Object} entries A list of dictionaries of values, each dictionary - * represents an exception. - */ - setExceptions: function(entries) { - var deleteCount = this.dataModel.length; - - if (this.isEditable()) { - // We don't want to remove the Add New Exception row. - deleteCount = deleteCount - 1; - } - - var args = [0, deleteCount]; - args.push.apply(args, entries); - this.dataModel.splice.apply(this.dataModel, args); - }, - - /** - * The browser has finished checking a pattern for validity. Update the list - * item to reflect this. - * - * @param {string} pattern The pattern. - * @param {bool} valid Whether said pattern is valid in the context of a - * content exception setting. - */ - patternValidityCheckComplete: function(pattern, valid) { - var listItems = this.items; - for (var i = 0; i < listItems.length; i++) { - var listItem = listItems[i]; - // Don't do anything for messages for the item if it is not the intended - // recipient, or if the response is stale (i.e. the input value has - // changed since we sent the request to analyze it). - if (pattern == listItem.input.value) - listItem.setPatternValid(valid); - } - }, - - /** - * Returns whether the rows are editable in this list. - */ - isEditable: function() { - // Exceptions of the following lists are not editable for now. - return !(this.contentType == 'notifications' || - this.contentType == 'location' || - this.contentType == 'fullscreen' || - this.contentType == 'media-stream'); - }, - - /** - * Removes all exceptions from the js model. - */ - reset: function() { - if (this.isEditable()) { - // The null creates the Add New Exception row. - this.dataModel = new ArrayDataModel([null]); - } else { - this.dataModel = new ArrayDataModel([]); - } - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var listItem = this.getListItemByIndex(index); - if (listItem.undeletable) - return; - - var dataItem = listItem.dataItem; - var args = [listItem.contentType]; - if (listItem.contentType == 'location') - args.push(dataItem['origin'], dataItem['embeddingOrigin']); - else if (listItem.contentType == 'notifications') - args.push(dataItem['origin'], dataItem['setting']); - else - args.push(listItem.mode, listItem.pattern); - - chrome.send('removeException', args); - }, - }; - - var OptionsPage = options.OptionsPage; - - /** - * Encapsulated handling of content settings list subpage. - * - * @constructor - */ - function ContentSettingsExceptionsArea() { - OptionsPage.call(this, 'contentExceptions', - loadTimeData.getString('contentSettingsPageTabTitle'), - 'content-settings-exceptions-area'); - } - - cr.addSingletonGetter(ContentSettingsExceptionsArea); - - ContentSettingsExceptionsArea.prototype = { - __proto__: OptionsPage.prototype, - - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var exceptionsLists = this.pageDiv.querySelectorAll('list'); - for (var i = 0; i < exceptionsLists.length; i++) { - options.contentSettings.ExceptionsList.decorate(exceptionsLists[i]); - } - - ContentSettingsExceptionsArea.hideOTRLists(false); - - // If the user types in the URL without a hash, show just cookies. - this.showList('cookies'); - - $('content-settings-exceptions-overlay-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - /** - * Shows one list and hides all others. - * - * @param {string} type The content type. - */ - showList: function(type) { - var header = this.pageDiv.querySelector('h1'); - header.textContent = loadTimeData.getString(type + '_header'); - - var divs = this.pageDiv.querySelectorAll('div[contentType]'); - for (var i = 0; i < divs.length; i++) { - if (divs[i].getAttribute('contentType') == type) - divs[i].hidden = false; - else - divs[i].hidden = true; - } - }, - - /** - * Called after the page has been shown. Show the content type for the - * location's hash. - */ - didShowPage: function() { - var hash = location.hash; - if (hash) - this.showList(hash.slice(1)); - }, - }; - - /** - * Called when the last incognito window is closed. - */ - ContentSettingsExceptionsArea.OTRProfileDestroyed = function() { - this.hideOTRLists(true); - }; - - /** - * Hides the incognito exceptions lists and optionally clears them as well. - * @param {boolean} clear Whether to clear the lists. - */ - ContentSettingsExceptionsArea.hideOTRLists = function(clear) { - var otrLists = document.querySelectorAll('list[mode=otr]'); - - for (var i = 0; i < otrLists.length; i++) { - otrLists[i].parentNode.hidden = true; - if (clear) - otrLists[i].reset(); - } - }; - - return { - ExceptionsListItem: ExceptionsListItem, - ExceptionsAddRowListItem: ExceptionsAddRowListItem, - ExceptionsList: ExceptionsList, - ContentSettingsExceptionsArea: ContentSettingsExceptionsArea, - }; -}); diff --git a/chrome/browser/resources/options2/content_settings_ui.js b/chrome/browser/resources/options2/content_settings_ui.js deleted file mode 100644 index dc7b0c4..0000000 --- a/chrome/browser/resources/options2/content_settings_ui.js +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - ////////////////////////////////////////////////////////////////////////////// - // ContentSettingsRadio class: - - // Define a constructor that uses an input element as its underlying element. - var ContentSettingsRadio = cr.ui.define('input'); - - ContentSettingsRadio.prototype = { - __proto__: HTMLInputElement.prototype, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'radio'; - var self = this; - - this.addEventListener('change', - function(e) { - chrome.send('setContentFilter', [this.name, this.value]); - }); - }, - }; - - /** - * Whether the content setting is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(ContentSettingsRadio, 'controlledBy', cr.PropertyKind.ATTR); - - ////////////////////////////////////////////////////////////////////////////// - // HandlersEnabledRadio class: - - // Define a constructor that uses an input element as its underlying element. - var HandlersEnabledRadio = cr.ui.define('input'); - - HandlersEnabledRadio.prototype = { - __proto__: HTMLInputElement.prototype, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'radio'; - var self = this; - - this.addEventListener('change', - function(e) { - chrome.send('setHandlersEnabled', [this.value == 'allow']); - }); - }, - }; - - // Export - return { - ContentSettingsRadio: ContentSettingsRadio, - HandlersEnabledRadio: HandlersEnabledRadio - }; - -}); - diff --git a/chrome/browser/resources/options2/controlled_setting.js b/chrome/browser/resources/options2/controlled_setting.js deleted file mode 100644 index 9ff806c..0000000 --- a/chrome/browser/resources/options2/controlled_setting.js +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var Preferences = options.Preferences; - - /** - * A controlled setting indicator that can be placed on a setting as an - * indicator that the value is controlled by some external entity such as - * policy or an extension. - * @constructor - * @extends {HTMLSpanElement} - */ - var ControlledSettingIndicator = cr.ui.define('span'); - - ControlledSettingIndicator.prototype = { - __proto__: HTMLSpanElement.prototype, - - /** - * Decorates the base element to show the proper icon. - */ - decorate: function() { - var self = this; - var doc = self.ownerDocument; - - // Create the details and summary elements. - var detailsContainer = doc.createElement('details'); - detailsContainer.appendChild(doc.createElement('summary')); - - // This should really create a div element, but that breaks :hover. See - // https://bugs.webkit.org/show_bug.cgi?id=72957 - var bubbleContainer = doc.createElement('p'); - bubbleContainer.className = 'controlled-setting-bubble'; - detailsContainer.appendChild(bubbleContainer); - - self.appendChild(detailsContainer); - - // If there is a pref, track its controlledBy property in order to be able - // to bring up the correct bubble. - if (this.hasAttribute('pref')) { - Preferences.getInstance().addEventListener( - this.getAttribute('pref'), - function(event) { - if (event.value) { - var controlledBy = event.value['controlledBy']; - self.controlledBy = controlledBy ? controlledBy : null; - } - }); - } - - self.addEventListener('click', self.show_); - }, - - - /** - * Closes the bubble. - */ - close: function() { - this.querySelector('details').removeAttribute('open'); - this.ownerDocument.removeEventListener('click', this.closeHandler_, true); - }, - - /** - * Constructs the bubble DOM tree and shows it. - * @private - */ - show_: function() { - var self = this; - var doc = self.ownerDocument; - - // Clear out the old bubble contents. - var bubbleContainer = this.querySelector('.controlled-setting-bubble'); - if (bubbleContainer) { - while (bubbleContainer.hasChildNodes()) - bubbleContainer.removeChild(bubbleContainer.lastChild); - } - - // Work out the bubble text. - defaultStrings = { - policy: loadTimeData.getString('controlledSettingPolicy'), - extension: loadTimeData.getString('controlledSettingExtension'), - recommended: loadTimeData.getString('controlledSettingRecommended'), - }; - - // No controller, no bubble. - if (!self.controlledBy || !self.controlledBy in defaultStrings) - return; - - var text = defaultStrings[self.controlledBy]; - - // Apply text overrides. - if (self.hasAttribute('text' + self.controlledBy)) - text = self.getAttribute('text' + self.controlledBy); - - // Create the DOM tree. - var bubbleText = doc.createElement('p'); - bubbleText.className = 'controlled-setting-bubble-text'; - bubbleText.textContent = text; - - var pref = self.getAttribute('pref'); - if (self.controlledBy == 'recommended' && pref) { - var container = doc.createElement('div'); - var action = doc.createElement('button'); - action.classList.add('link-button'); - action.classList.add('controlled-setting-bubble-action'); - action.textContent = - loadTimeData.getString('controlledSettingApplyRecommendation'); - action.addEventListener( - 'click', - function(e) { - Preferences.clearPref(pref); - }); - container.appendChild(action); - bubbleText.appendChild(container); - } - - bubbleContainer.appendChild(bubbleText); - - // One-time bubble-closing event handler. - self.closeHandler_ = this.close.bind(this); - doc.addEventListener('click', self.closeHandler_, true); - } - }; - - /** - * The controlling entity of the setting. Can take the values "policy", - * "extension", "recommended" or be unset. - */ - cr.defineProperty(ControlledSettingIndicator, 'controlledBy', - cr.PropertyKind.ATTR, - ControlledSettingIndicator.prototype.close); - - // Export. - return { - ControlledSettingIndicator: ControlledSettingIndicator - }; -}); diff --git a/chrome/browser/resources/options2/cookies_list.js b/chrome/browser/resources/options2/cookies_list.js deleted file mode 100644 index 0968f3c..0000000 --- a/chrome/browser/resources/options2/cookies_list.js +++ /dev/null @@ -1,903 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var DeletableItemList = options.DeletableItemList; - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; - - // This structure maps the various cookie type names from C++ (hence the - // underscores) to arrays of the different types of data each has, along with - // the i18n name for the description of that data type. - /** @const */ var cookieInfo = { - 'cookie': [['name', 'label_cookie_name'], - ['content', 'label_cookie_content'], - ['domain', 'label_cookie_domain'], - ['path', 'label_cookie_path'], - ['sendfor', 'label_cookie_send_for'], - ['accessibleToScript', 'label_cookie_accessible_to_script'], - ['created', 'label_cookie_created'], - ['expires', 'label_cookie_expires']], - 'app_cache': [['manifest', 'label_app_cache_manifest'], - ['size', 'label_local_storage_size'], - ['created', 'label_cookie_created'], - ['accessed', 'label_cookie_last_accessed']], - 'database': [['name', 'label_cookie_name'], - ['desc', 'label_webdb_desc'], - ['size', 'label_local_storage_size'], - ['modified', 'label_local_storage_last_modified']], - 'local_storage': [['origin', 'label_local_storage_origin'], - ['size', 'label_local_storage_size'], - ['modified', 'label_local_storage_last_modified']], - 'indexed_db': [['origin', 'label_indexed_db_origin'], - ['size', 'label_indexed_db_size'], - ['modified', 'label_indexed_db_last_modified']], - 'file_system': [['origin', 'label_file_system_origin'], - ['persistent', 'label_file_system_persistent_usage'], - ['temporary', 'label_file_system_temporary_usage']], - 'server_bound_cert': [['serverId', 'label_server_bound_cert_server_id'], - ['certType', 'label_server_bound_cert_type'], - ['created', 'label_server_bound_cert_created'], - ['expires', 'label_server_bound_cert_expires']], - 'flash_lso': [['domain', 'label_cookie_domain']], - }; - - /** - * Returns the item's height, like offsetHeight but such that it works better - * when the page is zoomed. See the similar calculation in @{code cr.ui.List}. - * This version also accounts for the animation done in this file. - * @param {Element} item The item to get the height of. - * @return {number} The height of the item, calculated with zooming in mind. - */ - function getItemHeight(item) { - var height = item.style.height; - // Use the fixed animation target height if set, in case the element is - // currently being animated and we'd get an intermediate height below. - if (height && height.substr(-2) == 'px') - return parseInt(height.substr(0, height.length - 2)); - return item.getBoundingClientRect().height; - } - - /** - * Create tree nodes for the objects in the data array, and insert them all - * into the given list using its @{code splice} method at the given index. - * @param {Array.} data The data objects for the nodes to add. - * @param {number} start The index at which to start inserting the nodes. - * @return {Array.} An array of CookieTreeNodes added. - */ - function spliceTreeNodes(data, start, list) { - var nodes = data.map(function(x) { return new CookieTreeNode(x); }); - // Insert [start, 0] at the beginning of the array of nodes, making it - // into the arguments we want to pass to @{code list.splice} below. - nodes.splice(0, 0, start, 0); - list.splice.apply(list, nodes); - // Remove the [start, 0] prefix and return the array of nodes. - nodes.splice(0, 2); - return nodes; - } - - /** - * Adds information about an app that protects this data item to the - * @{code element}. - * @param {Element} element The DOM element the information should be - appended to. - * @param {{id: string, name: string}} appInfo Information about an app. - */ - function addAppInfo(element, appInfo) { - var img = element.ownerDocument.createElement('img'); - img.src = 'chrome://extension-icon/' + appInfo.id + '/16/1'; - img.title = loadTimeData.getString('label_protected_by_apps') + - ' ' + appInfo.name; - img.className = 'protecting-app'; - element.appendChild(img); - } - - var parentLookup = {}; - var lookupRequests = {}; - - /** - * Creates a new list item for sites data. Note that these are created and - * destroyed lazily as they scroll into and out of view, so they must be - * stateless. We cache the expanded item in @{code CookiesList} though, so it - * can keep state. (Mostly just which item is selected.) - * @param {Object} origin Data used to create a cookie list item. - * @param {CookiesList} list The list that will contain this item. - * @constructor - * @extends {DeletableItem} - */ - function CookieListItem(origin, list) { - var listItem = new DeletableItem(null); - listItem.__proto__ = CookieListItem.prototype; - - listItem.origin = origin; - listItem.list = list; - listItem.decorate(); - - // This hooks up updateOrigin() to the list item, makes the top-level - // tree nodes (i.e., origins) register their IDs in parentLookup, and - // causes them to request their children if they have none. Note that we - // have special logic in the setter for the parent property to make sure - // that we can still garbage collect list items when they scroll out of - // view, even though it appears that we keep a direct reference. - if (origin) { - origin.parent = listItem; - origin.updateOrigin(); - } - - return listItem; - } - - CookieListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** @inheritDoc */ - decorate: function() { - this.siteChild = this.ownerDocument.createElement('div'); - this.siteChild.className = 'cookie-site'; - this.dataChild = this.ownerDocument.createElement('div'); - this.dataChild.className = 'cookie-data'; - this.sizeChild = this.ownerDocument.createElement('div'); - this.sizeChild.className = 'cookie-size'; - this.itemsChild = this.ownerDocument.createElement('div'); - this.itemsChild.className = 'cookie-items'; - this.infoChild = this.ownerDocument.createElement('div'); - this.infoChild.className = 'cookie-details'; - this.infoChild.hidden = true; - - if (this.origin.data.appId) { - this.siteChild.classList.add('app-cookie-site'); - this.itemsChild.classList.add('app-cookie-items'); - } - - var remove = this.ownerDocument.createElement('button'); - remove.textContent = loadTimeData.getString('remove_cookie'); - remove.onclick = this.removeCookie_.bind(this); - this.infoChild.appendChild(remove); - var content = this.contentElement; - content.appendChild(this.siteChild); - content.appendChild(this.dataChild); - content.appendChild(this.sizeChild); - content.appendChild(this.itemsChild); - this.itemsChild.appendChild(this.infoChild); - if (this.origin && this.origin.data) { - this.siteChild.textContent = this.origin.data.title; - this.siteChild.setAttribute('title', this.origin.data.title); - } - this.itemList_ = []; - }, - - /** @type {boolean} */ - get expanded() { - return this.expanded_; - }, - set expanded(expanded) { - if (this.expanded_ == expanded) - return; - this.expanded_ = expanded; - if (expanded) { - var oldExpanded = this.list.expandedItem; - this.list.expandedItem = this; - this.updateItems_(); - if (oldExpanded) - oldExpanded.expanded = false; - this.classList.add('show-items'); - } else { - if (this.list.expandedItem == this) { - this.list.expandedItem = null; - } - this.style.height = ''; - this.itemsChild.style.height = ''; - this.classList.remove('show-items'); - } - }, - - /** - * The callback for the "remove" button shown when an item is selected. - * Requests that the currently selected cookie be removed. - * @private - */ - removeCookie_: function() { - if (this.selectedIndex_ >= 0) { - var item = this.itemList_[this.selectedIndex_]; - if (item && item.node) - chrome.send('removeCookie', [item.node.pathId]); - } - }, - - /** - * Disable animation within this cookie list item, in preparation for making - * changes that will need to be animated. Makes it possible to measure the - * contents without displaying them, to set animation targets. - * @private - */ - disableAnimation_: function() { - this.itemsHeight_ = getItemHeight(this.itemsChild); - this.classList.add('measure-items'); - }, - - /** - * Enable animation after changing the contents of this cookie list item. - * See @{code disableAnimation_}. - * @private - */ - enableAnimation_: function() { - if (!this.classList.contains('measure-items')) - this.disableAnimation_(); - this.itemsChild.style.height = ''; - // This will force relayout in order to calculate the new heights. - var itemsHeight = getItemHeight(this.itemsChild); - var fixedHeight = getItemHeight(this) + itemsHeight - this.itemsHeight_; - this.itemsChild.style.height = this.itemsHeight_ + 'px'; - // Force relayout before enabling animation, so that if we have - // changed things since the last layout, they will not be animated - // during subsequent layouts. - this.itemsChild.offsetHeight; - this.classList.remove('measure-items'); - this.itemsChild.style.height = itemsHeight + 'px'; - this.style.height = fixedHeight + 'px'; - }, - - /** - * Updates the origin summary to reflect changes in its items. - * Both CookieListItem and CookieTreeNode implement this API. - * This implementation scans the descendants to update the text. - */ - updateOrigin: function() { - var info = { - cookies: 0, - database: false, - localStorage: false, - appCache: false, - indexedDb: false, - fileSystem: false, - serverBoundCerts: 0, - }; - if (this.origin) - this.origin.collectSummaryInfo(info); - - var list = []; - if (info.cookies > 1) - list.push(loadTimeData.getStringF('cookie_plural', info.cookies)); - else if (info.cookies > 0) - list.push(loadTimeData.getString('cookie_singular')); - if (info.database || info.indexedDb) - list.push(loadTimeData.getString('cookie_database_storage')); - if (info.localStorage) - list.push(loadTimeData.getString('cookie_local_storage')); - if (info.appCache) - list.push(loadTimeData.getString('cookie_app_cache')); - if (info.fileSystem) - list.push(loadTimeData.getString('cookie_file_system')); - if (info.serverBoundCerts) - list.push(loadTimeData.getString('cookie_server_bound_cert')); - if (info.flashLSO) - list.push(loadTimeData.getString('cookie_flash_lso')); - - var text = ''; - for (var i = 0; i < list.length; ++i) { - if (text.length > 0) - text += ', ' + list[i]; - else - text = list[i]; - } - this.dataChild.textContent = text; - - for (var key in info.appsProtectingThis) { - addAppInfo(this.dataChild, apps[key]); - } - - if (info.quota && info.quota.totalUsage) - this.sizeChild.textContent = info.quota.totalUsage; - - if (this.expanded) - this.updateItems_(); - }, - - /** - * Updates the items section to reflect changes, animating to the new state. - * Removes existing contents and calls @{code CookieTreeNode.createItems}. - * @private - */ - updateItems_: function() { - this.disableAnimation_(); - this.itemsChild.textContent = ''; - this.infoChild.hidden = true; - this.selectedIndex_ = -1; - this.itemList_ = []; - if (this.origin) - this.origin.createItems(this); - this.itemsChild.appendChild(this.infoChild); - this.enableAnimation_(); - }, - - /** - * Append a new cookie node "bubble" to this list item. - * @param {CookieTreeNode} node The cookie node to add a bubble for. - * @param {Element} div The DOM element for the bubble itself. - * @return {number} The index the bubble was added at. - */ - appendItem: function(node, div) { - this.itemList_.push({node: node, div: div}); - this.itemsChild.appendChild(div); - return this.itemList_.length - 1; - }, - - /** - * The currently selected cookie node ("cookie bubble") index. - * @type {number} - * @private - */ - selectedIndex_: -1, - - /** - * Get the currently selected cookie node ("cookie bubble") index. - * @type {number} - */ - get selectedIndex() { - return this.selectedIndex_; - }, - - /** - * Set the currently selected cookie node ("cookie bubble") index to - * @{code itemIndex}, unselecting any previously selected node first. - * @param {number} itemIndex The index to set as the selected index. - */ - set selectedIndex(itemIndex) { - // Get the list index up front before we change anything. - var index = this.list.getIndexOfListItem(this); - // Unselect any previously selected item. - if (this.selectedIndex_ >= 0) { - var item = this.itemList_[this.selectedIndex_]; - if (item && item.div) - item.div.removeAttribute('selected'); - } - // Special case: decrementing -1 wraps around to the end of the list. - if (itemIndex == -2) - itemIndex = this.itemList_.length - 1; - // Check if we're going out of bounds and hide the item details. - if (itemIndex < 0 || itemIndex >= this.itemList_.length) { - this.selectedIndex_ = -1; - this.disableAnimation_(); - this.infoChild.hidden = true; - this.enableAnimation_(); - return; - } - // Set the new selected item and show the item details for it. - this.selectedIndex_ = itemIndex; - this.itemList_[itemIndex].div.setAttribute('selected', ''); - this.disableAnimation_(); - this.itemList_[itemIndex].node.setDetailText(this.infoChild, - this.list.infoNodes); - this.infoChild.hidden = false; - this.enableAnimation_(); - // If we're near the bottom of the list this may cause the list item to go - // beyond the end of the visible area. Fix it after the animation is done. - var list = this.list; - window.setTimeout(function() { list.scrollIndexIntoView(index); }, 150); - }, - }; - - /** - * {@code CookieTreeNode}s mirror the structure of the cookie tree lazily, and - * contain all the actual data used to generate the {@code CookieListItem}s. - * @param {Object} data The data object for this node. - * @constructor - */ - function CookieTreeNode(data) { - this.data = data; - this.children = []; - } - - CookieTreeNode.prototype = { - /** - * Insert the given list of cookie tree nodes at the given index. - * Both CookiesList and CookieTreeNode implement this API. - * @param {Array.} data The data objects for the nodes to add. - * @param {number} start The index at which to start inserting the nodes. - */ - insertAt: function(data, start) { - var nodes = spliceTreeNodes(data, start, this.children); - for (var i = 0; i < nodes.length; i++) - nodes[i].parent = this; - this.updateOrigin(); - }, - - /** - * Remove a cookie tree node from the given index. - * Both CookiesList and CookieTreeNode implement this API. - * @param {number} index The index of the tree node to remove. - */ - remove: function(index) { - if (index < this.children.length) { - this.children.splice(index, 1); - this.updateOrigin(); - } - }, - - /** - * Clears all children. - * Both CookiesList and CookieTreeNode implement this API. - * It is used by CookiesList.loadChildren(). - */ - clear: function() { - // We might leave some garbage in parentLookup for removed children. - // But that should be OK because parentLookup is cleared when we - // reload the tree. - this.children = []; - this.updateOrigin(); - }, - - /** - * The counter used by startBatchUpdates() and endBatchUpdates(). - * @type {number} - */ - batchCount_: 0, - - /** - * See cr.ui.List.startBatchUpdates(). - * Both CookiesList (via List) and CookieTreeNode implement this API. - */ - startBatchUpdates: function() { - this.batchCount_++; - }, - - /** - * See cr.ui.List.endBatchUpdates(). - * Both CookiesList (via List) and CookieTreeNode implement this API. - */ - endBatchUpdates: function() { - if (!--this.batchCount_) - this.updateOrigin(); - }, - - /** - * Requests updating the origin summary to reflect changes in this item. - * Both CookieListItem and CookieTreeNode implement this API. - */ - updateOrigin: function() { - if (!this.batchCount_ && this.parent) - this.parent.updateOrigin(); - }, - - /** - * Summarize the information in this node and update @{code info}. - * This will recurse into child nodes to summarize all descendants. - * @param {Object} info The info object from @{code updateOrigin}. - */ - collectSummaryInfo: function(info) { - if (this.children.length > 0) { - for (var i = 0; i < this.children.length; ++i) - this.children[i].collectSummaryInfo(info); - } else if (this.data && !this.data.hasChildren) { - if (this.data.type == 'cookie') { - info.cookies++; - } else if (this.data.type == 'database') { - info.database = true; - } else if (this.data.type == 'local_storage') { - info.localStorage = true; - } else if (this.data.type == 'app_cache') { - info.appCache = true; - } else if (this.data.type == 'indexed_db') { - info.indexedDb = true; - } else if (this.data.type == 'file_system') { - info.fileSystem = true; - } else if (this.data.type == 'quota') { - info.quota = this.data; - } else if (this.data.type == 'server_bound_cert') { - info.serverBoundCerts++; - } else if (this.data.type == 'flash_lso') { - info.flashLSO = true; - } - - var apps = this.data.appsProtectingThis; - if (apps) { - if (!info.appsProtectingThis) - info.appsProtectingThis = {}; - apps.forEach(function(appInfo) { - info.appsProtectingThis[appInfo.id] = appInfo; - }); - } - } - }, - - /** - * Create the cookie "bubbles" for this node, recursing into children - * if there are any. Append the cookie bubbles to @{code item}. - * @param {CookieListItem} item The cookie list item to create items in. - */ - createItems: function(item) { - if (this.children.length > 0) { - for (var i = 0; i < this.children.length; ++i) - this.children[i].createItems(item); - return; - } - - if (!this.data || this.data.hasChildren) - return; - - var text = ''; - switch (this.data.type) { - case 'cookie': - case 'database': - text = this.data.name; - break; - default: - text = loadTimeData.getString('cookie_' + this.data.type); - } - if (!text) - return; - - var div = item.ownerDocument.createElement('div'); - div.className = 'cookie-item'; - // Help out screen readers and such: this is a clickable thing. - div.setAttribute('role', 'button'); - div.tabIndex = 0; - div.textContent = text; - var apps = this.data.appsProtectingThis; - if (apps) - apps.forEach(addAppInfo.bind(null, div)); - - var index = item.appendItem(this, div); - div.onclick = function() { - item.selectedIndex = (item.selectedIndex == index) ? -1 : index; - }; - }, - - /** - * Set the detail text to be displayed to that of this cookie tree node. - * Uses preallocated DOM elements for each cookie node type from @{code - * infoNodes}, and inserts the appropriate elements to @{code element}. - * @param {Element} element The DOM element to insert elements to. - * @param {Object.}>} infoNodes The map from cookie node types to maps from - * cookie attribute names to DOM elements to display cookie attribute - * values, created by @{code CookiesList.decorate}. - */ - setDetailText: function(element, infoNodes) { - var table; - if (this.data && !this.data.hasChildren && cookieInfo[this.data.type]) { - var info = cookieInfo[this.data.type]; - var nodes = infoNodes[this.data.type].info; - for (var i = 0; i < info.length; ++i) { - var name = info[i][0]; - if (name != 'id' && this.data[name]) - nodes[name].textContent = this.data[name]; - else - nodes[name].textContent = ''; - } - table = infoNodes[this.data.type].table; - } - - while (element.childNodes.length > 1) - element.removeChild(element.firstChild); - - if (table) - element.insertBefore(table, element.firstChild); - }, - - /** - * The parent of this cookie tree node. - * @type {?CookieTreeNode|CookieListItem} - */ - get parent() { - // See below for an explanation of this special case. - if (typeof this.parent_ == 'number') - return this.list_.getListItemByIndex(this.parent_); - return this.parent_; - }, - set parent(parent) { - if (parent == this.parent) - return; - - if (parent instanceof CookieListItem) { - // If the parent is to be a CookieListItem, then we keep the reference - // to it by its containing list and list index, rather than directly. - // This allows the list items to be garbage collected when they scroll - // out of view (except the expanded item, which we cache). This is - // transparent except in the setter and getter, where we handle it. - this.parent_ = parent.listIndex; - this.list_ = parent.list; - parent.addEventListener('listIndexChange', - this.parentIndexChanged_.bind(this)); - } else { - this.parent_ = parent; - } - - if (this.data && this.data.id) { - if (parent) - parentLookup[this.data.id] = this; - else - delete parentLookup[this.data.id]; - } - - if (this.data && this.data.hasChildren && - !this.children.length && !lookupRequests[this.data.id]) { - lookupRequests[this.data.id] = true; - chrome.send('loadCookie', [this.pathId]); - } - }, - - /** - * Called when the parent is a CookieListItem whose index has changed. - * See the code above that avoids keeping a direct reference to - * CookieListItem parents, to allow them to be garbage collected. - * @private - */ - parentIndexChanged_: function(event) { - if (typeof this.parent_ == 'number') { - this.parent_ = event.newValue; - // We set a timeout to update the origin, rather than doing it right - // away, because this callback may occur while the list items are - // being repopulated following a scroll event. Calling updateOrigin() - // immediately could trigger relayout that would reset the scroll - // position within the list, among other things. - window.setTimeout(this.updateOrigin.bind(this), 0); - } - }, - - /** - * The cookie tree path id. - * @type {string} - */ - get pathId() { - var parent = this.parent; - if (parent && parent instanceof CookieTreeNode) - return parent.pathId + ',' + this.data.id; - return this.data.id; - }, - }; - - /** - * Creates a new cookies list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {DeletableItemList} - */ - var CookiesList = cr.ui.define('list'); - - CookiesList.prototype = { - __proto__: DeletableItemList.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.classList.add('cookie-list'); - this.dataModel = new ArrayDataModel([]); - this.addEventListener('keydown', this.handleKeyLeftRight_.bind(this)); - var sm = new ListSingleSelectionModel(); - sm.addEventListener('change', this.cookieSelectionChange_.bind(this)); - sm.addEventListener('leadIndexChange', this.cookieLeadChange_.bind(this)); - this.selectionModel = sm; - this.infoNodes = {}; - this.fixedHeight = false; - var doc = this.ownerDocument; - // Create a table for each type of site data (e.g. cookies, databases, - // etc.) and save it so that we can reuse it for all origins. - for (var type in cookieInfo) { - var table = doc.createElement('table'); - table.className = 'cookie-details-table'; - var tbody = doc.createElement('tbody'); - table.appendChild(tbody); - var info = {}; - for (var i = 0; i < cookieInfo[type].length; i++) { - var tr = doc.createElement('tr'); - var name = doc.createElement('td'); - var data = doc.createElement('td'); - var pair = cookieInfo[type][i]; - name.className = 'cookie-details-label'; - name.textContent = loadTimeData.getString(pair[1]); - data.className = 'cookie-details-value'; - data.textContent = ''; - tr.appendChild(name); - tr.appendChild(data); - tbody.appendChild(tr); - info[pair[0]] = data; - } - this.infoNodes[type] = {table: table, info: info}; - } - }, - - /** - * Handles key down events and looks for left and right arrows, then - * dispatches to the currently expanded item, if any. - * @param {Event} e The keydown event. - * @private - */ - handleKeyLeftRight_: function(e) { - var id = e.keyIdentifier; - if ((id == 'Left' || id == 'Right') && this.expandedItem) { - var cs = this.ownerDocument.defaultView.getComputedStyle(this); - var rtl = cs.direction == 'rtl'; - if ((!rtl && id == 'Left') || (rtl && id == 'Right')) - this.expandedItem.selectedIndex--; - else - this.expandedItem.selectedIndex++; - this.scrollIndexIntoView(this.expandedItem.listIndex); - // Prevent the page itself from scrolling. - e.preventDefault(); - } - }, - - /** - * Called on selection model selection changes. - * @param {Event} ce The selection change event. - * @private - */ - cookieSelectionChange_: function(ce) { - ce.changes.forEach(function(change) { - var listItem = this.getListItemByIndex(change.index); - if (listItem) { - if (!change.selected) { - // We set a timeout here, rather than setting the item unexpanded - // immediately, so that if another item gets set expanded right - // away, it will be expanded before this item is unexpanded. It - // will notice that, and unexpand this item in sync with its own - // expansion. Later, this callback will end up having no effect. - window.setTimeout(function() { - if (!listItem.selected || !listItem.lead) - listItem.expanded = false; - }, 0); - } else if (listItem.lead) { - listItem.expanded = true; - } - } - }, this); - }, - - /** - * Called on selection model lead changes. - * @param {Event} pe The lead change event. - * @private - */ - cookieLeadChange_: function(pe) { - if (pe.oldValue != -1) { - var listItem = this.getListItemByIndex(pe.oldValue); - if (listItem) { - // See cookieSelectionChange_ above for why we use a timeout here. - window.setTimeout(function() { - if (!listItem.lead || !listItem.selected) - listItem.expanded = false; - }, 0); - } - } - if (pe.newValue != -1) { - var listItem = this.getListItemByIndex(pe.newValue); - if (listItem && listItem.selected) - listItem.expanded = true; - } - }, - - /** - * The currently expanded item. Used by CookieListItem above. - * @type {?CookieListItem} - */ - expandedItem: null, - - // from cr.ui.List - /** @inheritDoc */ - createItem: function(data) { - // We use the cached expanded item in order to allow it to maintain some - // state (like its fixed height, and which bubble is selected). - if (this.expandedItem && this.expandedItem.origin == data) - return this.expandedItem; - return new CookieListItem(data, this); - }, - - // from options.DeletableItemList - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var item = this.dataModel.item(index); - if (item) { - var pathId = item.pathId; - if (pathId) - chrome.send('removeCookie', [pathId]); - } - }, - - /** - * Insert the given list of cookie tree nodes at the given index. - * Both CookiesList and CookieTreeNode implement this API. - * @param {Array.} data The data objects for the nodes to add. - * @param {number} start The index at which to start inserting the nodes. - */ - insertAt: function(data, start) { - spliceTreeNodes(data, start, this.dataModel); - }, - - /** - * Remove a cookie tree node from the given index. - * Both CookiesList and CookieTreeNode implement this API. - * @param {number} index The index of the tree node to remove. - */ - remove: function(index) { - if (index < this.dataModel.length) - this.dataModel.splice(index, 1); - }, - - /** - * Clears the list. - * Both CookiesList and CookieTreeNode implement this API. - * It is used by CookiesList.loadChildren(). - */ - clear: function() { - parentLookup = {}; - this.dataModel.splice(0, this.dataModel.length); - this.redraw(); - }, - - /** - * Add tree nodes by given parent. - * @param {Object} parent The parent node. - * @param {number} start The index at which to start inserting the nodes. - * @param {Array} nodesData Nodes data array. - * @private - */ - addByParent_: function(parent, start, nodesData) { - if (!parent) - return; - - parent.startBatchUpdates(); - parent.insertAt(nodesData, start); - parent.endBatchUpdates(); - - cr.dispatchSimpleEvent(this, 'change'); - }, - - /** - * Add tree nodes by parent id. - * This is used by cookies_view.js. - * @param {string} parentId Id of the parent node. - * @param {number} start The index at which to start inserting the nodes. - * @param {Array} nodesData Nodes data array. - */ - addByParentId: function(parentId, start, nodesData) { - var parent = parentId ? parentLookup[parentId] : this; - this.addByParent_(parent, start, nodesData); - }, - - /** - * Removes tree nodes by parent id. - * This is used by cookies_view.js. - * @param {string} parentId Id of the parent node. - * @param {number} start The index at which to start removing the nodes. - * @param {number} count Number of nodes to remove. - */ - removeByParentId: function(parentId, start, count) { - var parent = parentId ? parentLookup[parentId] : this; - if (!parent) - return; - - parent.startBatchUpdates(); - while (count-- > 0) - parent.remove(start); - parent.endBatchUpdates(); - - cr.dispatchSimpleEvent(this, 'change'); - }, - - /** - * Loads the immediate children of given parent node. - * This is used by cookies_view.js. - * @param {string} parentId Id of the parent node. - * @param {Array} children The immediate children of parent node. - */ - loadChildren: function(parentId, children) { - if (parentId) - delete lookupRequests[parentId]; - var parent = parentId ? parentLookup[parentId] : this; - if (!parent) - return; - - parent.startBatchUpdates(); - parent.clear(); - this.addByParent_(parent, 0, children); - parent.endBatchUpdates(); - }, - }; - - return { - CookiesList: CookiesList - }; -}); diff --git a/chrome/browser/resources/options2/cookies_view.css b/chrome/browser/resources/options2/cookies_view.css deleted file mode 100644 index 99d24e8..0000000 --- a/chrome/browser/resources/options2/cookies_view.css +++ /dev/null @@ -1,212 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -/* Styles for the cookies list page. */ -.cookies-view-page { - height: 90%; - margin-left: -15px; - width: 720px; -} - -/* Styles for the cookies list elements in cookies_view.html. */ -.cookies-list { - -webkit-box-flex: 1; - /* This property overrides the |min-height: 192px;| property above due to - * special behavior of the cookies list. */ - border: 1px solid #D9D9D9; - margin: 0; - margin-top: 5px; - min-height: 0; -} - -.cookies-list-content-area { - -webkit-box-orient: vertical; - display: -webkit-box; - overflow-y: hidden; -} - -.cookies-column-headers { - -webkit-box-align: baseline; - -webkit-box-orient: horizontal; - display: -webkit-box; - position: relative; - width: 100%; -} - -.cookies-column-headers > * { - display: block; -} - -.cookies-column-headers h3 { - font-size: 105%; - font-weight: bold; - margin: 10px 0; -} - -/* Notice the width and padding for these columns match up with those below. */ -.cookies-site-column { - -webkit-padding-start: 7px; - width: 14em; -} - -.cookies-data-column { - -webkit-box-flex: 1; - -webkit-padding-start: 7px; -} - -/* Enable animating the height of items. */ -list.cookie-list .deletable-item { - -webkit-transition: height 150ms ease-in-out; -} - -/* Disable webkit-box display. */ -list.cookie-list .deletable-item > :first-child { - display: block; -} - -/* Force the X for deleting an origin to stay at the top. */ -list.cookie-list > .deletable-item > .close-button { - position: absolute; - right: 2px; - top: 8px; -} - -html[dir=rtl] list.cookie-list > .deletable-item > .close-button { - left: 2px; - right: auto; -} - -/* Styles for the site (aka origin) and its summary. */ -.cookie-site { - /* Notice that the width, margin, and padding match up with those above. */ - -webkit-margin-end: 2px; - -webkit-padding-start: 5px; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - width: 14em; -} - -list.cookie-list > .deletable-item[selected] .cookie-site { - -webkit-user-select: text; -} - -.cookie-data { - display: inline-block; - max-width: 410px; - overflow: hidden; - text-overflow: ellipsis; -} - -.cookie-size { - display: inline-block; - float: right; - margin-right: 0; -} - -list.cookie-list > .deletable-item[selected] .cookie-data { - -webkit-user-select: text; -} - - -/* Styles for the individual items (cookies, etc.). */ -.cookie-items { - /* Notice that the margin and padding match up with those above. */ - -webkit-margin-start: 14em; - -webkit-padding-start: 7px; - -webkit-transition: 150ms ease-in-out; - height: 0; - opacity: 0; - /* Make the cookie items wrap correctly. */ - white-space: normal; -} - -.measure-items .cookie-items { - -webkit-transition: none; - height: auto; - visibility: hidden; -} - -.show-items .cookie-items { - opacity: 1; -} - -.cookie-items .cookie-item { - background: rgb(224, 233, 245); - border: 1px solid rgb(131, 146, 174); - border-radius: 5px; - display: inline-block; - font-size: 85%; - height: auto; - margin: 2px 4px 2px 0; - max-width: 120px; - min-width: 40px; - overflow: hidden; - padding: 0 3px; - text-align: center; - text-overflow: ellipsis; -} - -.cookie-items .cookie-item:hover { - background: rgb(238, 243, 249); - border-color: rgb(100, 113, 135); -} - -.cookie-items .cookie-item[selected] { - background: rgb(245, 248, 248); - border-color: #B2B2B2; -} - -.cookie-items .cookie-item[selected]:hover { - background: rgb(245, 248, 248); - border-color: rgb(100, 113, 135); -} - -.cookie-items .cookie-item .protecting-app, -.cookie-data .protecting-app { - margin-bottom: -3px; - margin-left: 4px; -} - -/* Styles for the cookie details box. */ -.cookie-details { - background: rgb(245, 248, 248); - border: 1px solid #B2B2B2; - border-radius: 5px; - margin-top: 2px; - padding: 5px; -} - -list.cookie-list > .deletable-item[selected] .cookie-details { - -webkit-user-select: text; -} - -.cookie-details-table { - table-layout: fixed; - width: 100%; -} - -.cookie-details-label { - vertical-align: top; - white-space: pre; - width: 10em; -} - -.cookie-details-value { - word-wrap: break-word; -} - - -/* Styles specific to the app cookies window */ -#app-cookies-site-column { - width: 20em; -} - -.app-cookie-site { - width: 20em; -} - -.app-cookie-items { - -webkit-margin-start: 20em; -} diff --git a/chrome/browser/resources/options2/cookies_view.html b/chrome/browser/resources/options2/cookies_view.html deleted file mode 100644 index 2cc7b07..0000000 --- a/chrome/browser/resources/options2/cookies_view.html +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/chrome/browser/resources/options2/cookies_view.js b/chrome/browser/resources/options2/cookies_view.js deleted file mode 100644 index c80b93f..0000000 --- a/chrome/browser/resources/options2/cookies_view.js +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - - ///////////////////////////////////////////////////////////////////////////// - // CookiesView class: - - /** - * Encapsulated handling of the cookies and other site data page. - * @constructor - */ - function CookiesView(model) { - OptionsPage.call(this, 'cookies', - loadTimeData.getString('cookiesViewPageTabTitle'), - 'cookies-view-page'); - } - - cr.addSingletonGetter(CookiesView); - - CookiesView.prototype = { - __proto__: OptionsPage.prototype, - - /** - * The timer id of the timer set on search query change events. - * @type {number} - * @private - */ - queryDelayTimerId_: 0, - - /** - * The most recent search query, empty string if the query is empty. - * @type {string} - * @private - */ - lastQuery_: '', - - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.pageDiv.querySelector('.cookies-search-box').addEventListener( - 'search', this.handleSearchQueryChange_.bind(this)); - - this.pageDiv.querySelector('.remove-all-cookies-button').onclick = - function(e) { - chrome.send('removeAllCookies'); - }; - - var cookiesList = this.pageDiv.querySelector('.cookies-list'); - options.CookiesList.decorate(cookiesList); - - this.addEventListener('visibleChange', this.handleVisibleChange_); - - this.pageDiv.querySelector('.cookies-view-overlay-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - /** - * Clear search filter when the dialog is displayed. - * @inheritDoc - */ - didShowPage: function() { - this.pageDiv.querySelector('.cookies-search-box').value = ''; - }, - - /** - * Search cookie using text in |cookies-search-box|. - */ - searchCookie: function() { - this.queryDelayTimerId_ = 0; - var filter = this.pageDiv.querySelector('.cookies-search-box').value; - if (this.lastQuery_ != filter) { - this.lastQuery_ = filter; - chrome.send('updateCookieSearchResults', [filter]); - } - }, - - /** - * Handles search query changes. - * @param {!Event} e The event object. - * @private - */ - handleSearchQueryChange_: function(e) { - if (this.queryDelayTimerId_) - window.clearTimeout(this.queryDelayTimerId_); - - this.queryDelayTimerId_ = window.setTimeout( - this.searchCookie.bind(this), 500); - }, - - initialized_: false, - - /** - * Handler for OptionsPage's visible property change event. - * @param {Event} e Property change event. - * @private - */ - handleVisibleChange_: function(e) { - if (!this.visible) - return; - - // Inform the CookiesViewHandler whether we are operating in regular - // cookies dialog or the apps one. - chrome.send('setViewContext', [this.isAppContext()]); - - chrome.send('reloadCookies'); - - if (!this.initialized_) { - this.initialized_ = true; - this.searchCookie(); - } else { - this.pageDiv.querySelector('.cookies-list').redraw(); - } - - this.pageDiv.querySelector('.cookies-search-box').focus(); - }, - - isAppContext: function() { - return false; - }, - }; - - // CookiesViewHandler callbacks. - CookiesView.onTreeItemAdded = function(args) { - $('cookies-list').addByParentId(args[0], args[1], args[2]); - }; - - CookiesView.onTreeItemRemoved = function(args) { - $('cookies-list').removeByParentId(args[0], args[1], args[2]); - }; - - CookiesView.loadChildren = function(args) { - $('cookies-list').loadChildren(args[0], args[1]); - }; - - // Export - return { - CookiesView: CookiesView - }; - -}); diff --git a/chrome/browser/resources/options2/cookies_view_app.html b/chrome/browser/resources/options2/cookies_view_app.html deleted file mode 100644 index e0db2aa..0000000 --- a/chrome/browser/resources/options2/cookies_view_app.html +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/chrome/browser/resources/options2/cookies_view_app.js b/chrome/browser/resources/options2/cookies_view_app.js deleted file mode 100644 index b9ee641..0000000 --- a/chrome/browser/resources/options2/cookies_view_app.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - ///////////////////////////////////////////////////////////////////////////// - // CookiesViewApp class: - - /** - * Encapsulated handling of app cookies and other data page. It - * derives from the regular CookiesView. - * @constructor - */ - function CookiesViewApp(model) { - options.OptionsPage.call(this, 'app-cookies', - loadTimeData.getString('cookiesViewPageTabTitle'), - 'app-cookies-view-page'); - } - - cr.addSingletonGetter(CookiesViewApp); - - CookiesViewApp.prototype = { - __proto__: options.CookiesView.prototype, - - isAppContext: function() { - return true; - }, - }; - - // CookiesViewHandler callbacks. - CookiesViewApp.onTreeItemAdded = function(args) { - $('app-cookies-list').addByParentId(args[0], args[1], args[2]); - }; - - CookiesViewApp.onTreeItemRemoved = function(args) { - $('app-cookies-list').removeByParentId(args[0], args[1], args[2]); - }; - - CookiesViewApp.loadChildren = function(args) { - $('app-cookies-list').loadChildren(args[0], args[1]); - }; - - // Export - return { - CookiesViewApp: CookiesViewApp - }; -}); diff --git a/chrome/browser/resources/options2/deletable_item_list.js b/chrome/browser/resources/options2/deletable_item_list.js deleted file mode 100644 index 8d79902..0000000 --- a/chrome/browser/resources/options2/deletable_item_list.js +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var List = cr.ui.List; - /** @const */ var ListItem = cr.ui.ListItem; - - /** - * Creates a deletable list item, which has a button that will trigger a call - * to deleteItemAtIndex(index) in the list. - */ - var DeletableItem = cr.ui.define('li'); - - DeletableItem.prototype = { - __proto__: ListItem.prototype, - - /** - * The element subclasses should populate with content. - * @type {HTMLElement} - * @private - */ - contentElement_: null, - - /** - * The close button element. - * @type {HTMLElement} - * @private - */ - closeButtonElement_: null, - - /** - * Whether or not this item can be deleted. - * @type {boolean} - * @private - */ - deletable_: true, - - /** @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - - this.classList.add('deletable-item'); - - this.contentElement_ = this.ownerDocument.createElement('div'); - this.appendChild(this.contentElement_); - - this.closeButtonElement_ = this.ownerDocument.createElement('button'); - this.closeButtonElement_.className = - 'raw-button row-delete-button custom-appearance'; - this.closeButtonElement_.addEventListener('mousedown', - this.handleMouseDownUpOnClose_); - this.closeButtonElement_.addEventListener('mouseup', - this.handleMouseDownUpOnClose_); - this.closeButtonElement_.addEventListener('focus', - this.handleFocus_.bind(this)); - this.appendChild(this.closeButtonElement_); - }, - - /** - * Returns the element subclasses should add content to. - * @return {HTMLElement} The element subclasses should popuplate. - */ - get contentElement() { - return this.contentElement_; - }, - - /** - * Returns the close button element. - * @return {HTMLElement} The close | - - - diff --git a/chrome/browser/resources/options2/font_settings.js b/chrome/browser/resources/options2/font_settings.js deleted file mode 100644 index df02798..0000000 --- a/chrome/browser/resources/options2/font_settings.js +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - var OptionsPage = options.OptionsPage; - - /** - * FontSettings class - * Encapsulated handling of the 'Fonts and Encoding' page. - * @class - */ - function FontSettings() { - OptionsPage.call(this, - 'fonts', - loadTimeData.getString('fontSettingsPageTabTitle'), - 'font-settings'); - } - - cr.addSingletonGetter(FontSettings); - - FontSettings.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var standardFontRange = $('standard-font-size'); - standardFontRange.valueMap = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, - 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 48, 56, 64, 72]; - standardFontRange.continuous = false; - standardFontRange.notifyChange = this.standardRangeChanged_.bind(this); - standardFontRange.notifyPrefChange = - this.standardFontSizeChanged_.bind(this); - - var minimumFontRange = $('minimum-font-size'); - minimumFontRange.valueMap = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 20, 22, 24]; - minimumFontRange.continuous = false; - minimumFontRange.notifyChange = this.minimumRangeChanged_.bind(this); - minimumFontRange.notifyPrefChange = - this.minimumFontSizeChanged_.bind(this); - - var placeholder = loadTimeData.getString('fontSettingsPlaceholder'); - var elements = [$('standard-font-family'), $('serif-font-family'), - $('sans-serif-font-family'), $('fixed-font-family'), - $('font-encoding')]; - elements.forEach(function(el) { - el.appendChild(new Option(placeholder)); - el.setDisabled('noFontsAvailable', true); - }); - - $('font-settings-confirm').onclick = function() { - OptionsPage.closeOverlay(); - }; - }, - - /** - * Called by the options page when this page has been shown. - */ - didShowPage: function() { - // The fonts list may be large so we only load it when this page is - // loaded for the first time. This makes opening the options window - // faster and consume less memory if the user never opens the fonts - // dialog. - if (!this.hasShown) { - chrome.send('fetchFontsData'); - this.hasShown = true; - } - }, - - /** - * Called as the user changes the standard font size. This allows for - * reflecting the change in the UI before the preference has been changed. - * @param {Element} el The slider input element. - * @param {number} value The mapped value currently set by the slider. - * @private - */ - standardRangeChanged_: function(el, value) { - var fontSampleEl = $('standard-font-sample'); - this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, - true); - - fontSampleEl = $('serif-font-sample'); - this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, - true); - - fontSampleEl = $('sans-serif-font-sample'); - this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, - true); - - fontSampleEl = $('fixed-font-sample'); - this.setUpFontSample_(fontSampleEl, - value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, - fontSampleEl.style.fontFamily, false); - }, - - /** - * Sets the 'default_fixed_font_size' preference when the standard font - * size has been changed by the user. - * @param {Element} el The slider input element. - * @param {number} value The mapped value that has been saved. - * @private - */ - standardFontSizeChanged_: function(el, value) { - Preferences.setIntegerPref( - 'webkit.webprefs.default_fixed_font_size', - value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, ''); - }, - - /** - * Called as the user changes the miniumum font size. This allows for - * reflecting the change in the UI before the preference has been changed. - * @param {Element} el The slider input element. - * @param {number} value The mapped value currently set by the slider. - * @private - */ - minimumRangeChanged_: function(el, value) { - var fontSampleEl = $('minimum-font-sample'); - this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily, - true); - }, - - /** - * Sets the 'minimum_logical_font_size' preference when the minimum font - * size has been changed by the user. - * @param {Element} el The slider input element. - * @param {number} value The mapped value that has been saved. - * @private - */ - minimumFontSizeChanged_: function(el, value) { - Preferences.setIntegerPref( - 'webkit.webprefs.minimum_logical_font_size', value, ''); - }, - - /** - * Sets the text, font size and font family of the sample text. - * @param {Element} el The div containing the sample text. - * @param {number} size The font size of the sample text. - * @param {string} font The font family of the sample text. - * @param {bool} showSize True if the font size should appear in the sample. - * @private - */ - setUpFontSample_: function(el, size, font, showSize) { - var prefix = showSize ? (size + ': ') : ''; - el.textContent = prefix + - loadTimeData.getString('fontSettingsLoremIpsum'); - el.style.fontSize = size + 'px'; - if (font) - el.style.fontFamily = font; - }, - - /** - * Populates a select list and selects the specified item. - * @param {Element} element The select element to populate. - * @param {Array} items The array of items from which to populate. - * @param {string} selectedValue The selected item. - * @private - */ - populateSelect_: function(element, items, selectedValue) { - // Remove any existing content. - element.textContent = ''; - - // Insert new child nodes into select element. - var value, text, selected, option; - for (var i = 0; i < items.length; i++) { - value = items[i][0]; - text = items[i][1]; - dir = items[i][2]; - if (text) { - selected = value == selectedValue; - var option = new Option(text, value, false, selected); - option.dir = dir; - element.appendChild(option); - } else { - element.appendChild(document.createElement('hr')); - } - } - - element.setDisabled('noFontsAvailable', false); - } - }; - - // Chrome callbacks - FontSettings.setFontsData = function(fonts, encodings, selectedValues) { - FontSettings.getInstance().populateSelect_($('standard-font-family'), fonts, - selectedValues[0]); - FontSettings.getInstance().populateSelect_($('serif-font-family'), fonts, - selectedValues[1]); - FontSettings.getInstance().populateSelect_($('sans-serif-font-family'), - fonts, selectedValues[2]); - FontSettings.getInstance().populateSelect_($('fixed-font-family'), fonts, - selectedValues[3]); - FontSettings.getInstance().populateSelect_($('font-encoding'), encodings, - selectedValues[4]); - }; - - FontSettings.setUpStandardFontSample = function(font, size) { - FontSettings.getInstance().setUpFontSample_($('standard-font-sample'), size, - font, true); - }; - - FontSettings.setUpSerifFontSample = function(font, size) { - FontSettings.getInstance().setUpFontSample_($('serif-font-sample'), size, - font, true); - }; - - FontSettings.setUpSansSerifFontSample = function(font, size) { - FontSettings.getInstance().setUpFontSample_($('sans-serif-font-sample'), - size, font, true); - }; - - FontSettings.setUpFixedFontSample = function(font, size) { - FontSettings.getInstance().setUpFontSample_($('fixed-font-sample'), - size, font, false); - }; - - FontSettings.setUpMinimumFontSample = function(size) { - // If size is less than 6, represent it as six in the sample to account - // for the minimum logical font size. - if (size < 6) - size = 6; - FontSettings.getInstance().setUpFontSample_($('minimum-font-sample'), size, - null, true); - }; - - // Export - return { - FontSettings: FontSettings - }; -}); - diff --git a/chrome/browser/resources/options2/handler_options.css b/chrome/browser/resources/options2/handler_options.css deleted file mode 100644 index 116e5e7..0000000 --- a/chrome/browser/resources/options2/handler_options.css +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.handlers-column-headers { - display: -webkit-box; - font-size: 13px; - font-weight: bold; -} - -.handlers-type-column { - -webkit-margin-end: 10px; - -webkit-margin-start: 14px; - width: 100px; -} - -.handlers-site-column { - max-width: 180px; -} - -.handlers-site-column select { - max-width: 170px; -} - -.handlers-remove-column { - -webkit-box-flex: 1; -} - -.handlers-remove-link { - -webkit-transition: 150ms opacity; - color: #555; - cursor: pointer; - opacity: 0; - padding-left: 14px; - text-decoration: underline; -} - -div > .handlers-remove-column { - opacity: 0; -} - -div:not(.none):hover > .handlers-remove-column { - opacity: 1; -} - -#handlers { - min-height: 250px; -} - -#handler-options list { - border: solid 1px #D9D9D9; - border-radius: 2px; - margin-bottom: 10px; - margin-top: 4px; -} diff --git a/chrome/browser/resources/options2/handler_options.html b/chrome/browser/resources/options2/handler_options.html deleted file mode 100644 index ffa95b7..0000000 --- a/chrome/browser/resources/options2/handler_options.html +++ /dev/null @@ -1,36 +0,0 @@ - diff --git a/chrome/browser/resources/options2/handler_options.js b/chrome/browser/resources/options2/handler_options.js deleted file mode 100644 index c3d641a..0000000 --- a/chrome/browser/resources/options2/handler_options.js +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - ///////////////////////////////////////////////////////////////////////////// - // HandlerOptions class: - - /** - * Encapsulated handling of handler options page. - * @constructor - */ - function HandlerOptions() { - this.activeNavTab = null; - OptionsPage.call(this, - 'handlers', - loadTimeData.getString('handlersPageTabTitle'), - 'handler-options'); - } - - cr.addSingletonGetter(HandlerOptions); - - HandlerOptions.prototype = { - __proto__: OptionsPage.prototype, - - /** - * The handlers list. - * @type {DeletableItemList} - * @private - */ - handlersList_: null, - - /** @inheritDoc */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.createHandlersList_(); - - $('handler-options-overlay-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - /** - * Creates, decorates and initializes the handlers list. - * @private - */ - createHandlersList_: function() { - this.handlersList_ = $('handlers-list'); - options.HandlersList.decorate(this.handlersList_); - this.handlersList_.autoExpands = true; - - this.ignoredHandlersList_ = $('ignored-handlers-list'); - options.IgnoredHandlersList.decorate(this.ignoredHandlersList_); - this.ignoredHandlersList_.autoExpands = true; - }, - }; - - /** - * Sets the list of handlers shown by the view. - * @param {Array} Handlers to be shown in the view. - */ - HandlerOptions.setHandlers = function(handlers) { - $('handlers-list').setHandlers(handlers); - }; - - /** - * Sets the list of ignored handlers shown by the view. - * @param {Array} Handlers to be shown in the view. - */ - HandlerOptions.setIgnoredHandlers = function(handlers) { - $('ignored-handlers-section').hidden = handlers.length == 0; - $('ignored-handlers-list').setHandlers(handlers); - }; - - return { - HandlerOptions: HandlerOptions - }; -}); diff --git a/chrome/browser/resources/options2/handler_options_list.js b/chrome/browser/resources/options2/handler_options_list.js deleted file mode 100644 index 93c139a..0000000 --- a/chrome/browser/resources/options2/handler_options_list.js +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var List = cr.ui.List; - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var HandlerOptions = options.HandlerOptions; - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - - /** - * Creates a new ignored protocol / content handler list item. - * - * Accepts values in the form - * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'], - * @param {Object} entry A dictionary describing the handlers for a given - * protocol. - * @constructor - * @extends {cr.ui.DeletableItemList} - */ - function IgnoredHandlersListItem(entry) { - var el = cr.doc.createElement('div'); - el.dataItem = entry; - el.__proto__ = IgnoredHandlersListItem.prototype; - el.decorate(); - return el; - } - - IgnoredHandlersListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - // Protocol. - var protocolElement = document.createElement('div'); - protocolElement.textContent = this.dataItem[0]; - protocolElement.className = 'handlers-type-column'; - this.contentElement_.appendChild(protocolElement); - - // Site title. - var titleElement = document.createElement('div'); - titleElement.textContent = this.dataItem[2]; - titleElement.className = 'handlers-site-column'; - titleElement.title = this.dataItem[1]; - this.contentElement_.appendChild(titleElement); - }, - }; - - - var IgnoredHandlersList = cr.ui.define('list'); - - IgnoredHandlersList.prototype = { - __proto__: DeletableItemList.prototype, - - createItem: function(entry) { - return new IgnoredHandlersListItem(entry); - }, - - deleteItemAtIndex: function(index) { - chrome.send('removeIgnoredHandler', [this.dataModel.item(index)]); - }, - - /** - * The length of the list. - */ - get length() { - return this.dataModel.length; - }, - - /** - * Set the protocol handlers displayed by this list. See - * IgnoredHandlersListItem for an example of the format the list should - * take. - * - * @param {Object} list A list of ignored protocol handlers. - */ - setHandlers: function(list) { - this.dataModel = new ArrayDataModel(list); - }, - }; - - - - /** - * Creates a new protocol / content handler list item. - * - * Accepts values in the form - * { protocol: 'mailto', - * handlers: [ - * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'], - * ..., - * ], - * } - * @param {Object} entry A dictionary describing the handlers for a given - * protocol. - * @constructor - * @extends {cr.ui.ListItem} - */ - function HandlerListItem(entry) { - var el = cr.doc.createElement('div'); - el.dataItem = entry; - el.__proto__ = HandlerListItem.prototype; - el.decorate(); - return el; - } - - HandlerListItem.prototype = { - __proto__: ListItem.prototype, - - buildWidget_: function(data, delegate) { - // Protocol. - var protocolElement = document.createElement('div'); - protocolElement.textContent = data.protocol; - protocolElement.className = 'handlers-type-column'; - this.appendChild(protocolElement); - - // Handler selection. - var handlerElement = document.createElement('div'); - var selectElement = document.createElement('select'); - var defaultOptionElement = document.createElement('option'); - defaultOptionElement.selected = data.default_handler == -1; - defaultOptionElement.textContent = - loadTimeData.getString('handlers_none_handler'); - defaultOptionElement.value = -1; - selectElement.appendChild(defaultOptionElement); - - for (var i = 0; i < data.handlers.length; ++i) { - var optionElement = document.createElement('option'); - optionElement.selected = i == data.default_handler; - optionElement.textContent = data.handlers[i][2]; - optionElement.value = i; - selectElement.appendChild(optionElement); - } - - selectElement.addEventListener('change', function(e) { - var index = e.target.value; - if (index == -1) { - this.classList.add('none'); - delegate.clearDefault(data.protocol); - } else { - handlerElement.classList.remove('none'); - delegate.setDefault(data.handlers[index]); - } - }); - handlerElement.appendChild(selectElement); - handlerElement.className = 'handlers-site-column'; - if (data.default_handler == -1) - this.classList.add('none'); - this.appendChild(handlerElement); - - // Remove link. - var removeElement = document.createElement('div'); - removeElement.textContent = - loadTimeData.getString('handlers_remove_link'); - removeElement.addEventListener('click', function(e) { - var value = selectElement ? selectElement.value : 0; - delegate.removeHandler(value, data.handlers[value]); - }); - removeElement.className = 'handlers-remove-column handlers-remove-link'; - this.appendChild(removeElement); - }, - - /** @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - - var self = this; - var delegate = { - removeHandler: function(index, handler) { - chrome.send('removeHandler', [handler]); - }, - setDefault: function(handler) { - chrome.send('setDefault', [handler]); - }, - clearDefault: function(protocol) { - chrome.send('clearDefault', [protocol]); - }, - }; - - this.buildWidget_(this.dataItem, delegate); - }, - }; - - /** - * Create a new passwords list. - * @constructor - * @extends {cr.ui.List} - */ - var HandlersList = cr.ui.define('list'); - - HandlersList.prototype = { - __proto__: List.prototype, - - /** @inheritDoc */ - createItem: function(entry) { - return new HandlerListItem(entry); - }, - - /** - * The length of the list. - */ - get length() { - return this.dataModel.length; - }, - - /** - * Set the protocol handlers displayed by this list. - * See HandlerListItem for an example of the format the list should take. - * - * @param {Object} list A list of protocols with their registered handlers. - */ - setHandlers: function(list) { - this.dataModel = new ArrayDataModel(list); - }, - }; - - return { - IgnoredHandlersListItem: IgnoredHandlersListItem, - IgnoredHandlersList: IgnoredHandlersList, - HandlerListItem: HandlerListItem, - HandlersList: HandlersList, - }; -}); diff --git a/chrome/browser/resources/options2/home_page_overlay.css b/chrome/browser/resources/options2/home_page_overlay.css deleted file mode 100644 index bcece11..0000000 --- a/chrome/browser/resources/options2/home_page_overlay.css +++ /dev/null @@ -1,9 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#homepage-url-field { - display: inline-block; - margin-left: 10px; - width: 225px; -} diff --git a/chrome/browser/resources/options2/home_page_overlay.html b/chrome/browser/resources/options2/home_page_overlay.html deleted file mode 100644 index 2329507..0000000 --- a/chrome/browser/resources/options2/home_page_overlay.html +++ /dev/null @@ -1,32 +0,0 @@ - diff --git a/chrome/browser/resources/options2/home_page_overlay.js b/chrome/browser/resources/options2/home_page_overlay.js deleted file mode 100644 index 9753ab4..0000000 --- a/chrome/browser/resources/options2/home_page_overlay.js +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var SettingsDialog = options.SettingsDialog; - - /** - * HomePageOverlay class - * Dialog that allows users to set the home page. - * @extends {SettingsDialog} - */ - function HomePageOverlay() { - SettingsDialog.call(this, 'homePageOverlay', - loadTimeData.getString('homePageOverlayTabTitle'), - 'home-page-overlay', - $('home-page-confirm'), $('home-page-cancel')); - } - - cr.addSingletonGetter(HomePageOverlay); - - HomePageOverlay.prototype = { - __proto__: SettingsDialog.prototype, - - /** - * An autocomplete list that can be attached to the home page URL field. - * @type {cr.ui.AutocompleteList} - * @private - */ - autocompleteList_: null, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - SettingsDialog.prototype.initializePage.call(this); - - var self = this; - $('homepage-use-ntp').onchange = this.updateHomePageInput_.bind(this); - $('homepage-use-url').onchange = this.updateHomePageInput_.bind(this); - - var urlField = $('homepage-url-field'); - urlField.addEventListener('keydown', function(event) { - // Focus the 'OK' button when the user hits enter since people expect - // feedback indicating that they are done editing. - if (event.keyIdentifier == 'Enter' && self.autocompleteList_.hidden) - $('home-page-confirm').focus(); - }); - urlField.addEventListener('change', this.updateFavicon_.bind(this)); - - var suggestionList = new cr.ui.AutocompleteList(); - suggestionList.autoExpands = true; - suggestionList.suggestionUpdateRequestCallback = - this.requestAutocompleteSuggestions_.bind(this); - $('home-page-overlay').appendChild(suggestionList); - this.autocompleteList_ = suggestionList; - - urlField.addEventListener('focus', function(event) { - self.autocompleteList_.attachToInput(urlField); - }); - urlField.addEventListener('blur', function(event) { - self.autocompleteList_.detach(); - }); - - // Text fields may change widths when the window changes size, so make - // sure the suggestion list stays in sync. - window.addEventListener('resize', function() { - self.autocompleteList_.syncWidthToInput(); - }); - }, - - /** @inheritDoc */ - didShowPage: function() { - this.updateHomePageInput_(); - this.updateFavicon_(); - }, - - /** - * Updates the state of the homepage text input. The input is enabled only - * if the |homepage-use-url| radio button is checked. - * @private - */ - updateHomePageInput_: function() { - var urlField = $('homepage-url-field'); - var homePageUseURL = $('homepage-use-url'); - urlField.setDisabled('radio-choice', !homePageUseURL.checked); - }, - - /** - * Updates the background of the url field to show the favicon for the - * URL that is currently typed in. - * @private - */ - updateFavicon_: function() { - var urlField = $('homepage-url-field'); - urlField.style.backgroundImage = url('chrome://favicon/' + - urlField.value); - }, - - /** - * Sends an asynchronous request for new autocompletion suggestions for the - * the given query. When new suggestions are available, the C++ handler will - * call updateAutocompleteSuggestions_. - * @param {string} query List of autocomplete suggestions. - * @private - */ - requestAutocompleteSuggestions_: function(query) { - chrome.send('requestAutocompleteSuggestionsForHomePage', [query]); - }, - - /** - * Updates the autocomplete suggestion list with the given entries. - * @param {Array} pages List of autocomplete suggestions. - * @private - */ - updateAutocompleteSuggestions_: function(suggestions) { - var list = this.autocompleteList_; - // If the trigger for this update was a value being selected from the - // current list, do nothing. - if (list.targetInput && list.selectedItem && - list.selectedItem['url'] == list.targetInput.value) - return; - list.suggestions = suggestions; - }, - - /** - * Sets the 'show home button' and 'home page is new tab page' preferences. - * (The home page url preference is set automatically by the SettingsDialog - * code.) - */ - handleConfirm: function() { - // Strip whitespace. - var urlField = $('homepage-url-field'); - var homePageValue = urlField.value.replace(/\s*/g, ''); - urlField.value = homePageValue; - - // Don't save an empty URL for the home page. If the user left the field - // empty, switch to the New Tab page. - if (!homePageValue) - $('homepage-use-ntp').checked = true; - - SettingsDialog.prototype.handleConfirm.call(this); - }, - }; - - HomePageOverlay.updateAutocompleteSuggestions = function() { - var instance = HomePageOverlay.getInstance(); - instance.updateAutocompleteSuggestions_.apply(instance, arguments); - }; - - // Export - return { - HomePageOverlay: HomePageOverlay - }; -}); diff --git a/chrome/browser/resources/options2/import_data_overlay.css b/chrome/browser/resources/options2/import_data_overlay.css deleted file mode 100644 index 6cb5262..0000000 --- a/chrome/browser/resources/options2/import_data_overlay.css +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#import-from-div { - margin-bottom: 20px; -} - -#import-checkboxes > div:not(:first-child) { - -webkit-padding-start: 8px; - margin: 5px 0; -} - -#import-throbber { - margin: 4px 10px; - vertical-align: middle; - visibility: hidden; -} - -#import-success-header { - font-size: 1.2em; -} - -#import-success-image { - margin: 20px; - text-align: center; -} diff --git a/chrome/browser/resources/options2/import_data_overlay.html b/chrome/browser/resources/options2/import_data_overlay.html deleted file mode 100644 index 2f5880a..0000000 --- a/chrome/browser/resources/options2/import_data_overlay.html +++ /dev/null @@ -1,71 +0,0 @@ - diff --git a/chrome/browser/resources/options2/import_data_overlay.js b/chrome/browser/resources/options2/import_data_overlay.js deleted file mode 100644 index 1a38402..0000000 --- a/chrome/browser/resources/options2/import_data_overlay.js +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - - /** - * ImportDataOverlay class - * Encapsulated handling of the 'Import Data' overlay page. - * @class - */ - function ImportDataOverlay() { - OptionsPage.call(this, - 'importData', - loadTimeData.getString('importDataOverlayTabTitle'), - 'import-data-overlay'); - } - - cr.addSingletonGetter(ImportDataOverlay); - - ImportDataOverlay.prototype = { - // Inherit from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - OptionsPage.prototype.initializePage.call(this); - - var self = this; - var checkboxes = - document.querySelectorAll('#import-checkboxes input[type=checkbox]'); - for (var i = 0; i < checkboxes.length; i++) { - checkboxes[i].onchange = function() { - self.validateCommitButton_(); - }; - } - - $('import-browsers').onchange = function() { - self.updateCheckboxes_(); - self.validateCommitButton_(); - }; - - $('import-data-commit').onclick = function() { - chrome.send('importData', [ - String($('import-browsers').selectedIndex), - String($('import-history').checked), - String($('import-favorites').checked), - String($('import-passwords').checked), - String($('import-search').checked)]); - }; - - $('import-data-cancel').onclick = function() { - ImportDataOverlay.dismiss(); - }; - - $('import-data-show-bookmarks-bar').onchange = function() { - // Note: The callback 'toggleShowBookmarksBar' is handled within the - // browser options handler -- rather than the import data handler -- - // as the implementation is shared by several clients. - chrome.send('toggleShowBookmarksBar'); - } - - $('import-data-confirm').onclick = function() { - ImportDataOverlay.dismiss(); - }; - - // Form controls are disabled until the profile list has been loaded. - self.setControlsSensitive_(false); - }, - - /** - * Set enabled and checked state of the commit button. - * @private - */ - validateCommitButton_: function() { - var somethingToImport = - $('import-history').checked || $('import-favorites').checked || - $('import-passwords').checked || $('import-search').checked; - $('import-data-commit').disabled = !somethingToImport; - }, - - /** - * Sets the sensitivity of all the checkboxes and the commit button. - * @private - */ - setControlsSensitive_: function(sensitive) { - var checkboxes = - document.querySelectorAll('#import-checkboxes input[type=checkbox]'); - for (var i = 0; i < checkboxes.length; i++) - this.setUpCheckboxState_(checkboxes[i], sensitive); - $('import-data-commit').disabled = !sensitive; - }, - - /** - * Set enabled and checked states a checkbox element. - * @param {Object} checkbox A checkbox element. - * @param {boolean} enabled The enabled state of the chekbox. - * @private - */ - setUpCheckboxState_: function(checkbox, enabled) { - checkbox.setDisabled('noProfileData', !enabled); - }, - - /** - * Update the enabled and checked states of all checkboxes. - * @private - */ - updateCheckboxes_: function() { - var index = $('import-browsers').selectedIndex; - var browserProfile; - if (this.browserProfiles.length > index) - browserProfile = this.browserProfiles[index]; - var importOptions = ['history', 'favorites', 'passwords', 'search']; - for (var i = 0; i < importOptions.length; i++) { - var checkbox = $('import-' + importOptions[i]); - var enable = browserProfile && browserProfile[importOptions[i]]; - this.setUpCheckboxState_(checkbox, enable); - } - }, - - /** - * Update the supported browsers popup with given entries. - * @param {array} browsers List of supported browsers name. - * @private - */ - updateSupportedBrowsers_: function(browsers) { - this.browserProfiles = browsers; - var browserSelect = $('import-browsers'); - browserSelect.remove(0); // Remove the 'Loading...' option. - browserSelect.textContent = ''; - var browserCount = browsers.length; - - if (browserCount == 0) { - var option = new Option(loadTimeData.getString('noProfileFound'), 0); - browserSelect.appendChild(option); - - this.setControlsSensitive_(false); - } else { - this.setControlsSensitive_(true); - for (var i = 0; i < browserCount; i++) { - var browser = browsers[i]; - var option = new Option(browser['name'], browser['index']); - browserSelect.appendChild(option); - } - - this.updateCheckboxes_(); - this.validateCommitButton_(); - } - }, - - /** - * Clear import prefs set when user checks/unchecks a checkbox so that each - * checkbox goes back to the default "checked" state (or alternatively, to - * the state set by a recommended policy). - * @private - */ - clearUserPrefs_: function() { - var importPrefs = ['import_history', - 'import_bookmarks', - 'import_saved_passwords', - 'import_search_engine']; - for (var i = 0; i < importPrefs.length; i++) - Preferences.clearPref(importPrefs[i], undefined); - }, - - /** - * Update the dialog layout to reflect success state. - * @param {boolean} success If true, show success dialog elements. - * @private - */ - updateSuccessState_: function(success) { - var sections = document.querySelectorAll('.import-data-configure'); - for (var i = 0; i < sections.length; i++) - sections[i].hidden = success; - - sections = document.querySelectorAll('.import-data-success'); - for (var i = 0; i < sections.length; i++) - sections[i].hidden = !success; - }, - }; - - ImportDataOverlay.clearUserPrefs = function() { - ImportDataOverlay.getInstance().clearUserPrefs_(); - }; - - /** - * Update the supported browsers popup with given entries. - * @param {array} list of supported browsers name. - */ - ImportDataOverlay.updateSupportedBrowsers = function(browsers) { - ImportDataOverlay.getInstance().updateSupportedBrowsers_(browsers); - }; - - /** - * Update the UI to reflect whether an import operation is in progress. - * @param {boolean} state True if an import operation is in progress. - */ - ImportDataOverlay.setImportingState = function(state) { - var checkboxes = - document.querySelectorAll('#import-checkboxes input[type=checkbox]'); - for (var i = 0; i < checkboxes.length; i++) - checkboxes[i].setDisabled('Importing', state); - if (!state) - ImportDataOverlay.getInstance().updateCheckboxes_(); - $('import-browsers').disabled = state; - $('import-throbber').style.visibility = state ? 'visible' : 'hidden'; - ImportDataOverlay.getInstance().validateCommitButton_(); - }; - - /** - * Remove the import overlay from display. - */ - ImportDataOverlay.dismiss = function() { - ImportDataOverlay.clearUserPrefs(); - OptionsPage.closeOverlay(); - }; - - /** - * Show a message confirming the success of the import operation. - */ - ImportDataOverlay.confirmSuccess = function() { - var showBookmarksMessage = $('import-favorites').checked; - ImportDataOverlay.setImportingState(false); - $('import-find-your-bookmarks').hidden = !showBookmarksMessage; - ImportDataOverlay.getInstance().updateSuccessState_(true); - }; - - /** - * Show the import data overlay. - */ - ImportDataOverlay.show = function() { - // Make sure that any previous import success message is hidden, and - // we're showing the UI to import further data. - ImportDataOverlay.getInstance().updateSuccessState_(false); - - OptionsPage.navigateToPage('importData'); - }; - - // Export - return { - ImportDataOverlay: ImportDataOverlay - }; -}); diff --git a/chrome/browser/resources/options2/inline_editable_list.js b/chrome/browser/resources/options2/inline_editable_list.js deleted file mode 100644 index c9d6a93..0000000 --- a/chrome/browser/resources/options2/inline_editable_list.js +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - - /** - * Creates a new list item with support for inline editing. - * @constructor - * @extends {options.DeletableListItem} - */ - function InlineEditableItem() { - var el = cr.doc.createElement('div'); - InlineEditableItem.decorate(el); - return el; - } - - /** - * Decorates an element as a inline-editable list item. Note that this is - * a subclass of DeletableItem. - * @param {!HTMLElement} el The element to decorate. - */ - InlineEditableItem.decorate = function(el) { - el.__proto__ = InlineEditableItem.prototype; - el.decorate(); - }; - - InlineEditableItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * Whether or not this item can be edited. - * @type {boolean} - * @private - */ - editable_: true, - - /** - * Whether or not this is a placeholder for adding a new item. - * @type {boolean} - * @private - */ - isPlaceholder_: false, - - /** - * Fields associated with edit mode. - * @type {array} - * @private - */ - editFields_: null, - - /** - * Whether or not the current edit should be considered cancelled, rather - * than committed, when editing ends. - * @type {boolean} - * @private - */ - editCancelled_: true, - - /** - * The editable item corresponding to the last click, if any. Used to decide - * initial focus when entering edit mode. - * @type {HTMLElement} - * @private - */ - editClickTarget_: null, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - this.editFields_ = []; - this.addEventListener('mousedown', this.handleMouseDown_); - this.addEventListener('keydown', this.handleKeyDown_); - this.addEventListener('leadChange', this.handleLeadChange_); - }, - - /** @inheritDoc */ - selectionChanged: function() { - this.updateEditState(); - }, - - /** - * Called when this element gains or loses 'lead' status. Updates editing - * mode accordingly. - * @private - */ - handleLeadChange_: function() { - this.updateEditState(); - }, - - /** - * Updates the edit state based on the current selected and lead states. - */ - updateEditState: function() { - if (this.editable) - this.editing = this.selected && this.lead; - }, - - /** - * Whether the user is currently editing the list item. - * @type {boolean} - */ - get editing() { - return this.hasAttribute('editing'); - }, - set editing(editing) { - if (this.editing == editing) - return; - - if (editing) - this.setAttribute('editing', ''); - else - this.removeAttribute('editing'); - - if (editing) { - this.editCancelled_ = false; - - cr.dispatchSimpleEvent(this, 'edit', true); - - var focusElement = this.editClickTarget_ || this.initialFocusElement; - this.editClickTarget_ = null; - - // When this is called in response to the selectedChange event, - // the list grabs focus immediately afterwards. Thus we must delay - // our focus grab. - var self = this; - if (focusElement) { - window.setTimeout(function() { - // Make sure we are still in edit mode by the time we execute. - if (self.editing) { - focusElement.focus(); - focusElement.select(); - } - }, 50); - } - } else { - if (!this.editCancelled_ && this.hasBeenEdited && - this.currentInputIsValid) { - if (this.isPlaceholder) - this.parentNode.focusPlaceholder = true; - - this.updateStaticValues_(); - cr.dispatchSimpleEvent(this, 'commitedit', true); - } else { - this.resetEditableValues_(); - cr.dispatchSimpleEvent(this, 'canceledit', true); - } - } - }, - - /** - * Whether the item is editable. - * @type {boolean} - */ - get editable() { - return this.editable_; - }, - set editable(editable) { - this.editable_ = editable; - if (!editable) - this.editing = false; - }, - - /** - * Whether the item is a new item placeholder. - * @type {boolean} - */ - get isPlaceholder() { - return this.isPlaceholder_; - }, - set isPlaceholder(isPlaceholder) { - this.isPlaceholder_ = isPlaceholder; - if (isPlaceholder) - this.deletable = false; - }, - - /** - * The HTML element that should have focus initially when editing starts, - * if a specific element wasn't clicked. - * Defaults to the first element; can be overridden by subclasses if - * a different element should be focused. - * @type {HTMLElement} - */ - get initialFocusElement() { - return this.contentElement.querySelector('input'); - }, - - /** - * Whether the input in currently valid to submit. If this returns false - * when editing would be submitted, either editing will not be ended, - * or it will be cancelled, depending on the context. - * Can be overridden by subclasses to perform input validation. - * @type {boolean} - */ - get currentInputIsValid() { - return true; - }, - - /** - * Returns true if the item has been changed by an edit. - * Can be overridden by subclasses to return false when nothing has changed - * to avoid unnecessary commits. - * @type {boolean} - */ - get hasBeenEdited() { - return true; - }, - - /** - * Returns a div containing an , as well as static text if - * isPlaceholder is not true. - * @param {string} text The text of the cell. - * @return {HTMLElement} The HTML element for the cell. - * @private - */ - createEditableTextCell: function(text) { - var container = this.ownerDocument.createElement('div'); - - if (!this.isPlaceholder) { - var textEl = this.ownerDocument.createElement('div'); - textEl.className = 'static-text'; - textEl.textContent = text; - textEl.setAttribute('displaymode', 'static'); - container.appendChild(textEl); - } - - var inputEl = this.ownerDocument.createElement('input'); - inputEl.type = 'text'; - inputEl.value = text; - if (!this.isPlaceholder) { - inputEl.setAttribute('displaymode', 'edit'); - inputEl.staticVersion = textEl; - } else { - // At this point |this| is not attached to the parent list yet, so give - // a short timeout in order for the attachment to occur. - var self = this; - window.setTimeout(function() { - var list = self.parentNode; - if (list && list.focusPlaceholder) { - list.focusPlaceholder = false; - if (list.shouldFocusPlaceholder()) - inputEl.focus(); - } - }, 50); - } - - inputEl.addEventListener('focus', this.handleFocus_.bind(this)); - container.appendChild(inputEl); - this.editFields_.push(inputEl); - - return container; - }, - - /** - * Resets the editable version of any controls created by createEditable* - * to match the static text. - * @private - */ - resetEditableValues_: function() { - var editFields = this.editFields_; - for (var i = 0; i < editFields.length; i++) { - var staticLabel = editFields[i].staticVersion; - if (!staticLabel && !this.isPlaceholder) - continue; - - if (editFields[i].tagName == 'INPUT') { - editFields[i].value = - this.isPlaceholder ? '' : staticLabel.textContent; - } - // Add more tag types here as new createEditable* methods are added. - - editFields[i].setCustomValidity(''); - } - }, - - /** - * Sets the static version of any controls created by createEditable* - * to match the current value of the editable version. Called on commit so - * that there's no flicker of the old value before the model updates. - * @private - */ - updateStaticValues_: function() { - var editFields = this.editFields_; - for (var i = 0; i < editFields.length; i++) { - var staticLabel = editFields[i].staticVersion; - if (!staticLabel) - continue; - - if (editFields[i].tagName == 'INPUT') - staticLabel.textContent = editFields[i].value; - // Add more tag types here as new createEditable* methods are added. - } - }, - - /** - * Called when a key is pressed. Handles committing and canceling edits. - * @param {Event} e The key down event. - * @private - */ - handleKeyDown_: function(e) { - if (!this.editing) - return; - - var endEdit = false; - switch (e.keyIdentifier) { - case 'U+001B': // Esc - this.editCancelled_ = true; - endEdit = true; - break; - case 'Enter': - if (this.currentInputIsValid) - endEdit = true; - break; - } - - if (endEdit) { - // Blurring will trigger the edit to end; see InlineEditableItemList. - this.ownerDocument.activeElement.blur(); - // Make sure that handled keys aren't passed on and double-handled. - // (e.g., esc shouldn't both cancel an edit and close a subpage) - e.stopPropagation(); - } - }, - - /** - * Called when the list item is clicked. If the click target corresponds to - * an editable item, stores that item to focus when edit mode is started. - * @param {Event} e The mouse down event. - * @private - */ - handleMouseDown_: function(e) { - if (!this.editable || this.editing) - return; - - var clickTarget = e.target; - var editFields = this.editFields_; - for (var i = 0; i < editFields.length; i++) { - if (editFields[i] == clickTarget || - editFields[i].staticVersion == clickTarget) { - this.editClickTarget_ = editFields[i]; - return; - } - } - }, - }; - - /** - * Takes care of committing changes to inline editable list items when the - * window loses focus. - */ - function handleWindowBlurs() { - window.addEventListener('blur', function(e) { - var itemAncestor = findAncestor(document.activeElement, function(node) { - return node instanceof InlineEditableItem; - }); - if (itemAncestor) - document.activeElement.blur(); - }); - } - handleWindowBlurs(); - - var InlineEditableItemList = cr.ui.define('list'); - - InlineEditableItemList.prototype = { - __proto__: DeletableItemList.prototype, - - /** - * Focuses the input element of the placeholder if true. - * @type {boolean} - */ - focusPlaceholder: false, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.setAttribute('inlineeditable', ''); - this.addEventListener('hasElementFocusChange', - this.handleListFocusChange_); - }, - - /** - * Called when the list hierarchy as a whole loses or gains focus; starts - * or ends editing for the lead item if necessary. - * @param {Event} e The change event. - * @private - */ - handleListFocusChange_: function(e) { - var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex); - if (leadItem) { - if (e.newValue) - leadItem.updateEditState(); - else - leadItem.editing = false; - } - }, - - /** - * May be overridden by subclasses to disable focusing the placeholder. - * @return {boolean} True if the placeholder element should be focused on - * edit commit. - */ - shouldFocusPlaceholder: function() { - return true; - }, - }; - - // Export - return { - InlineEditableItem: InlineEditableItem, - InlineEditableItemList: InlineEditableItemList, - }; -}); diff --git a/chrome/browser/resources/options2/instant_confirm_overlay.css b/chrome/browser/resources/options2/instant_confirm_overlay.css deleted file mode 100644 index 3f6d77f..0000000 --- a/chrome/browser/resources/options2/instant_confirm_overlay.css +++ /dev/null @@ -1,16 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#instantConfirmOverlay { - width: 500px; -} - -#instant-action-spacer { - -webkit-box-flex: 1; -} - -#instant-confirm-text { - white-space: pre-line; -} diff --git a/chrome/browser/resources/options2/instant_confirm_overlay.html b/chrome/browser/resources/options2/instant_confirm_overlay.html deleted file mode 100644 index 54653b2..0000000 --- a/chrome/browser/resources/options2/instant_confirm_overlay.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/chrome/browser/resources/options2/instant_confirm_overlay.js b/chrome/browser/resources/options2/instant_confirm_overlay.js deleted file mode 100644 index 6cadd5c..0000000 --- a/chrome/browser/resources/options2/instant_confirm_overlay.js +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var SettingsDialog = options.SettingsDialog; - - /* - * InstantConfirmOverlay class - * Dialog to confirm that the user really wants to enable Chrome Instant. - * @extends {SettingsDialog} - */ - function InstantConfirmOverlay() { - SettingsDialog.call(this, - 'instantConfirm', - loadTimeData.getString('instantConfirmTitle'), - 'instantConfirmOverlay', - $('instantConfirmOk'), - $('instantConfirmCancel')); - }; - - cr.addSingletonGetter(InstantConfirmOverlay); - - InstantConfirmOverlay.prototype = { - __proto__: SettingsDialog.prototype, - - /** @inheritDoc */ - initializePage: function() { - SettingsDialog.prototype.initializePage.call(this); - }, - - /** @inheritDoc */ - handleConfirm: function() { - SettingsDialog.prototype.handleConfirm.call(this); - Preferences.setBooleanPref('instant.confirm_dialog_shown', true); - Preferences.setBooleanPref('instant.enabled', true); - }, - - /** @inheritDoc */ - handleCancel: function() { - SettingsDialog.prototype.handleCancel.call(this); - $('instant-enabled-control').checked = false; - }, - }; - - // Export - return { - InstantConfirmOverlay: InstantConfirmOverlay - }; -}); diff --git a/chrome/browser/resources/options2/language_add_language_overlay.html b/chrome/browser/resources/options2/language_add_language_overlay.html deleted file mode 100644 index 45a68c0..0000000 --- a/chrome/browser/resources/options2/language_add_language_overlay.html +++ /dev/null @@ -1,26 +0,0 @@ - diff --git a/chrome/browser/resources/options2/language_add_language_overlay.js b/chrome/browser/resources/options2/language_add_language_overlay.js deleted file mode 100644 index d5ebf93..0000000 --- a/chrome/browser/resources/options2/language_add_language_overlay.js +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/////////////////////////////////////////////////////////////////////////////// -// AddLanguageOverlay class: - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * Encapsulated handling of ChromeOS add language overlay page. - * @constructor - */ - function AddLanguageOverlay() { - OptionsPage.call(this, 'addLanguage', - loadTimeData.getString('add_button'), - 'add-language-overlay-page'); - } - - cr.addSingletonGetter(AddLanguageOverlay); - - AddLanguageOverlay.prototype = { - // Inherit AddLanguageOverlay from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * Initializes AddLanguageOverlay page. - * Calls base class implementation to starts preference initialization. - */ - initializePage: function() { - // Call base class implementation to starts preference initialization. - OptionsPage.prototype.initializePage.call(this); - - // Set up the cancel button. - $('add-language-overlay-cancel-button').onclick = function(e) { - OptionsPage.closeOverlay(); - }; - - // Create the language list with which users can add a language. - var addLanguageList = $('add-language-overlay-language-list'); - var languageListData = loadTimeData.getValue('languageList'); - for (var i = 0; i < languageListData.length; i++) { - var language = languageListData[i]; - var displayText = language.displayName; - // If the native name is different, add it. - if (language.displayName != language.nativeDisplayName) { - displayText += ' - ' + language.nativeDisplayName; - } - - if (cr.isChromeOS) { - var button = document.createElement('button'); - button.className = 'link-button'; - button.textContent = displayText; - button.languageCode = language.code; - var li = document.createElement('li'); - li.languageCode = language.code; - li.appendChild(button); - addLanguageList.appendChild(li); - } else { - var option = document.createElement('option'); - option.value = language.code; - option.textContent = displayText; - addLanguageList.appendChild(option); - } - } - }, - }; - - return { - AddLanguageOverlay: AddLanguageOverlay - }; -}); diff --git a/chrome/browser/resources/options2/language_list.js b/chrome/browser/resources/options2/language_list.js deleted file mode 100644 index e036490..0000000 --- a/chrome/browser/resources/options2/language_list.js +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - /** @const */ var List = cr.ui.List; - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; - - /** - * Creates a new Language list item. - * @param {Object} languageInfo The information of the language. - * @constructor - * @extends {DeletableItem.ListItem} - */ - function LanguageListItem(languageInfo) { - var el = cr.doc.createElement('li'); - el.__proto__ = LanguageListItem.prototype; - el.language_ = languageInfo; - el.decorate(); - return el; - }; - - LanguageListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * The language code of this language. - * @type {String} - * @private - */ - languageCode_: null, - - /** @inheritDoc */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - var languageCode = this.language_.code; - var languageOptions = options.LanguageOptions.getInstance(); - this.deletable = languageOptions.languageIsDeletable(languageCode); - this.languageCode = languageCode; - this.languageName = cr.doc.createElement('div'); - this.languageName.className = 'language-name'; - this.languageName.dir = this.language_.textDirection; - this.languageName.textContent = this.language_.displayName; - this.contentElement.appendChild(this.languageName); - this.title = this.language_.nativeDisplayName; - this.draggable = true; - }, - }; - - /** - * Creates a new language list. - * @param {Object=} opt_propertyBag Optional properties. - * @constructor - * @extends {cr.ui.List} - */ - var LanguageList = cr.ui.define('list'); - - /** - * Gets information of a language from the given language code. - * @param {string} languageCode Language code (ex. "fr"). - */ - LanguageList.getLanguageInfoFromLanguageCode = function(languageCode) { - // Build the language code to language info dictionary at first time. - if (!this.languageCodeToLanguageInfo_) { - this.languageCodeToLanguageInfo_ = {}; - var languageList = loadTimeData.getValue('languageList'); - for (var i = 0; i < languageList.length; i++) { - var languageInfo = languageList[i]; - this.languageCodeToLanguageInfo_[languageInfo.code] = languageInfo; - } - } - - return this.languageCodeToLanguageInfo_[languageCode]; - } - - /** - * Returns true if the given language code is valid. - * @param {string} languageCode Language code (ex. "fr"). - */ - LanguageList.isValidLanguageCode = function(languageCode) { - // Having the display name for the language code means that the - // language code is valid. - if (LanguageList.getLanguageInfoFromLanguageCode(languageCode)) { - return true; - } - return false; - } - - LanguageList.prototype = { - __proto__: DeletableItemList.prototype, - - // The list item being dragged. - draggedItem: null, - // The drop position information: "below" or "above". - dropPos: null, - // The preference is a CSV string that describes preferred languages - // in Chrome OS. The language list is used for showing the language - // list in "Language and Input" options page. - preferredLanguagesPref: 'settings.language.preferred_languages', - // The preference is a CSV string that describes accept languages used - // for content negotiation. To be more precise, the list will be used - // in "Accept-Language" header in HTTP requests. - acceptLanguagesPref: 'intl.accept_languages', - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.selectionModel = new ListSingleSelectionModel; - - // HACK(arv): http://crbug.com/40902 - window.addEventListener('resize', this.redraw.bind(this)); - - // Listen to pref change. - if (cr.isChromeOS) { - Preferences.getInstance().addEventListener(this.preferredLanguagesPref, - this.handlePreferredLanguagesPrefChange_.bind(this)); - } else { - Preferences.getInstance().addEventListener(this.acceptLanguagesPref, - this.handleAcceptLanguagesPrefChange_.bind(this)); - } - - // Listen to drag and drop events. - this.addEventListener('dragstart', this.handleDragStart_.bind(this)); - this.addEventListener('dragenter', this.handleDragEnter_.bind(this)); - this.addEventListener('dragover', this.handleDragOver_.bind(this)); - this.addEventListener('drop', this.handleDrop_.bind(this)); - this.addEventListener('dragleave', this.handleDragLeave_.bind(this)); - }, - - createItem: function(languageCode) { - languageInfo = LanguageList.getLanguageInfoFromLanguageCode(languageCode); - return new LanguageListItem(languageInfo); - }, - - /* - * For each item, determines whether it's deletable. - */ - updateDeletable: function() { - var items = this.items; - for (var i = 0; i < items.length; ++i) { - var item = items[i]; - var languageCode = item.languageCode; - var languageOptions = options.LanguageOptions.getInstance(); - item.deletable = languageOptions.languageIsDeletable(languageCode); - } - }, - - /* - * Adds a language to the language list. - * @param {string} languageCode language code (ex. "fr"). - */ - addLanguage: function(languageCode) { - // It shouldn't happen but ignore the language code if it's - // null/undefined, or already present. - if (!languageCode || this.dataModel.indexOf(languageCode) >= 0) { - return; - } - this.dataModel.push(languageCode); - // Select the last item, which is the language added. - this.selectionModel.selectedIndex = this.dataModel.length - 1; - - this.savePreference_(); - }, - - /* - * Gets the language codes of the currently listed languages. - */ - getLanguageCodes: function() { - return this.dataModel.slice(); - }, - - /* - * Gets the language code of the selected language. - */ - getSelectedLanguageCode: function() { - return this.selectedItem; - }, - - /* - * Selects the language by the given language code. - * @returns {boolean} True if the operation is successful. - */ - selectLanguageByCode: function(languageCode) { - var index = this.dataModel.indexOf(languageCode); - if (index >= 0) { - this.selectionModel.selectedIndex = index; - return true; - } - return false; - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - if (index >= 0) { - this.dataModel.splice(index, 1); - // Once the selected item is removed, there will be no selected item. - // Select the item pointed by the lead index. - index = this.selectionModel.leadIndex; - this.savePreference_(); - } - return index; - }, - - /* - * Computes the target item of drop event. - * @param {Event} e The drop or dragover event. - * @private - */ - getTargetFromDropEvent_: function(e) { - var target = e.target; - // e.target may be an inner element of the list item - while (target != null && !(target instanceof ListItem)) { - target = target.parentNode; - } - return target; - }, - - /* - * Handles the dragstart event. - * @param {Event} e The dragstart event. - * @private - */ - handleDragStart_: function(e) { - var target = e.target; - // ListItem should be the only draggable element type in the page, - // but just in case. - if (target instanceof ListItem) { - this.draggedItem = target; - e.dataTransfer.effectAllowed = 'move'; - // We need to put some kind of data in the drag or it will be - // ignored. Use the display name in case the user drags to a text - // field or the desktop. - e.dataTransfer.setData('text/plain', target.title); - } - }, - - /* - * Handles the dragenter event. - * @param {Event} e The dragenter event. - * @private - */ - handleDragEnter_: function(e) { - e.preventDefault(); - }, - - /* - * Handles the dragover event. - * @param {Event} e The dragover event. - * @private - */ - handleDragOver_: function(e) { - var dropTarget = this.getTargetFromDropEvent_(e); - // Determines whether the drop target is to accept the drop. - // The drop is only successful on another ListItem. - if (!(dropTarget instanceof ListItem) || - dropTarget == this.draggedItem) { - this.hideDropMarker_(); - return; - } - // Compute the drop postion. Should we move the dragged item to - // below or above the drop target? - var rect = dropTarget.getBoundingClientRect(); - var dy = e.clientY - rect.top; - var yRatio = dy / rect.height; - var dropPos = yRatio <= .5 ? 'above' : 'below'; - this.dropPos = dropPos; - this.showDropMarker_(dropTarget, dropPos); - e.preventDefault(); - }, - - /* - * Handles the drop event. - * @param {Event} e The drop event. - * @private - */ - handleDrop_: function(e) { - var dropTarget = this.getTargetFromDropEvent_(e); - this.hideDropMarker_(); - - // Delete the language from the original position. - var languageCode = this.draggedItem.languageCode; - var originalIndex = this.dataModel.indexOf(languageCode); - this.dataModel.splice(originalIndex, 1); - // Insert the language to the new position. - var newIndex = this.dataModel.indexOf(dropTarget.languageCode); - if (this.dropPos == 'below') - newIndex += 1; - this.dataModel.splice(newIndex, 0, languageCode); - // The cursor should move to the moved item. - this.selectionModel.selectedIndex = newIndex; - // Save the preference. - this.savePreference_(); - }, - - /* - * Handles the dragleave event. - * @param {Event} e The dragleave event - * @private - */ - handleDragLeave_: function(e) { - this.hideDropMarker_(); - }, - - /* - * Shows and positions the marker to indicate the drop target. - * @param {HTMLElement} target The current target list item of drop - * @param {string} pos 'below' or 'above' - * @private - */ - showDropMarker_: function(target, pos) { - window.clearTimeout(this.hideDropMarkerTimer_); - var marker = $('language-options-list-dropmarker'); - var rect = target.getBoundingClientRect(); - var markerHeight = 8; - if (pos == 'above') { - marker.style.top = (rect.top - markerHeight / 2) + 'px'; - } else { - marker.style.top = (rect.bottom - markerHeight / 2) + 'px'; - } - marker.style.width = rect.width + 'px'; - marker.style.left = rect.left + 'px'; - marker.style.display = 'block'; - }, - - /* - * Hides the drop marker. - * @private - */ - hideDropMarker_: function() { - // Hide the marker in a timeout to reduce flickering as we move between - // valid drop targets. - window.clearTimeout(this.hideDropMarkerTimer_); - this.hideDropMarkerTimer_ = window.setTimeout(function() { - $('language-options-list-dropmarker').style.display = ''; - }, 100); - }, - - /** - * Handles preferred languages pref change. - * @param {Event} e The change event object. - * @private - */ - handlePreferredLanguagesPrefChange_: function(e) { - var languageCodesInCsv = e.value.value; - var languageCodes = languageCodesInCsv.split(','); - - // Add the UI language to the initial list of languages. This is to avoid - // a bug where the UI language would be removed from the preferred - // language list by sync on first login. - // See: crosbug.com/14283 - languageCodes.push(navigator.language); - languageCodes = this.filterBadLanguageCodes_(languageCodes); - this.load_(languageCodes); - }, - - /** - * Handles accept languages pref change. - * @param {Event} e The change event object. - * @private - */ - handleAcceptLanguagesPrefChange_: function(e) { - var languageCodesInCsv = e.value.value; - var languageCodes = this.filterBadLanguageCodes_( - languageCodesInCsv.split(',')); - this.load_(languageCodes); - }, - - /** - * Loads given language list. - * @param {Array} languageCodes List of language codes. - * @private - */ - load_: function(languageCodes) { - // Preserve the original selected index. See comments below. - var originalSelectedIndex = (this.selectionModel ? - this.selectionModel.selectedIndex : -1); - this.dataModel = new ArrayDataModel(languageCodes); - if (originalSelectedIndex >= 0 && - originalSelectedIndex < this.dataModel.length) { - // Restore the original selected index if the selected index is - // valid after the data model is loaded. This is neeeded to keep - // the selected language after the languge is added or removed. - this.selectionModel.selectedIndex = originalSelectedIndex; - // The lead index should be updated too. - this.selectionModel.leadIndex = originalSelectedIndex; - } else if (this.dataModel.length > 0) { - // Otherwise, select the first item if it's not empty. - // Note that ListSingleSelectionModel won't select an item - // automatically, hence we manually select the first item here. - this.selectionModel.selectedIndex = 0; - } - }, - - /** - * Saves the preference. - */ - savePreference_: function() { - // Encode the language codes into a CSV string. - if (cr.isChromeOS) - Preferences.setStringPref(this.preferredLanguagesPref, - this.dataModel.slice().join(',')); - // Save the same language list as accept languages preference as - // well, but we need to expand the language list, to make it more - // acceptable. For instance, some web sites don't understand 'en-US' - // but 'en'. See crosbug.com/9884. - var acceptLanguages = this.expandLanguageCodes(this.dataModel.slice()); - Preferences.setStringPref(this.acceptLanguagesPref, - acceptLanguages.join(',')); - cr.dispatchSimpleEvent(this, 'save'); - }, - - /** - * Expands language codes to make these more suitable for Accept-Language. - * Example: ['en-US', 'ja', 'en-CA'] => ['en-US', 'en', 'ja', 'en-CA']. - * 'en' won't appear twice as this function eliminates duplicates. - * @param {Array} languageCodes List of language codes. - * @private - */ - expandLanguageCodes: function(languageCodes) { - var expandedLanguageCodes = []; - var seen = {}; // Used to eliminiate duplicates. - for (var i = 0; i < languageCodes.length; i++) { - var languageCode = languageCodes[i]; - if (!(languageCode in seen)) { - expandedLanguageCodes.push(languageCode); - seen[languageCode] = true; - } - var parts = languageCode.split('-'); - if (!(parts[0] in seen)) { - expandedLanguageCodes.push(parts[0]); - seen[parts[0]] = true; - } - } - return expandedLanguageCodes; - }, - - /** - * Filters bad language codes in case bad language codes are - * stored in the preference. Removes duplicates as well. - * @param {Array} languageCodes List of language codes. - * @private - */ - filterBadLanguageCodes_: function(languageCodes) { - var filteredLanguageCodes = []; - var seen = {}; - for (var i = 0; i < languageCodes.length; i++) { - // Check if the the language code is valid, and not - // duplicate. Otherwise, skip it. - if (LanguageList.isValidLanguageCode(languageCodes[i]) && - !(languageCodes[i] in seen)) { - filteredLanguageCodes.push(languageCodes[i]); - seen[languageCodes[i]] = true; - } - } - return filteredLanguageCodes; - }, - }; - - return { - LanguageList: LanguageList, - LanguageListItem: LanguageListItem - }; -}); diff --git a/chrome/browser/resources/options2/language_options.css b/chrome/browser/resources/options2/language_options.css deleted file mode 100644 index 470788d..0000000 --- a/chrome/browser/resources/options2/language_options.css +++ /dev/null @@ -1,215 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.language-options { - display: -webkit-box; - margin: 10px 0; -} - -.language-options-lower-left button, -.language-options-right button { - min-width: 70px; -} - -.language-options h3 { - -webkit-margin-start: 12px; - font-size: 100%; - font-weight: bold; - margin-bottom: 12px; - margin-top: 12px; -} - -.language-options-contents { - padding: 0 12px 4px; -} - -.language-options-contents span:not(.input-method-label) { - display: inline-block; - margin: 1px; - padding: 0.42em 10px; -} - -.language-options-header, -.language-options-footer { - line-height: 1.2em; - margin: 10px 0; -} - -.language-options-left, -.language-options-right { - border: 1px solid #ccc; - height: 400px; - padding: 0; - vertical-align: top; -} - -.language-options-left { - -webkit-box-orient: vertical; - - background-color: rgb(235, 239, 249); - - - background-color: white; - - display: -webkit-box; - width: 300px; -} - -.language-options-lower-left { - -webkit-box-flex: 0; - -webkit-padding-start: 12px; - padding-bottom: 10px; -} - -.language-options-right { - /* To share the center line with the left pane. */ - -webkit-margin-start: -1px; - width: 360px; -} - -.language-options-right h3:not(:first-of-type) { - margin-top: 24px; -} - -.language-options-notification { - background-color: rgb(255, 242, 158); - border-bottom: 1px solid #ccc; - border-top: 1px solid #ccc; - padding: 8px 30px 8px 12px; -} - -#language-options-input-method-list button { - -webkit-margin-start: 20px; - display: block; - /* Same margin as .settings-row. */ - margin-bottom: 0.65em; - margin-top: 0.65em; -} - -#language-options-list { - -webkit-box-flex: 1; - outline: none; - padding: 1px 0 0; - width: 100%; -} - -#language-options-list .language-name { - -webkit-box-flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -#language-options-list li { - -webkit-padding-start: 12px; - padding-bottom: 2px; - padding-top: 2px; -} - -#language-options-list-dropmarker { - background-clip: padding-box; - background-color: hsl(214, 91%, 65%); - border: 3px solid hsl(214, 91%, 65%); - border-bottom-color: transparent; - border-radius: 0; - border-top-color: transparent; - box-sizing: border-box; - display: none; - height: 8px; - overflow: hidden; - pointer-events: none; - position: fixed; - z-index: 10; -} - -#language-options-ui-restart-button { - margin-top: 4px; -} - -/* In ChromeOS we present the language choices as a big page of links. */ - -#add-language-overlay-language-list { - -webkit-column-count: 2; - -webkit-column-gap: 20px; -} - -#add-language-overlay-language-list > li > button.link-button { - padding-bottom: 8px; - padding-top: 8px; -} - -#add-language-overlay-page { - width: 800px; -} - -#add-language-overlay-page button.link-button { - padding-left: 0; - padding-right: 0; - text-align: left; -} - -#add-language-overlay-page ul { - margin: 0; - padding: 0; -} - - -/* TODO(kochi): This is temporary copy from new_tab.css */ -/* Notification */ - -#notification { - -webkit-transition: opacity 150ms; - background-color: hsl(52, 100%, 80%); - border: 1px solid rgb(211, 211, 211); - border-radius: 6px; - color: black; - display: table; - font-weight: bold; - /* Set the height and margin so that the element does not use any vertical - space. */ - height: 16px; - margin: -44px auto 12px auto; - opacity: 0; - padding: 7px 15px; - pointer-events: none; - position: relative; - white-space: nowrap; - z-index: 1; -} - -#notification > * { - display: table-cell; - max-width: 500px; - overflow: hidden; - text-overflow: ellipsis; -} - -#notification.show { - -webkit-transition: opacity 1s; - opacity: 1; - pointer-events: all; -} - -#notification .link { - -webkit-appearance: none; - -webkit-padding-start: 20px; - background: none; - border: 0; - color: rgba(0, 102, 204, 0.3); - cursor: pointer; - text-decoration: underline; -} - -#notification .link-color { - color: rgb(0, 102, 204); -} - -#chewing-max-chi-symbol-len { - height: 30%; - width: 100px; -} - -#add-language-overlay-page .content-area { - padding-bottom: 10px; -} diff --git a/chrome/browser/resources/options2/language_options.html b/chrome/browser/resources/options2/language_options.html deleted file mode 100644 index c668018..0000000 --- a/chrome/browser/resources/options2/language_options.html +++ /dev/null @@ -1,103 +0,0 @@ - diff --git a/chrome/browser/resources/options2/language_options.js b/chrome/browser/resources/options2/language_options.js deleted file mode 100644 index 4beb0f5..0000000 --- a/chrome/browser/resources/options2/language_options.js +++ /dev/null @@ -1,860 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// TODO(kochi): Generalize the notification as a component and put it -// in js/cr/ui/notification.js . - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var LanguageList = options.LanguageList; - - // Some input methods like Chinese Pinyin have config pages. - // This is the map of the input method names to their config page names. - /** @const */ var INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME = { - 'mozc': 'languageMozc', - 'mozc-chewing': 'languageChewing', - 'mozc-dv': 'languageMozc', - 'mozc-hangul': 'languageHangul', - 'mozc-jp': 'languageMozc', - 'pinyin': 'languagePinyin', - 'pinyin-dv': 'languagePinyin', - }; - - ///////////////////////////////////////////////////////////////////////////// - // LanguageOptions class: - - /** - * Encapsulated handling of ChromeOS language options page. - * @constructor - */ - function LanguageOptions(model) { - OptionsPage.call(this, 'languages', - loadTimeData.getString('languagePageTabTitle'), - 'languagePage'); - } - - cr.addSingletonGetter(LanguageOptions); - - // Inherit LanguageOptions from OptionsPage. - LanguageOptions.prototype = { - __proto__: OptionsPage.prototype, - - /* For recording the prospective language (the next locale after relaunch). - * @type {?string} - * @private - */ - prospectiveUiLanguageCode_: null, - - /** - * Initializes LanguageOptions page. - * Calls base class implementation to start preference initialization. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - var languageOptionsList = $('language-options-list'); - LanguageList.decorate(languageOptionsList); - - languageOptionsList.addEventListener('change', - this.handleLanguageOptionsListChange_.bind(this)); - languageOptionsList.addEventListener('save', - this.handleLanguageOptionsListSave_.bind(this)); - - this.prospectiveUiLanguageCode_ = - loadTimeData.getString('prospectiveUiLanguageCode'); - this.addEventListener('visibleChange', - this.handleVisibleChange_.bind(this)); - - if (cr.isChromeOS) { - $('chewing-confirm').onclick = $('hangul-confirm').onclick = - $('mozc-confirm').onclick = $('pinyin-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - - this.initializeInputMethodList_(); - this.initializeLanguageCodeToInputMethodIdsMap_(); - } - Preferences.getInstance().addEventListener(this.spellCheckDictionaryPref, - this.handleSpellCheckDictionaryPrefChange_.bind(this)); - - // Set up add button. - $('language-options-add-button').onclick = function(e) { - // Add the language without showing the overlay if it's specified in - // the URL hash (ex. lang_add=ja). Used for automated testing. - var match = document.location.hash.match(/\blang_add=([\w-]+)/); - if (match) { - var addLanguageCode = match[1]; - $('language-options-list').addLanguage(addLanguageCode); - } else { - OptionsPage.navigateToPage('addLanguage'); - } - }; - - if (cr.isChromeOS) { - // Listen to user clicks on the add language list. - var addLanguageList = $('add-language-overlay-language-list'); - addLanguageList.addEventListener('click', - this.handleAddLanguageListClick_.bind(this)); - } else { - // Listen to add language dialog ok button. - var addLanguageOkButton = $('add-language-overlay-ok-button'); - addLanguageOkButton.addEventListener('click', - this.handleAddLanguageOkButtonClick_.bind(this)); - - // Show experimental features if enabled. - if (loadTimeData.getBoolean('experimentalSpellCheckFeatures')) - $('auto-spell-correction-option').hidden = false; - - // Handle spell check enable/disable. - if (!cr.isMac) { - Preferences.getInstance().addEventListener( - this.enableSpellCheckPref, - this.updateEnableSpellCheck_.bind(this)); - - var spellCheckLanguageButton = getRequiredElement( - 'language-options-spell-check-language-button'); - spellCheckLanguageButton.addEventListener( - 'click', - this.handleSpellCheckLanguageButtonClick_.bind(this)); - } - } - - if (cr.isChromeOS) { - $('language-options-ui-restart-button').onclick = function() { - chrome.send('uiLanguageRestart'); - }; - } - - $('language-confirm').onclick = - OptionsPage.closeOverlay.bind(OptionsPage); - }, - - // The preference is a boolean that enables/disables spell checking. - enableSpellCheckPref: 'browser.enable_spellchecking', - // The preference is a CSV string that describes preload engines - // (i.e. active input methods). - preloadEnginesPref: 'settings.language.preload_engines', - // The list of preload engines, like ['mozc', 'pinyin']. - preloadEngines_: [], - // The preference is a string that describes the spell check - // dictionary language, like "en-US". - spellCheckDictionaryPref: 'spellcheck.dictionary', - spellCheckDictionary_: '', - // The map of language code to input method IDs, like: - // {'ja': ['mozc', 'mozc-jp'], 'zh-CN': ['pinyin'], ...} - languageCodeToInputMethodIdsMap_: {}, - - /** - * Initializes the input method list. - */ - initializeInputMethodList_: function() { - var inputMethodList = $('language-options-input-method-list'); - var inputMethodListData = loadTimeData.getValue('inputMethodList'); - var inputMethodPrototype = $('language-options-input-method-proto'); - - // Add all input methods, but make all of them invisible here. We'll - // change the visibility in handleLanguageOptionsListChange_() based - // on the selected language. Note that we only have less than 100 - // input methods, so creating DOM nodes at once here should be ok. - for (var i = 0; i < inputMethodListData.length; i++) { - var inputMethod = inputMethodListData[i]; - var element = inputMethodPrototype.cloneNode(true); - element.id = ''; - element.languageCodeSet = inputMethod.languageCodeSet; - var input = element.querySelectorAll('input')[0]; - input.inputMethodId = inputMethod.id; - var span = element.querySelectorAll('span')[0]; - span.textContent = inputMethod.displayName; - - // Listen to user clicks. - input.addEventListener('click', - this.handleCheckboxClick_.bind(this)); - - // Add the configure button if the config page is present for this - // input method. - if (inputMethod.id in INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME) { - var pageName = INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME[inputMethod.id]; - var button = this.createConfigureInputMethodButton_(inputMethod.id, - pageName); - element.appendChild(button); - } - inputMethodList.appendChild(element); - } - // Listen to pref change once the input method list is initialized. - Preferences.getInstance().addEventListener(this.preloadEnginesPref, - this.handlePreloadEnginesPrefChange_.bind(this)); - }, - - /** - * Creates a configure button for the given input method ID. - * @param {string} inputMethodId Input method ID (ex. "pinyin"). - * @param {string} pageName Name of the config page (ex. "languagePinyin"). - * @private - */ - createConfigureInputMethodButton_: function(inputMethodId, pageName) { - var button = document.createElement('button'); - button.textContent = loadTimeData.getString('configure'); - button.onclick = function(e) { - // Prevent the default action (i.e. changing the checked property - // of the checkbox). The button click here should not be handled - // as checkbox click. - e.preventDefault(); - chrome.send('inputMethodOptionsOpen', [inputMethodId]); - OptionsPage.navigateToPage(pageName); - }; - return button; - }, - - /** - * Handles OptionsPage's visible property change event. - * @param {Event} e Property change event. - * @private - */ - handleVisibleChange_: function(e) { - if (this.visible) { - $('language-options-list').redraw(); - chrome.send('languageOptionsOpen'); - } - }, - - /** - * Handles languageOptionsList's change event. - * @param {Event} e Change event. - * @private - */ - handleLanguageOptionsListChange_: function(e) { - var languageOptionsList = $('language-options-list'); - var languageCode = languageOptionsList.getSelectedLanguageCode(); - - // Select the language if it's specified in the URL hash (ex. lang=ja). - // Used for automated testing. - var match = document.location.hash.match(/\blang=([\w-]+)/); - if (match) { - var specifiedLanguageCode = match[1]; - if (languageOptionsList.selectLanguageByCode(specifiedLanguageCode)) { - languageCode = specifiedLanguageCode; - } - } - - if (cr.isWindows || cr.isChromeOS) - this.updateUiLanguageButton_(languageCode); - - if (!cr.isMac) { - this.updateSelectedLanguageName_(languageCode); - this.updateSpellCheckLanguageButton_(languageCode); - } - - if (cr.isChromeOS) - this.updateInputMethodList_(languageCode); - - this.updateLanguageListInAddLanguageOverlay_(); - }, - - /** - * Happens when a user changes back to the language they're currently using. - */ - currentLocaleWasReselected: function() { - this.updateUiLanguageButton_( - loadTimeData.getString('currentUiLanguageCode')); - }, - - /** - * Handles languageOptionsList's save event. - * @param {Event} e Save event. - * @private - */ - handleLanguageOptionsListSave_: function(e) { - if (cr.isChromeOS) { - // Sort the preload engines per the saved languages before save. - this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); - this.savePreloadEnginesPref_(); - } - }, - - /** - * Sorts preloadEngines_ by languageOptionsList's order. - * @param {Array} preloadEngines List of preload engines. - * @return {Array} Returns sorted preloadEngines. - * @private - */ - sortPreloadEngines_: function(preloadEngines) { - // For instance, suppose we have two languages and associated input - // methods: - // - // - Korean: hangul - // - Chinese: pinyin - // - // The preloadEngines preference should look like "hangul,pinyin". - // If the user reverse the order, the preference should be reorderd - // to "pinyin,hangul". - var languageOptionsList = $('language-options-list'); - var languageCodes = languageOptionsList.getLanguageCodes(); - - // Convert the list into a dictonary for simpler lookup. - var preloadEngineSet = {}; - for (var i = 0; i < preloadEngines.length; i++) { - preloadEngineSet[preloadEngines[i]] = true; - } - - // Create the new preload engine list per the language codes. - var newPreloadEngines = []; - for (var i = 0; i < languageCodes.length; i++) { - var languageCode = languageCodes[i]; - var inputMethodIds = this.languageCodeToInputMethodIdsMap_[ - languageCode]; - // Check if we have active input methods associated with the language. - for (var j = 0; j < inputMethodIds.length; j++) { - var inputMethodId = inputMethodIds[j]; - if (inputMethodId in preloadEngineSet) { - // If we have, add it to the new engine list. - newPreloadEngines.push(inputMethodId); - // And delete it from the set. This is necessary as one input - // method can be associated with more than one language thus - // we should avoid having duplicates in the new list. - delete preloadEngineSet[inputMethodId]; - } - } - } - - return newPreloadEngines; - }, - - /** - * Initializes the map of language code to input method IDs. - * @private - */ - initializeLanguageCodeToInputMethodIdsMap_: function() { - var inputMethodList = loadTimeData.getValue('inputMethodList'); - for (var i = 0; i < inputMethodList.length; i++) { - var inputMethod = inputMethodList[i]; - for (var languageCode in inputMethod.languageCodeSet) { - if (languageCode in this.languageCodeToInputMethodIdsMap_) { - this.languageCodeToInputMethodIdsMap_[languageCode].push( - inputMethod.id); - } else { - this.languageCodeToInputMethodIdsMap_[languageCode] = - [inputMethod.id]; - } - } - } - }, - - /** - * Updates the currently selected language name. - * @param {string} languageCode Language code (ex. "fr"). - * @private - */ - updateSelectedLanguageName_: function(languageCode) { - var languageInfo = LanguageList.getLanguageInfoFromLanguageCode( - languageCode); - var languageDisplayName = languageInfo.displayName; - var languageNativeDisplayName = languageInfo.nativeDisplayName; - var textDirection = languageInfo.textDirection; - - // If the native name is different, add it. - if (languageDisplayName != languageNativeDisplayName) { - languageDisplayName += ' - ' + languageNativeDisplayName; - } - - // Update the currently selected language name. - var languageName = $('language-options-language-name'); - languageName.textContent = languageDisplayName; - languageName.dir = textDirection; - }, - - /** - * Updates the UI language button. - * @param {string} languageCode Language code (ex. "fr"). - * @private - */ - updateUiLanguageButton_: function(languageCode) { - var uiLanguageButton = $('language-options-ui-language-button'); - var uiLanguageMessage = $('language-options-ui-language-message'); - var uiLanguageNotification = $('language-options-ui-notification-bar'); - - // Remove the event listener and add it back if useful. - uiLanguageButton.onclick = null; - - // Unhide the language button every time, as it could've been previously - // hidden by a language change. - uiLanguageButton.hidden = false; - - if (languageCode == this.prospectiveUiLanguageCode_) { - uiLanguageMessage.textContent = - loadTimeData.getString('is_displayed_in_this_language'); - showMutuallyExclusiveNodes( - [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); - } else if (languageCode in loadTimeData.getValue('uiLanguageCodeSet')) { - if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { - // In the guest mode for ChromeOS, changing UI language does not make - // sense because it does not take effect after browser restart. - uiLanguageButton.hidden = true; - uiLanguageMessage.hidden = true; - } else { - uiLanguageButton.textContent = - loadTimeData.getString('display_in_this_language'); - showMutuallyExclusiveNodes( - [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 0); - uiLanguageButton.onclick = function(e) { - chrome.send('uiLanguageChange', [languageCode]); - }; - } - } else { - uiLanguageMessage.textContent = - loadTimeData.getString('cannot_be_displayed_in_this_language'); - showMutuallyExclusiveNodes( - [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); - } - }, - - /** - * Updates the spell check language button. - * @param {string} languageCode Language code (ex. "fr"). - * @private - */ - updateSpellCheckLanguageButton_: function(languageCode) { - var spellCheckLanguageButton = - $('language-options-spell-check-language-button'); - var spellCheckLanguageMessage = - $('language-options-spell-check-language-message'); - - if (languageCode == this.spellCheckDictionary_) { - spellCheckLanguageMessage.textContent = - loadTimeData.getString('is_used_for_spell_checking'); - showMutuallyExclusiveNodes( - [spellCheckLanguageButton, spellCheckLanguageMessage], 1); - } else if (languageCode in - loadTimeData.getValue('spellCheckLanguageCodeSet')) { - spellCheckLanguageButton.textContent = - loadTimeData.getString('use_this_for_spell_checking'); - showMutuallyExclusiveNodes( - [spellCheckLanguageButton, spellCheckLanguageMessage], 0); - spellCheckLanguageButton.languageCode = languageCode; - } else if (!languageCode) { - spellCheckLanguageButton.hidden = true; - spellCheckLanguageMessage.hidden = true; - } else { - spellCheckLanguageMessage.textContent = - loadTimeData.getString('cannot_be_used_for_spell_checking'); - showMutuallyExclusiveNodes( - [spellCheckLanguageButton, spellCheckLanguageMessage], 1); - } - }, - - /** - * Updates the input method list. - * @param {string} languageCode Language code (ex. "fr"). - * @private - */ - updateInputMethodList_: function(languageCode) { - // Give one of the checkboxes or buttons focus, if it's specified in the - // URL hash (ex. focus=mozc). Used for automated testing. - var focusInputMethodId = -1; - var match = document.location.hash.match(/\bfocus=([\w:-]+)\b/); - if (match) { - focusInputMethodId = match[1]; - } - // Change the visibility of the input method list. Input methods that - // matches |languageCode| will become visible. - var inputMethodList = $('language-options-input-method-list'); - var methods = inputMethodList.querySelectorAll('.input-method'); - for (var i = 0; i < methods.length; i++) { - var method = methods[i]; - if (languageCode in method.languageCodeSet) { - method.hidden = false; - var input = method.querySelectorAll('input')[0]; - // Give it focus if the ID matches. - if (input.inputMethodId == focusInputMethodId) { - input.focus(); - } - } else { - method.hidden = true; - } - } - - if (focusInputMethodId == 'add') { - $('language-options-add-button').focus(); - } - }, - - /** - * Updates the language list in the add language overlay. - * @param {string} languageCode Language code (ex. "fr"). - * @private - */ - updateLanguageListInAddLanguageOverlay_: function(languageCode) { - // Change the visibility of the language list in the add language - // overlay. Languages that are already active will become invisible, - // so that users don't add the same language twice. - var languageOptionsList = $('language-options-list'); - var languageCodes = languageOptionsList.getLanguageCodes(); - var languageCodeSet = {}; - for (var i = 0; i < languageCodes.length; i++) { - languageCodeSet[languageCodes[i]] = true; - } - var addLanguageList = $('add-language-overlay-language-list'); - var lis = addLanguageList.querySelectorAll('li'); - for (var i = 0; i < lis.length; i++) { - // The first child button knows the language code. - var button = lis[i].childNodes[0]; - if (button.languageCode in languageCodeSet) { - lis[i].style.display = 'none'; - } else { - lis[i].style.display = 'block'; - } - } - }, - - /** - * Handles preloadEnginesPref change. - * @param {Event} e Change event. - * @private - */ - handlePreloadEnginesPrefChange_: function(e) { - var value = e.value.value; - this.preloadEngines_ = this.filterBadPreloadEngines_(value.split(',')); - this.updateCheckboxesFromPreloadEngines_(); - $('language-options-list').updateDeletable(); - }, - - /** - * Handles input method checkbox's click event. - * @param {Event} e Click event. - * @private - */ - handleCheckboxClick_: function(e) { - var checkbox = e.target; - if (this.preloadEngines_.length == 1 && !checkbox.checked) { - // Don't allow disabling the last input method. - this.showNotification_( - loadTimeData.getString('please_add_another_input_method'), - loadTimeData.getString('ok_button')); - checkbox.checked = true; - return; - } - if (checkbox.checked) { - chrome.send('inputMethodEnable', [checkbox.inputMethodId]); - } else { - chrome.send('inputMethodDisable', [checkbox.inputMethodId]); - } - this.updatePreloadEnginesFromCheckboxes_(); - this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); - this.savePreloadEnginesPref_(); - }, - - /** - * Handles add language list's click event. - * @param {Event} e Click event. - */ - handleAddLanguageListClick_: function(e) { - var languageOptionsList = $('language-options-list'); - var languageCode = e.target.languageCode; - // languageCode can be undefined, if click was made on some random - // place in the overlay, rather than a button. Ignore it. - if (!languageCode) { - return; - } - languageOptionsList.addLanguage(languageCode); - var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; - // Enable the first input method for the language added. - if (inputMethodIds && inputMethodIds[0] && - // Don't add the input method it's already present. This can - // happen if the same input method is shared among multiple - // languages (ex. English US keyboard is used for English US and - // Filipino). - this.preloadEngines_.indexOf(inputMethodIds[0]) == -1) { - this.preloadEngines_.push(inputMethodIds[0]); - this.updateCheckboxesFromPreloadEngines_(); - this.savePreloadEnginesPref_(); - } - OptionsPage.closeOverlay(); - }, - - /** - * Handles add language dialog ok button. - */ - handleAddLanguageOkButtonClick_: function() { - var languagesSelect = $('add-language-overlay-language-list'); - var selectedIndex = languagesSelect.selectedIndex; - if (selectedIndex >= 0) { - var selection = languagesSelect.options[selectedIndex]; - $('language-options-list').addLanguage(String(selection.value)); - OptionsPage.closeOverlay(); - } - }, - - /** - * Checks if languageCode is deletable or not. - * @param {String} languageCode the languageCode to check for deletability. - */ - languageIsDeletable: function(languageCode) { - // Don't allow removing the language if it's a UI language. - if (languageCode == this.prospectiveUiLanguageCode_) - return false; - return (!cr.isChromeOS || - this.canDeleteLanguage_(languageCode)); - }, - - /** - * Handles browse.enable_spellchecking change. - * @param {Event} e Change event. - * @private - */ - updateEnableSpellCheck_: function() { - var value = !$('enable-spell-check').checked; - - $('language-options-spell-check-language-button').disabled = value; - }, - - /** - * Handles spellCheckDictionaryPref change. - * @param {Event} e Change event. - * @private - */ - handleSpellCheckDictionaryPrefChange_: function(e) { - var languageCode = e.value.value; - this.spellCheckDictionary_ = languageCode; - var languageOptionsList = $('language-options-list'); - var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode(); - if (!cr.isMac) - this.updateSpellCheckLanguageButton_(selectedLanguageCode); - }, - - /** - * Handles spellCheckLanguageButton click. - * @param {Event} e Click event. - * @private - */ - handleSpellCheckLanguageButtonClick_: function(e) { - var languageCode = e.target.languageCode; - // Save the preference. - Preferences.setStringPref(this.spellCheckDictionaryPref, - languageCode); - chrome.send('spellCheckLanguageChange', [languageCode]); - }, - - /** - * Checks whether it's possible to remove the language specified by - * languageCode and returns true if possible. This function returns false - * if the removal causes the number of preload engines to be zero. - * - * @param {string} languageCode Language code (ex. "fr"). - * @return {boolean} Returns true on success. - * @private - */ - canDeleteLanguage_: function(languageCode) { - // First create the set of engines to be removed from input methods - // associated with the language code. - var enginesToBeRemovedSet = {}; - var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; - for (var i = 0; i < inputMethodIds.length; i++) { - enginesToBeRemovedSet[inputMethodIds[i]] = true; - } - - // Then eliminate engines that are also used for other active languages. - // For instance, if "xkb:us::eng" is used for both English and Filipino. - var languageCodes = $('language-options-list').getLanguageCodes(); - for (var i = 0; i < languageCodes.length; i++) { - // Skip the target language code. - if (languageCodes[i] == languageCode) { - continue; - } - // Check if input methods used in this language are included in - // enginesToBeRemovedSet. If so, eliminate these from the set, so - // we don't remove this time. - var inputMethodIdsForAnotherLanguage = - this.languageCodeToInputMethodIdsMap_[languageCodes[i]]; - for (var j = 0; j < inputMethodIdsForAnotherLanguage.length; j++) { - var inputMethodId = inputMethodIdsForAnotherLanguage[j]; - if (inputMethodId in enginesToBeRemovedSet) { - delete enginesToBeRemovedSet[inputMethodId]; - } - } - } - - // Update the preload engine list with the to-be-removed set. - var newPreloadEngines = []; - for (var i = 0; i < this.preloadEngines_.length; i++) { - if (!(this.preloadEngines_[i] in enginesToBeRemovedSet)) { - newPreloadEngines.push(this.preloadEngines_[i]); - } - } - // Don't allow this operation if it causes the number of preload - // engines to be zero. - return (newPreloadEngines.length > 0); - }, - - /** - * Saves the preload engines preference. - * @private - */ - savePreloadEnginesPref_: function() { - Preferences.setStringPref(this.preloadEnginesPref, - this.preloadEngines_.join(',')); - }, - - /** - * Updates the checkboxes in the input method list from the preload - * engines preference. - * @private - */ - updateCheckboxesFromPreloadEngines_: function() { - // Convert the list into a dictonary for simpler lookup. - var dictionary = {}; - for (var i = 0; i < this.preloadEngines_.length; i++) { - dictionary[this.preloadEngines_[i]] = true; - } - - var inputMethodList = $('language-options-input-method-list'); - var checkboxes = inputMethodList.querySelectorAll('input'); - for (var i = 0; i < checkboxes.length; i++) { - checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary); - } - }, - - /** - * Updates the preload engines preference from the checkboxes in the - * input method list. - * @private - */ - updatePreloadEnginesFromCheckboxes_: function() { - this.preloadEngines_ = []; - var inputMethodList = $('language-options-input-method-list'); - var checkboxes = inputMethodList.querySelectorAll('input'); - for (var i = 0; i < checkboxes.length; i++) { - if (checkboxes[i].checked) { - this.preloadEngines_.push(checkboxes[i].inputMethodId); - } - } - var languageOptionsList = $('language-options-list'); - languageOptionsList.updateDeletable(); - }, - - /** - * Filters bad preload engines in case bad preload engines are - * stored in the preference. Removes duplicates as well. - * @param {Array} preloadEngines List of preload engines. - * @private - */ - filterBadPreloadEngines_: function(preloadEngines) { - // Convert the list into a dictonary for simpler lookup. - var dictionary = {}; - var list = loadTimeData.getValue('inputMethodList'); - for (var i = 0; i < list.length; i++) { - dictionary[list[i].id] = true; - } - - var filteredPreloadEngines = []; - var seen = {}; - for (var i = 0; i < preloadEngines.length; i++) { - // Check if the preload engine is present in the - // dictionary, and not duplicate. Otherwise, skip it. - if (preloadEngines[i] in dictionary && !(preloadEngines[i] in seen)) { - filteredPreloadEngines.push(preloadEngines[i]); - seen[preloadEngines[i]] = true; - } - } - return filteredPreloadEngines; - }, - - // TODO(kochi): This is an adapted copy from new_tab.js. - // If this will go as final UI, refactor this to share the component with - // new new tab page. - /** - * Shows notification - * @private - */ - notificationTimeout_: null, - showNotification_: function(text, actionText, opt_delay) { - var notificationElement = $('notification'); - var actionLink = notificationElement.querySelector('.link-color'); - var delay = opt_delay || 10000; - - function show() { - window.clearTimeout(this.notificationTimeout_); - notificationElement.classList.add('show'); - document.body.classList.add('notification-shown'); - } - - function hide() { - window.clearTimeout(this.notificationTimeout_); - notificationElement.classList.remove('show'); - document.body.classList.remove('notification-shown'); - // Prevent tabbing to the hidden link. - actionLink.tabIndex = -1; - // Setting tabIndex to -1 only prevents future tabbing to it. If, - // however, the user switches window or a tab and then moves back to - // this tab the element may gain focus. We therefore make sure that we - // blur the element so that the element focus is not restored when - // coming back to this window. - actionLink.blur(); - } - - function delayedHide() { - this.notificationTimeout_ = window.setTimeout(hide, delay); - } - - notificationElement.firstElementChild.textContent = text; - actionLink.textContent = actionText; - - actionLink.onclick = hide; - actionLink.onkeydown = function(e) { - if (e.keyIdentifier == 'Enter') { - hide(); - } - }; - notificationElement.onmouseover = show; - notificationElement.onmouseout = delayedHide; - actionLink.onfocus = show; - actionLink.onblur = delayedHide; - // Enable tabbing to the link now that it is shown. - actionLink.tabIndex = 0; - - show(); - delayedHide(); - } - }; - - /** - * Shows the node at |index| in |nodes|, hides all others. - * @param {Array} nodes The nodes to be shown or hidden. - * @param {number} index The index of |nodes| to show. - */ - function showMutuallyExclusiveNodes(nodes, index) { - assert(index >= 0 && index < nodes.length); - for (var i = 0; i < nodes.length; ++i) { - assert(nodes[i] instanceof HTMLElement); // TODO(dbeam): Ignore null? - nodes[i].hidden = i != index; - } - } - - /** - * Chrome callback for when the UI language preference is saved. - * @param {string} languageCode The newly selected language to use. - */ - LanguageOptions.uiLanguageSaved = function(languageCode) { - this.prospectiveUiLanguageCode_ = languageCode; - - // If the user is no longer on the same language code, ignore. - if ($('language-options-list').getSelectedLanguageCode() != languageCode) - return; - - // Special case for when a user changes to a different language, and changes - // back to the same language without having restarted Chrome or logged - // in/out of ChromeOS. - if (languageCode == loadTimeData.getString('currentUiLanguageCode')) { - LanguageOptions.getInstance().currentLocaleWasReselected(); - return; - } - - // Otherwise, show a notification telling the user that their changes will - // only take effect after restart. - showMutuallyExclusiveNodes([$('language-options-ui-language-button'), - $('language-options-ui-notification-bar')], 1); - }; - - // Export - return { - LanguageOptions: LanguageOptions - }; -}); diff --git a/chrome/browser/resources/options2/manage_profile_overlay.css b/chrome/browser/resources/options2/manage_profile_overlay.css deleted file mode 100644 index 8c17101..0000000 --- a/chrome/browser/resources/options2/manage_profile_overlay.css +++ /dev/null @@ -1,147 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#manage-profile-overlay { - width: 512px; -} - -.profile-icon-grid-item { - height: 31px; - margin: 2px 4px; - padding: 4px; - width: 38px; -} - -.profile-icon { - height: 31px; - width: 38px; -} - -#create-profile-name-label-container, -#create-profile-name-input-container, -#manage-profile-name-label-container, -#manage-profile-name-input-container { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - -webkit-box-pack: center; - display: -webkit-box; - margin-left: auto; - margin-right: auto; - width: 50%; -} - -#create-profile-name:invalid { - background-color: pink; -} - -#manage-profile-name:invalid { - background-color: pink; -} - -#create-profile-error-bubble { - -webkit-transition: max-height 200ms, padding 200ms; - background-color: rgb(238, 185, 57); - border-radius: 4px; - font-weight: bold; - margin-left: auto; - margin-right: auto; - max-height: 50px; - overflow: hidden; - padding: 1px 10px; - text-align: center; - width: 80%; -} - -#manage-profile-error-bubble { - -webkit-transition: max-height 200ms, padding 200ms; - background-color: rgb(238, 185, 57); - border-radius: 4px; - font-weight: bold; - margin-left: auto; - margin-right: auto; - max-height: 50px; - overflow: hidden; - padding: 1px 10px; - text-align: center; - width: 80%; -} - -#create-profile-error-bubble[hidden] { - display: block !important; - max-height: 0; - padding: 0 10px; -} - -#manage-profile-error-bubble[hidden] { - display: block !important; - max-height: 0; - padding: 0 10px; -} - -#create-profile-icon-grid, -#manage-profile-icon-grid { - background-color: rgba(255, 255, 255, 0.75); - padding: 2px; -} - -/* Adds a grey horizontal line below content area */ -#create-profile-content:after, -#manage-profile-content:after { - background: #c0c0c0; - content: ''; - display: block; - height: 1px; - margin-top: 25px; - width: 100%; -} - -#create-profile-content > :first-child { - margin-bottom: 10px; -} - -#manage-profile-content > :first-child { - margin-bottom: 10px; -} - -#create-profile-content > :last-child { - margin-top: 10px; -} - -#manage-profile-content > :last-child { - margin-top: 10px; -} - -#create-profile-content > :not(:first-child):not(:last-child) { - margin-bottom: 10px; - margin-top: 10px; -} - -#manage-profile-content > :not(:first-child):not(:last-child) { - margin-bottom: 10px; - margin-top: 10px; -} - -#create-profile-content > #create-profile-name-input-container, -#manage-profile-content > #manage-profile-name-input-container { - margin-top: 5px; -} - -#create-profile-content > #create-profile-name-label-container, -#manage-profile-content > #manage-profile-name-label-container { - margin-bottom: 5px; - margin-top: 10px; -} - -.action-area-checkbox-container { - -webkit-box-flex: 1; -} - -#delete-profile-message { - -webkit-padding-start: 48px; - background-repeat: no-repeat; -} - -html[dir='rtl'] #delete-profile-message { - background-position: right; - diff --git a/chrome/browser/resources/options2/manage_profile_overlay.html b/chrome/browser/resources/options2/manage_profile_overlay.html deleted file mode 100644 index 3170dcc..0000000 --- a/chrome/browser/resources/options2/manage_profile_overlay.html +++ /dev/null @@ -1,92 +0,0 @@ - diff --git a/chrome/browser/resources/options2/manage_profile_overlay.js b/chrome/browser/resources/options2/manage_profile_overlay.js deleted file mode 100644 index c4997f7..0000000 --- a/chrome/browser/resources/options2/manage_profile_overlay.js +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var OptionsPage = options.OptionsPage; - var ArrayDataModel = cr.ui.ArrayDataModel; - - /** - * ManageProfileOverlay class - * Encapsulated handling of the 'Manage profile...' overlay page. - * @constructor - * @class - */ - function ManageProfileOverlay() { - OptionsPage.call(this, 'manageProfile', - loadTimeData.getString('manageProfileTabTitle'), - 'manage-profile-overlay'); - }; - - cr.addSingletonGetter(ManageProfileOverlay); - - ManageProfileOverlay.prototype = { - // Inherit from OptionsPage. - __proto__: OptionsPage.prototype, - - // Info about the currently managed/deleted profile. - profileInfo_: null, - - // An object containing all known profile names. - profileNames_: {}, - - // The currently selected icon in the icon grid. - iconGridSelectedURL_: null, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - OptionsPage.prototype.initializePage.call(this); - - var self = this; - var iconGrid = $('manage-profile-icon-grid'); - var createIconGrid = $('create-profile-icon-grid'); - options.ProfilesIconGrid.decorate(iconGrid); - options.ProfilesIconGrid.decorate(createIconGrid); - iconGrid.addEventListener('change', function(e) { - self.onIconGridSelectionChanged_('manage'); - }); - createIconGrid.addEventListener('change', function(e) { - self.onIconGridSelectionChanged_('create'); - }); - - $('manage-profile-name').oninput = function(event) { - self.onNameChanged_(event, 'manage'); - }; - $('create-profile-name').oninput = function(event) { - self.onNameChanged_(event, 'create'); - }; - $('manage-profile-cancel').onclick = - $('delete-profile-cancel').onclick = - $('create-profile-cancel').onclick = function(event) { - OptionsPage.closeOverlay(); - }; - $('manage-profile-ok').onclick = function(event) { - OptionsPage.closeOverlay(); - self.submitManageChanges_(); - }; - $('delete-profile-ok').onclick = function(event) { - OptionsPage.closeOverlay(); - chrome.send('deleteProfile', [self.profileInfo_.filePath]); - }; - $('create-profile-ok').onclick = function(event) { - OptionsPage.closeOverlay(); - // Get the user's chosen name and icon, or default if they do not - // wish to customize their profile. - var name = $('create-profile-name').value; - var icon_url = createIconGrid.selectedItem; - chrome.send('createProfile', [name, icon_url]); - }; - }, - - /** @inheritDoc */ - didShowPage: function() { - chrome.send('requestDefaultProfileIcons'); - - // Just ignore the manage profile dialog on Chrome OS, they use /accounts. - if (!cr.isChromeOS && window.location.pathname == '/manageProfile') - ManageProfileOverlay.getInstance().prepareForManageDialog_(); - - $('manage-profile-name').focus(); - $('create-profile-name').focus(); - }, - - /** - * Set the profile info used in the dialog. - * @param {Object} profileInfo An object of the form: - * profileInfo = { - * name: "Profile Name", - * iconURL: "chrome://path/to/icon/image", - * filePath: "/path/to/profile/data/on/disk" - * isCurrentProfile: false, - * }; - * @param {String} mode A label that specifies the type of dialog - * box which is currently being viewed (i.e. 'create' or - * 'manage'). - * @private - */ - setProfileInfo_: function(profileInfo, mode) { - this.iconGridSelectedURL_ = profileInfo.iconURL; - this.profileInfo_ = profileInfo; - $(mode + '-profile-name').value = profileInfo.name; - $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL; - }, - - /** - * Sets the name of the currently edited profile. - * @private - */ - setProfileName_: function(name) { - if (this.profileInfo_) - this.profileInfo_.name = name; - $('manage-profile-name').value = name; - }, - - /** - * the user will use to choose their profile icon. - * @param {Array.} iconURLs An array of icon URLs. - * @private - */ - receiveDefaultProfileIcons_: function(iconGrid, iconURLs) { - $(iconGrid).dataModel = new ArrayDataModel(iconURLs); - - if (this.profileInfo_) - $(iconGrid).selectedItem = this.profileInfo_.iconURL; - - var grid = $(iconGrid); - // Recalculate the measured item size. - grid.measured_ = null; - grid.columns = 0; - grid.redraw(); - }, - - /** - * Set a dictionary of all profile names. These are used to prevent the - * user from naming two profiles the same. - * @param {Object} profileNames A dictionary of profile names. - * @private - */ - receiveProfileNames_: function(profileNames) { - this.profileNames_ = profileNames; - }, - - /** - * Display the error bubble, with |errorText| in the bubble. - * @param {string} errorText The localized string id to display as an error. - * @param {String} mode A label that specifies the type of dialog - * box which is currently being viewed (i.e. 'create' or - * 'manage'). - * @private - */ - showErrorBubble_: function(errorText, mode) { - var nameErrorEl = $(mode + '-profile-error-bubble'); - nameErrorEl.hidden = false; - nameErrorEl.textContent = loadTimeData.getString(errorText); - - $(mode + '-profile-ok').disabled = true; - }, - - /** - * Hide the error bubble. - * @param {String} mode A label that specifies the type of dialog - * box which is currently being viewed (i.e. 'create' or - * 'manage'). - * @private - */ - hideErrorBubble_: function(mode) { - $(mode + '-profile-error-bubble').hidden = true; - $(mode + '-profile-ok').disabled = false; - }, - - /** - * oninput callback for field. - * @param {Event} event The event object. - * @param {String} mode A label that specifies the type of dialog - * box which is currently being viewed (i.e. 'create' or - * 'manage'). - * @private - */ - onNameChanged_: function(event, mode) { - var newName = event.target.value; - var oldName = this.profileInfo_.name; - - if (newName == oldName) { - this.hideErrorBubble_(mode); - } else if (this.profileNames_[newName] != undefined) { - this.showErrorBubble_('manageProfilesDuplicateNameError', mode); - } else { - this.hideErrorBubble_(mode); - - var nameIsValid = $(mode + '-profile-name').validity.valid; - $(mode + '-profile-ok').disabled = !nameIsValid; - } - }, - - /** - * Called when the user clicks "OK". Saves the newly changed profile info. - * @private - */ - submitManageChanges_: function() { - var name = $('manage-profile-name').value; - var iconURL = $('manage-profile-icon-grid').selectedItem; - chrome.send('setProfileNameAndIcon', - [this.profileInfo_.filePath, name, iconURL]); - }, - - /** - * Called when the selected icon in the icon grid changes. - * @param {String} mode A label that specifies the type of dialog - * box which is currently being viewed (i.e. 'create' or - * 'manage'). - * @private - */ - onIconGridSelectionChanged_: function(mode) { - var iconURL = $(mode + '-profile-icon-grid').selectedItem; - if (!iconURL || iconURL == this.iconGridSelectedURL_) - return; - this.iconGridSelectedURL_ = iconURL; - chrome.send('profileIconSelectionChanged', - [this.profileInfo_.filePath, iconURL]); - }, - - /** - * Updates the contents of the "Manage Profile" section of the dialog, - * and shows that section. - * @private - */ - prepareForManageDialog_: function() { - var profileInfo = BrowserOptions.getCurrentProfile(); - ManageProfileOverlay.setProfileInfo(profileInfo, 'manage'); - $('manage-profile-overlay-create').hidden = true; - $('manage-profile-overlay-manage').hidden = false; - $('manage-profile-overlay-delete').hidden = true; - this.hideErrorBubble_('manage'); - }, - - /** - * Display the "Manage Profile" dialog. - * @private - */ - showManageDialog_: function() { - this.prepareForManageDialog_(); - OptionsPage.navigateToPage('manageProfile'); - }, - - /** - * Display the "Delete Profile" dialog. - * @param {Object} profileInfo The profile object of the profile to delete. - * @private - */ - showDeleteDialog_: function(profileInfo) { - ManageProfileOverlay.setProfileInfo(profileInfo, 'manage'); - $('manage-profile-overlay-create').hidden = true; - $('manage-profile-overlay-manage').hidden = true; - $('manage-profile-overlay-delete').hidden = false; - $('delete-profile-message').textContent = - loadTimeData.getStringF('deleteProfileMessage', profileInfo.name); - $('delete-profile-message').style.backgroundImage = 'url("' + - profileInfo.iconURL + '")'; - - // Because this dialog isn't useful when refreshing or as part of the - // history, don't create a history entry for it when showing. - OptionsPage.showPageByName('manageProfile', false); - }, - - /** - * Display the "Create Profile" dialog. - * @param {Object} profileInfo The profile object of the profile to - * create. Upon creation, this object only needs a name and an avatar. - * @private - */ - showCreateDialog_: function(profileInfo) { - ManageProfileOverlay.setProfileInfo(profileInfo, 'create'); - $('manage-profile-overlay-create').hidden = false; - $('manage-profile-overlay-manage').hidden = true; - $('manage-profile-overlay-delete').hidden = true; - $('create-profile-instructions').textContent = - loadTimeData.getStringF('createProfileInstructions'); - ManageProfileOverlay.getInstance().hideErrorBubble_('create'); - - OptionsPage.showPageByName('manageProfile', false); - }, - - }; - - // Forward public APIs to private implementations. - [ - 'receiveDefaultProfileIcons', - 'receiveProfileNames', - 'setProfileInfo', - 'setProfileName', - 'showManageDialog', - 'showDeleteDialog', - 'showCreateDialog', - ].forEach(function(name) { - ManageProfileOverlay[name] = function() { - var instance = ManageProfileOverlay.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - // Export - return { - ManageProfileOverlay: ManageProfileOverlay - }; -}); diff --git a/chrome/browser/resources/options2/media_galleries_list.js b/chrome/browser/resources/options2/media_galleries_list.js deleted file mode 100644 index 417084b..0000000 --- a/chrome/browser/resources/options2/media_galleries_list.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var DeletableItemList = options.DeletableItemList; - - /** - * @constructor - * @extends {DeletableItem} - */ - function MediaGalleriesListItem(galleryInfo) { - var el = cr.doc.createElement('div'); - el.galleryInfo_ = galleryInfo; - el.__proto__ = MediaGalleriesListItem.prototype; - el.decorate(); - return el; - } - - MediaGalleriesListItem.prototype = { - __proto__: DeletableItem.prototype, - - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - var span = this.ownerDocument.createElement('span'); - span.textContent = this.galleryInfo_.displayName; - this.contentElement.appendChild(span); - this.contentElement.title = this.galleryInfo_.path; - }, - }; - - var MediaGalleriesList = cr.ui.define('list'); - - MediaGalleriesList.prototype = { - __proto__: DeletableItemList.prototype, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - this.autoExpands_ = true; - }, - - /** @inheritDoc */ - createItem: function(galleryInfo) { - return new MediaGalleriesListItem(galleryInfo); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - chrome.send('forgetGallery', [this.dataModel.item(index).id]); - }, - }; - - return { - MediaGalleriesList: MediaGalleriesList - }; -}); diff --git a/chrome/browser/resources/options2/media_galleries_manager_overlay.html b/chrome/browser/resources/options2/media_galleries_manager_overlay.html deleted file mode 100644 index 5f4e10d..0000000 --- a/chrome/browser/resources/options2/media_galleries_manager_overlay.html +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/chrome/browser/resources/options2/media_galleries_manager_overlay.js b/chrome/browser/resources/options2/media_galleries_manager_overlay.js deleted file mode 100644 index e1a24ff..0000000 --- a/chrome/browser/resources/options2/media_galleries_manager_overlay.js +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * This class is an overlay which allows the user to add or remove media - * galleries, and displays known media galleries. - * @constructor - * @extends {OptionsPage} - */ - function MediaGalleriesManager() { - OptionsPage.call(this, 'manageGalleries', - loadTimeData.getString('manageMediaGalleriesTabTitle'), - 'manage-media-galleries-overlay'); - } - - cr.addSingletonGetter(MediaGalleriesManager); - - MediaGalleriesManager.prototype = { - __proto__: OptionsPage.prototype, - - /** - * Decorate the overlay and set up event handlers. - */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.availableGalleriesList_ = $('available-galleries-list'); - options.MediaGalleriesList.decorate(this.availableGalleriesList_); - - $('new-media-gallery').addEventListener('click', function() { - chrome.send('addNewGallery'); - }); - - $('manage-media-confirm').addEventListener( - 'click', OptionsPage.closeOverlay.bind(OptionsPage)); - - this.addEventListener('visibleChange', this.handleVisibleChange_); - }, - - /** - * @inheritDoc - * @private - */ - handleVisibleChange_: function() { - if (!this.visible) - return; - - if (this.availableGalleriesList_) - this.availableGalleriesList_.redraw(); - }, - - /** - * @param {Array} galleries List of structs describibing galleries. - * @private - */ - setAvailableMediaGalleries_: function(galleries) { - $('available-galleries-list').dataModel = new ArrayDataModel(galleries); - // TODO(estade): show this section by default. - $('media-galleries-section').hidden = false; - }, - }, - - // Forward public APIs to private implementations. - [ - 'setAvailableMediaGalleries', - ].forEach(function(name) { - MediaGalleriesManager[name] = function() { - var instance = MediaGalleriesManager.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - // Export - return { - MediaGalleriesManager: MediaGalleriesManager - }; -}); diff --git a/chrome/browser/resources/options2/options.html b/chrome/browser/resources/options2/options.html deleted file mode 100644 index c5bcac0..0000000 --- a/chrome/browser/resources/options2/options.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
-
-
- - - - - - - diff --git a/chrome/browser/resources/options2/options.js b/chrome/browser/resources/options2/options.js deleted file mode 100644 index 487c517..0000000 --- a/chrome/browser/resources/options2/options.js +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var AddLanguageOverlay = options.AddLanguageOverlay; -var AlertOverlay = options.AlertOverlay; -var AutofillEditAddressOverlay = options.AutofillEditAddressOverlay; -var AutofillEditCreditCardOverlay = options.AutofillEditCreditCardOverlay; -var AutofillOptions = options.AutofillOptions; -var BrowserOptions = options.BrowserOptions; -var ClearBrowserDataOverlay = options.ClearBrowserDataOverlay; -var ContentSettings = options.ContentSettings; -var ContentSettingsExceptionsArea = - options.contentSettings.ContentSettingsExceptionsArea; -var CookiesView = options.CookiesView; -var CookiesViewApp = options.CookiesViewApp; -var FontSettings = options.FontSettings; -var HandlerOptions = options.HandlerOptions; -var HomePageOverlay = options.HomePageOverlay; -var ImportDataOverlay = options.ImportDataOverlay; -var InstantConfirmOverlay = options.InstantConfirmOverlay; -var LanguageOptions = options.LanguageOptions; -var MediaGalleriesManager = options.MediaGalleriesManager; -var OptionsFocusManager = options.OptionsFocusManager; -var OptionsPage = options.OptionsPage; -var PasswordManager = options.PasswordManager; -var Preferences = options.Preferences; -var PreferredNetworks = options.PreferredNetworks; -var ManageProfileOverlay = options.ManageProfileOverlay; -var SearchEngineManager = options.SearchEngineManager; -var SearchPage = options.SearchPage; -var SpellingConfirmOverlay = options.SpellingConfirmOverlay; -var StartupOverlay = options.StartupOverlay; -var SyncSetupOverlay = options.SyncSetupOverlay; - -/** - * DOMContentLoaded handler, sets up the page. - */ -function load() { - // Decorate the existing elements in the document. - cr.ui.decorate('input[pref][type=checkbox]', options.PrefCheckbox); - cr.ui.decorate('input[pref][type=number]', options.PrefNumber); - cr.ui.decorate('input[pref][type=radio]', options.PrefRadio); - cr.ui.decorate('input[pref][type=range]', options.PrefRange); - cr.ui.decorate('select[pref]', options.PrefSelect); - cr.ui.decorate('input[pref][type=text]', options.PrefTextField); - cr.ui.decorate('input[pref][type=url]', options.PrefTextField); - cr.ui.decorate('button[pref]', options.PrefButton); - cr.ui.decorate('#content-settings-page input[type=radio]:not(.handler-radio)', - options.ContentSettingsRadio); - cr.ui.decorate('#content-settings-page input[type=radio].handler-radio', - options.HandlersEnabledRadio); - cr.ui.decorate('span.controlled-setting-indicator', - options.ControlledSettingIndicator); - - // Top level pages. - OptionsPage.register(SearchPage.getInstance()); - OptionsPage.register(BrowserOptions.getInstance()); - - // Overlays. - OptionsPage.registerOverlay(AddLanguageOverlay.getInstance(), - LanguageOptions.getInstance()); - OptionsPage.registerOverlay(AlertOverlay.getInstance()); - OptionsPage.registerOverlay(AutofillEditAddressOverlay.getInstance(), - AutofillOptions.getInstance()); - OptionsPage.registerOverlay(AutofillEditCreditCardOverlay.getInstance(), - AutofillOptions.getInstance()); - OptionsPage.registerOverlay(AutofillOptions.getInstance(), - BrowserOptions.getInstance(), - [$('autofill-settings')]); - OptionsPage.registerOverlay(ClearBrowserDataOverlay.getInstance(), - BrowserOptions.getInstance(), - [$('privacyClearDataButton')]); - OptionsPage.registerOverlay(ContentSettings.getInstance(), - BrowserOptions.getInstance(), - [$('privacyContentSettingsButton')]); - OptionsPage.registerOverlay(ContentSettingsExceptionsArea.getInstance(), - ContentSettings.getInstance()); - OptionsPage.registerOverlay(CookiesView.getInstance(), - ContentSettings.getInstance(), - [$('privacyContentSettingsButton'), - $('show-cookies-button')]); - OptionsPage.registerOverlay(CookiesViewApp.getInstance(), - ContentSettings.getInstance(), - [$('privacyContentSettingsButton'), - $('show-app-cookies-button')]); - OptionsPage.registerOverlay(FontSettings.getInstance(), - BrowserOptions.getInstance(), - [$('fontSettingsCustomizeFontsButton')]); - if (HandlerOptions && $('manage-handlers-button')) { - OptionsPage.registerOverlay(HandlerOptions.getInstance(), - ContentSettings.getInstance(), - [$('manage-handlers-button')]); - } - OptionsPage.registerOverlay(HomePageOverlay.getInstance(), - BrowserOptions.getInstance(), - [$('change-home-page')]); - OptionsPage.registerOverlay(ImportDataOverlay.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(InstantConfirmOverlay.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(LanguageOptions.getInstance(), - BrowserOptions.getInstance(), - [$('language-button')]); - OptionsPage.registerOverlay(ManageProfileOverlay.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(MediaGalleriesManager.getInstance(), - ContentSettings.getInstance(), - [$('manage-galleries-button')]); - OptionsPage.registerOverlay(PasswordManager.getInstance(), - BrowserOptions.getInstance(), - [$('manage-passwords')]); - OptionsPage.registerOverlay(SearchEngineManager.getInstance(), - BrowserOptions.getInstance(), - [$('manage-default-search-engines')]); - OptionsPage.registerOverlay(SpellingConfirmOverlay.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(StartupOverlay.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(SyncSetupOverlay.getInstance(), - BrowserOptions.getInstance()); - if (cr.isChromeOS) { - OptionsPage.registerOverlay(AccountsOptions.getInstance(), - BrowserOptions.getInstance(), - [$('manage-accounts-button')]); - OptionsPage.registerOverlay(BluetoothOptions.getInstance(), - BrowserOptions.getInstance(), - [$('bluetooth-add-device')]); - OptionsPage.registerOverlay(BluetoothPairing.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(ChangePictureOptions.getInstance(), - BrowserOptions.getInstance(), - [$('account-picture')]); - OptionsPage.registerOverlay(DetailsInternetPage.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay(DisplayOptions.getInstance(), - BrowserOptions.getInstance(), - [$('display-options-button')]); - OptionsPage.registerOverlay(KeyboardOverlay.getInstance(), - BrowserOptions.getInstance(), - [$('keyboard-settings-button')]); - OptionsPage.registerOverlay(PointerOverlay.getInstance(), - BrowserOptions.getInstance(), - [$('pointer-settings-button')]); - OptionsPage.registerOverlay(PreferredNetworks.getInstance(), - BrowserOptions.getInstance()); - OptionsPage.registerOverlay( - new OptionsPage('languageChewing', - loadTimeData.getString('languageChewingPageTabTitle'), - 'languageChewingPage'), - LanguageOptions.getInstance()); - OptionsPage.registerOverlay( - new OptionsPage('languageHangul', - loadTimeData.getString('languageHangulPageTabTitle'), - 'languageHangulPage'), - LanguageOptions.getInstance()); - OptionsPage.registerOverlay( - new OptionsPage('languageMozc', - loadTimeData.getString('languageMozcPageTabTitle'), - 'languageMozcPage'), - LanguageOptions.getInstance()); - OptionsPage.registerOverlay( - new OptionsPage('languagePinyin', - loadTimeData.getString('languagePinyinPageTabTitle'), - 'languagePinyinPage'), - LanguageOptions.getInstance()); - } - - - if (SetWallpaperOptions) { - OptionsPage.registerOverlay(SetWallpaperOptions.getInstance(), - BrowserOptions.getInstance(), - [$('set-wallpaper')]); - } - - - if (!cr.isWindows && !cr.isMac) { - OptionsPage.registerOverlay(CertificateBackupOverlay.getInstance(), - CertificateManager.getInstance()); - OptionsPage.registerOverlay(CertificateEditCaTrustOverlay.getInstance(), - CertificateManager.getInstance()); - OptionsPage.registerOverlay(CertificateImportErrorOverlay.getInstance(), - CertificateManager.getInstance()); - OptionsPage.registerOverlay(CertificateManager.getInstance(), - BrowserOptions.getInstance(), - [$('certificatesManageButton')]); - OptionsPage.registerOverlay(CertificateRestoreOverlay.getInstance(), - CertificateManager.getInstance()); - } - - OptionsFocusManager.getInstance().initialize(); - Preferences.getInstance().initialize(); - OptionsPage.initialize(); - - var path = document.location.pathname; - - if (path.length > 1) { - // Skip starting slash and remove trailing slash (if any). - var pageName = path.slice(1).replace(/\/$/, ''); - OptionsPage.showPageByName(pageName, true, {replaceState: true}); - } else { - OptionsPage.showDefaultPage(); - } - - var subpagesNavTabs = document.querySelectorAll('.subpages-nav-tabs'); - for (var i = 0; i < subpagesNavTabs.length; i++) { - subpagesNavTabs[i].onclick = function(event) { - OptionsPage.showTab(event.srcElement); - }; - } - - if (navigator.plugins['Shockwave Flash']) - document.documentElement.setAttribute('hasFlashPlugin', ''); - - window.setTimeout(function() { - document.documentElement.classList.remove('loading'); - }); -} - -document.documentElement.classList.add('loading'); -document.addEventListener('DOMContentLoaded', load); - -/** - * Listener for the |beforeunload| event. - */ -window.onbeforeunload = function() { - options.OptionsPage.willClose(); -}; - -/** - * Listener for the |popstate| event. - * @param {Event} e The |popstate| event. - */ -window.onpopstate = function(e) { - options.OptionsPage.setState(e.state); -}; diff --git a/chrome/browser/resources/options2/options_bundle.js b/chrome/browser/resources/options2/options_bundle.js deleted file mode 100644 index 2bbd43d47..0000000 --- a/chrome/browser/resources/options2/options_bundle.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This file exists to aggregate all of the javascript used by the -// settings page into a single file which will be flattened and served -// as a single resource. - - - - - - - - - - - - - - - - - - - - - - - - - - - - var AccountsOptions = options.AccountsOptions; - var ChangePictureOptions = options.ChangePictureOptions; - var DetailsInternetPage = options.internet.DetailsInternetPage; - var DisplayOptions = options.DisplayOptions; - var BluetoothOptions = options.BluetoothOptions; - var BluetoothPairing = options.BluetoothPairing; - var KeyboardOverlay = options.KeyboardOverlay; - var PointerOverlay = options.PointerOverlay; - var UIAccountTweaks = uiAccountTweaks.UIAccountTweaks; - - - - var SetWallpaperOptions = options.SetWallpaperOptions; - - - - - - - - - var CertificateManager = options.CertificateManager; - var CertificateRestoreOverlay = options.CertificateRestoreOverlay; - var CertificateBackupOverlay = options.CertificateBackupOverlay; - var CertificateEditCaTrustOverlay = options.CertificateEditCaTrustOverlay; - var CertificateImportErrorOverlay = options.CertificateImportErrorOverlay; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/browser/resources/options2/options_focus_manager.js b/chrome/browser/resources/options2/options_focus_manager.js deleted file mode 100644 index d608141..0000000 --- a/chrome/browser/resources/options2/options_focus_manager.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var FocusManager = cr.ui.FocusManager; - var OptionsPage = options.OptionsPage; - - function OptionsFocusManager() { - } - - cr.addSingletonGetter(OptionsFocusManager); - - OptionsFocusManager.prototype = { - __proto__: FocusManager.prototype, - - /** @inheritDoc */ - getFocusParent: function() { - var topPage = OptionsPage.getTopmostVisiblePage().pageDiv; - - // The default page and search page include a search field that is a - // sibling of the rest of the page instead of a child. Thus, use the - // parent node to allow the search field to receive focus. - if (topPage.parentNode.id == 'page-container') - return topPage.parentNode; - - return topPage; - }, - }; - - return { - OptionsFocusManager: OptionsFocusManager, - }; -}); diff --git a/chrome/browser/resources/options2/options_page.css b/chrome/browser/resources/options2/options_page.css deleted file mode 100644 index 90d9c70..0000000 --- a/chrome/browser/resources/options2/options_page.css +++ /dev/null @@ -1,584 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -body { - position: relative; -} - -#main-content { - bottom: 0; - display: -webkit-box; - left: 0; - position: absolute; - right: 0; - top: 0; -} - -#mainview { - -webkit-box-align: stretch; - bottom: 0; - left: 0; - margin: 0; - position: absolute; - right: 0; - top: 0; - z-index: 1; -} - -#mainview-content { - min-height: 100%; - position: relative; -} - -#page-container { - box-sizing: border-box; - max-width: 888px; - min-width: 600px; -} - -body.uber-frame #searchBox { - position: fixed; - z-index: 4; -} - -div.disabled { - color: #999; -} - -.settings-row { - display: block; - margin: 0.65em 0; -} - -.hbox { - -webkit-box-orient: horizontal; - display: -webkit-box; -} - -.vbox { - -webkit-box-orient: vertical; - display: -webkit-box; -} - -.box-align-center { - -webkit-box-align: center; -} - -.stretch { - -webkit-box-flex: 1; -} - -.frozen { - position: fixed; -} - -#overlay-container-1 { - z-index: 11; -} -#overlay-container-2 { - z-index: 12; -} -#overlay-container-3 { - z-index: 13; -} - -.raw-button, -.raw-button:hover, -.raw-button:active { - -webkit-box-shadow: none; - background-color: transparent; - background-repeat: no-repeat; - border: none; - min-width: 0; - padding: 1px 6px; -} - -.bottom-strip { - border-top: none; - bottom: 0; - padding: 12px; - position: absolute; - right: 0; -} - -/* Omit top padding (currently only on #settings) whenever the search page is - * showing. - */ -#searchPage:not([hidden]) + #settings { - padding-top: 0; -} - -.page list { - /* Min height is a multiple of the list item height (32) */ - min-height: 192px; -} - -.option { - margin-top: 0; -} - -.transparent { - opacity: 0; -} - -.touch-slider { - -webkit-appearance: slider-horizontal; -} - -.settings-list, -.settings-list-empty { - border: 1px solid #d9d9d9; - border-radius: 2px; -} - -.settings-list-empty { - background-color: #f4f4f4; - box-sizing: border-box; - min-height: 125px; - padding-left: 20px; - padding-top: 20px; -} - - -/* Editable text field properties */ -.editable-text-field > * { - -webkit-box-align: center; - -webkit-transition: 150ms background-color; - border: none; - box-sizing: border-box; - display: -webkit-box; - height: 20px; - margin: 0; -} - -.editable-text-field > .spacer { - /* The above height rule should not apply to spacers. */ - height: 0; -} - -.editable-text-field .editable-text { - padding: 2px 3px; -} - -.editable-text-field .static-text { - height: 24px; - overflow: hidden; - padding: 3px 4px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.editable-text-field:not([editable]) > [displaymode='edit'] { - display: none; -} - -.editable-text-field[editable] > [displaymode='static'] { - display: none; -} - -.editable-text-field[empty] > input[type='text'] { - color: #ccc; - font-style: italic; -} - -.editable-text-field[disabled] { - opacity: 0.6; -} - -/* Editable List properties */ -list > * { - -webkit-box-align: center; - -webkit-transition: 150ms background-color; - border: none; - border-radius: 0; /* TODO(dbeam): Is this necessary? */ - box-sizing: border-box; - display: -webkit-box; - height: 32px; - margin: 0; -} - -list > .spacer { - /* The above height rule should not apply to spacers. When redraw is called - on the list they will be given an explicit element height but this ensures - they have 0 height to begin with. */ - height: 0; -} - -list:not([disabled]) > :hover { - background-color: rgb(228, 236, 247); -} - -/* TODO(stuartmorgan): Once this becomes the list style for other WebUI pages - * these rules can be simplified (since they wont need to override other rules). - */ - -list:not([hasElementFocus]) > [selected], -list:not([hasElementFocus]) > [lead][selected] { - background-color: #d0d0d0; - background-image: none; -} - -list[hasElementFocus] > [selected], -list[hasElementFocus] > [lead][selected], -list:not([hasElementFocus]) > [selected]:hover, -list:not([hasElementFocus]) > [selected][lead]:hover { - background-color: rgb(187, 206, 233); - background-image: none; -} - -list[hasElementFocus] > [lead], -list[hasElementFocus] > [lead][selected] { - border-bottom: 1px solid rgb(120, 146, 180); - border-top: 1px solid rgb(120, 146, 180); -} - -list[hasElementFocus] > [lead]:nth-child(2), -list[hasElementFocus] > [lead][selected]:nth-child(2) { - border-top: 1px solid transparent; -} - -list[hasElementFocus] > [lead]:nth-last-child(2), -list[hasElementFocus] > [lead][selected]:nth-last-child(2) { - border-bottom: 1px solid transparent; -} - -list[disabled] > [lead][selected], -list[disabled]:focus > [lead][selected] { - border: none; -} - -list[disabled] { - opacity: 0.6; -} - -list > .heading { - color: #666; -} - -list > .heading:hover { - background-color: transparent; - border-color: transparent; -} - -list .deletable-item { - -webkit-box-align: center; -} - -list .deletable-item > :first-child { - -webkit-box-align: center; - -webkit-box-flex: 1; - -webkit-padding-end: 5px; - display: -webkit-box; -} - -list .row-delete-button { - -webkit-transition: 150ms opacity; - background-color: transparent; - /* TODO(stuartmorgan): Replace with real images once they are available. */ - background-image: -webkit-image-set( - url('../../../../ui/resources/default_100_percent/close_bar.png') 1x, - url('../../../../ui/resources/default_200_percent/close_bar.png') 2x); - border: none; - display: block; - height: 16px; - opacity: 1; - width: 16px; -} - -list > *:not(:hover):not([selected]):not([lead]) .row-delete-button, -list:not([hasElementFocus]) > *:not(:hover):not([selected]) .row-delete-button, -list[disabled] .row-delete-button, -list .row-delete-button[disabled] { - opacity: 0; - pointer-events: none; -} - -/* HostedApp entries use the disabled closing button to display the App's - * favicon, as an indicator that instead of deleting the permission here - * the user has to remove the hosted app.*/ -list div[role='listitem'][managedby='HostedApp'] .row-delete-button { - opacity: 1; -} - -list .row-delete-button:hover { - background-image: -webkit-image-set( - url('../../../../ui/resources/default_100_percent/close_bar_hover.png') - 1x, - url('../../../../ui/resources/default_200_percent/close_bar_hover.png') - 2x); -} - -list .row-delete-button:active { - background-image: -webkit-image-set( - url('../../../../ui/resources/default_100_percent/close_bar_pressed.png') - 1x, - url('../../../../ui/resources/default_200_percent/close_bar_pressed.png') - 2x); -} - -list .static-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -list[type='text'][inlineeditable] input { - box-sizing: border-box; - margin: 0; - width: 100%; -} - -list > :not([editing]) [displaymode='edit'] { - display: none; -} - -list > [editing] [displaymode='static'] { - display: none; -} - -list > [editing] input:invalid { - /* TODO(stuartmorgan): Replace with validity badge */ - background-color: pink; -} - -.list-inline-button { - -webkit-appearance: none; - -webkit-transition: opacity 150ms; - background: rgb(138, 170, 237); - border: none; - border-radius: 2px; - color: white; - font-weight: bold; - opacity: 0.7; -} - -.list-inline-button:hover { - opacity: 1; -} - -.option-name { - padding-right: 5px; -} - -html[dir=rtl].option-name { - padding-left: 5px; -} - -.favicon-cell { - -webkit-padding-start: 20px; - background-position: left; - background-repeat: no-repeat; -} - -input[type='url'].favicon-cell { - -webkit-padding-start: 22px; - background-position-x: 4px; -} - -/* TODO(jhawkins): Use something better than 99.3% when CSS3 background - * positioning is available. - */ -html[dir=rtl] input.favicon-cell { - background-position-x: 99.3%; -} - -list .favicon-cell { - -webkit-margin-start: 7px; - -webkit-padding-start: 26px; - display: block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -html[dir=rtl] list .favicon-cell { - background-position: right; -} - -html[enable-background-mode=false] #background-mode-section { - display: none; -} - -/* UI Controls */ - -/* LIST */ -list[hasElementFocus] { - - outline: 1px solid rgba(0, 128, 256, 0.5); - outline-offset: -2px; - - - /* This matches the native list outline on Mac */ - outline-color: rgb(117, 154, 217); - outline-offset: -1px; - outline-style: auto; - outline-width: 5px; - -} - -.suboption { - -webkit-margin-start: 23px; -} - -list.autocomplete-suggestions { - background-color: white; - border: 1px solid #aaa; - border-radius: 2px; - min-height: 0; - opacity: 0.9; - position: fixed; - z-index: 3; -} - -list.autocomplete-suggestions > div { - height: auto; -} - -list.autocomplete-suggestions:not([hasElementFocus]) > [selected], -list.autocomplete-suggestions:not([hasElementFocus]) > [lead][selected] { - background-color: rgb(187, 206, 233); -} - -html:not([hasFlashPlugin]) .flash-plugin-area, -/* If the Flash plug-in supports the NPP_ClearSiteData API, we don't need to - * show the link to the Flash storage settings manager: - */ -html[flashPluginSupportsClearSiteData] .flash-plugin-area, -html:not([flashPluginSupportsClearSiteData]) .clear-plugin-lso-data-enabled, -html[flashPluginSupportsClearSiteData] .clear-plugin-lso-data-disabled, -html:not([enablePepperFlashSettings]) .pepper-flash-settings { - display: none; -} - -/* Controlled setting indicator and bubble. */ -.controlled-setting-indicator { - display: inline-block; - /* Establish a containing block for absolutely positioning the bubble. */ - position: relative; - vertical-align: text-bottom; -} - -.controlled-setting-indicator[controlled-by] summary { - background-size: contain; - height: 16px; - width: 16px; -} - -.controlled-setting-indicator summary::-webkit-details-marker { - display: none; -} - -.controlled-setting-indicator[controlled-by='policy'] summary { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY_GRAY'); - background-size: 16px; -} - -.controlled-setting-indicator[controlled-by='policy'] summary:hover { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY'); - background-size: 16px; -} - -.controlled-setting-indicator[controlled-by='extension'] summary { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION_GRAY'); - background-size: 16px; -} - -.controlled-setting-indicator[controlled-by='extension'] summary:hover { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION'); - background-size: 16px; -} - -.controlled-setting-indicator[controlled-by='recommended'] summary { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED_GRAY'); - background-size: 16px; -} - -.controlled-setting-indicator[controlled-by='recommended'] summary:hover { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED'); - background-size: 16px; -} - -.controlled-setting-bubble { - -webkit-margin-start: -20px; - background-color: white; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0 2px 2px #ddd; - margin-top: 10px; - padding: 10px; - position: absolute; - top: 50%; - z-index: 10; -} - -html[dir='ltr'] .controlled-setting-bubble { - left: 50%; -} - -html[dir='rtl'] .controlled-setting-bubble { - right: 50%; -} - -.controlled-setting-bubble::before { - -webkit-margin-start: 4px; - border-color: #ccc transparent; - border-style: solid; - border-width: 0 5px 5px; - content: ''; - position: absolute; - top: -5px; -} - -.controlled-setting-bubble::after { - -webkit-margin-start: 5px; - border-color: white transparent; - border-style: solid; - border-width: 0 4px 4px; - content: ''; - position: absolute; - top: -4px; -} - -.controlled-setting-bubble-text { - -webkit-padding-start: 30px; - background-repeat: no-repeat; - margin: 0; - min-height: 32px; - min-width: 200px; -} - -.controlled-setting-indicator[controlled-by='policy'] - .controlled-setting-bubble-text { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_MANDATORY_LARGE'); - background-size: 22px; -} - -.controlled-setting-indicator[controlled-by='extension'] - .controlled-setting-bubble-text { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_EXTENSION_LARGE'); - background-size: 22px; -} - -.controlled-setting-indicator[controlled-by='recommended'] - .controlled-setting-bubble-text { - background-image: - url('chrome://theme/IDR_CONTROLLED_SETTING_RECOMMENDED_LARGE'); - background-size: 22px; -} - -html[dir='rtl'] .controlled-setting-bubble-text { - background-position: right top; -} - -.controlled-setting-bubble-action { - padding: 0 !important; -} diff --git a/chrome/browser/resources/options2/options_page.js b/chrome/browser/resources/options2/options_page.js deleted file mode 100644 index 7401694..0000000 --- a/chrome/browser/resources/options2/options_page.js +++ /dev/null @@ -1,908 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - ///////////////////////////////////////////////////////////////////////////// - // OptionsPage class: - - /** - * Base class for options page. - * @constructor - * @param {string} name Options page name. - * @param {string} title Options page title, used for history. - * @extends {EventTarget} - */ - function OptionsPage(name, title, pageDivName) { - this.name = name; - this.title = title; - this.pageDivName = pageDivName; - this.pageDiv = $(this.pageDivName); - this.tab = null; - this.lastFocusedElement = null; - } - - /** @const */ var HORIZONTAL_OFFSET = 155; - - /** - * This is the absolute difference maintained between standard and - * fixed-width font sizes. Refer http://crbug.com/91922. - */ - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD = 3; - - /** - * Main level option pages. Maps lower-case page names to the respective page - * object. - * @protected - */ - OptionsPage.registeredPages = {}; - - /** - * Pages which are meant to behave like modal dialogs. Maps lower-case overlay - * names to the respective overlay object. - * @protected - */ - OptionsPage.registeredOverlayPages = {}; - - /** - * Gets the default page (to be shown on initial load). - */ - OptionsPage.getDefaultPage = function() { - return BrowserOptions.getInstance(); - }; - - /** - * Shows the default page. - */ - OptionsPage.showDefaultPage = function() { - this.navigateToPage(this.getDefaultPage().name); - }; - - /** - * "Navigates" to a page, meaning that the page will be shown and the - * appropriate entry is placed in the history. - * @param {string} pageName Page name. - */ - OptionsPage.navigateToPage = function(pageName) { - this.showPageByName(pageName, true); - }; - - /** - * Shows a registered page. This handles both top-level and overlay pages. - * @param {string} pageName Page name. - * @param {boolean} updateHistory True if we should update the history after - * showing the page. - * @param {Object=} opt_propertyBag An optional bag of properties including - * replaceState (if history state should be replaced instead of pushed). - * @private - */ - OptionsPage.showPageByName = function(pageName, - updateHistory, - opt_propertyBag) { - // If |opt_propertyBag| is non-truthy, homogenize to object. - opt_propertyBag = opt_propertyBag || {}; - - // Find the currently visible root-level page. - var rootPage = null; - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; - if (page.visible && !page.parentPage) { - rootPage = page; - break; - } - } - - // Find the target page. - var targetPage = this.registeredPages[pageName.toLowerCase()]; - if (!targetPage || !targetPage.canShowPage()) { - // If it's not a page, try it as an overlay. - if (!targetPage && this.showOverlay_(pageName, rootPage)) { - if (updateHistory) - this.updateHistoryState_(!!opt_propertyBag.replaceState); - return; - } else { - targetPage = this.getDefaultPage(); - } - } - - pageName = targetPage.name.toLowerCase(); - var targetPageWasVisible = targetPage.visible; - - // Determine if the root page is 'sticky', meaning that it - // shouldn't change when showing an overlay. This can happen for special - // pages like Search. - var isRootPageLocked = - rootPage && rootPage.sticky && targetPage.parentPage; - - var allPageNames = Array.prototype.concat.call( - Object.keys(this.registeredPages), - Object.keys(this.registeredOverlayPages)); - - // Notify pages if they will be hidden. - for (var i = 0; i < allPageNames.length; ++i) { - var name = allPageNames[i]; - var page = this.registeredPages[name] || - this.registeredOverlayPages[name]; - if (!page.parentPage && isRootPageLocked) - continue; - if (page.willHidePage && name != pageName && - !page.isAncestorOfPage(targetPage)) { - page.willHidePage(); - } - } - - // Update visibilities to show only the hierarchy of the target page. - for (var i = 0; i < allPageNames.length; ++i) { - var name = allPageNames[i]; - var page = this.registeredPages[name] || - this.registeredOverlayPages[name]; - if (!page.parentPage && isRootPageLocked) - continue; - page.visible = name == pageName || page.isAncestorOfPage(targetPage); - } - - // Update the history and current location. - if (updateHistory) - this.updateHistoryState_(!!opt_propertyBag.replaceState); - - // Update tab title. - this.setTitle_(targetPage.title); - - // Notify pages if they were shown. - for (var i = 0; i < allPageNames.length; ++i) { - var name = allPageNames[i]; - var page = this.registeredPages[name] || - this.registeredOverlayPages[name]; - if (!page.parentPage && isRootPageLocked) - continue; - if (!targetPageWasVisible && page.didShowPage && - (name == pageName || page.isAncestorOfPage(targetPage))) { - page.didShowPage(); - } - } - }; - - /** - * Sets the title of the page. This is accomplished by calling into the - * parent page API. - * @param {String} title The title string. - * @private - */ - OptionsPage.setTitle_ = function(title) { - uber.invokeMethodOnParent('setTitle', {title: title}); - }; - - /** - * Scrolls the page to the correct position (the top when opening an overlay, - * or the old scroll position a previously hidden overlay becomes visible). - * @private - */ - OptionsPage.updateScrollPosition_ = function() { - var container = $('page-container'); - var scrollTop = container.oldScrollTop || 0; - container.oldScrollTop = undefined; - window.scroll(document.body.scrollLeft, scrollTop); - }; - - /** - * Pushes the current page onto the history stack, overriding the last page - * if it is the generic chrome://settings/. - * @param {boolean} replace If true, allow no history events to be created. - * @param {object=} opt_params A bag of optional params, including: - * {boolean} ignoreHash Whether to include the hash or not. - * @private - */ - OptionsPage.updateHistoryState_ = function(replace, opt_params) { - var page = this.getTopmostVisiblePage(); - var path = window.location.pathname + window.location.hash; - if (path) - path = path.slice(1).replace(/\/(?:#|$)/, ''); // Remove trailing slash. - // The page is already in history (the user may have clicked the same link - // twice). Do nothing. - if (path == page.name && - !document.documentElement.classList.contains('loading')) { - return; - } - - var hash = opt_params && opt_params.ignoreHash ? '' : window.location.hash; - - // If settings are embedded, tell the outer page to set its "path" to the - // inner frame's path. - var outerPath = (page == this.getDefaultPage() ? '' : page.name) + hash; - uber.invokeMethodOnParent('setPath', {path: outerPath}); - - // If there is no path, the current location is chrome://settings/. - // Override this with the new page. - var historyFunction = path && !replace ? window.history.pushState : - window.history.replaceState; - historyFunction.call(window.history, - {pageName: page.name}, - page.title, - '/' + page.name + hash); - - // Update tab title. - this.setTitle_(page.title); - }; - - /** - * Shows a registered Overlay page. Does not update history. - * @param {string} overlayName Page name. - * @param {OptionPage} rootPage The currently visible root-level page. - * @return {boolean} whether we showed an overlay. - */ - OptionsPage.showOverlay_ = function(overlayName, rootPage) { - var overlay = this.registeredOverlayPages[overlayName.toLowerCase()]; - if (!overlay || !overlay.canShowPage()) - return false; - - // Save the currently focused element in the page for restoration later. - var currentPage = this.getTopmostVisiblePage(); - if (currentPage) - currentPage.lastFocusedElement = document.activeElement; - - if ((!rootPage || !rootPage.sticky) && overlay.parentPage) - this.showPageByName(overlay.parentPage.name, false); - - if (!overlay.visible) { - overlay.visible = true; - if (overlay.didShowPage) overlay.didShowPage(); - } - - // Update tab title. - this.setTitle_(overlay.title); - - $('searchBox').setAttribute('aria-hidden', true); - - return true; - }; - - /** - * Returns whether or not an overlay is visible. - * @return {boolean} True if an overlay is visible. - * @private - */ - OptionsPage.isOverlayVisible_ = function() { - return this.getVisibleOverlay_() != null; - }; - - /** - * Returns the currently visible overlay, or null if no page is visible. - * @return {OptionPage} The visible overlay. - */ - OptionsPage.getVisibleOverlay_ = function() { - var topmostPage = null; - for (var name in this.registeredOverlayPages) { - var page = this.registeredOverlayPages[name]; - if (page.visible && - (!topmostPage || page.nestingLevel > topmostPage.nestingLevel)) { - topmostPage = page; - } - } - return topmostPage; - }; - - /** - * Restores the last focused element on a given page. - */ - OptionsPage.restoreLastFocusedElement_ = function() { - var currentPage = this.getTopmostVisiblePage(); - if (currentPage.lastFocusedElement) - currentPage.lastFocusedElement.focus(); - }; - - /** - * Closes the visible overlay. Updates the history state after closing the - * overlay. - */ - OptionsPage.closeOverlay = function() { - var overlay = this.getVisibleOverlay_(); - if (!overlay) - return; - - overlay.visible = false; - - if (overlay.didClosePage) overlay.didClosePage(); - this.updateHistoryState_(false, {ignoreHash: true}); - - this.restoreLastFocusedElement_(); - if (!this.isOverlayVisible_()) - $('searchBox').removeAttribute('aria-hidden'); - }; - - /** - * Cancels (closes) the overlay, due to the user pressing . - */ - OptionsPage.cancelOverlay = function() { - // Blur the active element to ensure any changed pref value is saved. - document.activeElement.blur(); - var overlay = this.getVisibleOverlay_(); - // Let the overlay handle the if it wants to. - if (overlay.handleCancel) { - overlay.handleCancel(); - this.restoreLastFocusedElement_(); - } else { - this.closeOverlay(); - } - }; - - /** - * Hides the visible overlay. Does not affect the history state. - * @private - */ - OptionsPage.hideOverlay_ = function() { - var overlay = this.getVisibleOverlay_(); - if (overlay) - overlay.visible = false; - }; - - /** - * Returns the pages which are currently visible, ordered by nesting level - * (ascending). - * @return {Array.OptionPage} The pages which are currently visible, ordered - * by nesting level (ascending). - */ - OptionsPage.getVisiblePages_ = function() { - var visiblePages = []; - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; - if (page.visible) - visiblePages[page.nestingLevel] = page; - } - return visiblePages; - }; - - /** - * Returns the topmost visible page (overlays excluded). - * @return {OptionPage} The topmost visible page aside any overlay. - * @private - */ - OptionsPage.getTopmostVisibleNonOverlayPage_ = function() { - var topPage = null; - for (var name in this.registeredPages) { - var page = this.registeredPages[name]; - if (page.visible && - (!topPage || page.nestingLevel > topPage.nestingLevel)) - topPage = page; - } - - return topPage; - }; - - /** - * Returns the topmost visible page, or null if no page is visible. - * @return {OptionPage} The topmost visible page. - */ - OptionsPage.getTopmostVisiblePage = function() { - // Check overlays first since they're top-most if visible. - return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_(); - }; - - /** - * Updates managed banner visibility state based on the topmost page. - */ - OptionsPage.updateManagedBannerVisibility = function() { - var topPage = this.getTopmostVisiblePage(); - if (topPage) - topPage.updateManagedBannerVisibility(); - }; - - /** - * Shows the tab contents for the given navigation tab. - * @param {!Element} tab The tab that the user clicked. - */ - OptionsPage.showTab = function(tab) { - // Search parents until we find a tab, or the nav bar itself. This allows - // tabs to have child nodes, e.g. labels in separately-styled spans. - while (tab && !tab.classList.contains('subpages-nav-tabs') && - !tab.classList.contains('tab')) { - tab = tab.parentNode; - } - if (!tab || !tab.classList.contains('tab')) - return; - - // Find tab bar of the tab. - var tabBar = tab; - while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) { - tabBar = tabBar.parentNode; - } - if (!tabBar) - return; - - if (tabBar.activeNavTab != null) { - tabBar.activeNavTab.classList.remove('active-tab'); - $(tabBar.activeNavTab.getAttribute('tab-contents')).classList. - remove('active-tab-contents'); - } - - tab.classList.add('active-tab'); - $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents'); - tabBar.activeNavTab = tab; - }; - - /** - * Registers new options page. - * @param {OptionsPage} page Page to register. - */ - OptionsPage.register = function(page) { - this.registeredPages[page.name.toLowerCase()] = page; - page.initializePage(); - }; - - /** - * Find an enclosing section for an element if it exists. - * @param {Element} element Element to search. - * @return {OptionPage} The section element, or null. - * @private - */ - OptionsPage.findSectionForNode_ = function(node) { - while (node = node.parentNode) { - if (node.nodeName == 'SECTION') - return node; - } - return null; - }; - - /** - * Registers a new Overlay page. - * @param {OptionsPage} overlay Overlay to register. - * @param {OptionsPage} parentPage Associated parent page for this overlay. - * @param {Array} associatedControls Array of control elements associated with - * this page. - */ - OptionsPage.registerOverlay = function(overlay, - parentPage, - associatedControls) { - this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay; - overlay.parentPage = parentPage; - if (associatedControls) { - overlay.associatedControls = associatedControls; - if (associatedControls.length) { - overlay.associatedSection = - this.findSectionForNode_(associatedControls[0]); - } - } - - // Reverse the button strip for views. See the documentation of - // reverseButtonStrip_() for an explanation of why this is necessary. - if (cr.isViews) - this.reverseButtonStrip_(overlay); - - overlay.tab = undefined; - overlay.isOverlay = true; - overlay.initializePage(); - }; - - /** - * Reverses the child elements of a button strip. This is necessary because - * WebKit does not alter the tab order for elements that are visually reversed - * using -webkit-box-direction: reverse, and the button order is reversed for - * views. See https://bugs.webkit.org/show_bug.cgi?id=62664 for more - * information. - * @param {Object} overlay The overlay containing the button strip to reverse. - * @private - */ - OptionsPage.reverseButtonStrip_ = function(overlay) { - var buttonStrips = overlay.pageDiv.querySelectorAll('.button-strip'); - - // Reverse all button-strips in the overlay. - for (var j = 0; j < buttonStrips.length; j++) { - var buttonStrip = buttonStrips[j]; - - var childNodes = buttonStrip.childNodes; - for (var i = childNodes.length - 1; i >= 0; i--) - buttonStrip.appendChild(childNodes[i]); - } - }; - - /** - * Callback for window.onpopstate. - * @param {Object} data State data pushed into history. - */ - OptionsPage.setState = function(data) { - if (data && data.pageName) { - this.willClose(); - this.showPageByName(data.pageName, false); - } - }; - - /** - * Callback for window.onbeforeunload. Used to notify overlays that they will - * be closed. - */ - OptionsPage.willClose = function() { - var overlay = this.getVisibleOverlay_(); - if (overlay && overlay.didClosePage) - overlay.didClosePage(); - }; - - /** - * Freezes/unfreezes the scroll position of the root page container. - * @param {boolean} freeze Whether the page should be frozen. - * @private - */ - OptionsPage.setRootPageFrozen_ = function(freeze) { - var container = $('page-container'); - if (container.classList.contains('frozen') == freeze) - return; - - if (freeze) { - // Lock the width, since auto width computation may change. - container.style.width = window.getComputedStyle(container).width; - container.oldScrollTop = document.body.scrollTop; - container.classList.add('frozen'); - var verticalPosition = - container.getBoundingClientRect().top - container.oldScrollTop; - container.style.top = verticalPosition + 'px'; - this.updateFrozenElementHorizontalPosition_(container); - } else { - container.classList.remove('frozen'); - container.style.top = ''; - container.style.left = ''; - container.style.right = ''; - container.style.width = ''; - } - }; - - /** - * Freezes/unfreezes the scroll position of the root page based on the current - * page stack. - */ - OptionsPage.updateRootPageFreezeState = function() { - var topPage = OptionsPage.getTopmostVisiblePage(); - if (topPage) - this.setRootPageFrozen_(topPage.isOverlay); - }; - - /** - * Initializes the complete options page. This will cause all C++ handlers to - * be invoked to do final setup. - */ - OptionsPage.initialize = function() { - chrome.send('coreOptionsInitialize'); - uber.onContentFrameLoaded(); - - document.addEventListener('scroll', this.handleScroll_.bind(this)); - - // Trigger the scroll handler manually to set the initial state. - this.handleScroll_(); - - // Shake the dialog if the user clicks outside the dialog bounds. - var containers = [$('overlay-container-1'), $('overlay-container-2')]; - for (var i = 0; i < containers.length; i++) { - var overlay = containers[i]; - cr.ui.overlay.setupOverlay(overlay); - overlay.addEventListener('cancelOverlay', - OptionsPage.cancelOverlay.bind(OptionsPage)); - } - }; - - /** - * Does a bounds check for the element on the given x, y client coordinates. - * @param {Element} e The DOM element. - * @param {number} x The client X to check. - * @param {number} y The client Y to check. - * @return {boolean} True if the point falls within the element's bounds. - * @private - */ - OptionsPage.elementContainsPoint_ = function(e, x, y) { - var clientRect = e.getBoundingClientRect(); - return x >= clientRect.left && x <= clientRect.right && - y >= clientRect.top && y <= clientRect.bottom; - }; - - /** - * Called when the page is scrolled; moves elements that are position:fixed - * but should only behave as if they are fixed for vertical scrolling. - * @private - */ - OptionsPage.handleScroll_ = function() { - this.updateAllFrozenElementPositions_(); - }; - - /** - * Updates all frozen pages to match the horizontal scroll position. - * @private - */ - OptionsPage.updateAllFrozenElementPositions_ = function() { - var frozenElements = document.querySelectorAll('.frozen'); - for (var i = 0; i < frozenElements.length; i++) - this.updateFrozenElementHorizontalPosition_(frozenElements[i]); - }; - - /** - * Updates the given frozen element to match the horizontal scroll position. - * @param {HTMLElement} e The frozen element to update. - * @private - */ - OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) { - if (isRTL()) - e.style.right = HORIZONTAL_OFFSET + 'px'; - else - e.style.left = HORIZONTAL_OFFSET - document.body.scrollLeft + 'px'; - }; - - OptionsPage.setClearPluginLSODataEnabled = function(enabled) { - if (enabled) { - document.documentElement.setAttribute( - 'flashPluginSupportsClearSiteData', ''); - } else { - document.documentElement.removeAttribute( - 'flashPluginSupportsClearSiteData'); - } - }; - - OptionsPage.setPepperFlashSettingsEnabled = function(enabled) { - if (enabled) { - document.documentElement.setAttribute( - 'enablePepperFlashSettings', ''); - } else { - document.documentElement.removeAttribute( - 'enablePepperFlashSettings'); - } - }; - - OptionsPage.prototype = { - __proto__: cr.EventTarget.prototype, - - /** - * The parent page of this option page, or null for top-level pages. - * @type {OptionsPage} - */ - parentPage: null, - - /** - * The section on the parent page that is associated with this page. - * Can be null. - * @type {Element} - */ - associatedSection: null, - - /** - * An array of controls that are associated with this page. The first - * control should be located on a top-level page. - * @type {OptionsPage} - */ - associatedControls: null, - - /** - * Initializes page content. - */ - initializePage: function() {}, - - /** - * Updates managed banner visibility state. This function iterates over - * all input fields of a page and if any of these is marked as managed - * it triggers the managed banner to be visible. The banner can be enforced - * being on through the managed flag of this class but it can not be forced - * being off if managed items exist. - */ - updateManagedBannerVisibility: function() { - var bannerDiv = this.pageDiv.querySelector('.managed-prefs-banner'); - // Create a banner for the overlay if we don't have one. - if (!bannerDiv) { - bannerDiv = $('managed-prefs-banner').cloneNode(true); - bannerDiv.id = null; - - if (this.isOverlay) { - var content = this.pageDiv.querySelector('.content-area'); - content.parentElement.insertBefore(bannerDiv, content); - } else { - bannerDiv.classList.add('main-page-banner'); - var header = this.pageDiv.querySelector('header'); - header.appendChild(bannerDiv); - } - } - - var controlledByPolicy = false; - var controlledByExtension = false; - var inputElements = this.pageDiv.querySelectorAll('input[controlled-by]'); - for (var i = 0; i < inputElements.length; i++) { - if (inputElements[i].controlledBy == 'policy') - controlledByPolicy = true; - else if (inputElements[i].controlledBy == 'extension') - controlledByExtension = true; - } - - if (!controlledByPolicy && !controlledByExtension) { - this.pageDiv.classList.remove('showing-banner'); - } else { - this.pageDiv.classList.add('showing-banner'); - - var text = bannerDiv.querySelector('#managed-prefs-text'); - if (controlledByPolicy && !controlledByExtension) { - text.textContent = - loadTimeData.getString('policyManagedPrefsBannerText'); - } else if (!controlledByPolicy && controlledByExtension) { - text.textContent = - loadTimeData.getString('extensionManagedPrefsBannerText'); - } else if (controlledByPolicy && controlledByExtension) { - text.textContent = loadTimeData.getString( - 'policyAndExtensionManagedPrefsBannerText'); - } - } - }, - - /** - * Gets the container div for this page if it is an overlay. - * @type {HTMLElement} - */ - get container() { - assert(this.isOverlay); - return this.pageDiv.parentNode; - }, - - /** - * Gets page visibility state. - * @type {boolean} - */ - get visible() { - // If this is an overlay dialog it is no longer considered visible while - // the overlay is fading out. See http://crbug.com/118629. - if (this.isOverlay && - this.container.classList.contains('transparent')) { - return false; - } - return !this.pageDiv.hidden; - }, - - /** - * Sets page visibility. - * @type {boolean} - */ - set visible(visible) { - if ((this.visible && visible) || (!this.visible && !visible)) - return; - - // If using an overlay, the visibility of the dialog is toggled at the - // same time as the overlay to show the dialog's out transition. This - // is handled in setOverlayVisible. - if (this.isOverlay) { - this.setOverlayVisible_(visible); - } else { - this.pageDiv.hidden = !visible; - this.onVisibilityChanged_(); - } - - cr.dispatchPropertyChange(this, 'visible', visible, !visible); - }, - - /** - * Shows or hides an overlay (including any visible dialog). - * @param {boolean} visible Whether the overlay should be visible or not. - * @private - */ - setOverlayVisible_: function(visible) { - assert(this.isOverlay); - var pageDiv = this.pageDiv; - var container = this.container; - - if (visible) { - uber.invokeMethodOnParent('beginInterceptingEvents'); - this.pageDiv.removeAttribute('aria-hidden'); - if (this.parentPage) - this.parentPage.pageDiv.setAttribute('aria-hidden', true); - } else { - if (this.parentPage) - this.parentPage.pageDiv.removeAttribute('aria-hidden'); - } - - if (container.hidden != visible) { - if (visible) { - // If the container is set hidden and then immediately set visible - // again, the fadeCompleted_ callback would cause it to be erroneously - // hidden again. Removing the transparent tag avoids that. - container.classList.remove('transparent'); - - // Hide all dialogs in this container since a different one may have - // been previously visible before fading out. - var pages = container.querySelectorAll('.page'); - for (var i = 0; i < pages.length; i++) - pages[i].hidden = true; - // Show the new dialog. - pageDiv.hidden = false; - } - return; - } - - if (visible) { - container.hidden = false; - pageDiv.hidden = false; - // NOTE: This is a hacky way to force the container to layout which - // will allow us to trigger the webkit transition. - container.scrollTop; - container.classList.remove('transparent'); - this.onVisibilityChanged_(); - } else { - var self = this; - // TODO: Use an event delegate to avoid having to subscribe and - // unsubscribe for webkitTransitionEnd events. - container.addEventListener('webkitTransitionEnd', function f(e) { - if (e.target != e.currentTarget || e.propertyName != 'opacity') - return; - container.removeEventListener('webkitTransitionEnd', f); - self.fadeCompleted_(); - }); - container.classList.add('transparent'); - } - }, - - /** - * Called when a container opacity transition finishes. - * @private - */ - fadeCompleted_: function() { - if (this.container.classList.contains('transparent')) { - this.pageDiv.hidden = true; - this.container.hidden = true; - this.onVisibilityChanged_(); - if (this.nestingLevel == 1) - uber.invokeMethodOnParent('stopInterceptingEvents'); - } - }, - - /** - * Called when a page is shown or hidden to update the root options page - * based on this page's visibility. - * @private - */ - onVisibilityChanged_: function() { - OptionsPage.updateRootPageFreezeState(); - OptionsPage.updateManagedBannerVisibility(); - - if (this.isOverlay && !this.visible) - OptionsPage.updateScrollPosition_(); - }, - - /** - * The nesting level of this page. - * @type {number} The nesting level of this page (0 for top-level page) - */ - get nestingLevel() { - var level = 0; - var parent = this.parentPage; - while (parent) { - level++; - parent = parent.parentPage; - } - return level; - }, - - /** - * Whether the page is considered 'sticky', such that it will - * remain a top-level page even if sub-pages change. - * @type {boolean} True if this page is sticky. - */ - get sticky() { - return false; - }, - - /** - * Checks whether this page is an ancestor of the given page in terms of - * subpage nesting. - * @param {OptionsPage} page The potential descendent of this page. - * @return {boolean} True if |page| is nested under this page. - */ - isAncestorOfPage: function(page) { - var parent = page.parentPage; - while (parent) { - if (parent == this) - return true; - parent = parent.parentPage; - } - return false; - }, - - /** - * Whether it should be possible to show the page. - * @return {boolean} True if the page should be shown. - */ - canShowPage: function() { - return true; - }, - }; - - // Export - return { - OptionsPage: OptionsPage - }; -}); diff --git a/chrome/browser/resources/options2/password_manager.css b/chrome/browser/resources/options2/password_manager.css deleted file mode 100644 index 18314ef..0000000 --- a/chrome/browser/resources/options2/password_manager.css +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#password-manager > div.content-area { - width: 600px; -} - -#password-search-column { - bottom: 10px; - position: absolute; - right: 0; -} - -html[dir=rtl] #password-search-column { - left: 0; - right: auto; -} - -#password-list-headers { - position: relative; - width: 100%; -} - -#passwords-title { - display: inline-block; -} diff --git a/chrome/browser/resources/options2/password_manager.html b/chrome/browser/resources/options2/password_manager.html deleted file mode 100644 index 656e786..0000000 --- a/chrome/browser/resources/options2/password_manager.html +++ /dev/null @@ -1,35 +0,0 @@ - diff --git a/chrome/browser/resources/options2/password_manager.js b/chrome/browser/resources/options2/password_manager.js deleted file mode 100644 index 43b5e44..0000000 --- a/chrome/browser/resources/options2/password_manager.js +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - ///////////////////////////////////////////////////////////////////////////// - // PasswordManager class: - - /** - * Encapsulated handling of password and exceptions page. - * @constructor - */ - function PasswordManager() { - this.activeNavTab = null; - OptionsPage.call(this, - 'passwords', - loadTimeData.getString('passwordsPageTabTitle'), - 'password-manager'); - } - - cr.addSingletonGetter(PasswordManager); - - PasswordManager.prototype = { - __proto__: OptionsPage.prototype, - - /** - * The saved passwords list. - * @type {DeletableItemList} - * @private - */ - savedPasswordsList_: null, - - /** - * The password exceptions list. - * @type {DeletableItemList} - * @private - */ - passwordExceptionsList_: null, - - /** - * The timer id of the timer set on search query change events. - * @type {number} - * @private - */ - queryDelayTimerId_: 0, - - /** - * The most recent search query, or null if the query is empty. - * @type {?string} - * @private - */ - lastQuery_: null, - - /** @inheritDoc */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - $('password-manager-confirm').onclick = function() { - OptionsPage.closeOverlay(); - }; - - $('password-search-box').addEventListener('search', - this.handleSearchQueryChange_.bind(this)); - - this.createSavedPasswordsList_(); - this.createPasswordExceptionsList_(); - }, - - /** @inheritDoc */ - canShowPage: function() { - return !(cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()); - }, - - /** @inheritDoc */ - didShowPage: function() { - // Updating the password lists may cause a blocking platform dialog pop up - // (Mac, Linux), so we delay this operation until the page is shown. - chrome.send('updatePasswordLists'); - $('password-search-box').focus(); - }, - - /** - * Creates, decorates and initializes the saved passwords list. - * @private - */ - createSavedPasswordsList_: function() { - this.savedPasswordsList_ = $('saved-passwords-list'); - options.passwordManager.PasswordsList.decorate(this.savedPasswordsList_); - this.savedPasswordsList_.autoExpands = true; - }, - - /** - * Creates, decorates and initializes the password exceptions list. - * @private - */ - createPasswordExceptionsList_: function() { - this.passwordExceptionsList_ = $('password-exceptions-list'); - options.passwordManager.PasswordExceptionsList.decorate( - this.passwordExceptionsList_); - this.passwordExceptionsList_.autoExpands = true; - }, - - /** - * Handles search query changes. - * @param {!Event} e The event object. - * @private - */ - handleSearchQueryChange_: function(e) { - if (this.queryDelayTimerId_) - window.clearTimeout(this.queryDelayTimerId_); - - // Searching cookies uses a timeout of 500ms. We use a shorter timeout - // because there are probably fewer passwords and we want the UI to be - // snappier since users will expect that it's "less work." - this.queryDelayTimerId_ = window.setTimeout( - this.searchPasswords_.bind(this), 250); - }, - - /** - * Search passwords using text in |password-search-box|. - * @private - */ - searchPasswords_: function() { - this.queryDelayTimerId_ = 0; - var filter = $('password-search-box').value; - filter = (filter == '') ? null : filter; - if (this.lastQuery_ != filter) { - this.lastQuery_ = filter; - // Searching for passwords has the side effect of requerying the - // underlying password store. This is done intentionally, as on OS X and - // Linux they can change from outside and we won't be notified of it. - chrome.send('updatePasswordLists'); - } - }, - - /** - * Updates the visibility of the list and empty list placeholder. - * @param {!List} list The list to toggle visilibility for. - */ - updateListVisibility_: function(list) { - var empty = list.dataModel.length == 0; - var listPlaceHolderID = list.id + '-empty-placeholder'; - list.hidden = empty; - $(listPlaceHolderID).hidden = !empty; - }, - - /** - * Updates the data model for the saved passwords list with the values from - * |entries|. - * @param {Array} entries The list of saved password data. - */ - setSavedPasswordsList_: function(entries) { - if (this.lastQuery_) { - // Implement password searching here in javascript, rather than in C++. - // The number of saved passwords shouldn't be too big for us to handle. - var query = this.lastQuery_; - var filter = function(entry, index, list) { - // Search both URL and username. - if (entry[0].indexOf(query) >= 0 || entry[1].indexOf(query) >= 0) { - // Keep the original index so we can delete correctly. See also - // deleteItemAtIndex() in password_manager_list.js that uses this. - entry[3] = index; - return true; - } - return false; - }; - entries = entries.filter(filter); - } - this.savedPasswordsList_.dataModel = new ArrayDataModel(entries); - this.updateListVisibility_(this.savedPasswordsList_); - }, - - /** - * Updates the data model for the password exceptions list with the values - * from |entries|. - * @param {Array} entries The list of password exception data. - */ - setPasswordExceptionsList_: function(entries) { - this.passwordExceptionsList_.dataModel = new ArrayDataModel(entries); - this.updateListVisibility_(this.passwordExceptionsList_); - }, - }; - - /** - * Removes a saved password. - * @param {number} rowIndex indicating the row to remove. - */ - PasswordManager.removeSavedPassword = function(rowIndex) { - chrome.send('removeSavedPassword', [String(rowIndex)]); - }; - - /** - * Removes a password exception. - * @param {number} rowIndex indicating the row to remove. - */ - PasswordManager.removePasswordException = function(rowIndex) { - chrome.send('removePasswordException', [String(rowIndex)]); - }; - - /** - * Removes all saved passwords. - */ - PasswordManager.removeAllPasswords = function() { - chrome.send('removeAllSavedPasswords'); - }; - - /** - * Removes all password exceptions. - */ - PasswordManager.removeAllPasswordExceptions = function() { - chrome.send('removeAllPasswordExceptions'); - }; - - PasswordManager.setSavedPasswordsList = function(entries) { - PasswordManager.getInstance().setSavedPasswordsList_(entries); - }; - - PasswordManager.setPasswordExceptionsList = function(entries) { - PasswordManager.getInstance().setPasswordExceptionsList_(entries); - }; - - // Export - return { - PasswordManager: PasswordManager - }; - -}); diff --git a/chrome/browser/resources/options2/password_manager_list.css b/chrome/browser/resources/options2/password_manager_list.css deleted file mode 100644 index 8688619..0000000 --- a/chrome/browser/resources/options2/password_manager_list.css +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#saved-passwords-list .list-inline-button { - -webkit-transition: opacity 150ms; - background: rgb(138, 170, 237); - font-size: 0.9em; - height: 18px; - padding: 0 2px; - position: absolute; - top: 3px; -} - -html[dir='ltr'] #saved-passwords-list .list-inline-button { - right: 2px; -} - -html[dir='rtl'] #saved-passwords-list .list-inline-button { - left: 2px; -} - -input[type='password'].inactive-password { - background: transparent; - border: none; -} - -#saved-passwords-list .url { - box-sizing: border-box; - width: 40%; -} - -#saved-passwords-list .name { - -webkit-box-flex: 1; - width: 20%; -} - -#saved-passwords-list .password { - -webkit-box-flex: 1; - position: relative; -} - -#saved-passwords-list .password input[type='password'], -#saved-passwords-list .password input[type='text'] { - box-sizing: border-box; - width: 100%; -} - -#password-exceptions-list .url { - -webkit-box-flex: 1; -} - -#saved-passwords-list .url, -#saved-passwords-list .name, -#password-exceptions-list .url { - overflow: hidden; - text-overflow: ellipsis; -} diff --git a/chrome/browser/resources/options2/password_manager_list.js b/chrome/browser/resources/options2/password_manager_list.js deleted file mode 100644 index 483c271..0000000 --- a/chrome/browser/resources/options2/password_manager_list.js +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.passwordManager', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var DeletableItemList = options.DeletableItemList; - /** @const */ var DeletableItem = options.DeletableItem; - /** @const */ var List = cr.ui.List; - - /** - * Creates a new passwords list item. - * @param {Array} entry An array of the form [url, username, password]. When - * the list has been filtered, a fourth element [index] may be present. - * @constructor - * @extends {cr.ui.ListItem} - */ - function PasswordListItem(entry, showPasswords) { - var el = cr.doc.createElement('div'); - el.dataItem = entry; - el.__proto__ = PasswordListItem.prototype; - el.decorate(showPasswords); - - return el; - } - - PasswordListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** @inheritDoc */ - decorate: function(showPasswords) { - DeletableItem.prototype.decorate.call(this); - - // The URL of the site. - var urlLabel = this.ownerDocument.createElement('div'); - urlLabel.classList.add('favicon-cell'); - urlLabel.classList.add('weakrtl'); - urlLabel.classList.add('url'); - urlLabel.setAttribute('title', this.url); - urlLabel.textContent = this.url; - - // The favicon URL is prefixed with "origin/", which essentially removes - // the URL path past the top-level domain and ensures that a scheme (e.g., - // http) is being used. This ensures that the favicon returned is the - // default favicon for the domain and that the URL has a scheme if none - // is present in the password manager. - urlLabel.style.backgroundImage = - url('chrome://favicon/origin/' + this.url); - this.contentElement.appendChild(urlLabel); - - // The stored username. - var usernameLabel = this.ownerDocument.createElement('div'); - usernameLabel.className = 'name'; - usernameLabel.textContent = this.username; - this.contentElement.appendChild(usernameLabel); - - // The stored password. - var passwordInputDiv = this.ownerDocument.createElement('div'); - passwordInputDiv.className = 'password'; - - // The password input field. - var passwordInput = this.ownerDocument.createElement('input'); - passwordInput.type = 'password'; - passwordInput.className = 'inactive-password'; - passwordInput.readOnly = true; - passwordInput.value = showPasswords ? this.password : '********'; - passwordInputDiv.appendChild(passwordInput); - - // The show/hide button. - if (showPasswords) { - var button = this.ownerDocument.createElement('button'); - button.hidden = true; - button.className = 'list-inline-button custom-appearance'; - button.textContent = loadTimeData.getString('passwordShowButton'); - button.addEventListener('click', this.onClick_, true); - passwordInputDiv.appendChild(button); - } - - this.contentElement.appendChild(passwordInputDiv); - }, - - /** @inheritDoc */ - selectionChanged: function() { - var passwordInput = this.querySelector('input[type=password]'); - var textInput = this.querySelector('input[type=text]'); - var input = passwordInput || textInput; - var button = input.nextSibling; - // |button| doesn't exist when passwords can't be shown. - if (!button) - return; - if (this.selected) { - input.classList.remove('inactive-password'); - button.hidden = false; - } else { - input.classList.add('inactive-password'); - button.hidden = true; - } - }, - - /** - * On-click event handler. Swaps the type of the input field from password - * to text and back. - * @private - */ - onClick_: function(event) { - // The password is the input element previous to the button. - var button = event.currentTarget; - var passwordInput = button.previousSibling; - if (passwordInput.type == 'password') { - passwordInput.type = 'text'; - button.textContent = loadTimeData.getString('passwordHideButton'); - } else { - passwordInput.type = 'password'; - button.textContent = loadTimeData.getString('passwordShowButton'); - } - }, - - /** - * Get and set the URL for the entry. - * @type {string} - */ - get url() { - return this.dataItem[0]; - }, - set url(url) { - this.dataItem[0] = url; - }, - - /** - * Get and set the username for the entry. - * @type {string} - */ - get username() { - return this.dataItem[1]; - }, - set username(username) { - this.dataItem[1] = username; - }, - - /** - * Get and set the password for the entry. - * @type {string} - */ - get password() { - return this.dataItem[2]; - }, - set password(password) { - this.dataItem[2] = password; - }, - }; - - /** - * Creates a new PasswordExceptions list item. - * @param {Array} entry A pair of the form [url, username]. - * @constructor - * @extends {Deletable.ListItem} - */ - function PasswordExceptionsListItem(entry) { - var el = cr.doc.createElement('div'); - el.dataItem = entry; - el.__proto__ = PasswordExceptionsListItem.prototype; - el.decorate(); - - return el; - } - - PasswordExceptionsListItem.prototype = { - __proto__: DeletableItem.prototype, - - /** - * Call when an element is decorated as a list item. - */ - decorate: function() { - DeletableItem.prototype.decorate.call(this); - - // The URL of the site. - var urlLabel = this.ownerDocument.createElement('div'); - urlLabel.className = 'url'; - urlLabel.classList.add('favicon-cell'); - urlLabel.classList.add('weakrtl'); - urlLabel.textContent = this.url; - - // The favicon URL is prefixed with "origin/", which essentially removes - // the URL path past the top-level domain and ensures that a scheme (e.g., - // http) is being used. This ensures that the favicon returned is the - // default favicon for the domain and that the URL has a scheme if none - // is present in the password manager. - urlLabel.style.backgroundImage = - url('chrome://favicon/origin/' + this.url); - this.contentElement.appendChild(urlLabel); - }, - - /** - * Get the url for the entry. - * @type {string} - */ - get url() { - return this.dataItem; - }, - set url(url) { - this.dataItem = url; - }, - }; - - /** - * Create a new passwords list. - * @constructor - * @extends {cr.ui.List} - */ - var PasswordsList = cr.ui.define('list'); - - PasswordsList.prototype = { - __proto__: DeletableItemList.prototype, - - /** - * Whether passwords can be revealed or not. - * @type {boolean} - * @private - */ - showPasswords_: true, - - /** @inheritDoc */ - decorate: function() { - DeletableItemList.prototype.decorate.call(this); - Preferences.getInstance().addEventListener( - 'profile.password_manager_allow_show_passwords', - this.onPreferenceChanged_.bind(this)); - }, - - /** - * Listener for changes on the preference. - * @param {Event} event The preference update event. - * @private - */ - onPreferenceChanged_: function(event) { - this.showPasswords_ = event.value.value; - this.redraw(); - }, - - /** @inheritDoc */ - createItem: function(entry) { - return new PasswordListItem(entry, this.showPasswords_); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var item = this.dataModel.item(index); - if (item && item.length > 3) { - // The fourth element, if present, is the original index to delete. - index = item[3]; - } - PasswordManager.removeSavedPassword(index); - }, - - /** - * The length of the list. - */ - get length() { - return this.dataModel.length; - }, - }; - - /** - * Create a new passwords list. - * @constructor - * @extends {cr.ui.List} - */ - var PasswordExceptionsList = cr.ui.define('list'); - - PasswordExceptionsList.prototype = { - __proto__: DeletableItemList.prototype, - - /** @inheritDoc */ - createItem: function(entry) { - return new PasswordExceptionsListItem(entry); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - PasswordManager.removePasswordException(index); - }, - - /** - * The length of the list. - */ - get length() { - return this.dataModel.length; - }, - }; - - return { - PasswordListItem: PasswordListItem, - PasswordExceptionsListItem: PasswordExceptionsListItem, - PasswordsList: PasswordsList, - PasswordExceptionsList: PasswordExceptionsList, - }; -}); diff --git a/chrome/browser/resources/options2/pref_ui.js b/chrome/browser/resources/options2/pref_ui.js deleted file mode 100644 index 82bab86..0000000 --- a/chrome/browser/resources/options2/pref_ui.js +++ /dev/null @@ -1,846 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// TODO(jhawkins): Add dialog-pref support to all preference controls. - -cr.define('options', function() { - - var Preferences = options.Preferences; - - /** - * Allows an element to be disabled for several reasons. - * The element is disabled if at least one reason is true, and the reasons - * can be set separately. - * @private - * @param {!HTMLElement} el The element to update. - * @param {string} reason The reason for disabling the element. - * @param {boolean} disabled Whether the element should be disabled or enabled - * for the given |reason|. - */ - function updateDisabledState_(el, reason, disabled) { - if (!el.disabledReasons) - el.disabledReasons = {}; - if (el.disabled && (Object.keys(el.disabledReasons).length == 0)) { - // The element has been previously disabled without a reason, so we add - // one to keep it disabled. - el.disabledReasons['other'] = true; - } - if (!el.disabled) { - // If the element is not disabled, there should be no reason, except for - // 'other'. - delete el.disabledReasons['other']; - if (Object.keys(el.disabledReasons).length > 0) - console.error('Element is not disabled but should be'); - } - if (disabled) { - el.disabledReasons[reason] = true; - } else { - delete el.disabledReasons[reason]; - } - el.disabled = Object.keys(el.disabledReasons).length > 0; - } - - /** - * Helper function to update element's state from pref change event. - * @private - * @param {!HTMLElement} el The element to update. - * @param {!Event} event The pref change event. - */ - function updateElementState_(el, event) { - el.controlledBy = null; - - if (!event.value) - return; - - updateDisabledState_(el, 'notUserModifiable', event.value.disabled); - - el.controlledBy = event.value.controlledBy; - - OptionsPage.updateManagedBannerVisibility(); - } - - ///////////////////////////////////////////////////////////////////////////// - // PrefCheckbox class: - // TODO(jhawkins): Refactor all this copy-pasted code! - - // Define a constructor that uses an input element as its underlying element. - var PrefCheckbox = cr.ui.define('input'); - - PrefCheckbox.prototype = { - // Set up the prototype chain - __proto__: HTMLInputElement.prototype, - - /** - * The stored value of the preference that this checkbox controls. - * @type {boolean} - */ - prefValue_: null, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'checkbox'; - var self = this; - - self.initializeValueType(self.getAttribute('value-type')); - - // Listen to pref changes. - Preferences.getInstance().addEventListener(this.pref, function(event) { - self.prefValue_ = Boolean(event.value.value); - self.resetPrefState(); - - updateElementState_(self, event); - }); - - // Listen to user events. - this.addEventListener('change', function(e) { - if (self.customChangeHandler(e)) - return; - - if (!this.dialogPref) - this.updatePreferenceValue_(); - }); - }, - - /** - * Update the preference value based on the checkbox state. - * @private - */ - updatePreferenceValue_: function() { - var value = this.inverted_pref ? !this.checked : this.checked; - switch (this.valueType) { - case 'number': - Preferences.setIntegerPref(this.pref, Number(value), this.metric); - break; - case 'boolean': - Preferences.setBooleanPref(this.pref, value, this.metric); - break; - } - }, - - /** - * Called by SettingsDialog to save the preference. - */ - savePrefState: function() { - this.updatePreferenceValue_(); - }, - - /** - * Called by SettingsDialog to reset the UI to match the current preference - * value. - */ - resetPrefState: function() { - this.checked = this.inverted_pref ? !this.prefValue_ : this.prefValue_; - }, - - /** - * Sets up options in checkbox element. - * @param {String} valueType The preference type for this checkbox. - */ - initializeValueType: function(valueType) { - this.valueType = valueType || 'boolean'; - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - - /** - * This method is called first while processing an onchange event. If it - * returns false, regular onchange processing continues (setting the - * associated pref, etc). If it returns true, the rest of the onchange is - * not performed. I.e., this works like stopPropagation or cancelBubble. - * @param {Event} event Change event. - */ - customChangeHandler: function(event) { - return false; - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefCheckbox, 'pref', cr.PropertyKind.ATTR); - - /** - * A special preference type specific to dialogs. These preferences are reset - * when the dialog is shown and are not saved until the user confirms the - * dialog. - * @type {boolean} - */ - cr.defineProperty(PrefCheckbox, 'dialogPref', cr.PropertyKind.BOOL_ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefCheckbox, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefCheckbox, 'metric', cr.PropertyKind.ATTR); - - /** - * Whether to use inverted pref value. - * @type {boolean} - */ - cr.defineProperty(PrefCheckbox, 'inverted_pref', cr.PropertyKind.BOOL_ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefRadio class: - - //Define a constructor that uses an input element as its underlying element. - var PrefRadio = cr.ui.define('input'); - - PrefRadio.prototype = { - // Set up the prototype chain - __proto__: HTMLInputElement.prototype, - - // Stores the initialized value of the preference used to reset the input - // in resetPrefState(). - storedValue_: null, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'radio'; - var self = this; - - // Listen to preference changes. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - if (self.customChangeHandler(event)) - return; - self.checked = String(event.value.value) == self.value; - self.storedValue_ = self.checked; - - updateElementState_(self, event); - }); - - // Dialog preferences are not saved until savePrefState() is explicitly - // called. - if (!this.dialogPref) - this.onchange = this.savePrefState.bind(this); - }, - - /** - * Resets the input to the stored value. - */ - resetPrefState: function() { - this.checked = this.storedValue_; - }, - - /** - * Saves the value of the input back into the preference. May be called - * directly to save dialog preferences. - */ - savePrefState: function() { - this.storedValue_ = this.checked; - if (this.value == 'true' || this.value == 'false') { - var value = String(this.value); - Preferences.setBooleanPref(this.pref, value == String(this.checked), - this.metric); - } else { - Preferences.setIntegerPref(this.pref, parseInt(this.value, 10), - this.metric); - } - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - - /** - * This method is called first while processing an onchange event. If it - * returns false, regular onchange processing continues (setting the - * associated pref, etc). If it returns true, the rest of the onchange is - * not performed. I.e., this works like stopPropagation or cancelBubble. - * @param {Event} event Change event. - */ - customChangeHandler: function(event) { - return false; - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefRadio, 'pref', cr.PropertyKind.ATTR); - - /** - * A special preference type specific to dialogs. These preferences are reset - * when the dialog is shown and are not saved until the user confirms the - * dialog. - * @type {boolean} - */ - cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefRadio, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefRadio, 'metric', cr.PropertyKind.ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefNumeric class: - - // Define a constructor that uses an input element as its underlying element. - var PrefNumeric = function() {}; - PrefNumeric.prototype = { - // Set up the prototype chain - __proto__: HTMLInputElement.prototype, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - var self = this; - - // Listen to pref changes. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - self.value = event.value.value; - - updateElementState_(self, event); - }); - - // Listen to user events. - this.addEventListener('change', - function(e) { - if (this.validity.valid) { - Preferences.setIntegerPref(self.pref, self.value, self.metric); - } - }); - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefNumeric, 'pref', cr.PropertyKind.ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefNumeric, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefNumeric, 'metric', cr.PropertyKind.ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefNumber class: - - // Define a constructor that uses an input element as its underlying element. - var PrefNumber = cr.ui.define('input'); - - PrefNumber.prototype = { - // Set up the prototype chain - __proto__: PrefNumeric.prototype, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'number'; - PrefNumeric.prototype.decorate.call(this); - - // Listen to user events. - this.addEventListener('input', - function(e) { - if (this.validity.valid) { - Preferences.setIntegerPref(self.pref, self.value, self.metric); - } - }); - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - ///////////////////////////////////////////////////////////////////////////// - // PrefRange class: - - // Define a constructor that uses an input element as its underlying element. - var PrefRange = cr.ui.define('input'); - - PrefRange.prototype = { - // Set up the prototype chain - __proto__: HTMLInputElement.prototype, - - /** - * The map from input range value to the corresponding preference value. - */ - valueMap: undefined, - - /** - * If true, the associated pref will be modified on each onchange event; - * otherwise, the pref will only be modified on the onmouseup event after - * the drag. - */ - continuous: true, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - this.type = 'range'; - - // Update the UI when the pref changes. - Preferences.getInstance().addEventListener( - this.pref, this.onPrefChange_.bind(this)); - - // Listen to user events. - // TODO(jhawkins): Add onmousewheel handling once the associated WK bug is - // fixed. - // https://bugs.webkit.org/show_bug.cgi?id=52256 - this.onchange = this.onChange_.bind(this); - this.onkeyup = this.onmouseup = this.onInputUp_.bind(this); - }, - - /** - * Event listener that updates the UI when the underlying pref changes. - * @param {Event} event The event that details the pref change. - * @private - */ - onPrefChange_: function(event) { - var value = event.value.value; - if (value != undefined) - this.value = this.valueMap ? this.valueMap.indexOf(value) : value; - }, - - /** - * onchange handler that sets the pref when the user changes the value of - * the input element. - * @private - */ - onChange_: function(event) { - if (this.continuous) - this.setRangePref_(); - - if (this.notifyChange) - this.notifyChange(this, this.mapValueToRange_(this.value)); - }, - - /** - * Sets the integer value of |pref| to the value of this element. - * @private - */ - setRangePref_: function() { - Preferences.setIntegerPref( - this.pref, this.mapValueToRange_(this.value), this.metric); - - if (this.notifyPrefChange) - this.notifyPrefChange(this, this.mapValueToRange_(this.value)); - }, - - /** - * onkeyup/onmouseup handler that modifies the pref if |continuous| is - * false. - * @private - */ - onInputUp_: function(event) { - if (!this.continuous) - this.setRangePref_(); - }, - - /** - * Maps the value of this element into the range provided by the client, - * represented by |valueMap|. - * @param {number} value The value to map. - * @private - */ - mapValueToRange_: function(value) { - return this.valueMap ? this.valueMap[value] : value; - }, - - /** - * Called when the client has specified non-continuous mode and the value of - * the range control changes. - * @param {Element} el This element. - * @param {number} value The value of this element. - */ - notifyChange: function(el, value) { - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefRange, 'pref', cr.PropertyKind.ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefRange, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefRange, 'metric', cr.PropertyKind.ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefSelect class: - - // Define a constructor that uses a select element as its underlying element. - var PrefSelect = cr.ui.define('select'); - - PrefSelect.prototype = { - // Set up the prototype chain - __proto__: HTMLSelectElement.prototype, - - /** - * @type {string} The stored value of the preference that this select - * controls. - */ - prefValue_: null, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - var self = this; - - // Listen to pref changes. - Preferences.getInstance().addEventListener(this.pref, function(event) { - // Make sure |value| is a string, because the value is stored as a - // string in the HTMLOptionElement. - value = event.value.value.toString(); - - updateElementState_(self, event); - self.prefValue_ = value; - self.resetPrefState(); - }); - - // Listen to user events. - this.addEventListener('change', function(event) { - if (!self.dialogPref) - self.updatePreference_(self.prefValue_); - }); - }, - - /** - * Resets the input to the stored value. - */ - resetPrefState: function() { - var found = false; - for (var i = 0; i < this.options.length; i++) { - if (this.options[i].value == this.prefValue_) { - this.selectedIndex = i; - found = true; - } - } - - // Item not found, select first item. - if (!found) - this.selectedIndex = 0; - - if (this.onchange) - this.onchange(event); - }, - - /** - * Updates the preference to the currently selected value. - */ - updatePreference_: function() { - if (!this.dataType) { - console.error('undefined data type for pref: ' + - this.dataType); - } - }, - - /** - * Called by SettingsDialog to save the stored value to preferences. - */ - savePrefState: function() { - this.updatePreference_(); - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefSelect, 'pref', cr.PropertyKind.ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefSelect, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * A special preference type specific to dialogs. These preferences are reset - * when the dialog is shown and are not saved until the user confirms the - * dialog. - * @type {boolean} - */ - cr.defineProperty(PrefSelect, 'dialogPref', cr.PropertyKind.BOOL_ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefSelect, 'metric', cr.PropertyKind.ATTR); - - /** - * The data type for the preference options. - * @type {string} - */ - cr.defineProperty(PrefSelect, 'dataType', cr.PropertyKind.ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefTextField class: - - // Define a constructor that uses an input element as its underlying element. - var PrefTextField = cr.ui.define('input'); - - PrefTextField.prototype = { - // Set up the prototype chain - __proto__: HTMLInputElement.prototype, - - /** - * @type {string} The stored value of the preference that this text field - * controls. - */ - prefValue_: null, - - /** - * Saves the value of the input back into the preference. May be called - * directly to save dialog preferences. - */ - savePrefState: function() { - switch (this.dataType) { - case 'number': - Preferences.setIntegerPref(this.pref, this.value, this.metric); - break; - case 'double': - Preferences.setDoublePref(this.pref, this.value, this.metric); - break; - case 'url': - Preferences.setURLPref(this.pref, this.value, this.metric); - break; - default: - Preferences.setStringPref(this.pref, this.value, this.metric); - break; - } - }, - - /** - * Resets the input to the stored value. - */ - resetPrefState: function() { - this.value = this.prefValue_; - }, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - var self = this; - - // Listen to pref changes. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - self.value = event.value.value; - - updateElementState_(self, event); - - self.prefValue_ = self.value; - }); - - // Listen to user events. - if (!this.dialogPref) - this.addEventListener('change', this.savePrefState.bind(this)); - - window.addEventListener('unload', - function() { - if (document.activeElement == self) - self.blur(); - }); - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefTextField, 'pref', cr.PropertyKind.ATTR); - - /** - * A special preference type specific to dialogs. These preferences are reset - * when the dialog is shown and are not saved until the user confirms the - * dialog. - * @type {boolean} - */ - cr.defineProperty(PrefTextField, 'dialogPref', cr.PropertyKind.BOOL_ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefTextField, 'controlledBy', cr.PropertyKind.ATTR); - - /** - * The user metric string. - * @type {string} - */ - cr.defineProperty(PrefTextField, 'metric', cr.PropertyKind.ATTR); - - /** - * The data type for the preference options. - * @type {string} - */ - cr.defineProperty(PrefTextField, 'dataType', cr.PropertyKind.ATTR); - - ///////////////////////////////////////////////////////////////////////////// - // PrefButton class: - - // Define a constructor that uses a button element as its underlying element. - var PrefButton = cr.ui.define('button'); - - PrefButton.prototype = { - // Set up the prototype chain - __proto__: HTMLButtonElement.prototype, - - /** - * Initialization function for the cr.ui framework. - */ - decorate: function() { - var self = this; - - // Listen to pref changes. This element behaves like a normal button and - // doesn't affect the underlying preference; it just becomes disabled - // when the preference is managed, and its value is false. - // This is useful for buttons that should be disabled when the underlying - // boolean preference is set to false by a policy or extension. - Preferences.getInstance().addEventListener(this.pref, - function(event) { - var e = { - value: { - 'disabled': event.value.disabled && !event.value.value, - 'controlledBy': event.value.controlledBy - } - }; - updateElementState_(self, e); - }); - }, - - /** - * See |updateDisabledState_| above. - */ - setDisabled: function(reason, disabled) { - updateDisabledState_(this, reason, disabled); - }, - }; - - /** - * The preference name. - * @type {string} - */ - cr.defineProperty(PrefButton, 'pref', cr.PropertyKind.ATTR); - - /** - * Whether the preference is controlled by something else than the user's - * settings (either 'policy' or 'extension'). - * @type {string} - */ - cr.defineProperty(PrefButton, 'controlledBy', cr.PropertyKind.ATTR); - - // Export - return { - PrefCheckbox: PrefCheckbox, - PrefNumber: PrefNumber, - PrefNumeric: PrefNumeric, - PrefRadio: PrefRadio, - PrefRange: PrefRange, - PrefSelect: PrefSelect, - PrefTextField: PrefTextField, - PrefButton: PrefButton - }; - -}); diff --git a/chrome/browser/resources/options2/preferences.js b/chrome/browser/resources/options2/preferences.js deleted file mode 100644 index 807e45e..0000000 --- a/chrome/browser/resources/options2/preferences.js +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - - ///////////////////////////////////////////////////////////////////////////// - // Preferences class: - - /** - * Preferences class manages access to Chrome profile preferences. - * @constructor - */ - function Preferences() { - } - - cr.addSingletonGetter(Preferences); - - /** - * Sets value of a boolean preference. - * and signals its changed value. - * @param {string} name Preference name. - * @param {boolean} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setBooleanPref = function(name, value, metric) { - var argumentList = [name, Boolean(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setBooleanPref', argumentList); - }; - - /** - * Sets value of an integer preference. - * and signals its changed value. - * @param {string} name Preference name. - * @param {number} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setIntegerPref = function(name, value, metric) { - var argumentList = [name, Number(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setIntegerPref', argumentList); - }; - - /** - * Sets value of a double-valued preference. - * and signals its changed value. - * @param {string} name Preference name. - * @param {number} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setDoublePref = function(name, value, metric) { - var argumentList = [name, Number(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setDoublePref', argumentList); - }; - - /** - * Sets value of a string preference. - * and signals its changed value. - * @param {string} name Preference name. - * @param {string} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setStringPref = function(name, value, metric) { - var argumentList = [name, String(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setStringPref', argumentList); - }; - - /** - * Sets value of a string preference that represents a URL - * and signals its changed value. The value will be fixed to be a valid URL. - * @param {string} name Preference name. - * @param {string} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setURLPref = function(name, value, metric) { - var argumentList = [name, String(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setURLPref', argumentList); - }; - - /** - * Sets value of a JSON list preference. - * and signals its changed value. - * @param {string} name Preference name. - * @param {Array} value New preference value. - * @param {string} metric User metrics identifier. - */ - Preferences.setListPref = function(name, value, metric) { - var argumentList = [name, JSON.stringify(value)]; - if (metric != undefined) argumentList.push(metric); - chrome.send('setListPref', argumentList); - }; - - /** - * Clears value of a JSON preference. - * @param {string} name Preference name. - * @param {string} metric User metrics identifier. - */ - Preferences.clearPref = function(name, metric) { - var argumentList = [name]; - if (metric != undefined) argumentList.push(metric); - chrome.send('clearPref', argumentList); - }; - - Preferences.prototype = { - __proto__: cr.EventTarget.prototype, - - // Map of registered preferences. - registeredPreferences_: {}, - - /** - * Adds an event listener to the target. - * @param {string} type The name of the event. - * @param {!Function|{handleEvent:Function}} handler The handler for the - * event. This is called when the event is dispatched. - */ - addEventListener: function(type, handler) { - cr.EventTarget.prototype.addEventListener.call(this, type, handler); - this.registeredPreferences_[type] = true; - }, - - /** - * Initializes preference reading and change notifications. - */ - initialize: function() { - var params1 = ['Preferences.prefsFetchedCallback']; - var params2 = ['Preferences.prefsChangedCallback']; - for (var prefName in this.registeredPreferences_) { - params1.push(prefName); - params2.push(prefName); - } - chrome.send('fetchPrefs', params1); - chrome.send('observePrefs', params2); - }, - - /** - * Helper function for flattening of dictionary passed via fetchPrefs - * callback. - * @param {string} prefix Preference name prefix. - * @param {object} dict Map with preference values. - */ - flattenMapAndDispatchEvent_: function(prefix, dict) { - for (var prefName in dict) { - if (typeof dict[prefName] == 'object' && - !this.registeredPreferences_[prefix + prefName]) { - this.flattenMapAndDispatchEvent_(prefix + prefName + '.', - dict[prefName]); - } else { - var event = new cr.Event(prefix + prefName); - event.value = dict[prefName]; - this.dispatchEvent(event); - } - } - } - }; - - /** - * Callback for fetchPrefs method. - * @param {object} dict Map of fetched property values. - */ - Preferences.prefsFetchedCallback = function(dict) { - Preferences.getInstance().flattenMapAndDispatchEvent_('', dict); - }; - - /** - * Callback for observePrefs method. - * @param {array} notification An array defining changed preference values. - * notification[0] contains name of the change preference while its new value - * is stored in notification[1]. - */ - Preferences.prefsChangedCallback = function(notification) { - var event = new cr.Event(notification[0]); - event.value = notification[1]; - Preferences.getInstance().dispatchEvent(event); - }; - - // Export - return { - Preferences: Preferences - }; - -}); diff --git a/chrome/browser/resources/options2/profiles_icon_grid.js b/chrome/browser/resources/options2/profiles_icon_grid.js deleted file mode 100644 index 287231c..0000000 --- a/chrome/browser/resources/options2/profiles_icon_grid.js +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var ListItem = cr.ui.ListItem; - /** @const */ var Grid = cr.ui.Grid; - /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; - - /** - * Creates a new profile icon grid item. - * @param {Object} iconURL The profile icon URL. - * @constructor - * @extends {cr.ui.GridItem} - */ - function ProfilesIconGridItem(iconURL) { - var el = cr.doc.createElement('span'); - el.iconURL_ = iconURL; - ProfilesIconGridItem.decorate(el); - return el; - } - - /** - * Decorates an element as a profile grid item. - * @param {!HTMLElement} el The element to decorate. - */ - ProfilesIconGridItem.decorate = function(el) { - el.__proto__ = ProfilesIconGridItem.prototype; - el.decorate(); - }; - - ProfilesIconGridItem.prototype = { - __proto__: ListItem.prototype, - - /** @inheritDoc */ - decorate: function() { - ListItem.prototype.decorate.call(this); - var imageEl = cr.doc.createElement('img'); - imageEl.className = 'profile-icon'; - imageEl.src = this.iconURL_; - this.appendChild(imageEl); - - this.className = 'profile-icon-grid-item'; - }, - }; - - var ProfilesIconGrid = cr.ui.define('grid'); - - ProfilesIconGrid.prototype = { - __proto__: Grid.prototype, - - /** @inheritDoc */ - decorate: function() { - Grid.prototype.decorate.call(this); - this.selectionModel = new ListSingleSelectionModel(); - }, - - /** @inheritDoc */ - createItem: function(iconURL) { - return new ProfilesIconGridItem(iconURL); - }, - }; - - return { - ProfilesIconGrid: ProfilesIconGrid - }; -}); - diff --git a/chrome/browser/resources/options2/search_box.html b/chrome/browser/resources/options2/search_box.html deleted file mode 100644 index f27e1e7..0000000 --- a/chrome/browser/resources/options2/search_box.html +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/chrome/browser/resources/options2/search_engine_manager.css b/chrome/browser/resources/options2/search_engine_manager.css deleted file mode 100644 index f9c17c8..0000000 --- a/chrome/browser/resources/options2/search_engine_manager.css +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#search-engine-manager-page { - width: 700px; -} - -.search-engine-list input { - -webkit-box-flex: 1; - display: -webkit-box; -} - -.search-engine-list > div { - display: -webkit-box; -} - -.search-engine-list .favicon { - height: 16px; - line-height: 16px; - padding: 0 7px; - width: 16px; -} - -.search-engine-list .name-column { - -webkit-box-align: center; - -webkit-padding-end: 1ex; - box-sizing: border-box; - display: -webkit-box; - width: 30%; -} - -.search-engine-list .name-column :last-child { - -webkit-box-flex: 1; -} - -.search-engine-list .keyword-column { - -webkit-padding-end: 1ex; - box-sizing: border-box; - width: 26%; -} - -.search-engine-list .url-column { - box-sizing: border-box; - width: 44%; -} - -.search-engine-list .keyword-column, -.search-engine-list .url-column { - color: #666; -} - -.search-engine-list .default .name-column, -.search-engine-list .default .keyword-column { - font-weight: bold; -} - -/* For temporary Make Default button */ -.search-engine-list .url-column { - -webkit-box-align: center; - display: -webkit-box; -} - -.search-engine-list .url-column :first-child { - -webkit-box-flex: 1; -} - -.search-engine-list .url-column .list-inline-button { - margin-top: 0; - padding: 1px 6px 2px 6px; -} - -.search-engine-list > :not(:hover):not([editing]) .url-column - .list-inline-button { - display: none; -} - -/* End temporary Make Default button styling */ diff --git a/chrome/browser/resources/options2/search_engine_manager.html b/chrome/browser/resources/options2/search_engine_manager.html deleted file mode 100644 index 0e300b2..0000000 --- a/chrome/browser/resources/options2/search_engine_manager.html +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/chrome/browser/resources/options2/search_engine_manager.js b/chrome/browser/resources/options2/search_engine_manager.js deleted file mode 100644 index f986aa1..0000000 --- a/chrome/browser/resources/options2/search_engine_manager.js +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - - /** - * Encapsulated handling of search engine management page. - * @constructor - */ - function SearchEngineManager() { - this.activeNavTab = null; - OptionsPage.call(this, 'searchEngines', - loadTimeData.getString('searchEngineManagerPageTabTitle'), - 'search-engine-manager-page'); - } - - cr.addSingletonGetter(SearchEngineManager); - - SearchEngineManager.prototype = { - __proto__: OptionsPage.prototype, - - /** - * List for default search engine options. - * @private - */ - defaultsList_: null, - - /** - * List for other search engine options. - * @private - */ - othersList_: null, - - /** - * List for extension keywords. - * @private - extensionList_ : null, - - /** inheritDoc */ - initializePage: function() { - OptionsPage.prototype.initializePage.call(this); - - this.defaultsList_ = $('default-search-engine-list'); - this.setUpList_(this.defaultsList_); - - this.othersList_ = $('other-search-engine-list'); - this.setUpList_(this.othersList_); - - this.extensionList_ = $('extension-keyword-list'); - this.setUpList_(this.extensionList_); - - $('search-engine-manager-confirm').onclick = function() { - OptionsPage.closeOverlay(); - }; - }, - - /** - * Sets up the given list as a search engine list - * @param {List} list The list to set up. - * @private - */ - setUpList_: function(list) { - options.search_engines.SearchEngineList.decorate(list); - list.autoExpands = true; - }, - - /** - * Updates the search engine list with the given entries. - * @private - * @param {Array} defaultEngines List of possible default search engines. - * @param {Array} otherEngines List of other search engines. - * @param {Array} keywords List of keywords from extensions. - */ - updateSearchEngineList_: function(defaultEngines, otherEngines, keywords) { - this.defaultsList_.dataModel = new ArrayDataModel(defaultEngines); - - otherEngines = otherEngines.map(function(x) { - return [x, x['name'].toLocaleLowerCase()]; - }).sort(function(a, b) { - return a[1].localeCompare(b[1]); - }).map(function(x) { - return x[0]; - }); - - var othersModel = new ArrayDataModel(otherEngines); - // Add a "new engine" row. - othersModel.push({ - 'modelIndex': '-1', - 'canBeEdited': true - }); - this.othersList_.dataModel = othersModel; - - if (keywords.length > 0) { - $('extension-keyword-div').hidden = false; - var extensionsModel = new ArrayDataModel(keywords); - this.extensionList_.dataModel = extensionsModel; - } else { - $('extension-keyword-div').hidden = true; - } - }, - }; - - SearchEngineManager.updateSearchEngineList = function(defaultEngines, - otherEngines, - keywords) { - SearchEngineManager.getInstance().updateSearchEngineList_(defaultEngines, - otherEngines, - keywords); - }; - - SearchEngineManager.validityCheckCallback = function(validity, modelIndex) { - // Forward to both lists; the one without a matching modelIndex will ignore - // it. - SearchEngineManager.getInstance().defaultsList_.validationComplete( - validity, modelIndex); - SearchEngineManager.getInstance().othersList_.validationComplete( - validity, modelIndex); - }; - - // Export - return { - SearchEngineManager: SearchEngineManager - }; - -}); - diff --git a/chrome/browser/resources/options2/search_engine_manager_engine_list.js b/chrome/browser/resources/options2/search_engine_manager_engine_list.js deleted file mode 100644 index 9d6b5ae..0000000 --- a/chrome/browser/resources/options2/search_engine_manager_engine_list.js +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options.search_engines', function() { - /** @const */ var InlineEditableItemList = options.InlineEditableItemList; - /** @const */ var InlineEditableItem = options.InlineEditableItem; - /** @const */ var ListSelectionController = cr.ui.ListSelectionController; - - /** - * Creates a new search engine list item. - * @param {Object} searchEnigne The search engine this represents. - * @constructor - * @extends {cr.ui.ListItem} - */ - function SearchEngineListItem(searchEngine) { - var el = cr.doc.createElement('div'); - el.searchEngine_ = searchEngine; - SearchEngineListItem.decorate(el); - return el; - } - - /** - * Decorates an element as a search engine list item. - * @param {!HTMLElement} el The element to decorate. - */ - SearchEngineListItem.decorate = function(el) { - el.__proto__ = SearchEngineListItem.prototype; - el.decorate(); - }; - - SearchEngineListItem.prototype = { - __proto__: InlineEditableItem.prototype, - - /** - * Input field for editing the engine name. - * @type {HTMLElement} - * @private - */ - nameField_: null, - - /** - * Input field for editing the engine keyword. - * @type {HTMLElement} - * @private - */ - keywordField_: null, - - /** - * Input field for editing the engine url. - * @type {HTMLElement} - * @private - */ - urlField_: null, - - /** - * Whether or not an input validation request is currently outstanding. - * @type {boolean} - * @private - */ - waitingForValidation_: false, - - /** - * Whether or not the current set of input is known to be valid. - * @type {boolean} - * @private - */ - currentlyValid_: false, - - /** @inheritDoc */ - decorate: function() { - InlineEditableItem.prototype.decorate.call(this); - - var engine = this.searchEngine_; - - if (engine['modelIndex'] == '-1') { - this.isPlaceholder = true; - engine['name'] = ''; - engine['keyword'] = ''; - engine['url'] = ''; - } - - this.currentlyValid_ = !this.isPlaceholder; - - if (engine['default']) - this.classList.add('default'); - - this.deletable = engine['canBeRemoved']; - - // Construct the name column. - var nameColEl = this.ownerDocument.createElement('div'); - nameColEl.className = 'name-column'; - nameColEl.classList.add('weakrtl'); - this.contentElement.appendChild(nameColEl); - - // Add the favicon. - var faviconDivEl = this.ownerDocument.createElement('div'); - faviconDivEl.className = 'favicon'; - var imgEl = this.ownerDocument.createElement('img'); - imgEl.src = 'chrome://favicon/iconurl/' + engine['iconURL']; - faviconDivEl.appendChild(imgEl); - nameColEl.appendChild(faviconDivEl); - - var nameEl = this.createEditableTextCell(engine['displayName']); - nameEl.classList.add('weakrtl'); - nameColEl.appendChild(nameEl); - - // Then the keyword column. - var keywordEl = this.createEditableTextCell(engine['keyword']); - keywordEl.className = 'keyword-column'; - keywordEl.classList.add('weakrtl'); - this.contentElement.appendChild(keywordEl); - - // And the URL column. - var urlEl = this.createEditableTextCell(engine['url']); - var urlWithButtonEl = this.ownerDocument.createElement('div'); - urlWithButtonEl.appendChild(urlEl); - urlWithButtonEl.className = 'url-column'; - urlWithButtonEl.classList.add('weakrtl'); - this.contentElement.appendChild(urlWithButtonEl); - // Add the Make Default button. Temporary until drag-and-drop re-ordering - // is implemented. When this is removed, remove the extra div above. - if (engine['canBeDefault']) { - var makeDefaultButtonEl = this.ownerDocument.createElement('button'); - makeDefaultButtonEl.className = 'custom-appearance list-inline-button'; - makeDefaultButtonEl.textContent = - loadTimeData.getString('makeDefaultSearchEngineButton'); - makeDefaultButtonEl.onclick = function(e) { - chrome.send('managerSetDefaultSearchEngine', [engine['modelIndex']]); - }; - // Don't select the row when clicking the button. - makeDefaultButtonEl.onmousedown = function(e) { - e.stopPropagation(); - }; - urlWithButtonEl.appendChild(makeDefaultButtonEl); - } - - // Do final adjustment to the input fields. - this.nameField_ = nameEl.querySelector('input'); - // The editable field uses the raw name, not the display name. - this.nameField_.value = engine['name']; - this.keywordField_ = keywordEl.querySelector('input'); - this.urlField_ = urlEl.querySelector('input'); - - if (engine['urlLocked']) - this.urlField_.disabled = true; - - if (this.isPlaceholder) { - this.nameField_.placeholder = - loadTimeData.getString('searchEngineTableNamePlaceholder'); - this.keywordField_.placeholder = - loadTimeData.getString('searchEngineTableKeywordPlaceholder'); - this.urlField_.placeholder = - loadTimeData.getString('searchEngineTableURLPlaceholder'); - } - - var fields = [this.nameField_, this.keywordField_, this.urlField_]; - for (var i = 0; i < fields.length; i++) { - fields[i].oninput = this.startFieldValidation_.bind(this); - } - - // Listen for edit events. - if (engine['canBeEdited']) { - this.addEventListener('edit', this.onEditStarted_.bind(this)); - this.addEventListener('canceledit', this.onEditCancelled_.bind(this)); - this.addEventListener('commitedit', this.onEditCommitted_.bind(this)); - } else { - this.editable = false; - } - }, - - /** @inheritDoc */ - get currentInputIsValid() { - return !this.waitingForValidation_ && this.currentlyValid_; - }, - - /** @inheritDoc */ - get hasBeenEdited() { - var engine = this.searchEngine_; - return this.nameField_.value != engine['name'] || - this.keywordField_.value != engine['keyword'] || - this.urlField_.value != engine['url']; - }, - - /** - * Called when entering edit mode; starts an edit session in the model. - * @param {Event} e The edit event. - * @private - */ - onEditStarted_: function(e) { - var editIndex = this.searchEngine_['modelIndex']; - chrome.send('editSearchEngine', [String(editIndex)]); - this.startFieldValidation_(); - }, - - /** - * Called when committing an edit; updates the model. - * @param {Event} e The end event. - * @private - */ - onEditCommitted_: function(e) { - chrome.send('searchEngineEditCompleted', this.getInputFieldValues_()); - }, - - /** - * Called when cancelling an edit; informs the model and resets the control - * states. - * @param {Event} e The cancel event. - * @private - */ - onEditCancelled_: function() { - chrome.send('searchEngineEditCancelled'); - - // The name field has been automatically set to match the display name, - // but it should use the raw name instead. - this.nameField_.value = this.searchEngine_['name']; - this.currentlyValid_ = !this.isPlaceholder; - }, - - /** - * Returns the input field values as an array suitable for passing to - * chrome.send. The order of the array is important. - * @private - * @return {array} The current input field values. - */ - getInputFieldValues_: function() { - return [this.nameField_.value, - this.keywordField_.value, - this.urlField_.value]; - }, - - /** - * Begins the process of asynchronously validing the input fields. - * @private - */ - startFieldValidation_: function() { - this.waitingForValidation_ = true; - var args = this.getInputFieldValues_(); - args.push(this.searchEngine_['modelIndex']); - chrome.send('checkSearchEngineInfoValidity', args); - }, - - /** - * Callback for the completion of an input validition check. - * @param {Object} validity A dictionary of validitation results. - */ - validationComplete: function(validity) { - this.waitingForValidation_ = false; - // TODO(stuartmorgan): Implement the full validation UI with - // checkmark/exclamation mark icons and tooltips showing the errors. - if (validity.name) { - this.nameField_.setCustomValidity(''); - } else { - this.nameField_.setCustomValidity( - loadTimeData.getString('editSearchEngineInvalidTitleToolTip')); - } - - if (validity.keyword) { - this.keywordField_.setCustomValidity(''); - } else { - this.keywordField_.setCustomValidity( - loadTimeData.getString('editSearchEngineInvalidKeywordToolTip')); - } - - if (validity.url) { - this.urlField_.setCustomValidity(''); - } else { - this.urlField_.setCustomValidity( - loadTimeData.getString('editSearchEngineInvalidURLToolTip')); - } - - this.currentlyValid_ = validity.name && validity.keyword && validity.url; - }, - }; - - var SearchEngineList = cr.ui.define('list'); - - SearchEngineList.prototype = { - __proto__: InlineEditableItemList.prototype, - - /** @inheritDoc */ - createItem: function(searchEngine) { - return new SearchEngineListItem(searchEngine); - }, - - /** @inheritDoc */ - deleteItemAtIndex: function(index) { - var modelIndex = this.dataModel.item(index)['modelIndex']; - chrome.send('removeSearchEngine', [String(modelIndex)]); - }, - - /** - * Passes the results of an input validation check to the requesting row - * if it's still being edited. - * @param {number} modelIndex The model index of the item that was checked. - * @param {Object} validity A dictionary of validitation results. - */ - validationComplete: function(validity, modelIndex) { - // If it's not still being edited, it no longer matters. - var currentSelection = this.selectedItem; - if (!currentSelection) - return; - var listItem = this.getListItem(currentSelection); - if (listItem.editing && currentSelection['modelIndex'] == modelIndex) - listItem.validationComplete(validity); - }, - }; - - // Export - return { - SearchEngineList: SearchEngineList - }; - -}); - diff --git a/chrome/browser/resources/options2/search_page.css b/chrome/browser/resources/options2/search_page.css deleted file mode 100644 index 18b0fb1..0000000 --- a/chrome/browser/resources/options2/search_page.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.search-hidden { - display: none !important; -} - -.search-highlighted { - background-color: rgba(255, 240, 120, 0.9); -} - -/* Container for the elements that make up the search bubble. */ -.search-bubble { - left: 0; - margin-top: 5px; - pointer-events: none; - position: absolute; - top: -1000px; /* Minor hack: position off-screen by default. */ - /* Create a z-context for search-bubble-innards, its after and before. */ - z-index: 1; -} - -/* Contains the text content of the bubble. */ -.search-bubble-innards { - background: -webkit-linear-gradient(rgba(255, 248, 172, 0.9), - rgba(255, 243, 128, 0.9)); - border-radius: 2px; - padding: 4px 10px; - text-align: center; - width: 100px; -} - -/* Provides the border around the bubble (has to be behind :after). */ -.search-bubble-innards::before { - border: 1px solid rgb(220, 198, 72); - border-radius: 2px; - bottom: -1px; - content: ''; - left: -1px; - position: absolute; - right: -1px; - top: -1px; - z-index: -2; -} - -/* Provides the arrow which points at the anchor element. */ -.search-bubble-innards::after { - -webkit-transform: rotate(45deg); - background: - -webkit-linear-gradient(-45deg, rgb(251, 255, 181), - rgb(255, 248, 172) 50%, - rgba(255, 248, 172, 0)); - border: 1px solid rgb(220, 198, 72); - border-bottom-color: transparent; - border-right-color: transparent; - content: ''; - height: 12px; - left: 53px; - position: absolute; - top: -7px; - width: 12px; - z-index: -1; -} - -.search-bubble-wrapper { - position: relative; -} - -#searchPage.page { - /* The search page needs no padding as it's provided by showing the results - * from each page. */ - padding: 0; -} diff --git a/chrome/browser/resources/options2/search_page.html b/chrome/browser/resources/options2/search_page.html deleted file mode 100644 index 073c505..0000000 --- a/chrome/browser/resources/options2/search_page.html +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/chrome/browser/resources/options2/search_page.js b/chrome/browser/resources/options2/search_page.js deleted file mode 100644 index ac28df4..0000000 --- a/chrome/browser/resources/options2/search_page.js +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * Encapsulated handling of a search bubble. - * @constructor - */ - function SearchBubble(text) { - var el = cr.doc.createElement('div'); - SearchBubble.decorate(el); - el.content = text; - return el; - } - - SearchBubble.decorate = function(el) { - el.__proto__ = SearchBubble.prototype; - el.decorate(); - }; - - SearchBubble.prototype = { - __proto__: HTMLDivElement.prototype, - - decorate: function() { - this.className = 'search-bubble'; - - this.innards_ = cr.doc.createElement('div'); - this.innards_.className = 'search-bubble-innards'; - this.appendChild(this.innards_); - - // We create a timer to periodically update the position of the bubbles. - // While this isn't all that desirable, it's the only sure-fire way of - // making sure the bubbles stay in the correct location as sections - // may dynamically change size at any time. - this.intervalId = setInterval(this.updatePosition.bind(this), 250); - }, - - /** - * Sets the text message in the bubble. - * @param {string} text The text the bubble will show. - */ - set content(text) { - this.innards_.textContent = text; - }, - - /** - * Attach the bubble to the element. - */ - attachTo: function(element) { - var parent = element.parentElement; - if (!parent) - return; - if (parent.tagName == 'TD') { - // To make absolute positioning work inside a table cell we need - // to wrap the bubble div into another div with position:relative. - // This only works properly if the element is the first child of the - // table cell which is true for all options pages. - this.wrapper = cr.doc.createElement('div'); - this.wrapper.className = 'search-bubble-wrapper'; - this.wrapper.appendChild(this); - parent.insertBefore(this.wrapper, element); - } else { - parent.insertBefore(this, element); - } - }, - - /** - * Clear the interval timer and remove the element from the page. - */ - dispose: function() { - clearInterval(this.intervalId); - - var child = this.wrapper || this; - var parent = child.parentNode; - if (parent) - parent.removeChild(child); - }, - - /** - * Update the position of the bubble. Called at creation time and then - * periodically while the bubble remains visible. - */ - updatePosition: function() { - // This bubble is 'owned' by the next sibling. - var owner = (this.wrapper || this).nextSibling; - - // If there isn't an offset parent, we have nothing to do. - if (!owner.offsetParent) - return; - - // Position the bubble below the location of the owner. - var left = owner.offsetLeft + owner.offsetWidth / 2 - - this.offsetWidth / 2; - var top = owner.offsetTop + owner.offsetHeight; - - // Update the position in the CSS. Cache the last values for - // best performance. - if (left != this.lastLeft) { - this.style.left = left + 'px'; - this.lastLeft = left; - } - if (top != this.lastTop) { - this.style.top = top + 'px'; - this.lastTop = top; - } - }, - }; - - /** - * Encapsulated handling of the search page. - * @constructor - */ - function SearchPage() { - OptionsPage.call(this, 'search', - loadTimeData.getString('searchPageTabTitle'), - 'searchPage'); - } - - cr.addSingletonGetter(SearchPage); - - SearchPage.prototype = { - // Inherit SearchPage from OptionsPage. - __proto__: OptionsPage.prototype, - - /** - * A boolean to prevent recursion. Used by setSearchText_(). - * @type {Boolean} - * @private - */ - insideSetSearchText_: false, - - /** - * Initialize the page. - */ - initializePage: function() { - // Call base class implementation to start preference initialization. - OptionsPage.prototype.initializePage.call(this); - - this.searchField = $('search-field'); - - // Handle search events. (No need to throttle, WebKit's search field - // will do that automatically.) - this.searchField.onsearch = function(e) { - this.setSearchText_(e.currentTarget.value); - }.bind(this); - - // Install handler for key presses. - document.addEventListener('keydown', - this.keyDownEventHandler_.bind(this)); - }, - - /** - * @inheritDoc - */ - get sticky() { - return true; - }, - - /** - * Called after this page has shown. - */ - didShowPage: function() { - // This method is called by the Options page after all pages have - // had their visibilty attribute set. At this point we can perform the - // search specific DOM manipulation. - this.setSearchActive_(true); - }, - - /** - * Called before this page will be hidden. - */ - willHidePage: function() { - // This method is called by the Options page before all pages have - // their visibilty attribute set. Before that happens, we need to - // undo the search specific DOM manipulation that was performed in - // didShowPage. - this.setSearchActive_(false); - }, - - /** - * Update the UI to reflect whether we are in a search state. - * @param {boolean} active True if we are on the search page. - * @private - */ - setSearchActive_: function(active) { - // It's fine to exit if search wasn't active and we're not going to - // activate it now. - if (!this.searchActive_ && !active) - return; - - this.searchActive_ = active; - - if (active) { - var hash = location.hash; - if (hash) { - this.searchField.value = - decodeURIComponent(hash.slice(1).replace(/\+/g, ' ')); - } else if (!this.searchField.value) { - // This should only happen if the user goes directly to - // chrome://settings-frame/search - OptionsPage.showDefaultPage(); - return; - } - - // Move 'advanced' sections into the main settings page to allow - // searching. - if (!this.advancedSections_) { - this.advancedSections_ = - $('advanced-settings-container').querySelectorAll('section'); - for (var i = 0, section; section = this.advancedSections_[i]; i++) - $('settings').appendChild(section); - } - } - - var pagesToSearch = this.getSearchablePages_(); - for (var key in pagesToSearch) { - var page = pagesToSearch[key]; - - if (!active) - page.visible = false; - - // Update the visible state of all top-level elements that are not - // sections (ie titles, button strips). We do this before changing - // the page visibility to avoid excessive re-draw. - for (var i = 0, childDiv; childDiv = page.pageDiv.children[i]; i++) { - if (active) { - if (childDiv.tagName != 'SECTION') - childDiv.classList.add('search-hidden'); - } else { - childDiv.classList.remove('search-hidden'); - } - } - - if (active) { - // When search is active, remove the 'hidden' tag. This tag may have - // been added by the OptionsPage. - page.pageDiv.hidden = false; - } - } - - if (active) { - this.setSearchText_(this.searchField.value); - this.searchField.focus(); - } else { - // After hiding all page content, remove any search results. - this.unhighlightMatches_(); - this.removeSearchBubbles_(); - - // Move 'advanced' sections back into their original container. - if (this.advancedSections_) { - for (var i = 0, section; section = this.advancedSections_[i]; i++) - $('advanced-settings-container').appendChild(section); - this.advancedSections_ = null; - } - } - }, - - /** - * Set the current search criteria. - * @param {string} text Search text. - * @private - */ - setSearchText_: function(text) { - // Prevent recursive execution of this method. - if (this.insideSetSearchText_) return; - this.insideSetSearchText_ = true; - - // Cleanup the search query string. - text = SearchPage.canonicalizeQuery(text); - - // Set the hash on the current page, and the enclosing uber page - var hash = text ? '#' + encodeURIComponent(text) : ''; - var path = text ? this.name : ''; - window.location.hash = hash; - uber.invokeMethodOnParent('setPath', {path: path + hash}); - - // Toggle the search page if necessary. - if (text) { - if (!this.searchActive_) - OptionsPage.showPageByName(this.name, false); - } else { - if (this.searchActive_) - OptionsPage.showPageByName(OptionsPage.getDefaultPage().name, false); - - this.insideSetSearchText_ = false; - return; - } - - var foundMatches = false; - - // Remove any prior search results. - this.unhighlightMatches_(); - this.removeSearchBubbles_(); - - var pagesToSearch = this.getSearchablePages_(); - for (var key in pagesToSearch) { - var page = pagesToSearch[key]; - var elements = page.pageDiv.querySelectorAll('section'); - for (var i = 0, node; node = elements[i]; i++) { - node.classList.add('search-hidden'); - } - } - - var bubbleControls = []; - - // Generate search text by applying lowercase and escaping any characters - // that would be problematic for regular expressions. - var searchText = - text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); - // Generate a regular expression for hilighting search terms. - var regExp = new RegExp('(' + searchText + ')', 'ig'); - - if (searchText.length) { - // Search all top-level sections for anchored string matches. - for (var key in pagesToSearch) { - var page = pagesToSearch[key]; - var elements = - page.pageDiv.querySelectorAll('section'); - for (var i = 0, node; node = elements[i]; i++) { - if (this.highlightMatches_(regExp, node)) { - node.classList.remove('search-hidden'); - if (!node.hidden) - foundMatches = true; - } - } - } - - // Search all sub-pages, generating an array of top-level sections that - // we need to make visible. - var subPagesToSearch = this.getSearchableSubPages_(); - var control, node; - for (var key in subPagesToSearch) { - var page = subPagesToSearch[key]; - if (this.highlightMatches_(regExp, page.pageDiv)) { - this.revealAssociatedSections_(page); - - bubbleControls = - bubbleControls.concat(this.getAssociatedControls_(page)); - - foundMatches = true; - } - } - } - - // Configure elements on the search results page based on search results. - $('searchPageNoMatches').hidden = foundMatches; - - // Create search balloons for sub-page results. - length = bubbleControls.length; - for (var i = 0; i < length; i++) - this.createSearchBubble_(bubbleControls[i], text); - - // Cleanup the recursion-prevention variable. - this.insideSetSearchText_ = false; - }, - - /** - * Reveal the associated section for |subpage|, as well as the one for its - * |parentPage|, and its |parentPage|'s |parentPage|, etc. - * @private - */ - revealAssociatedSections_: function(subpage) { - for (var page = subpage; page; page = page.parentPage) { - var section = page.associatedSection; - if (section) - section.classList.remove('search-hidden'); - } - }, - - /** - * @return {!Array.} all the associated controls for |subpage|, - * including |subpage.associatedControls| as well as any controls on parent - * pages that are indirectly necessary to get to the subpage. - * @private - */ - getAssociatedControls_: function(subpage) { - var controls = []; - for (var page = subpage; page; page = page.parentPage) { - if (page.associatedControls) - controls = controls.concat(page.associatedControls); - } - return controls; - }, - - /** - * Wraps matches in spans. - * @param {RegExp} regExp The search query (in regexp form). - * @param {Element} element An HTML container element to recursively search - * within. - * @return {boolean} true if the element was changed. - * @private - */ - highlightMatches_: function(regExp, element) { - var found = false; - var div, child, tmp; - - // Walk the tree, searching each TEXT node. - var walker = document.createTreeWalker(element, - NodeFilter.SHOW_TEXT, - null, - false); - var node = walker.nextNode(); - while (node) { - var textContent = node.nodeValue; - // Perform a search and replace on the text node value. - var split = textContent.split(regExp); - if (split.length > 1) { - found = true; - var nextNode = walker.nextNode(); - var parentNode = node.parentNode; - parentNode.removeChild(node); - node = nextNode; - - for (var i = 0; i < split.length; ++i) { - if (i % 2 == 0) { - parentNode.appendChild(document.createTextNode(split[i])); - } else { - var span = document.createElement('span'); - span.className = 'search-highlighted'; - span.textContent = split[i]; - parentNode.appendChild(span); - } - } - } else { - node = walker.nextNode(); - } - } - - return found; - }, - - /** - * Removes all search highlight tags from the document. - * @private - */ - unhighlightMatches_: function() { - // Find all search highlight elements. - var elements = document.querySelectorAll('.search-highlighted'); - - // For each element, remove the highlighting. - var parent, i; - for (var i = 0, node; node = elements[i]; i++) { - parent = node.parentNode; - - // Replace the highlight element with the first child (the text node). - parent.replaceChild(node.firstChild, node); - - // Normalize the parent so that multiple text nodes will be combined. - parent.normalize(); - } - }, - - /** - * Creates a search result bubble attached to an element. - * @param {Element} element An HTML element, usually a button. - * @param {string} text A string to show in the bubble. - * @private - */ - createSearchBubble_: function(element, text) { - // avoid appending multiple bubbles to a button. - var sibling = element.previousElementSibling; - if (sibling && (sibling.classList.contains('search-bubble') || - sibling.classList.contains('search-bubble-wrapper'))) - return; - - var parent = element.parentElement; - if (parent) { - var bubble = new SearchBubble(text); - bubble.attachTo(element); - bubble.updatePosition(); - } - }, - - /** - * Removes all search match bubbles. - * @private - */ - removeSearchBubbles_: function() { - var elements = document.querySelectorAll('.search-bubble'); - var length = elements.length; - for (var i = 0; i < length; i++) - elements[i].dispose(); - }, - - /** - * Builds a list of top-level pages to search. Omits the search page and - * all sub-pages. - * @return {Array} An array of pages to search. - * @private - */ - getSearchablePages_: function() { - var name, page, pages = []; - for (name in OptionsPage.registeredPages) { - if (name != this.name) { - page = OptionsPage.registeredPages[name]; - if (!page.parentPage) - pages.push(page); - } - } - return pages; - }, - - /** - * Builds a list of sub-pages (and overlay pages) to search. Ignore pages - * that have no associated controls. - * @return {Array} An array of pages to search. - * @private - */ - getSearchableSubPages_: function() { - var name, pageInfo, page, pages = []; - for (name in OptionsPage.registeredPages) { - page = OptionsPage.registeredPages[name]; - if (page.parentPage && page.associatedSection) - pages.push(page); - } - for (name in OptionsPage.registeredOverlayPages) { - page = OptionsPage.registeredOverlayPages[name]; - if (page.associatedSection && page.pageDiv != undefined) - pages.push(page); - } - return pages; - }, - - /** - * A function to handle key press events. - * @return {Event} a keydown event. - * @private - */ - keyDownEventHandler_: function(event) { - /** @const */ var ESCAPE_KEY_CODE = 27; - /** @const */ var FORWARD_SLASH_KEY_CODE = 191; - - switch (event.keyCode) { - case ESCAPE_KEY_CODE: - if (event.target == this.searchField) { - this.setSearchText_(''); - this.searchField.blur(); - event.stopPropagation(); - event.preventDefault(); - } - break; - case FORWARD_SLASH_KEY_CODE: - if (!/INPUT|SELECT|BUTTON|TEXTAREA/.test(event.target.tagName) && - !event.ctrlKey && !event.altKey) { - this.searchField.focus(); - event.stopPropagation(); - event.preventDefault(); - } - break; - } - }, - }; - - /** - * Standardizes a user-entered text query by removing extra whitespace. - * @param {string} The user-entered text. - * @return {string} The trimmed query. - */ - SearchPage.canonicalizeQuery = function(text) { - // Trim beginning and ending whitespace. - return text.replace(/^\s+|\s+$/g, ''); - }; - - // Export - return { - SearchPage: SearchPage - }; - -}); diff --git a/chrome/browser/resources/options2/settings_dialog.js b/chrome/browser/resources/options2/settings_dialog.js deleted file mode 100644 index 609bbaa..0000000 --- a/chrome/browser/resources/options2/settings_dialog.js +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Base class for dialogs that require saving preferences on - * confirm and resetting preference inputs on cancel. - */ - -cr.define('options', function() { - /** @const */ var OptionsPage = options.OptionsPage; - - /** - * Base class for settings dialogs. - * @constructor - * @param {string} name See OptionsPage constructor. - * @param {string} title See OptionsPage constructor. - * @param {string} pageDivName See OptionsPage constructor. - * @param {HTMLInputElement} okButton The confirmation button element. - * @param {HTMLInputElement} cancelButton The cancellation button element. - * @extends {OptionsPage} - */ - function SettingsDialog(name, title, pageDivName, okButton, cancelButton) { - OptionsPage.call(this, name, title, pageDivName); - this.okButton = okButton; - this.cancelButton = cancelButton; - } - - SettingsDialog.prototype = { - __proto__: OptionsPage.prototype, - - /** - * @inheritDoc - */ - initializePage: function() { - this.okButton.onclick = this.handleConfirm.bind(this); - this.cancelButton.onclick = this.handleCancel.bind(this); - }, - - /** - * Handles the confirm button by saving the dialog preferences. - */ - handleConfirm: function() { - OptionsPage.closeOverlay(); - - var els = this.pageDiv.querySelectorAll('[dialog-pref]'); - for (var i = 0; i < els.length; i++) { - if (els[i].savePrefState) - els[i].savePrefState(); - } - }, - - /** - * Handles the cancel button by closing the overlay. - */ - handleCancel: function() { - OptionsPage.closeOverlay(); - - var els = this.pageDiv.querySelectorAll('[dialog-pref]'); - for (var i = 0; i < els.length; i++) { - if (els[i].resetPrefState) - els[i].resetPrefState(); - } - }, - }; - - return { - SettingsDialog: SettingsDialog - }; -}); diff --git a/chrome/browser/resources/options2/spelling_confirm_overlay.css b/chrome/browser/resources/options2/spelling_confirm_overlay.css deleted file mode 100644 index 434c26c..0000000 --- a/chrome/browser/resources/options2/spelling_confirm_overlay.css +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#spelling-confirm-overlay { - width: 500px; -} diff --git a/chrome/browser/resources/options2/spelling_confirm_overlay.html b/chrome/browser/resources/options2/spelling_confirm_overlay.html deleted file mode 100644 index d58ce10..0000000 --- a/chrome/browser/resources/options2/spelling_confirm_overlay.html +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/chrome/browser/resources/options2/spelling_confirm_overlay.js b/chrome/browser/resources/options2/spelling_confirm_overlay.js deleted file mode 100644 index 86e6598..0000000 --- a/chrome/browser/resources/options2/spelling_confirm_overlay.js +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - var SettingsDialog = options.SettingsDialog; - - /* - * SpellingConfirmOverlay class - * Dialog to confirm that the user really wants to use the Spelling service - * @extends {SettingsDialog} - */ - function SpellingConfirmOverlay() { - SettingsDialog.call(this, - 'spellingConfirm', - loadTimeData.getString('spellingConfirmTitle'), - 'spelling-confirm-overlay', - $('spelling-confirm-ok'), - $('spelling-confirm-cancel')); - }; - - cr.addSingletonGetter(SpellingConfirmOverlay); - - SpellingConfirmOverlay.prototype = { - __proto__: SettingsDialog.prototype, - - /** @inheritDoc */ - initializePage: function() { - SettingsDialog.prototype.initializePage.call(this); - }, - - /** @inheritDoc */ - handleConfirm: function() { - SettingsDialog.prototype.handleConfirm.call(this); - Preferences.setBooleanPref('spellcheck.use_spelling_service', true); - Preferences.setBooleanPref('spellcheck.confirm_dialog_shown', true); - }, - - /** @inheritDoc */ - handleCancel: function() { - SettingsDialog.prototype.handleCancel.call(this); - $('spelling-enabled-control').checked = false; - }, - }; - - // Export - return { - SpellingConfirmOverlay: SpellingConfirmOverlay - }; -}); diff --git a/chrome/browser/resources/options2/startup_overlay.css b/chrome/browser/resources/options2/startup_overlay.css deleted file mode 100644 index dc74deb..0000000 --- a/chrome/browser/resources/options2/startup_overlay.css +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#startup-overlay { - min-width: 500px; -} - -#startupPagesList { - margin-bottom: 20px; - min-height: 64px; -} - -#startupPagesList .title { - width: 40%; -} - -#startupPagesList .url { - -webkit-box-flex: 1; - color: #666; -} - -#startupPagesList > * { - max-width: 700px; -} - -#startupPagesListDropmarker { - background-clip: padding-box; - background-color: hsl(214, 91%, 65%); - border: 2px solid hsl(214, 91%, 65%); - border-bottom-color: transparent; - border-radius: 0; - border-top-color: transparent; - box-sizing: border-box; - display: none; - height: 6px; - overflow: hidden; - pointer-events: none; - position: fixed; - z-index: 10; -} diff --git a/chrome/browser/resources/options2/startup_overlay.html b/chrome/browser/resources/options2/startup_overlay.html deleted file mode 100644 index 1ec343d..0000000 --- a/chrome/browser/resources/options2/startup_overlay.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/chrome/browser/resources/options2/startup_overlay.js b/chrome/browser/resources/options2/startup_overlay.js deleted file mode 100644 index f452ab2..0000000 --- a/chrome/browser/resources/options2/startup_overlay.js +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('options', function() { - /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; - /** @const */ var OptionsPage = options.OptionsPage; - /** @const */ var SettingsDialog = options.SettingsDialog; - - /** - * StartupOverlay class - * Encapsulated handling of the 'Set Startup pages' overlay page. - * @constructor - * @class - */ - function StartupOverlay() { - SettingsDialog.call(this, 'startup', - loadTimeData.getString('startupPagesOverlayTabTitle'), - 'startup-overlay', - $('startup-overlay-confirm'), - $('startup-overlay-cancel')); - }; - - cr.addSingletonGetter(StartupOverlay); - - StartupOverlay.prototype = { - __proto__: SettingsDialog.prototype, - - /** - * An autocomplete list that can be attached to a text field during editing. - * @type {HTMLElement} - * @private - */ - autocompleteList_: null, - - startup_pages_pref_: { - 'name': 'session.urls_to_restore_on_startup', - 'disabled': false - }, - - /** - * Initialize the page. - */ - initializePage: function() { - SettingsDialog.prototype.initializePage.call(this); - - var self = this; - - var startupPagesList = $('startupPagesList'); - options.browser_options.StartupPageList.decorate(startupPagesList); - startupPagesList.autoExpands = true; - - $('startupUseCurrentButton').onclick = function(event) { - chrome.send('setStartupPagesToCurrentPages'); - }; - - Preferences.getInstance().addEventListener( - this.startup_pages_pref_.name, - this.handleStartupPageListChange_.bind(this)); - - var suggestionList = new cr.ui.AutocompleteList(); - suggestionList.autoExpands = true; - suggestionList.suggestionUpdateRequestCallback = - this.requestAutocompleteSuggestions_.bind(this); - $('startup-overlay').appendChild(suggestionList); - this.autocompleteList_ = suggestionList; - startupPagesList.autocompleteList = suggestionList; - }, - - /** @inheritDoc */ - handleConfirm: function() { - SettingsDialog.prototype.handleConfirm.call(this); - chrome.send('commitStartupPrefChanges'); - }, - - /** @inheritDoc */ - handleCancel: function() { - SettingsDialog.prototype.handleCancel.call(this); - chrome.send('cancelStartupPrefChanges'); - }, - - /** - * Sets the enabled state of the custom startup page list - * @param {boolean} disable True to disable, false to enable - */ - setControlsDisabled: function(disable) { - var startupPagesList = $('startupPagesList'); - startupPagesList.disabled = disable; - startupPagesList.setAttribute('tabindex', disable ? -1 : 0); - // Explicitly set disabled state for input text elements. - var inputs = startupPagesList.querySelectorAll("input[type='text']"); - for (var i = 0; i < inputs.length; i++) - inputs[i].disabled = disable; - $('startupUseCurrentButton').disabled = disable; - }, - - /** - * Enables or disables the the custom startup page list controls - * based on the whether the 'pages to restore on startup' pref is enabled. - */ - updateControlStates: function() { - this.setControlsDisabled( - this.startup_pages_pref_.disabled); - }, - - /** - * Handles change events of the preference - * 'session.urls_to_restore_on_startup'. - * @param {event} preference changed event. - * @private - */ - handleStartupPageListChange_: function(event) { - this.startup_pages_pref_.disabled = event.value['disabled']; - this.updateControlStates(); - }, - - /** - * Updates the startup pages list with the given entries. - * @param {Array} pages List of startup pages. - * @private - */ - updateStartupPages_: function(pages) { - var model = new ArrayDataModel(pages); - // Add a "new page" row. - model.push({ - 'modelIndex': '-1' - }); - $('startupPagesList').dataModel = model; - }, - - /** - * Sends an asynchronous request for new autocompletion suggestions for the - * the given query. When new suggestions are available, the C++ handler will - * call updateAutocompleteSuggestions_. - * @param {string} query List of autocomplete suggestions. - * @private - */ - requestAutocompleteSuggestions_: function(query) { - chrome.send('requestAutocompleteSuggestionsForStartupPages', [query]); - }, - - /** - * Updates the autocomplete suggestion list with the given entries. - * @param {Array} pages List of autocomplete suggestions. - * @private - */ - updateAutocompleteSuggestions_: function(suggestions) { - var list = this.autocompleteList_; - // If the trigger for this update was a value being selected from the - // current list, do nothing. - if (list.targetInput && list.selectedItem && - list.selectedItem['url'] == list.targetInput.value) { - return; - } - list.suggestions = suggestions; - }, - }; - - // Forward public APIs to private implementations. - [ - 'updateStartupPages', - 'updateAutocompleteSuggestions', - ].forEach(function(name) { - StartupOverlay[name] = function() { - var instance = StartupOverlay.getInstance(); - return instance[name + '_'].apply(instance, arguments); - }; - }); - - // Export - return { - StartupOverlay: StartupOverlay - }; -}); diff --git a/chrome/browser/resources/options2/startup_section.html b/chrome/browser/resources/options2/startup_section.html deleted file mode 100644 index 778c870..0000000 --- a/chrome/browser/resources/options2/startup_section.html +++ /dev/null @@ -1,36 +0,0 @@ -
-

-
-
- -
-
- - -
-
- -
-
-
diff --git a/chrome/browser/resources/options2/subpages_tab_controls.css b/chrome/browser/resources/options2/subpages_tab_controls.css deleted file mode 100644 index c49c077..0000000 --- a/chrome/browser/resources/options2/subpages_tab_controls.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.subpages-nav-tabs .tab { - padding: 4px 8px; - position: relative; -} - -.subpages-nav-tabs .active-tab { - -webkit-box-shadow: 8px -8px 12px -6px rgb(240, 240, 240); - background: white; - border: 1px solid #ddd; - border-bottom: 2px solid white; - border-top-left-radius: 3px; - border-top-right-radius: 3px; - position: relative; -} - -/* To avoid tabs changing size when they are clicked and their labels become - * bold, we actually put two labels inside each tab: an inactive label and an - * active label. Only one is visible at a time, but the bold label is used to - * size the tab even when it's not visible. This keeps the tab size constant. - */ -.subpages-nav-tabs .active-tab-label, -.subpages-nav-tabs .tab-label:hover { - font-weight: bold; -} - -.subpages-nav-tabs .tab-label { - left: 9px; - position: absolute; - top: 5px; -} - -html[dir=rtl] .subpages-nav-tabs .tab-label { - right: 9px; -} - -.subpages-nav-tabs .active-tab-label, -.subpages-nav-tabs .active-tab .tab-label { - visibility: hidden; -} - -/* .tab is not removed when .active-tab is added, so we must - * override the hidden visibility above in the active tab case. - */ -.subpages-nav-tabs .active-tab .active-tab-label { - visibility: visible; -} - -.subpages-nav-tabs { - background-image: -webkit-gradient( - linear, - left top, - left bottom, - color-stop(0, rgb(255,255,255)), - color-stop(0.6, rgb(255,255,255)), - color-stop(0.8, rgb(250, 250, 250)), - color-stop(1.0, rgb(242,242,242)) - ); - border-bottom: 1px solid #ddd; - padding: 4px 20px; -} - -.subpages-tab-contents { - -webkit-padding-start: 10px; - display: none; - padding-top: 15px; -} - -.active-tab-contents { - display: block; -} diff --git a/chrome/browser/resources/options2/sync_section.html b/chrome/browser/resources/options2/sync_section.html deleted file mode 100644 index 4f7c980..0000000 --- a/chrome/browser/resources/options2/sync_section.html +++ /dev/null @@ -1,62 +0,0 @@ - -
-

- - -
- - - - - -
- -
-
-
- - - - - -
- -
-
-
- -
- - - - - - -
- - -
-
- - - diff --git a/chrome/browser/resources/options2_resources.grd b/chrome/browser/resources/options2_resources.grd deleted file mode 100644 index e4630b1..0000000 --- a/chrome/browser/resources/options2_resources.grd +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/chrome/browser/resources/options_resources.grd b/chrome/browser/resources/options_resources.grd new file mode 100644 index 0000000..fb8e4c5 --- /dev/null +++ b/chrome/browser/resources/options_resources.grd @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/chrome/browser/resources/sync_promo/sync_promo.js b/chrome/browser/resources/sync_promo/sync_promo.js index 4ce8299..86030d0 100644 --- a/chrome/browser/resources/sync_promo/sync_promo.js +++ b/chrome/browser/resources/sync_promo/sync_promo.js @@ -3,7 +3,7 @@ // found in the LICENSE file. // TODO(sail): Refactor options_page and remove this include. - + diff --git a/chrome/browser/resources/web_dev_style/js_checker.py b/chrome/browser/resources/web_dev_style/js_checker.py index c065e36..66cb9e1 100644 --- a/chrome/browser/resources/web_dev_style/js_checker.py +++ b/chrome/browser/resources/web_dev_style/js_checker.py @@ -79,7 +79,7 @@ class JSChecker(object): path.join(resources, 'net_internals'), path.join(resources, 'network_action_predictor'), path.join(resources, 'ntp4'), - path.join(resources, 'options2'), + path.join(resources, 'options'), path.join(resources, 'print_preview'), path.join(resources, 'profiler'), path.join(resources, 'sync_promo'), diff --git a/chrome/browser/ui/webui/options2/options_ui.cc b/chrome/browser/ui/webui/options2/options_ui.cc index a8f55ae..54bdf70 100644 --- a/chrome/browser/ui/webui/options2/options_ui.cc +++ b/chrome/browser/ui/webui/options2/options_ui.cc @@ -54,7 +54,7 @@ #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" -#include "grit/options2_resources.h" +#include "grit/options_resources.h" #include "grit/theme_resources.h" #include "net/base/escape.h" #include "ui/base/layout.h" @@ -148,11 +148,11 @@ void OptionsUIHTMLSource::StartDataRequest(const std::string& path, } else if (path == kOptionsBundleJsFile) { // Return (and cache) the options javascript code. response_bytes = ui::ResourceBundle::GetSharedInstance(). - LoadDataResourceBytes(IDR_OPTIONS2_BUNDLE_JS, ui::SCALE_FACTOR_NONE); + LoadDataResourceBytes(IDR_OPTIONS_BUNDLE_JS, ui::SCALE_FACTOR_NONE); } else { // Return (and cache) the main options html page as the default. response_bytes = ui::ResourceBundle::GetSharedInstance(). - LoadDataResourceBytes(IDR_OPTIONS2_HTML, ui::SCALE_FACTOR_NONE); + LoadDataResourceBytes(IDR_OPTIONS_HTML, ui::SCALE_FACTOR_NONE); } SendResponse(request_id, response_bytes); diff --git a/chrome/chrome_resources.gyp b/chrome/chrome_resources.gyp index 102fdb4..fe6d9af 100644 --- a/chrome/chrome_resources.gyp +++ b/chrome/chrome_resources.gyp @@ -32,9 +32,9 @@ 'includes': [ '../build/grit_action.gypi' ], }, { - 'action_name': 'options2_resources', + 'action_name': 'options_resources', 'variables': { - 'grit_grd_file': 'browser/resources/options2_resources.grd', + 'grit_grd_file': 'browser/resources/options_resources.grd', }, 'includes': [ '../build/grit_action.gypi' ], }, diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids index 4b49797..1796eea 100644 --- a/tools/gritsettings/resource_ids +++ b/tools/gritsettings/resource_ids @@ -159,7 +159,7 @@ "chrome/browser/resources/options_resources.grd": { "includes": [23000], }, - "chrome/browser/resources/options2_resources.grd": { + "chrome/browser/resources/options_resources.grd": { "structures": [23200], }, "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": { -- cgit v1.1