From 82a351c5d9991ee94cf632f4df10f2304e4e07fe Mon Sep 17 00:00:00 2001 From: "karol@jagiello.it" <1cyfraikilkaliter> Date: Sun, 12 Nov 2017 22:00:40 +0100 Subject: [PATCH] bump to v 1.0 --- LICENSE => LICENSE.txt | 0 _locales/en/messages.json | 611 ++++++--- _locales/ru/messages.json | 11 +- background.html | 8 + background.js | 292 ---- bg_ch.js | 341 +++++ bg_ff.js | 433 ++++++ bg_v015_to_v1.js | 106 ++ defaults.js | 298 ---- files_chrome/background.html | 8 + files_chrome/bg_ch.js | 341 +++++ files_firefox/background.html | 8 + .../manifest.json | 26 +- {manifest_opera => files_opera}/manifest.json | 2 +- .../manifest.json | 4 +- manifest.json | 38 +- options.html | 496 +++---- options.js | 581 -------- options/donate_bitcoin.png | Bin 0 -> 6305 bytes options/donate_eth.png | Bin 0 -> 66447 bytes options/donate_paypal.png | Bin 0 -> 4176 bytes options/donate_title.svg | 86 ++ options/options.css | 101 ++ options/options.js | 645 +++++++++ options/options_color_border.svg | 68 + options/options_color_border_hover - Copy.svg | 64 + options/options_color_border_hover.svg | 64 + options/options_color_brush.svg | 94 ++ options/options_color_brush_hover.svg | 62 + options/options_color_bucket.svg | 77 ++ options/options_color_bucket_hover.svg | 74 + options/options_color_toolbar_icon.svg | 77 ++ options/options_color_toolbar_icon_hover.svg | 77 ++ options/options_color_x.svg | 75 + options/options_color_x_hover.svg | 72 + options/options_copy_icon.png | Bin 0 -> 7486 bytes options/options_font_color.svg | 62 + options/options_font_color_hover.svg | 62 + options/options_font_style_italic.svg | 67 + options/options_font_style_italic_hover.svg | 67 + options/options_font_style_normal.svg | 62 + options/options_font_style_normal_hover.svg | 62 + options/options_font_weight_bold.svg | 62 + options/options_font_weight_bold_hover.svg | 62 + options/options_scale_minus.svg | 62 + options/options_scale_minus_hover.svg | 70 + options/options_scale_plus.svg | 67 + options/options_scale_plus_hover.svg | 75 + options/options_scrollbar_size_indicator.svg | 107 ++ .../options_tabs_indentation_indicator.svg | 109 ++ options/options_tabs_roundness_indicator.svg | 94 ++ options/options_tabs_size_indicator.svg | 109 ++ options/refresh.js | 35 + options/sample_tabs.js | 125 ++ options/theme.js | 193 +++ options/theme_size_settings_0.css | 168 +++ options/theme_size_settings_1.css | 168 +++ options/theme_size_settings_2.css | 168 +++ options/theme_size_settings_3.css | 168 +++ options/theme_size_settings_4.css | 168 +++ scripts/IO.js | 106 +- scripts/backup.js | 266 ++++ scripts/chrome.js | 170 +++ scripts/drag_and_drop.js | 323 +++++ scripts/events_chrome.js | 150 -- scripts/events_refresh.js | 145 -- scripts/events_tabs.js | 323 ----- scripts/folders.js | 72 + scripts/global.js | 206 +++ scripts/global_variables.js | 17 - scripts/groups.js | 295 ++++ scripts/{menu_tabs.js => menu.js} | 77 +- scripts/refresh.js | 177 +++ scripts/tabs.js | 556 +++++--- scripts/toolbar.js | 366 +++-- sidebar.html | 83 +- sidebar.js | 292 ++-- theme/expand_closed.svg | 94 ++ theme/expand_open.svg | 68 + theme/folder_closed.svg | 94 ++ theme/folder_open.svg | 95 ++ theme/theme.css | 1215 ++++++++++++----- theme/theme_colors.css | 278 ++++ theme/theme_size_preset_0.css | 30 + theme/theme_size_preset_1.css | 30 + theme/theme_size_preset_2.css | 30 + theme/theme_size_preset_3.css | 30 + theme/theme_size_preset_4.css | 30 + theme/toolbar_edit.svg | 83 ++ theme/toolbar_groups.svg | 81 ++ theme/toolbar_groups_hide.svg | 81 ++ theme/toolbar_import.svg | 78 ++ theme/toolbar_load_bak1.svg | 82 ++ theme/toolbar_load_bak2.svg | 82 ++ theme/toolbar_load_bak3.svg | 82 ++ theme/toolbar_merge.svg | 114 ++ .../{toolbar_new.svg => toolbar_plus_big.svg} | 0 theme/toolbar_plus_small.svg | 74 + theme/toolbar_save.svg | 71 + theme/toolbar_trashcan.svg | 84 ++ .../{toolbar_tools.svg => toolbar_wrench.svg} | 0 101 files changed, 10924 insertions(+), 3238 deletions(-) rename LICENSE => LICENSE.txt (100%) create mode 100644 background.html delete mode 100644 background.js create mode 100644 bg_ch.js create mode 100644 bg_ff.js create mode 100644 bg_v015_to_v1.js delete mode 100644 defaults.js create mode 100644 files_chrome/background.html create mode 100644 files_chrome/bg_ch.js create mode 100644 files_firefox/background.html rename {manifest_firefox => files_firefox}/manifest.json (65%) rename {manifest_opera => files_opera}/manifest.json (93%) rename {manifest_vivaldi => files_vivaldi}/manifest.json (88%) delete mode 100644 options.js create mode 100644 options/donate_bitcoin.png create mode 100644 options/donate_eth.png create mode 100644 options/donate_paypal.png create mode 100644 options/donate_title.svg create mode 100644 options/options.css create mode 100644 options/options.js create mode 100644 options/options_color_border.svg create mode 100644 options/options_color_border_hover - Copy.svg create mode 100644 options/options_color_border_hover.svg create mode 100644 options/options_color_brush.svg create mode 100644 options/options_color_brush_hover.svg create mode 100644 options/options_color_bucket.svg create mode 100644 options/options_color_bucket_hover.svg create mode 100644 options/options_color_toolbar_icon.svg create mode 100644 options/options_color_toolbar_icon_hover.svg create mode 100644 options/options_color_x.svg create mode 100644 options/options_color_x_hover.svg create mode 100644 options/options_copy_icon.png create mode 100644 options/options_font_color.svg create mode 100644 options/options_font_color_hover.svg create mode 100644 options/options_font_style_italic.svg create mode 100644 options/options_font_style_italic_hover.svg create mode 100644 options/options_font_style_normal.svg create mode 100644 options/options_font_style_normal_hover.svg create mode 100644 options/options_font_weight_bold.svg create mode 100644 options/options_font_weight_bold_hover.svg create mode 100644 options/options_scale_minus.svg create mode 100644 options/options_scale_minus_hover.svg create mode 100644 options/options_scale_plus.svg create mode 100644 options/options_scale_plus_hover.svg create mode 100644 options/options_scrollbar_size_indicator.svg create mode 100644 options/options_tabs_indentation_indicator.svg create mode 100644 options/options_tabs_roundness_indicator.svg create mode 100644 options/options_tabs_size_indicator.svg create mode 100644 options/refresh.js create mode 100644 options/sample_tabs.js create mode 100644 options/theme.js create mode 100644 options/theme_size_settings_0.css create mode 100644 options/theme_size_settings_1.css create mode 100644 options/theme_size_settings_2.css create mode 100644 options/theme_size_settings_3.css create mode 100644 options/theme_size_settings_4.css create mode 100644 scripts/backup.js create mode 100644 scripts/chrome.js create mode 100644 scripts/drag_and_drop.js delete mode 100644 scripts/events_chrome.js delete mode 100644 scripts/events_refresh.js delete mode 100644 scripts/events_tabs.js create mode 100644 scripts/folders.js create mode 100644 scripts/global.js delete mode 100644 scripts/global_variables.js create mode 100644 scripts/groups.js rename scripts/{menu_tabs.js => menu.js} (77%) create mode 100644 scripts/refresh.js create mode 100644 theme/expand_closed.svg create mode 100644 theme/expand_open.svg create mode 100644 theme/folder_closed.svg create mode 100644 theme/folder_open.svg create mode 100644 theme/theme_colors.css create mode 100644 theme/theme_size_preset_0.css create mode 100644 theme/theme_size_preset_1.css create mode 100644 theme/theme_size_preset_2.css create mode 100644 theme/theme_size_preset_3.css create mode 100644 theme/theme_size_preset_4.css create mode 100644 theme/toolbar_edit.svg create mode 100644 theme/toolbar_groups.svg create mode 100644 theme/toolbar_groups_hide.svg create mode 100644 theme/toolbar_import.svg create mode 100644 theme/toolbar_load_bak1.svg create mode 100644 theme/toolbar_load_bak2.svg create mode 100644 theme/toolbar_load_bak3.svg create mode 100644 theme/toolbar_merge.svg rename theme/{toolbar_new.svg => toolbar_plus_big.svg} (100%) create mode 100644 theme/toolbar_plus_small.svg create mode 100644 theme/toolbar_save.svg create mode 100644 theme/toolbar_trashcan.svg rename theme/{toolbar_tools.svg => toolbar_wrench.svg} (100%) diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 5c48ba2..882e7ea 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -5,6 +5,8 @@ "extDesc": { "message": "Manage your tabs in the sidebar!" }, + + "button_new": { "message": "Press left mouse button to open new tab. \nPress middle mouse button to clone the active tab. \nPress right mouse button to scroll the list to the active tab." }, @@ -23,15 +25,15 @@ "button_tools": { "message": "Tools" }, + "button_groups": { + "message": "Groups" + }, "filter_search_go_prev": { "message": "Previous search result" }, "filter_search_go_next": { "message": "Next search result" }, - "button_filter_type": { - "message": "Search titles or urls" - }, "button_bookmarks": { "message": "Unsorted bookmarks" }, @@ -53,6 +55,59 @@ "button_discard": { "message": "Unload tabs" }, + "button_filter_type": { + "message": "Search titles or urls" + }, + + + + + + "button_groups_toolbar_hide": { + "message": "Hide/Show Groups toolbar" + }, + "button_new_group": { + "message": "New group" + }, + "button_remove_group": { + "message": "Remove group.\nHold shift key to close tabs from this group" + }, + "button_edit_group": { + "message": "Edit group" + }, + + "button_import_group": { + "message": "Import group" + }, + "button_export_group": { + "message": "Export group" + }, + + "button_backup": { + "message": "Session" + }, + "button_import_bak": { + "message": "Import session" + }, + "button_import_merge_bak": { + "message": "Import and merge session.\nImporter will try to match current tabs with those from saved session, instead of opening a new window." + }, + "button_export_bak": { + "message": "Export session" + }, + "button_load_bak1": { + "message": "EMERGENCY if lost groupings: Load latest internal backup (autosave is made every 5 minutes)" + }, + "button_load_bak2": { + "message": "EMERGENCY if lost groupings: Load previous to latest internal backup (autosave is made every 10 minutes)" + }, + "button_load_bak3": { + "message": "EMERGENCY if lost groupings: Load oldest internal backup (autosave is made every 30 minutes)" + }, + + + + "tabs_menu_expand_all": { "message": "Expand all trees" }, @@ -89,6 +144,9 @@ "tabs_menu_unpin": { "message": "Unpin" }, + "tabs_menu_close_tree": { + "message": "Close tree" + }, "tabs_menu_close": { "message": "Close" }, @@ -104,6 +162,14 @@ "tabs_menu_discard": { "message": "Unload" }, + + + + + + + + "options_vivaldi": { "message": " Vivaldi " }, @@ -122,8 +188,11 @@ "options_tabs": { "message": " Tabs " }, - "options_skip_load": { - "message": "discard tree structure after browser's restart, this option is for those who don't use browser's session. Basically it disables loading database at startup." + "options_syncro_tabbar_tabs_order": { + "message": "synchronise TabBar tabs order with Tree Tabs (with too many tabs, after moving tabs, Tree Tabs may be unresponsive for a second)" + }, + "options_switch_with_scroll": { + "message": "switch tabs with mouse wheel" }, "options_close_with_MMB": { "message": "close tabs with middle mouse button" @@ -131,18 +200,22 @@ "options_always_show_close": { "message": "show close button on all tabs" }, + "options_never_show_close": { + "message": "don't show close button (option above will be ignored)" + }, "options_close_other_trees": { "message": "automatically collapse other trees on expand" }, - "options_promote_children": { - "message": "promote children tabs on close, if disabled, when closing the parent of a tree structure, all tabs will be closed (be careful, because undo close tab will not recover the trees structure)" - }, "options_open_tree_on_hover": { "message": "auto expand collapsed trees when dragging and holding for a second over them" }, - "options_max_tree_depth": { - "message": "maximum tree depth: set it to -1 for unlimited branches, 0 for flat tabs placement (no trees), any number above 0 will be its maximum" + "options_promote_children": { + "message": "promote children tabs on close, if disabled, when closing the parent of a tree structure, all tabs will be closed (be careful, because undo close tab will not recover the trees structure)" }, + "options_skip_load": { + "message": "discard tree structure after browser's restart, this option is for those who don't use browser's session. Basically it disables loading database at startup." + }, + "options_append_child_tab": { "message": "append children tabs at the" }, @@ -152,20 +225,9 @@ "options_append_child_tab_bottom": { "message": "bottom" }, - "options_append_child_tab_after_limit": { - "message": "once reached tree depth, place tab on the same level, but" - }, - "options_append_child_tab_after_limit_top": { - "message": "at the top" - }, - "options_append_child_tab_after_limit_after": { - "message": "after parent" - }, - "options_append_child_tab_after_limit_bottom": { - "message": "at the bottom" - }, + "options_append_orphan_tab": { - "message": "append orphan tabs (opened from +, shortcut or bookmark)" + "message": "append orphan tabs (new tabs, tabs opened from shortcuts or from bookmarks)" }, "options_append_orphan_tab_top": { "message": "at the top" @@ -179,6 +241,7 @@ "options_append_orphan_tab_as_child": { "message": "treat as active's tab child" }, + "options_after_closing_active_tab": { "message": "after closing active tab," }, @@ -191,50 +254,44 @@ "options_after_closing_active_tab_go_browser": { "message": "let browser handle which tab to activate" }, + + "options_append_child_tab_after_limit": { + "message": "once reached tree depth, place tab on the same level, but" + }, + "options_append_child_tab_after_limit_top": { + "message": "at the top" + }, + "options_append_child_tab_after_limit_after": { + "message": "after parent" + }, + "options_append_child_tab_after_limit_bottom": { + "message": "at the bottom" + }, + "options_max_tree_drag_drop": { + "message": "Limit Drag&Drop to tree's maximum depth as well" + }, + + "options_max_tree_depth": { + "message": "maximum tree depth: set it to -1 for unlimited branches, 0 for flat tabs placement (no trees), any number above 0 will be its maximum" + }, + + + + + + + + + + "options_theme": { "message": "Theme" }, - "options_theme_tabs": { - "message": " Tabs look " - }, - "options_theme_tabs_sample_text_normal": { - "message": "Normal" - }, - "options_theme_tabs_sample_text_active_selected": { - "message": "Active and selected" - }, - "options_theme_tabs_sample_text_discarded": { - "message": "Unloaded (discarded)" - }, - "options_theme_tabs_sample_text_search_result": { - "message": "Search result" - }, - "options_theme_tabs_sample_text_search_result_higlighted": { - "message": "Search result higlighted" - }, - "options_theme_tabs_sample_text_search_result_selected": { - "message": "Search result, selected" - }, - "options_theme_tabs_sample_text_search_result_selected_active": { - "message": "Search result, selected, active" - }, - "options_toolbar": { - "message": " Toolbar " - }, - "options_available_buttons": { - "message": "Drag and drop buttons to arrange them, drop to the green box, buttons you don't want to use" - }, - "options_scrollbars": { - "message": " Scrollbars " - }, - "options_scrollbar_pin_list": { - "message": "pinned tabs bar scrollbar height" - }, - "options_scrollbar_tab_list": { - "message": "tabs list scrollbar width" - }, - "options_tabs_size": { - "message": "Tabs size" + + + + "options_rename_theme_button": { + "message": "Rename" }, "options_add_theme_button": { "message": "Add new" @@ -242,15 +299,216 @@ "options_remove_theme_button": { "message": "Remove" }, - "options_export_theme_button": { - "message": "Export" - }, "options_import_theme_button": { "message": "Import" }, - "options_rename_theme_button": { - "message": "Rename" + "options_export_theme_button": { + "message": "Export" }, + + + + + + + + "options_toolbar": { + "message": " Toolbar " + }, + "options_available_buttons": { + "message": "Drag and drop buttons to arrange them, drop to the green box, buttons you don't want to use" + }, + + "options_reset_toolbar_button": { + "message": "Reset toolbar" + }, + + + + + + + "options_theme_tabs": { + "message": " Tabs look " + }, + + + + + + + "options_theme_tabs_sample_text_normal": { + "message": "Normal" + }, + "options_theme_tabs_sample_text_normal_hover": { + "message": "Normal, mouse hover over" + }, + "options_theme_tabs_sample_text_normal_selected": { + "message": "Normal selected" + }, + "options_theme_tabs_sample_text_normal_selected_hover": { + "message": "Normal selected, mouse hover over" + }, + "options_theme_tabs_sample_text_active": { + "message": "Active" + }, + "options_theme_tabs_sample_text_active_hover": { + "message": "Active, mouse hover over" + }, + "options_theme_tabs_sample_text_active_selected": { + "message": "Active and selected" + }, + "options_theme_tabs_sample_text_active_selected_hover": { + "message": "Active and selected, mouse hover over" + }, + + "options_theme_tabs_sample_text_discarded": { + "message": "Unloaded (discarded)" + }, + "options_theme_tabs_sample_text_discarded_hover": { + "message": "Unloaded, mouse hover over" + }, + "options_theme_tabs_sample_text_discarded_selected": { + "message": "Unloaded and selected" + }, + "options_theme_tabs_sample_text_discarded_selected_hover": { + "message": "Unloaded and selected, mouse hover over" + }, + + "options_theme_tabs_sample_text_search_result": { + "message": "Search result" + }, + "options_theme_tabs_sample_text_search_result_hover": { + "message": "Search result, mouse hover over" + }, + "options_theme_tabs_sample_text_search_result_active": { + "message": "Search result active" + }, + "options_theme_tabs_sample_text_search_result_active_hover": { + "message": "Search result active, mouse hover over" + }, + + "options_theme_tabs_sample_text_search_result_selected": { + "message": "Search result selected" + }, + "options_theme_tabs_sample_text_search_result_selected_hover": { + "message": "Search result selected, mouse hover over" + }, + "options_theme_tabs_sample_text_search_result_selected_active": { + "message": "Search result selected, active" + }, + "options_theme_tabs_sample_text_search_result_selected_active_hover": { + "message": "Search result selected, active, mouse hover over" + }, + + + + "options_theme_tabs_sample_text_search_result_highlighted": { + "message": "Search result highlighted" + }, + "options_theme_tabs_sample_text_search_result_highlighted_hover": { + "message": "Search result highlighted, mouse hover over" + }, + "options_theme_tabs_sample_text_search_result_highlighted_active": { + "message": "Search result highlighted, active" + }, + "options_theme_tabs_sample_text_search_result_highlighted_active_hover": { + "message": "Search result highlighted, active, mouse hover over" + }, + + + + + + "options_theme_tabs_sample_text_search_result_highlighted_selected": { + "message": "Search result highlighted, selected" + }, + "options_theme_tabs_sample_text_search_result_highlighted_selected_hover": { + "message": "Search result highlighted, selected, mouse hover over" + }, + "options_theme_tabs_sample_text_search_result_highlighted_selected_active": { + "message": "Search result highlighted, selected, active" + }, + "options_theme_tabs_sample_text_search_result_highlighted_selected_active_hover": { + "message": "Search result highlighted, selected, active, mouse hover over" + }, + + + + + + + + + + + "options_color_pick_filter_clear_icon": { + "message": "Clear search result x button color" + }, + + "options_color_pick_hover": { + "message": "On hover" + }, + "options_color_pick_border": { + "message": "Border color" + }, + "options_color_pick_background": { + "message": "Fill color" + }, + "options_color_pick_font": { + "message": "Font color" + }, + + "options_tabs_indentation_down": { + "message": "Decrease tabs indentation" + }, + "options_tabs_indentation_up": { + "message": "Increase tabs indentation" + }, + + "options_tabs_roundness_down": { + "message": "Make tabs corners more square" + }, + "options_tabs_roundness_up": { + "message": "Make tabs rounder" + }, + + "options_tabs_size_down": { + "message": "Decrease tabs size" + }, + "options_tabs_size_up": { + "message": "Increase tabs size" + }, + + + + + + + + + + "options_tab_list_scrollbar_width_down": { + "message": "Decrease scrollbars width" + }, + "options_tab_list_scrollbar_width_up": { + "message": "Increase scrollbars width" + }, + "options_tab_list_scrollbar_height_down": { + "message": "Decrease scrollbars height" + }, + "options_tab_list_scrollbar_height_up": { + "message": "Increase scrollbars height" + }, + + + + + + + + + "options_there_is_a_theme_with_this_name": { "message": "Theme with this name already exists, try a new name" }, @@ -266,160 +524,70 @@ "options_loaded_theme_newer_version": { "message": "Looks like loaded theme was saved in a newer version of the extension, can't load!" }, - "options_color_theme_toolbar_background": { - "message": " toolbar background" - }, - "options_color_toolbar_border_bottom": { - "message": " toolbar borders" - }, - "options_color_button_icons": { - "message": " button icon" - }, - "options_color_button_border": { - "message": " button borders" - }, - "options_color_button_background": { - "message": " button background" - }, - "options_color_button_hover_border": { - "message": " button hover border" - }, - "options_color_button_hover_background": { - "message": " button hover background" - }, - "options_color_filter_box_background": { - "message": " searchbox background" - }, - "options_color_filter_box_border": { - "message": " searchbox borders" - }, - "options_color_filter_box_font": { - "message": " searchbox font" - }, - "options_color_filter_clear_icon": { - "message": " searchbox clear button" - }, - "options_color_pin_list_border_bottom": { - "message": " pin list separator line" - }, - "options_color_pin_list_background": { - "message": " pin list background" - }, - "options_color_tab_list_background": { - "message": " tab list background" - }, - "options_color_tab_background": { - "message": " tab background" - }, - "options_color_tab_border": { - "message": " tab border" - }, - "options_color_tab_hover_background": { - "message": " hover over tab, background" - }, + + + + + + + + - "options_color_tab_hover_border": { - "message": " hover over tab, border" - }, - "options_color_drag_indicator": { - "message": " drag&drop indicator" - }, - "options_color_tab_title": { - "message": " tab title font" - }, - "options_color_tab_title_active": { - "message": " active tab title font" - }, - "options_color_tab_title_discarded": { - "message": " unloaded tab title font" - }, - "options_color_tab_selected_background": { - "message": " selected tab background" - }, - "options_color_tab_selected_border": { - "message": " active/selected tab border" - }, - "options_color_tab_selected_hover_border": { - "message": " hover over active/selected tab border" - }, - "options_color_tab_selected_hover_background": { - "message": " hover over active/selected tab background" - }, - "options_color_tab_filtered": { - "message": " search result" - }, - "options_color_tab_filtered_highlighted": { - "message": " search result highlighted" - }, - "options_color_tab_filtered_selected": { - "message": " search result selected" - }, - "options_color_tab_filtered_selected_active": { - "message": " search result selected active" - }, - "options_color_close_x": { - "message": " close button (x)" - }, - "options_color_close_hover_x": { - "message": " hover over close button, x color" - }, - "options_color_close_hover_border": { - "message": " hover over close button, border" - }, - "options_color_close_hover_background": { - "message": " hover over close button, background" - }, - "options_color_expand_open_border": { - "message": " open tab expand box, border" - }, - "options_color_expand_open_background": { - "message": " open tab expand box, background" - }, - "options_color_expand_closed_border": { - "message": " closed tab expand box, border" - }, - "options_color_expand_closed_background": { - "message": " closed tab expand box, background" - }, - "options_color_expand_lines": { - "message": " tree hierarchy lines" - }, - "options_color_scrollbar_thumb": { - "message": " scrollbar thumb" - }, - "options_color_scrollbar_thumb_hover": { - "message": " scrollbar thumb hover" - }, - "options_color_scrollbar_track": { - "message": " scrollbar background" - }, - "options_color_tabs_menu_background": { - "message": " background" - }, - "options_color_tabs_menu_border": { - "message": " border" - }, - "options_color_tabs_menu_hover_background": { - "message": " hover over item, background" - }, - "options_color_tabs_menu_hover_border": { - "message": " hover over item, border" - }, - "options_color_tabs_menu_font": { - "message": " font" - }, - "options_color_tabs_menu_separator": { - "message": " separator" - }, "options_example_menu_item": { "message": "menu item" }, "options_menu": { "message": " Menu " }, - "options_active_tab_font_bold": { - "message": "bold font for active tab" + + + + + + + "options_vivaldi_copied_url": { + "message": "Web Panel Url has been copied to clipboard, add a new Web Panel and paste url." }, + "options_copied_wallet_address": { + "message": "Wallet address has been copied to clipboard" + }, + + + "options_clear_data": { + "message": "Sidebar is not loading? Clear everything! ATTENTION! Tabs arrangement will be lost as well!" + }, + + + + + + + + + + + + "group_edit_button_cancel": { + "message": "Cancel" + }, + + "group_edit_button_confirm": { + "message": "Ok" + }, + + + + + + + + "caption_ungrouped_group": { + "message": "Ungrouped tabs" + }, + "caption_noname_group": { + "message": "untitled" + }, + "caption_clear_filter": { "message": "Clear search results" }, @@ -428,14 +596,9 @@ }, "caption_searchbox": { "message": " Search tabs..." - }, - "options_never_show_close": { - "message": "don't show close button (option above will be ignored)" - }, - "tabs_menu_close_tree": { - "message": "Close tree" - }, - "options_faster_scroll": { - "message": "Faster scrolling" } + + + + } diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index 89e69aa..395a78f 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -350,7 +350,7 @@ "options_theme_tabs_sample_text_search_result": { "message": "Результат поиска" }, - "options_theme_tabs_sample_text_search_result_higlighted": { + "options_theme_tabs_sample_text_search_result_highlighted": { "message": "Выделенный результат поиска" }, "options_theme_tabs_sample_text_search_result_selected": { @@ -427,5 +427,14 @@ }, "options_active_tab_font_bold": { "message": "жирный шрифт для активной вкладки" + }, + "options_faster_scroll": { + "message": "Быстрая прокрутка" + }, + "options_never_show_close": { + "message": "не показывать кнопку закрытия (опция выше будет проигнорирована)" + }, + "tabs_menu_close_tree": { + "message": "Закрыть дерево" } } diff --git a/background.html b/background.html new file mode 100644 index 0000000..44d782d --- /dev/null +++ b/background.html @@ -0,0 +1,8 @@ + + +
+ + + + + \ No newline at end of file diff --git a/background.js b/background.js deleted file mode 100644 index b8458bb..0000000 --- a/background.js +++ /dev/null @@ -1,292 +0,0 @@ -var opt = { - "skip_load": false, "new_open_below": false, "pin_list_multi_row": false, "close_with_MMB": true, - "always_show_close": false, "allow_pin_close": false, - "append_child_tab": "bottom", "append_child_tab_after_limit": "after", - "append_orphan_tab": "bottom", "after_closing_active_tab": "below", "close_other_trees": false, - "promote_children": true, "open_tree_on_hover": true, "max_tree_depth": -1, "never_show_close": false, "faster_scroll": false -}; -var opt_toolbar = { - "active_toolbar_tool": "", "filter_type": "url" -}; - -var hold = true, - started = false, - schedule_save = 0, - - tabs = {}, - - dt = {tabsIds: [], DropAfter: true, DropToTabId: 0, DropToIndex: 0, CameFromWindowId: 0, DroppedToWindowId: 0}, - - caption_clear_filter = chrome.i18n.getMessage("caption_clear_filter"), - caption_loading = chrome.i18n.getMessage("caption_loading"), - caption_searchbox = chrome.i18n.getMessage("caption_searchbox"); - -function Start(){ - started = true; - - // open options to set defaults - if (localStorage.getItem("themeDefault") === null){ - chrome.tabs.create({url: "options.html" }); - } - - // all variables needed to load data - var loaded_options = {}, loaded_opt_toolbar = {}; - - // set loaded options - if (localStorage.getItem("current_options") !== null){ - loaded_options = JSON.parse(localStorage["current_options"]); - } - for (var parameter in opt) { - if (loaded_options[parameter] != undefined && opt[parameter] != undefined){ - opt[parameter] = loaded_options[parameter]; - } - } - - // toolbar shelfs options (search url-title and which shelf is active) - if (localStorage.getItem("current_toolbar_options") !== null){ - loaded_opt_toolbar = JSON.parse(localStorage["current_toolbar_options"]); - } - for (var parameter in opt_toolbar) { - if (loaded_opt_toolbar[parameter] != undefined && opt_toolbar[parameter] != undefined){ - opt_toolbar[parameter] = loaded_opt_toolbar[parameter]; - } - } - - - LoadTabs(0); -} - - - -function LoadTabs(retry){ - chrome.tabs.query({windowType: "normal"}, function(qtabs){ - - // will loop forever if session restore tab is found - if (navigator.userAgent.match("Firefox") !== null){ - var halt = false; - for (var t = 0; t < qtabs.length; t++){ - if (qtabs[t].url.match("sessionrestore")){ - halt = true; - chrome.tabs.update(qtabs[t].id, { active: true }); - break; - } - } - if (halt){ - setTimeout(function(){ - LoadTabs(retry); - }, 2000); - return; - } - } - - // create current tabs object - qtabs.forEach(function(Tab){ - HashTab(Tab); - }); - - var reference_tabs = {}; - var tabs_matched = 0; - - // compare saved tabs from storage to current session tabs, but can be skipped if set in options - if (opt.skip_load == false){ - qtabs.forEach(function(Tab){ - for (var t = 0; t < 9999; t++){ - if (localStorage.getItem("t"+t) !== null){ - var LoadedTab = JSON.parse(localStorage["t"+t]); - if (LoadedTab[1] === tabs[Tab.id].h && reference_tabs[LoadedTab[0]] == undefined){ - reference_tabs[LoadedTab[0]] = Tab.id; - tabs[Tab.id].p = LoadedTab[2]; - tabs[Tab.id].n = LoadedTab[3]; - tabs[Tab.id].o = LoadedTab[4]; - tabs_matched++; - break; - } - - } else { - break; - } - - } - }); - - // replace parents tabIds to new ones, for that purpose reference_tabs was made before - for (var tabId in tabs){ - if (reference_tabs[tabs[tabId].p] != undefined){ - tabs[tabId].p = reference_tabs[tabs[tabId].p]; - } - } - } - - - // will try to find tabs for 10 times, roughly 30 seconds - if (opt.skip_load == true || retry > 10 || localStorage.getItem("t0") === null || localStorage.getItem("t_count") === null || (tabs_matched > JSON.parse(localStorage["t_count"]))){ - hold = false; - StartChromeListeners(); - PeriodicCheck(); - AutoSaveData(); - } else { - setTimeout(function(){ - LoadTabs(retry+1); - }, 3000); - } - }); -} - -// once a minute checking for missing tabs -function PeriodicCheck(){ - setTimeout(function(){ - PeriodicCheck(); - if (!hold){ - chrome.tabs.query({windowType: "normal"}, function(qtabs){ - qtabs.forEach(function(Tab){ - if (tabs[Tab.id] == undefined){ - HashTab(Tab); - setTimeout(function(){ - chrome.runtime.sendMessage({command: "recheck_tabs"}); - },300); - setTimeout(function(){ - schedule_save++; - },600); - } - }); - }); - } - },60000); -} - -// save every 2 seconds if there is anything to save obviously -function AutoSaveData(){ - setTimeout(function(){ - AutoSaveData(); - if (schedule_save > 0){ - schedule_save = 1; - } - if (!hold && schedule_save > 0 && Object.keys(tabs).length > 1){ - chrome.tabs.query({windowType: "normal"}, function(qtabs){ - localStorage["t_count"] = qtabs.length*0.5; - for (var t = 0; t < qtabs.length; t++){ - if (tabs[qtabs[t].id] != undefined && tabs[qtabs[t].id].h != undefined && tabs[qtabs[t].id].p != undefined && tabs[qtabs[t].id].n != undefined && tabs[qtabs[t].id].o != undefined){ - var Tab = JSON.stringify([qtabs[t].id, tabs[qtabs[t].id].h, tabs[qtabs[t].id].p, tabs[qtabs[t].id].n, tabs[qtabs[t].id].o]); - if (localStorage.getItem("t"+t) == null || localStorage["t"+t] !== Tab){ - localStorage["t"+t] = Tab; - } - } - } - schedule_save--; - }); - } - }, 1000); -} - -function SaveOptions(){ - localStorage["current_options"] = JSON.stringify(opt); -} -function SaveToolbarOptions(){ - localStorage["current_toolbar_options"] = JSON.stringify(opt_toolbar); -} - -function HashTab(tab){ - if (tabs[tab.id] == undefined){ - tabs[tab.id] = {h: 0, p: tab.pinned ? "pin_list" : "tab_list", n: tab.index, o: "n"}; - } - var hash = 0; - if (tab.url.length === 0){ - return 0; - } - for (var i = 0; i < tab.url.length; i++){ - hash = (hash << 5)-hash; - hash = hash+tab.url.charCodeAt(i); - hash |= 0; - } - tabs[tab.id].h = hash; -} - -// start all listeners -function StartChromeListeners(){ - chrome.tabs.onCreated.addListener(function(tab){ - HashTab(tab); - chrome.runtime.sendMessage({command: "tab_created", windowId: tab.windowId, tab: tab, tabId: tab.id}); - schedule_save++; - }); - chrome.tabs.onAttached.addListener(function(tabId, attachInfo){ - chrome.tabs.get(tabId, function(tab){ - if (tabs[tabId] == undefined){ HashTab(tab); } - chrome.runtime.sendMessage({command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tabId}); - }); - schedule_save++; - }); - chrome.tabs.onRemoved.addListener(function(tabId, removeInfo){ - chrome.runtime.sendMessage({command: "tab_removed", windowId: removeInfo.windowId, tabId: tabId}); - delete tabs[tabId]; - schedule_save++; - }); - chrome.tabs.onDetached.addListener(function(tabId, detachInfo){ - if (tabs[tabId] == undefined){ HashTab(tab); } - chrome.runtime.sendMessage({command: "tab_removed", windowId: detachInfo.oldWindowId, tabId: tabId}); - schedule_save++; - }); - chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){ - if (tabs[tabId] == undefined || changeInfo.url != undefined){ HashTab(tab); } - if (changeInfo.pinned == true){ tabs[tabId].p = "pin_list"; } - if (changeInfo.pinned == false){ tabs[tabId].p = "tab_list"; } - chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tabId, changeInfo: changeInfo}); - if (changeInfo.url != undefined || changeInfo.pinned != undefined){schedule_save++;} - }); - chrome.tabs.onMoved.addListener(function(tabId, moveInfo){ - if (tabs[tabId] == undefined){ HashTab(tab); } - chrome.runtime.sendMessage({command: "tab_moved", windowId: moveInfo.windowId, tabId: tabId, moveInfo: moveInfo}); - schedule_save++; - }); - chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId){ - chrome.tabs.get(addedTabId, function(tab){ - if (addedTabId == removedTabId){ - chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tab.id, changeInfo: {status: tab.status, url: tab.url, title: tab.title, audible: tab.audible, mutedInfo: tab.mutedInfo}}); - } else { - if (tabs[removedTabId]){ - tabs[addedTabId] = {h: GetHash(tab.url), p: tabs[removedTabId].p, n: tabs[removedTabId].n, o: tabs[removedTabId].o}; - } else { - HashTab(tab); - } - chrome.runtime.sendMessage({command: "tab_removed", windowId: tab.windowId, tabId: removedTabId}); - chrome.runtime.sendMessage({command: "tab_attached", windowId: tab.windowId, tab: tab, tabId: addedTabId}); - delete tabs[removedTabId]; - } - schedule_save++; - }); - }); - chrome.tabs.onActivated.addListener(function(activeInfo){ - chrome.runtime.sendMessage({command: "tab_activated", windowId: activeInfo.windowId, tabId: activeInfo.tabId}); - }); -} - -chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){ - switch(message.command){ - case "background_start": - if (!started){ - Start(); - } - break; - case "reload": - window.location.reload(); - break; - case "options_save": - SaveOptions(); - break; - case "toolbar_options_save": - SaveToolbarOptions(); - break; - } -}); -function log(m){ - console.log(m); -} -chrome.runtime.onStartup.addListener(function(){ - Start(); -}); - -if (navigator.userAgent.match("Firefox") === null){ - chrome.runtime.onSuspend.addListener(function(){ - hold = true; - }); -} - diff --git a/bg_ch.js b/bg_ch.js new file mode 100644 index 0000000..1325714 --- /dev/null +++ b/bg_ch.js @@ -0,0 +1,341 @@ +// Copyright (c) 2017 kroppy. All rights reserved. +// Use of this source code is governed by a Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license +// that can be found at https://creativecommons.org/licenses/by-nc-nd/4.0/ + +if (localStorage.getItem("t0") !== null){ + LoadV015(0); +} else { + LoadPreferences(); + ChromeLoadTabs(0); + ChromeMessageListeners(); +} + + +function ChromeLoadTabs(retry) { + chrome.windows.getAll({windowTypes: ['normal'], populate: true}, function(w) { + + var refTabs = {}; + var tabs_matched = 0; + + // load tabs and windows from hdd + var w_count = LoadData("w_count", 0); + var t_count = LoadData("t_count", 0); + var LoadedWindows = LoadData("windows", []); + var LoadedTabs = LoadData("tabs", []); + + // if loaded tabs mismatch by 50%, then try to load back + if (LoadedTabs.length < t_count*0.5 || retry > 0) { + LoadedTabs = LoadData("tabs_BAK"+retry, []); + } + // if loaded windows mismatch, then try to load back + if (LoadedWindows.length < w_count || retry > 0) { + LoadedWindows = LoadData("windows_BAK"+retry, []); + } + + // CACHED COUNTS + var WinCount = w.length; + var LoadedWinCount = LoadedWindows.length; + var LoadedTabsCount = LoadedTabs.length; + + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + if (w[wIndex].tabs[0].url != "chrome://videopopout/") { // this is for opera for their extra video popup, which is weirdly queried as a "normal" window + let winId = w[wIndex].id; + let url1 = w[wIndex].tabs[0].url; + let url2 = w[wIndex].tabs[w[wIndex].tabs.length-1].url; + windows[winId] = {group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + for (var LwIndex = 0; LwIndex < LoadedWinCount; LwIndex++) { + if (LoadedWindows[LwIndex].url1 == url1 || LoadedWindows[LwIndex].url2 == url2) { + windows[winId].group_bar = LoadedWindows[LwIndex].group_bar; + windows[winId].active_shelf = LoadedWindows[LwIndex].active_shelf; + windows[winId].active_group = LoadedWindows[LwIndex].active_group; + if (Object.keys(LoadedWindows[LwIndex].groups).length > 0) { windows[winId].groups = Object.assign({}, LoadedWindows[LwIndex].groups); } + if (Object.keys(LoadedWindows[LwIndex].folders).length > 0) { windows[winId].folders = Object.assign({}, LoadedWindows[LwIndex].folders); } + LoadedWindows[LwIndex].url1 = ""; + LoadedWindows[LwIndex].url2 = ""; + break; + } + } + } + } + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + var TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + ChromeHashURL(w[wIndex].tabs[tabIndex]); + } + } + + // compare saved tabs from storage to current session tabs, but can be skipped if set in options + if (opt.skip_load == false && LoadedTabs.length > 0) { + // match loaded tabs + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + var TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + for (var LtabIndex = 0; LtabIndex < LoadedTabsCount; LtabIndex++) { + let tabId = w[wIndex].tabs[tabIndex].id; + if (LoadedTabs[LtabIndex].hash == tabs[tabId].hash && refTabs[LoadedTabs[LtabIndex].id] == undefined) { + refTabs[LoadedTabs[LtabIndex].id] = tabId; + tabs[tabId].parent = LoadedTabs[LtabIndex].parent; + tabs[tabId].index = LoadedTabs[LtabIndex].index; + tabs[tabId].expand = LoadedTabs[LtabIndex].expand; + LoadedTabs[LtabIndex].hash = undefined; + tabs_matched++; + break; + } + } + } + } + + // replace parents tabIds for new ones, for that purpose refTabs was made before + for (var tabId in tabs) { + if (refTabs[tabs[tabId].parent] != undefined) { + tabs[tabId].parent = refTabs[tabs[tabId].parent]; + } + } + } + + + // replace active tab ids for each group using refTabs + for (var windowId in windows) { + for (var group in windows[windowId].groups) { + if (refTabs[windows[windowId].groups[group].activetab]) { + windows[windowId].groups[group].activetab = refTabs[windows[windowId].groups[group].activetab]; + } + } + } + + + + + // will try to find tabs for 3 times + if (opt.skip_load == true || retry > 2 || (tabs_matched > t_count*0.5)) { + schedule_save++; + hold = false; + ChromeAutoSaveData("", 1000); + ChromeAutoSaveData("_BAK1", 300000); + ChromeAutoSaveData("_BAK2", 600000); + ChromeAutoSaveData("_BAK3", 1800000); + ChromeListeners(); + } else { + setTimeout(function() {ChromeLoadTabs(retry+1);}, 2000); + } + }); +} + +// You maybe are asking yourself why I save tabs in array? It's because, instead of, keeping 2 index numbers (one for browser tabs on top and one for my index in tree), it's easier to just arrange them in order and save it in localstorage. +// Another reason is that Object does not preserve order in chrome, I've been told that in Firefox it is. But I can't trust that. +async function ChromeAutoSaveData(BackupName, LoopTimer) { + setTimeout(function() { + ChromeAutoSaveData(BackupName, LoopTimer); + if (schedule_save > 1 || BackupName != "") {schedule_save = 1;} + if (!hold && schedule_save > 0 && Object.keys(tabs).length > 1) { + chrome.windows.getAll({windowTypes: ['normal'], populate: true}, function(w) { + var WinCount = w.length; + var t_count = 0; + var counter = 0; + var Windows = []; + var Tabs = []; + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + t_count += w[wIndex].tabs.length; + } + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + let winId = w[wIndex].id; + if (windows[winId] != undefined && windows[winId].group_bar != undefined && windows[winId].active_shelf != undefined && windows[winId].active_group != undefined && windows[winId].groups != undefined && windows[winId].folders != undefined) { + Windows.push({url1: w[wIndex].tabs[0].url, url2: w[wIndex].tabs[w[wIndex].tabs.length-1].url, group_bar: windows[winId].group_bar, active_shelf: windows[winId].active_shelf, active_group: windows[winId].active_group, groups: windows[winId].groups, folders: windows[winId].folders}); + } + + let TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + let tabId = w[wIndex].tabs[tabIndex].id; + if (tabs[tabId] != undefined && tabs[tabId].hash != undefined && tabs[tabId].parent != undefined && tabs[tabId].index != undefined && tabs[tabId].expand != undefined) { + Tabs.push({id: tabId, hash: tabs[tabId].hash, parent: tabs[tabId].parent, index: tabs[tabId].index, expand: tabs[tabId].expand}); + counter++; + } + } + if (counter == t_count) { + localStorage["t_count"] = JSON.stringify(t_count); + localStorage["w_count"] = JSON.stringify(WinCount); + localStorage["windows"+BackupName] = JSON.stringify(Windows); + localStorage["tabs"+BackupName] = JSON.stringify(Tabs); + } + } + schedule_save--; + }); + } + }, LoopTimer); +} + +function ChromeHashURL(tab){ + if (tabs[tab.id] == undefined) { + tabs[tab.id] = {hash: 0, parent: tab.pinned ? "pin_list" : "tab_list", index: tab.index, expand: "n"}; + } + var hash = 0; + for (var charIndex = 0; charIndex < tab.url.length; charIndex++){ + hash += tab.url.charCodeAt(charIndex); + } + tabs[tab.id].hash = hash; +} + +function ReplaceParents(oldTabId, newTabId) { + for (var tabId in tabs) { + if (tabs[tabId].parent == oldTabId) { + tabs[tabId].parent = newTabId; + } + } + + // TO DO FOLDERS +} + +// start all listeners +function ChromeListeners() { + chrome.tabs.onCreated.addListener(function(tab) { + ChromeHashURL(tab); + chrome.runtime.sendMessage({command: "tab_created", windowId: tab.windowId, tab: tab, tabId: tab.id}); + schedule_save++; + }); + + chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) { + chrome.runtime.sendMessage({command: "tab_removed", windowId: removeInfo.windowId, tabId: tabId}); + delete tabs[tabId]; + schedule_save++; + }); + + chrome.tabs.onAttached.addListener(function(tabId, attachInfo) { + chrome.tabs.get(tabId, function(tab) { + chrome.runtime.sendMessage({command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tabId, ParentId: tabs[tabId].parent}); + }); + schedule_save++; + }); + + chrome.tabs.onDetached.addListener(function(tabId, detachInfo) { + chrome.runtime.sendMessage({command: "tab_detached", windowId: detachInfo.oldWindowId, tabId: tabId}); + schedule_save++; + }); + + chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (tabs[tabId] == undefined || changeInfo.url != undefined) { + ChromeHashURL(tab); + } + if (changeInfo.pinned != undefined) { + schedule_save++; + } + if (changeInfo.pinned == true) { + tabs[tabId].parent = "pin_list"; + } + if (changeInfo.title != undefined && !tab.active) { + chrome.runtime.sendMessage({command: "tab_attention", windowId: tab.windowId, tabId: tabId}); + } + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tabId, changeInfo: changeInfo}); + }); + + chrome.tabs.onMoved.addListener(function(tabId, moveInfo) { + schedule_save++; + }); + + chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) { + chrome.tabs.get(addedTabId, function(tab) { + if (addedTabId == removedTabId) { + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tab.id, changeInfo: {status: tab.status, url: tab.url, title: tab.title, audible: tab.audible, mutedInfo: tab.mutedInfo}}); + } else { + ReplaceParents(tabId, tab.id); + if (tabs[removedTabId]) { + tabs[addedTabId] = tabs[removedTabId]; + } else { + ChromeHashURL(tab); + } + chrome.runtime.sendMessage({command: "tab_removed", windowId: tab.windowId, tabId: removedTabId}); + chrome.runtime.sendMessage({command: "tab_attached", windowId: tab.windowId, tab: tab, tabId: addedTabId}); + delete tabs[removedTabId]; + } + schedule_save++; + }); + }); + + chrome.tabs.onActivated.addListener(function(activeInfo) { + chrome.runtime.sendMessage({command: "tab_activated", windowId: activeInfo.windowId, tabId: activeInfo.tabId}); + }); + + chrome.windows.onCreated.addListener(function(window) { + windows[window.id] = {group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + schedule_save++; + }); + + chrome.windows.onRemoved.addListener(function(windowId) { + delete windows[windowId]; + schedule_save++; + }); + + chrome.runtime.onSuspend.addListener(function() { + hold = true; + }); +} + +function ChromeMessageListeners() { + chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { + switch(message.command) { + case "reload": + window.location.reload(); + break; + case "get_windows": + sendResponse(windows); + break; + case "get_groups": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].groups); + } + break; + case "save_groups": + windows[message.windowId].groups = Object.assign({}, message.groups); + schedule_save++; + break; + + case "set_active_group": + windows[message.windowId].active_group = message.active_group; + schedule_save++; + break; + case "get_active_group": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_group); + } + break; + case "set_active_shelf": + windows[message.windowId].active_shelf = message.active_shelf; + schedule_save++; + break; + case "get_active_shelf": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_shelf); + } + break; + case "set_group_bar": + windows[message.windowId].group_bar = message.group_bar; + schedule_save++; + break; + case "get_group_bar": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].group_bar); + } + break; + case "console_log": + console.log(message.m); + break; + case "get_browser_tabs": + sendResponse(tabs); + break; + case "is_bg_busy": + sendResponse(hold); + break; + case "update_tab": + if (tabs[message.tabId]) { + for (var parameter in message.tab) { + tabs[message.tabId][parameter] = message.tab[parameter]; + } + schedule_save++; + } + break; + } + }); +} diff --git a/bg_ff.js b/bg_ff.js new file mode 100644 index 0000000..aa21d86 --- /dev/null +++ b/bg_ff.js @@ -0,0 +1,433 @@ +// Copyright (c) 2017 kroppy. All rights reserved. +// Use of this source code is governed by a Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license +// that can be found at https://creativecommons.org/licenses/by-nc-nd/4.0/ + +if (localStorage.getItem("t0") !== null){ + LoadV015(0); +} else { + LoadPreferences(); + FirefoxStart(); + FirefoxMessageListeners(); +} + +function FirefoxStart() { + var SafeToRun = true; + chrome.tabs.query({windowType: "normal"}, function(t) { + // will loop forever if session restore tab is found + for (var tabIndex = 0; tabIndex < t.length; tabIndex++) { + if (t[tabIndex].url.match("about:sessionrestore") && t.length < 5) { + SafeToRun = false; + chrome.tabs.update(t[tabIndex].id, { active: true }); + } + if (tabIndex == t.length-1) { + if (SafeToRun) { + FirefoxLoadTabs(0); + } else { + setTimeout(function() { + FirefoxStart(); + }, 1000); + } + } + } + }); +} + + +function FirefoxLoadTabs(retry) { + chrome.windows.getAll({windowTypes: ["normal"], populate: true}, function(w) { + + var refTabs = {}; + var tabs_matched = 0; + var tabs_count = 0; + for (var wIndex = 0; wIndex < w.length; wIndex++) { + tabs_count += w[wIndex].tabs.length; + } + + // load tabs and windows from hdd + var LoadedWindows = LoadData("windows", []); + var LoadedTabs = LoadData("tabs", []); + + // if loaded tabs mismatch by 50%, then try to load back + if (LoadedTabs.length < tabs_count*0.5 || retry > 0) { + LoadedTabs = LoadData("tabs_BAK"+retry, []); + } + // if loaded windows mismatch, then try to load back + if (LoadedWindows.length < w.length || retry > 0) { + LoadedWindows = LoadData("windows_BAK"+retry, []); + } + + // CACHED COUNTS AND STUFF + var lastWinId = w[w.length-1].id; + var lastTabId = w[w.length-1].tabs[w[w.length-1].tabs.length-1].id; + + var LoadedWinCount = LoadedWindows.length; + var LoadedTabsCount = LoadedTabs.length; + var WinCount = w.length; + + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + + let winIndex = wIndex; + let winId = w[winIndex].id; + let tabsCount = w[winIndex].tabs.length; + + let win = Promise.resolve(browser.sessions.getWindowValue(winId, "TTId")).then(function(TTId) { // LOAD TTID FROM FIREFOX GET WINDOW VALUE + if (TTId != undefined) { + windows[winId] = {ttid: TTId, group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + } else { + windows[winId] = {ttid: "", group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + } + + for (var tIndex = 0; tIndex < tabsCount; tIndex++) { + + let tabIndex = tIndex; + let tabId = w[winIndex].tabs[tabIndex].id; + let tabPinned = w[winIndex].tabs[tabIndex].pinned; + + let tab = Promise.resolve(browser.sessions.getTabValue(tabId, "TTId")).then(function(TTId) { // LOAD TTID FROM FIREFOX GET TAB VALUE + if (TTId != undefined) { + tabs[tabId] = {ttid: TTId, parent: tabPinned ? "pin_list" : "tab_list", index: tabIndex, expand: "n"}; + } else { + tabs[tabId] = {ttid: "", parent: tabPinned ? "pin_list" : "tab_list", index: tabIndex, expand: "n"}; + } + // IF ON LAST TAB AND LAST WINDOW, START MATCHING LOADED DATA + if (tabId == lastTabId && winId == lastWinId) { + for (var ThisSessonWinId in windows) { + if (windows[ThisSessonWinId].ttid != ""){ + for (var LwIndex = 0; LwIndex < LoadedWinCount; LwIndex++) { + if (LoadedWindows[LwIndex].ttid == windows[ThisSessonWinId].ttid) { + windows[ThisSessonWinId].group_bar = LoadedWindows[LwIndex].group_bar; + windows[ThisSessonWinId].active_shelf = LoadedWindows[LwIndex].active_shelf; + windows[ThisSessonWinId].active_group = LoadedWindows[LwIndex].active_group; + if (Object.keys(LoadedWindows[LwIndex].groups).length > 0) { windows[ThisSessonWinId].groups = Object.assign({}, LoadedWindows[LwIndex].groups); } + if (Object.keys(LoadedWindows[LwIndex].folders).length > 0) { windows[ThisSessonWinId].folders = Object.assign({}, LoadedWindows[LwIndex].folders); } + LoadedWindows[LwIndex].ttid = ""; + break; + } + } + } else { + AppendWinTTId(parseInt(ThisSessonWinId)); + } + } + // OK, DONE WITH WINDOWS, START TABS LOOP + for (var ThisSessonTabId in tabs) { + if (tabs[ThisSessonTabId].ttid != ""){ + for (var LtabIndex = 0; LtabIndex < LoadedTabsCount; LtabIndex++) { + if (LoadedTabs[LtabIndex].ttid == tabs[ThisSessonTabId].ttid) { + refTabs[LoadedTabs[LtabIndex].id] = ThisSessonTabId; + tabs[ThisSessonTabId].parent = LoadedTabs[LtabIndex].parent; + tabs[ThisSessonTabId].index = LoadedTabs[LtabIndex].index; + tabs[ThisSessonTabId].expand = LoadedTabs[LtabIndex].expand; + LoadedTabs[LtabIndex].ttid = ""; + tabs_matched++; + break; + } + } + } else { + AppendTabTTId(parseInt(ThisSessonTabId)); + } + } + // OK, DONE, NOW REPLACE OLD PARENTS IDS WITH THIS SESSION IDS + for (var ThisSessonTabId in tabs) { + if (refTabs[tabs[ThisSessonTabId].parent] != undefined) { + tabs[ThisSessonTabId].parent = refTabs[tabs[ThisSessonTabId].parent]; + } + } + // OK, SAME THING FOR ACTIVE TABS IN GROUPS + + for (var ThisSessonWinId in windows) { + for (var group in windows[ThisSessonWinId].groups) { + if (refTabs[windows[ThisSessonWinId].groups[group].activetab]) { + windows[ThisSessonWinId].groups[group].activetab = refTabs[windows[ThisSessonWinId].groups[group].activetab]; + } + } + } + + // TODO + // replace parent tab ids for each folder using reference_tabs, unless tabs will be nested ONLY in tabs and folders ONLY in folders, I did not decide yet + + + // will try to find tabs for 3 times + if (opt.skip_load == true || retry > 2 || (tabs_matched > tabs_count*0.5)) { + hold = false; + FirefoxAutoSaveData("", 1000); + FirefoxAutoSaveData("_BAK1", 300000); + FirefoxAutoSaveData("_BAK2", 600000); + FirefoxAutoSaveData("_BAK3", 1800000); + FirefoxListeners(); + } else { + setTimeout(function() {FirefoxLoadTabs(retry+1);}, 2000); + } + + } + }); + } + }); + } + }); +} + +// save every second if there is anything to save obviously +// async function FirefoxAutoSaveData(BAK, timer) { +async function FirefoxAutoSaveData(BackupName, LoopTimer) { + setTimeout(function() { + FirefoxAutoSaveData(BackupName, LoopTimer); + if (schedule_save > 1 || BackupName != "") {schedule_save = 1;} + if (!hold && schedule_save > 0 && Object.keys(tabs).length > 1) { + chrome.windows.getAll({windowTypes: ['normal'], populate: true}, function(w) { + var WinCount = w.length; + var t_count = 0; + var counter = 0; + var Windows = []; + var Tabs = []; + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + t_count = t_count + w[wIndex].tabs.length; + } + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + let winId = w[wIndex].id; + if (windows[winId] != undefined && windows[winId].ttid != undefined && windows[winId].group_bar != undefined && windows[winId].active_shelf != undefined && windows[winId].active_group != undefined && windows[winId].groups != undefined && windows[winId].folders != undefined) { + Windows.push({ttid: windows[winId].ttid, group_bar: windows[winId].group_bar, active_shelf: windows[winId].active_shelf, active_group: windows[winId].active_group, groups: windows[winId].groups, folders: windows[winId].folders}); + } + + let TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + let tabId = w[wIndex].tabs[tabIndex].id; + if (tabs[tabId] != undefined && tabs[tabId].ttid != undefined && tabs[tabId].parent != undefined && tabs[tabId].index != undefined && tabs[tabId].expand != undefined) { + Tabs.push({id: tabId, ttid: tabs[tabId].ttid, parent: tabs[tabId].parent, index: tabs[tabId].index, expand: tabs[tabId].expand}); + counter++; + } + } + + if (counter == t_count) { + localStorage["t_count"] = JSON.stringify(t_count); + localStorage["w_count"] = JSON.stringify(WinCount); + localStorage["windows"+BackupName] = JSON.stringify(Windows); + localStorage["tabs"+BackupName] = JSON.stringify(Tabs); + } + } + schedule_save--; + }); + } + }, LoopTimer); +} + +function GenerateNewWindowID(){ + var newID = GenerateRandomID(); + var newIdAvailable = true; + for (var windowId in windows) { + if (windows[windowId].ttid == newID) { + newIdAvailable = false; + } + } + if (newIdAvailable) { + return newID; + } else { + GenerateNewWindowID(); + } +} + +function GenerateNewTabID(){ + var newID = GenerateRandomID(); + var newIdAvailable = true; + for (var tabId in tabs) { + if (tabs[tabId].ttid == newID) { + newIdAvailable = false; + } + } + if (newIdAvailable) { + return newID; + } else { + GenerateNewTabID(); + } +} + +function AppendTabTTId(tabId){ + let NewTTTabId = GenerateNewTabID(); + browser.sessions.setTabValue(tabId, "TTId", NewTTTabId); + if (tabs[tabId] != undefined) { + tabs[tabId].ttid = NewTTTabId; + } else { + tabs[tabId] = {ttid: NewTTTabId, parent: "tab_list", index: 0, expand: "n"}; + } +} + +function AppendWinTTId(windowId){ + let NewTTWindowId = GenerateNewWindowID(); + browser.sessions.setWindowValue(windowId, "TTId", NewTTWindowId); + if (windows[windowId] != undefined) { + windows[windowId].ttid = NewTTWindowId; + } else { + windows[windowId] = {ttid: NewTTWindowId, group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + } +} + +function ReplaceParents(oldTabId, newTabId) { + for (var tabId in tabs) { + if (tabs[tabId].parent == oldTabId) { + console.log("replaced tab id "+oldTabId+" with "+newTabId); + tabs[tabId].parent = newTabId; + } + } + + // TO DO FOLDERS +} + +var DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA = {}; + +// start all listeners +function FirefoxListeners() { + + browser.browserAction.onClicked.addListener(function() { + browser.sidebarAction.setPanel({panel: (browser.extension.getURL("/sidebar.html")) }); + browser.sidebarAction.open(); + }); + + chrome.tabs.onCreated.addListener(function(tab) { + AppendTabTTId(tab.id); + chrome.runtime.sendMessage({command: "tab_created", windowId: tab.windowId, tab: tab, tabId: tab.id}); + schedule_save++; + }); + + chrome.tabs.onAttached.addListener(function(tabId, attachInfo) { + chrome.tabs.get(tabId, function(tab) { + ReplaceParents(tabId, tab.id); + DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabId] = tab.id; + tabs[tab.id] = tabs[tabId]; + AppendTabTTId(tab.id); + // let ParentId = DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabs[tabId].parent] ? DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabs[tabId].parent] : tabs[tabId].parent; + // chrome.runtime.sendMessage({command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tabId, ParentId: ParentId}); + chrome.runtime.sendMessage({command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tabId, ParentId: tabs[tabId].parent}); + schedule_save++; + }); + }); + + chrome.tabs.onDetached.addListener(function(tabId, detachInfo) { + let detachTabId = tabId; + if (DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabId] != undefined) { + detachTabId = DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabId]; + setTimeout(function() { + delete DETACHED_TABS___Bug1398272___WTF_ARE_YOU_DOING_MOZILLA[tabId]; + },2000); + } + chrome.runtime.sendMessage({command: "tab_detached", windowId: detachInfo.oldWindowId, tabId: detachTabId}); + }); + + chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) { + chrome.runtime.sendMessage({command: "tab_removed", windowId: removeInfo.windowId, tabId: tabId}); + delete tabs[tabId]; + schedule_save++; + }); + + chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (changeInfo.pinned == true) { + tabs[tabId].parent = "pin_list"; + } + if (changeInfo.title != undefined && !tab.active) { + chrome.runtime.sendMessage({command: "tab_attention", windowId: tab.windowId, tabId: tabId}); + } + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tabId, changeInfo: changeInfo}); + }); + + chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) { + chrome.tabs.get(addedTabId, function(tab) { + if (addedTabId == removedTabId) { + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tab.id, changeInfo: {status: tab.status, url: tab.url, title: tab.title, audible: tab.audible, mutedInfo: tab.mutedInfo}}); + } else { + if (tabs[removedTabId]) { + tabs[addedTabId] = tabs[removedTabId]; + } + ReplaceParents(tabId, tab.id); + chrome.runtime.sendMessage({command: "tab_removed", windowId: tab.windowId, tabId: removedTabId}); + chrome.runtime.sendMessage({command: "tab_attached", windowId: tab.windowId, tab: tab, tabId: addedTabId, ParentId: tabs[addedTabId].parent}); + delete tabs[removedTabId]; + } + setTimeout(function() { + AppendTabTTId(addedTabId); + schedule_save++; + }, 100); + + }); + }); + + chrome.tabs.onActivated.addListener(function(activeInfo) { + chrome.runtime.sendMessage({command: "tab_activated", windowId: activeInfo.windowId, tabId: activeInfo.tabId}); + }); + + chrome.windows.onCreated.addListener(function(window) { + AppendWinTTId(window.id); + schedule_save++; + }); + + chrome.windows.onRemoved.addListener(function(windowId) { + delete windows[windowId]; + schedule_save++; + }); +} + +function FirefoxMessageListeners() { + chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { + switch(message.command) { + case "reload": + window.location.reload(); + break; + case "get_windows": + sendResponse(windows); + break; + case "get_groups": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].groups); + } + break; + case "save_groups": + windows[message.windowId].groups = Object.assign({}, message.groups); + schedule_save++; + break; + case "set_active_group": + windows[message.windowId].active_group = message.active_group; + schedule_save++; + break; + case "get_active_group": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_group); + } + break; + case "set_active_shelf": + windows[message.windowId].active_shelf = message.active_shelf; + schedule_save++; + break; + case "get_active_shelf": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_shelf); + } + break; + case "set_group_bar": + windows[message.windowId].group_bar = message.group_bar; + schedule_save++; + break; + case "get_group_bar": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].group_bar); + } + break; + case "console_log": + console.log(message.m); + break; + case "get_browser_tabs": + sendResponse(tabs); + break; + case "is_bg_busy": + sendResponse(hold); + break; + case "update_tab": + if (tabs[message.tabId]) { + for (var parameter in message.tab) { + tabs[message.tabId][parameter] = message.tab[parameter]; + } + schedule_save++; + } + break; + } + }); +} diff --git a/bg_v015_to_v1.js b/bg_v015_to_v1.js new file mode 100644 index 0000000..1e59612 --- /dev/null +++ b/bg_v015_to_v1.js @@ -0,0 +1,106 @@ +// Copyright (c) 2017 kroppy. All rights reserved. +// Use of this source code is governed by a Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license +// that can be found at https://creativecommons.org/licenses/by-nc-nd/4.0/ + +function OldHashTab(tab){ + if (tabs[tab.id] == undefined){ + tabs[tab.id] = {ttid: "", hash: 0, h: 0, parent: tab.pinned ? "pin_list" : "tab_list", index: tab.index, expand: "n"}; + } + var hash = 0; + if (tab.url.length === 0){ + return 0; + } + for (var i = 0; i < tab.url.length; i++){ + hash = (hash << 5)-hash; + hash = hash+tab.url.charCodeAt(i); + hash |= 0; + } + tabs[tab.id].h = hash; +} + + +function LoadV015(retry){ + var loaded_options = {}; + for (var parameter in DefaultPreferences) { + opt[parameter] = DefaultPreferences[parameter]; + } + // set loaded options + if (localStorage.getItem("current_options") !== null){ + loaded_options = JSON.parse(localStorage["current_options"]); + } + for (var parameter in opt) { + if (loaded_options[parameter] != undefined && opt[parameter] != undefined){ + opt[parameter] = loaded_options[parameter]; + } + } + SavePreferences(); + if (localStorage.getItem("current_options") !== null){ + localStorage.removeItem("current_options"); + + } + + chrome.tabs.query({windowType: "normal"}, function(qtabs){ + // create current tabs object + qtabs.forEach(function(Tab){ + OldHashTab(Tab); + }); + + var reference_tabs = {}; + var tabs_to_save = []; + var tabs_matched = 0; + + // compare saved tabs from storage to current session tabs, but can be skipped if set in options + qtabs.forEach(function(Tab){ + for (var t = 0; t < 9999; t++){ + if (localStorage.getItem("t"+t) !== null){ + var LoadedTab = JSON.parse(localStorage["t"+t]); + if (LoadedTab[1] === tabs[Tab.id].h && reference_tabs[LoadedTab[0]] == undefined){ + reference_tabs[LoadedTab[0]] = Tab.id; + tabs[Tab.id].parent = LoadedTab[2]; + tabs[Tab.id].index = LoadedTab[3]; + tabs[Tab.id].expand = LoadedTab[4]; + tabs_matched++; + break; + } + + } else { + break; + } + + } + }); + + // replace parents tabIds to new ones, for that purpose reference_tabs was made before + for (var tabId in tabs){ + if (reference_tabs[tabs[tabId].parent] != undefined){ + tabs[tabId].parent = reference_tabs[tabs[tabId].parent]; + } + } + + if (browserId == "F") { + // append ids to firefox tabs + qtabs.forEach(function(Tab){ + AppendTabTTId(Tab.id); + }); + qtabs.forEach(function(Tab){ + tabs_to_save.push({id: Tab.id, ttid: tabs[tabId].ttid, parent: tabs[Tab.id].parent, index: tabs[Tab.id].index, expand: tabs[Tab.id].expand}); + }); + } else { + // create new hashes + qtabs.forEach(function(Tab){ + ChromeHashURL(Tab); + }); + qtabs.forEach(function(Tab){ + tabs_to_save.push({id: Tab.id, hash: tabs[Tab.id].hash, parent: tabs[Tab.id].parent, index: tabs[Tab.id].index, expand: tabs[Tab.id].expand}); + }); + } + localStorage["t_count"] = JSON.stringify(qtabs.length); + localStorage["tabs"] = JSON.stringify(tabs_to_save); + for (var t = 0; t < 9999; t++){ + if (localStorage.getItem("t"+t) !== null){ + localStorage.removeItem("t"+t); + } + } + window.location.reload(); + }); +} diff --git a/defaults.js b/defaults.js deleted file mode 100644 index 63d00c8..0000000 --- a/defaults.js +++ /dev/null @@ -1,298 +0,0 @@ -var ColorsSet = { - // scrolls - "scrollbar_thumb": "#cdcdcd", - "scrollbar_thumb_hover": "#a6a6a6", - "scrollbar_track": "#e4e4e4", - - // toolbar - "toolbar_background": "#f2f2f2", - "toolbar_border_bottom": "#cccccc", - - "button_border": "#f2f2f2", - "button_background": "#f2f2f2", - - "button_hover_border": "#bebebe", - "button_hover_background": "#dcdcdc", - - "button_icons": "#808080", - - "filter_box_background": "#fafafa", - "filter_box_border": "#cccccc", - "filter_box_font": "#333333", - "filter_clear_icon": "#808080", - - - // lists - "pin_list_border_bottom": "#cccccc", - "pin_list_background": "#fafafa", - "tab_list_background": "#fafafa", - - // tabs - "tab_background": "#f2f2f2", - "tab_border": "#bebebe", - - "tab_hover_background": "#d7d7d7", - "tab_hover_border": "#878787", - - "tab_selected_background": "#e5f3fb", - "tab_selected_border": "#70c0e7", - - "tab_selected_hover_border": "#78aee5", - "tab_selected_hover_background": "#d0e2f0", - - "tab_filtered": "#e8e000", - "tab_filtered_highlighted": "#ffa500", - - "tab_filtered_selected": "#0f8079", - "tab_filtered_selected_active": "#1299a9", - - "active_font_weight": "bold", - - // tabs title - "tab_title": "#000000", - "tab_title_active": "#000000", - "tab_title_discarded": "#7e7e7e", - - // drag&drop placeholder indicator - "drag_indicator": "#339bf3", - - // close button - "close_x": "#7d7d7d", - "close_hover_x": "#fbfcfe", - - "close_hover_border": "#757676", - "close_hover_background": "#939394", - - // trees expand - "expand_open_border": "#339bf3", - "expand_open_background": "#d0e2f0", - - "expand_closed_border": "#969696", - "expand_closed_background": "#eaeaea", - - "expand_lines": "#cccccc", - - "tabs_menu_font": "#333333", - - "tabs_menu_background": "#fafafa", - "tabs_menu_border": "#bebebe", - - "tabs_menu_hover_background": "#efefef", - "tabs_menu_hover_border": "#bebebe", - - "tabs_menu_separator": "#efefef" -} - -var TabsSizeSets = [ -//0 - { - "pin_width": "22px", - "pin_height": "20px", - - "tab_height": "15px", - "tab_height_line": "17px", - - "expand_box_size": "5px", - "expand_box_top": "4px", - "expand_box_left": "3px", - - "expand_line_h_top": "7px", - "expand_line_h_width": "12px", - "expand_line_h_oc_width": "3px", - - "expand_line_v_top": "-7px", - "expand_line_v_left": "0px", - "expand_line_v_last_height": "15px", - - "title_padding_with_close": "20px", - "title_font_size": "10.5px", - - "title_padding_left": "19px", - - "drag_area_top": "6px", - "drag_area_bottom": "4px", - - "close_top": "1px", - "close_right": "1px", - "close_size": "11px", - - "favicon_size": "13px 13px", - "favicon_pos": "2px center" - - }, -//1 - { - "pin_width": "24px", - "pin_height": "22px", - - "tab_height": "17px", - "tab_height_line": "19px", - - "expand_box_size": "5px", - "expand_box_top": "5px", - "expand_box_left": "3px", - - "expand_line_h_top": "8px", - "expand_line_h_width": "12px", - "expand_line_h_oc_width": "3px", - - "expand_line_v_top": "-8px", - "expand_line_v_left": "0px", - "expand_line_v_last_height": "17px", - - "title_padding_with_close": "20px", - "title_font_size": "10.5px", - - "title_padding_left": "20px", - - "drag_area_top": "7px", - "drag_area_bottom": "5px", - - "close_top": "2px", - "close_right": "2px", - "close_size": "11px", - - "favicon_size": "14px 14px", - "favicon_pos": "3px center" - }, -//2 - { - "pin_width": "26px", - "pin_height": "24px", - - "tab_height": "19px", - "tab_height_line": "23px", - - "expand_box_size": "5px", - "expand_box_top": "6px", - "expand_box_left": "3px", - - "expand_line_h_top": "9px", - "expand_line_h_width": "12px", - "expand_line_h_oc_width": "3px", - - "expand_line_v_top": "-9px", - "expand_line_v_left": "0px", - "expand_line_v_last_height": "19px", - - "title_padding_with_close": "24px", - "title_font_size": "12px", - - "title_padding_left": "25px", - - "drag_area_top": "7px", - "drag_area_bottom": "5px", - - "close_top": "2px", - "close_right": "2px", - "close_size": "13px", - - "favicon_size": "16px 16px", - "favicon_pos": "4px center" - }, -//3 - { - "pin_width": "28px", - "pin_height": "26px", - - "tab_height": "21px", - "tab_height_line": "25px", - - "expand_box_size": "5px", - "expand_box_top": "7px", - "expand_box_left": "3px", - - "expand_line_h_top": "10px", - "expand_line_h_width": "12px", - "expand_line_h_oc_width": "3px", - - "expand_line_v_top": "-10px", - "expand_line_v_left": "0px", - "expand_line_v_last_height": "21px", - - "title_padding_with_close": "24px", - "title_font_size": "12px", - - "title_padding_left": "25px", - - "drag_area_top": "8px", - "drag_area_bottom": "5px", - - "close_top": "3px", - "close_right": "3px", - "close_size": "13px", - - "favicon_size": "16px 16px", - "favicon_pos": "4px center" - }, -//4 - { - "pin_width": "30px", - "pin_height": "28px", - - "tab_height": "23px", - "tab_height_line": "26px", - - "expand_box_size": "5px", - "expand_box_top": "8px", - "expand_box_left": "3px", - - "expand_line_h_top": "11px", - "expand_line_h_width": "12px", - "expand_line_h_oc_width": "3px", - - "expand_line_v_top": "-11px", - "expand_line_v_left": "0px", - "expand_line_v_last_height": "23px", - - "title_padding_with_close": "24px", - "title_font_size": "12.5px", - - "title_padding_left": "25px", - - "drag_area_top": "9px", - "drag_area_bottom": "6px", - - "close_top": "4px", - "close_right": "4px", - "close_size": "14px", - - "favicon_size": "16px 16px", - "favicon_pos": "4px center" - } -]; - -var CurrentThemeVersion = 1; -var TabsSizeSet = 2; -var ScrollbarPinList = 4; -var ScrollbarTabList = 16; -var ToolbarShow = true; -var ToolbarSet = $("#toolbar").html(); - - -function SaveTheme(themeName){ - var themeObj = { - "toolbar": ToolbarSet, - "ToolbarShow": ToolbarShow, - "ColorsSet": ColorsSet, - "TabsSizeSetNumber": TabsSizeSet, - "TabsSizeSet": TabsSizeSets[TabsSizeSet], - "ScrollbarPinList": ScrollbarPinList, - "ScrollbarTabList": ScrollbarTabList, - "theme_version": CurrentThemeVersion - }; - - localStorage["theme"+themeName] = JSON.stringify(themeObj); - return themeObj; -} - -if (localStorage.getItem("themeDefault") === null){ - SaveTheme("Default"); - localStorage["current_theme"] = "Default"; -} else { - var theme = JSON.parse(localStorage["themeDefault"]); - if (theme.theme_version != CurrentThemeVersion){ - SaveTheme("Default"); - } -} - diff --git a/files_chrome/background.html b/files_chrome/background.html new file mode 100644 index 0000000..44d782d --- /dev/null +++ b/files_chrome/background.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/files_chrome/bg_ch.js b/files_chrome/bg_ch.js new file mode 100644 index 0000000..1325714 --- /dev/null +++ b/files_chrome/bg_ch.js @@ -0,0 +1,341 @@ +// Copyright (c) 2017 kroppy. All rights reserved. +// Use of this source code is governed by a Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) license +// that can be found at https://creativecommons.org/licenses/by-nc-nd/4.0/ + +if (localStorage.getItem("t0") !== null){ + LoadV015(0); +} else { + LoadPreferences(); + ChromeLoadTabs(0); + ChromeMessageListeners(); +} + + +function ChromeLoadTabs(retry) { + chrome.windows.getAll({windowTypes: ['normal'], populate: true}, function(w) { + + var refTabs = {}; + var tabs_matched = 0; + + // load tabs and windows from hdd + var w_count = LoadData("w_count", 0); + var t_count = LoadData("t_count", 0); + var LoadedWindows = LoadData("windows", []); + var LoadedTabs = LoadData("tabs", []); + + // if loaded tabs mismatch by 50%, then try to load back + if (LoadedTabs.length < t_count*0.5 || retry > 0) { + LoadedTabs = LoadData("tabs_BAK"+retry, []); + } + // if loaded windows mismatch, then try to load back + if (LoadedWindows.length < w_count || retry > 0) { + LoadedWindows = LoadData("windows_BAK"+retry, []); + } + + // CACHED COUNTS + var WinCount = w.length; + var LoadedWinCount = LoadedWindows.length; + var LoadedTabsCount = LoadedTabs.length; + + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + if (w[wIndex].tabs[0].url != "chrome://videopopout/") { // this is for opera for their extra video popup, which is weirdly queried as a "normal" window + let winId = w[wIndex].id; + let url1 = w[wIndex].tabs[0].url; + let url2 = w[wIndex].tabs[w[wIndex].tabs.length-1].url; + windows[winId] = {group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + for (var LwIndex = 0; LwIndex < LoadedWinCount; LwIndex++) { + if (LoadedWindows[LwIndex].url1 == url1 || LoadedWindows[LwIndex].url2 == url2) { + windows[winId].group_bar = LoadedWindows[LwIndex].group_bar; + windows[winId].active_shelf = LoadedWindows[LwIndex].active_shelf; + windows[winId].active_group = LoadedWindows[LwIndex].active_group; + if (Object.keys(LoadedWindows[LwIndex].groups).length > 0) { windows[winId].groups = Object.assign({}, LoadedWindows[LwIndex].groups); } + if (Object.keys(LoadedWindows[LwIndex].folders).length > 0) { windows[winId].folders = Object.assign({}, LoadedWindows[LwIndex].folders); } + LoadedWindows[LwIndex].url1 = ""; + LoadedWindows[LwIndex].url2 = ""; + break; + } + } + } + } + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + var TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + ChromeHashURL(w[wIndex].tabs[tabIndex]); + } + } + + // compare saved tabs from storage to current session tabs, but can be skipped if set in options + if (opt.skip_load == false && LoadedTabs.length > 0) { + // match loaded tabs + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + var TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + for (var LtabIndex = 0; LtabIndex < LoadedTabsCount; LtabIndex++) { + let tabId = w[wIndex].tabs[tabIndex].id; + if (LoadedTabs[LtabIndex].hash == tabs[tabId].hash && refTabs[LoadedTabs[LtabIndex].id] == undefined) { + refTabs[LoadedTabs[LtabIndex].id] = tabId; + tabs[tabId].parent = LoadedTabs[LtabIndex].parent; + tabs[tabId].index = LoadedTabs[LtabIndex].index; + tabs[tabId].expand = LoadedTabs[LtabIndex].expand; + LoadedTabs[LtabIndex].hash = undefined; + tabs_matched++; + break; + } + } + } + } + + // replace parents tabIds for new ones, for that purpose refTabs was made before + for (var tabId in tabs) { + if (refTabs[tabs[tabId].parent] != undefined) { + tabs[tabId].parent = refTabs[tabs[tabId].parent]; + } + } + } + + + // replace active tab ids for each group using refTabs + for (var windowId in windows) { + for (var group in windows[windowId].groups) { + if (refTabs[windows[windowId].groups[group].activetab]) { + windows[windowId].groups[group].activetab = refTabs[windows[windowId].groups[group].activetab]; + } + } + } + + + + + // will try to find tabs for 3 times + if (opt.skip_load == true || retry > 2 || (tabs_matched > t_count*0.5)) { + schedule_save++; + hold = false; + ChromeAutoSaveData("", 1000); + ChromeAutoSaveData("_BAK1", 300000); + ChromeAutoSaveData("_BAK2", 600000); + ChromeAutoSaveData("_BAK3", 1800000); + ChromeListeners(); + } else { + setTimeout(function() {ChromeLoadTabs(retry+1);}, 2000); + } + }); +} + +// You maybe are asking yourself why I save tabs in array? It's because, instead of, keeping 2 index numbers (one for browser tabs on top and one for my index in tree), it's easier to just arrange them in order and save it in localstorage. +// Another reason is that Object does not preserve order in chrome, I've been told that in Firefox it is. But I can't trust that. +async function ChromeAutoSaveData(BackupName, LoopTimer) { + setTimeout(function() { + ChromeAutoSaveData(BackupName, LoopTimer); + if (schedule_save > 1 || BackupName != "") {schedule_save = 1;} + if (!hold && schedule_save > 0 && Object.keys(tabs).length > 1) { + chrome.windows.getAll({windowTypes: ['normal'], populate: true}, function(w) { + var WinCount = w.length; + var t_count = 0; + var counter = 0; + var Windows = []; + var Tabs = []; + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + t_count += w[wIndex].tabs.length; + } + + for (var wIndex = 0; wIndex < WinCount; wIndex++) { + let winId = w[wIndex].id; + if (windows[winId] != undefined && windows[winId].group_bar != undefined && windows[winId].active_shelf != undefined && windows[winId].active_group != undefined && windows[winId].groups != undefined && windows[winId].folders != undefined) { + Windows.push({url1: w[wIndex].tabs[0].url, url2: w[wIndex].tabs[w[wIndex].tabs.length-1].url, group_bar: windows[winId].group_bar, active_shelf: windows[winId].active_shelf, active_group: windows[winId].active_group, groups: windows[winId].groups, folders: windows[winId].folders}); + } + + let TabsCount = w[wIndex].tabs.length; + for (var tabIndex = 0; tabIndex < TabsCount; tabIndex++) { + let tabId = w[wIndex].tabs[tabIndex].id; + if (tabs[tabId] != undefined && tabs[tabId].hash != undefined && tabs[tabId].parent != undefined && tabs[tabId].index != undefined && tabs[tabId].expand != undefined) { + Tabs.push({id: tabId, hash: tabs[tabId].hash, parent: tabs[tabId].parent, index: tabs[tabId].index, expand: tabs[tabId].expand}); + counter++; + } + } + if (counter == t_count) { + localStorage["t_count"] = JSON.stringify(t_count); + localStorage["w_count"] = JSON.stringify(WinCount); + localStorage["windows"+BackupName] = JSON.stringify(Windows); + localStorage["tabs"+BackupName] = JSON.stringify(Tabs); + } + } + schedule_save--; + }); + } + }, LoopTimer); +} + +function ChromeHashURL(tab){ + if (tabs[tab.id] == undefined) { + tabs[tab.id] = {hash: 0, parent: tab.pinned ? "pin_list" : "tab_list", index: tab.index, expand: "n"}; + } + var hash = 0; + for (var charIndex = 0; charIndex < tab.url.length; charIndex++){ + hash += tab.url.charCodeAt(charIndex); + } + tabs[tab.id].hash = hash; +} + +function ReplaceParents(oldTabId, newTabId) { + for (var tabId in tabs) { + if (tabs[tabId].parent == oldTabId) { + tabs[tabId].parent = newTabId; + } + } + + // TO DO FOLDERS +} + +// start all listeners +function ChromeListeners() { + chrome.tabs.onCreated.addListener(function(tab) { + ChromeHashURL(tab); + chrome.runtime.sendMessage({command: "tab_created", windowId: tab.windowId, tab: tab, tabId: tab.id}); + schedule_save++; + }); + + chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) { + chrome.runtime.sendMessage({command: "tab_removed", windowId: removeInfo.windowId, tabId: tabId}); + delete tabs[tabId]; + schedule_save++; + }); + + chrome.tabs.onAttached.addListener(function(tabId, attachInfo) { + chrome.tabs.get(tabId, function(tab) { + chrome.runtime.sendMessage({command: "tab_attached", windowId: attachInfo.newWindowId, tab: tab, tabId: tabId, ParentId: tabs[tabId].parent}); + }); + schedule_save++; + }); + + chrome.tabs.onDetached.addListener(function(tabId, detachInfo) { + chrome.runtime.sendMessage({command: "tab_detached", windowId: detachInfo.oldWindowId, tabId: tabId}); + schedule_save++; + }); + + chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (tabs[tabId] == undefined || changeInfo.url != undefined) { + ChromeHashURL(tab); + } + if (changeInfo.pinned != undefined) { + schedule_save++; + } + if (changeInfo.pinned == true) { + tabs[tabId].parent = "pin_list"; + } + if (changeInfo.title != undefined && !tab.active) { + chrome.runtime.sendMessage({command: "tab_attention", windowId: tab.windowId, tabId: tabId}); + } + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tabId, changeInfo: changeInfo}); + }); + + chrome.tabs.onMoved.addListener(function(tabId, moveInfo) { + schedule_save++; + }); + + chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) { + chrome.tabs.get(addedTabId, function(tab) { + if (addedTabId == removedTabId) { + chrome.runtime.sendMessage({command: "tab_updated", windowId: tab.windowId, tab: tab, tabId: tab.id, changeInfo: {status: tab.status, url: tab.url, title: tab.title, audible: tab.audible, mutedInfo: tab.mutedInfo}}); + } else { + ReplaceParents(tabId, tab.id); + if (tabs[removedTabId]) { + tabs[addedTabId] = tabs[removedTabId]; + } else { + ChromeHashURL(tab); + } + chrome.runtime.sendMessage({command: "tab_removed", windowId: tab.windowId, tabId: removedTabId}); + chrome.runtime.sendMessage({command: "tab_attached", windowId: tab.windowId, tab: tab, tabId: addedTabId}); + delete tabs[removedTabId]; + } + schedule_save++; + }); + }); + + chrome.tabs.onActivated.addListener(function(activeInfo) { + chrome.runtime.sendMessage({command: "tab_activated", windowId: activeInfo.windowId, tabId: activeInfo.tabId}); + }); + + chrome.windows.onCreated.addListener(function(window) { + windows[window.id] = {group_bar: true, active_shelf: "", active_group: "tab_list", groups: {tab_list: {id: "tab_list", index: 0, activetab: 0, name: caption_ungrouped_group, font: ""}}, folders: {}}; + schedule_save++; + }); + + chrome.windows.onRemoved.addListener(function(windowId) { + delete windows[windowId]; + schedule_save++; + }); + + chrome.runtime.onSuspend.addListener(function() { + hold = true; + }); +} + +function ChromeMessageListeners() { + chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { + switch(message.command) { + case "reload": + window.location.reload(); + break; + case "get_windows": + sendResponse(windows); + break; + case "get_groups": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].groups); + } + break; + case "save_groups": + windows[message.windowId].groups = Object.assign({}, message.groups); + schedule_save++; + break; + + case "set_active_group": + windows[message.windowId].active_group = message.active_group; + schedule_save++; + break; + case "get_active_group": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_group); + } + break; + case "set_active_shelf": + windows[message.windowId].active_shelf = message.active_shelf; + schedule_save++; + break; + case "get_active_shelf": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].active_shelf); + } + break; + case "set_group_bar": + windows[message.windowId].group_bar = message.group_bar; + schedule_save++; + break; + case "get_group_bar": + if (windows[message.windowId]) { + sendResponse(windows[message.windowId].group_bar); + } + break; + case "console_log": + console.log(message.m); + break; + case "get_browser_tabs": + sendResponse(tabs); + break; + case "is_bg_busy": + sendResponse(hold); + break; + case "update_tab": + if (tabs[message.tabId]) { + for (var parameter in message.tab) { + tabs[message.tabId][parameter] = message.tab[parameter]; + } + schedule_save++; + } + break; + } + }); +} diff --git a/files_firefox/background.html b/files_firefox/background.html new file mode 100644 index 0000000..7d90673 --- /dev/null +++ b/files_firefox/background.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/manifest_firefox/manifest.json b/files_firefox/manifest.json similarity index 65% rename from manifest_firefox/manifest.json rename to files_firefox/manifest.json index f8e21f6..cab8a82 100644 --- a/manifest_firefox/manifest.json +++ b/files_firefox/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "default_locale": "en", "background": { - "scripts": [ "background.js" ], + "page": "background.html", "persistent": true }, "name": "Tree Tabs", @@ -15,32 +15,24 @@ "16": "icons/16.png" }, "permissions": [ "tabs", "sessions", "