diff --git a/.gitignore b/.gitignore index 20804872..55b4cf8e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,11 +24,13 @@ core /*_history tags /webstore.zip +foo.* # Output directories /build* /doc/ /dist/ +*-packed/ # Test outputs *.e diff --git a/README.md b/README.md index 4e7b1f2c..c53f2e00 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,11 @@ Copyright (c) 2017 Chris White and contributors. CC-BY-SA 4.0 International. See [LICENSE.md](LICENSE.md) for details, which are controlling in case of any difference between that file and this section. -Contributors: +Contributors (in alphabetical order, case-insensitive): - [Jasmine Hegman](https://github.com/r4j4h) + - [Procyon-b](https://github.com/Procyon-b) + - [rwexmd](https://github.com/rwexmd) Originally inspired by [Tabs Outliner](https://chrome.google.com/webstore/detail/tabs-outliner/eggkanocgddhmamlbiijnphhppkpkmkl) diff --git a/bundle.sh b/bundle.sh old mode 100644 new mode 100755 diff --git a/check-webstore.sh b/check-webstore.sh index f840ce8a..faa7e953 100755 --- a/check-webstore.sh +++ b/check-webstore.sh @@ -23,7 +23,8 @@ find tabfern \( -prune -o -name \*.swp -prune -o \ -wholename tabfern/webstore -prune -o -name .git\* -prune -o \ -wholename tabfern/scraps -prune -o -wholename tabfern/webstore.js -prune -o \ -wholename tabfern/webstore.zip -prune -o -wholename tabfern/test -prune -o \ - -wholename tabfern/node_modules -prune \) \ + -wholename tabfern/node_modules -prune -o \ + -wholename tabfern/_locales/README-patching.txt -prune \) \ -o \( -type f \( -exec dash -c 'f="webstore/${0#tabfern/}" ; [ -f "$f" ]' {} \; -o -print \) \) if [[ $1 = '-z' ]]; then diff --git a/icons-old/Gold Star-349834968906 - jhnri4 - pd-oca - 14in16px.svg b/icons-old/Gold Star-349834968906 - jhnri4 - pd-oca - 14in16px.svg deleted file mode 100755 index 039aa70c..00000000 --- a/icons-old/Gold Star-349834968906 - jhnri4 - pd-oca - 14in16px.svg +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - Openclipart - - - - 2010-11-01T00:04:47 - Favorite icon - https://openclipart.org/detail/93367/favorite-icon-by-jhnri4 - - - jhnri4 - - - - - action - favorite - gold - icon - star - yellow - - - - - - - - - - - diff --git a/icons-old/accept.png b/icons-old/accept.png deleted file mode 100644 index 89c8129a..00000000 Binary files a/icons-old/accept.png and /dev/null differ diff --git a/icons-old/add.png b/icons-old/add.png deleted file mode 100644 index 6332fefe..00000000 Binary files a/icons-old/add.png and /dev/null differ diff --git a/icons-old/anchor.png b/icons-old/anchor.png deleted file mode 100644 index 9b3422c6..00000000 Binary files a/icons-old/anchor.png and /dev/null differ diff --git a/icons-old/application.png b/icons-old/application.png deleted file mode 100644 index 1dee9e36..00000000 Binary files a/icons-old/application.png and /dev/null differ diff --git a/icons-old/application_add.png b/icons-old/application_add.png deleted file mode 100644 index 2e945076..00000000 Binary files a/icons-old/application_add.png and /dev/null differ diff --git a/icons-old/application_cascade.png b/icons-old/application_cascade.png deleted file mode 100644 index da5c622e..00000000 Binary files a/icons-old/application_cascade.png and /dev/null differ diff --git a/icons-old/application_delete.png b/icons-old/application_delete.png deleted file mode 100644 index 0a335acf..00000000 Binary files a/icons-old/application_delete.png and /dev/null differ diff --git a/icons-old/application_double.png b/icons-old/application_double.png deleted file mode 100644 index 647592f2..00000000 Binary files a/icons-old/application_double.png and /dev/null differ diff --git a/icons-old/application_edit.png b/icons-old/application_edit.png deleted file mode 100644 index fb2efb87..00000000 Binary files a/icons-old/application_edit.png and /dev/null differ diff --git a/icons-old/application_error.png b/icons-old/application_error.png deleted file mode 100644 index b35fa571..00000000 Binary files a/icons-old/application_error.png and /dev/null differ diff --git a/icons-old/application_form.png b/icons-old/application_form.png deleted file mode 100644 index 807b862c..00000000 Binary files a/icons-old/application_form.png and /dev/null differ diff --git a/icons-old/application_form_add.png b/icons-old/application_form_add.png deleted file mode 100644 index 28c2175e..00000000 Binary files a/icons-old/application_form_add.png and /dev/null differ diff --git a/icons-old/application_form_delete.png b/icons-old/application_form_delete.png deleted file mode 100644 index cd305ec8..00000000 Binary files a/icons-old/application_form_delete.png and /dev/null differ diff --git a/icons-old/application_form_edit.png b/icons-old/application_form_edit.png deleted file mode 100644 index af486c94..00000000 Binary files a/icons-old/application_form_edit.png and /dev/null differ diff --git a/icons-old/application_form_magnify.png b/icons-old/application_form_magnify.png deleted file mode 100644 index 7b7fbd17..00000000 Binary files a/icons-old/application_form_magnify.png and /dev/null differ diff --git a/icons-old/application_get.png b/icons-old/application_get.png deleted file mode 100644 index 28e41ea2..00000000 Binary files a/icons-old/application_get.png and /dev/null differ diff --git a/icons-old/application_go.png b/icons-old/application_go.png deleted file mode 100644 index 5cc2b0dd..00000000 Binary files a/icons-old/application_go.png and /dev/null differ diff --git a/icons-old/application_home.png b/icons-old/application_home.png deleted file mode 100644 index b60d0c85..00000000 Binary files a/icons-old/application_home.png and /dev/null differ diff --git a/icons-old/application_key.png b/icons-old/application_key.png deleted file mode 100644 index 998d65c6..00000000 Binary files a/icons-old/application_key.png and /dev/null differ diff --git a/icons-old/application_lightning.png b/icons-old/application_lightning.png deleted file mode 100644 index 7e91545c..00000000 Binary files a/icons-old/application_lightning.png and /dev/null differ diff --git a/icons-old/application_link.png b/icons-old/application_link.png deleted file mode 100644 index f8fbb3ed..00000000 Binary files a/icons-old/application_link.png and /dev/null differ diff --git a/icons-old/application_osx.png b/icons-old/application_osx.png deleted file mode 100644 index 9f022ece..00000000 Binary files a/icons-old/application_osx.png and /dev/null differ diff --git a/icons-old/application_osx_terminal.png b/icons-old/application_osx_terminal.png deleted file mode 100644 index b3d8ce01..00000000 Binary files a/icons-old/application_osx_terminal.png and /dev/null differ diff --git a/icons-old/application_put.png b/icons-old/application_put.png deleted file mode 100644 index c30cf598..00000000 Binary files a/icons-old/application_put.png and /dev/null differ diff --git a/icons-old/application_side_boxes.png b/icons-old/application_side_boxes.png deleted file mode 100644 index efbf3c4f..00000000 Binary files a/icons-old/application_side_boxes.png and /dev/null differ diff --git a/icons-old/application_side_contract.png b/icons-old/application_side_contract.png deleted file mode 100644 index 3585f94d..00000000 Binary files a/icons-old/application_side_contract.png and /dev/null differ diff --git a/icons-old/application_side_expand.png b/icons-old/application_side_expand.png deleted file mode 100644 index 030cf7c3..00000000 Binary files a/icons-old/application_side_expand.png and /dev/null differ diff --git a/icons-old/application_side_list.png b/icons-old/application_side_list.png deleted file mode 100644 index 248eaf1a..00000000 Binary files a/icons-old/application_side_list.png and /dev/null differ diff --git a/icons-old/application_side_tree.png b/icons-old/application_side_tree.png deleted file mode 100644 index f04a52b3..00000000 Binary files a/icons-old/application_side_tree.png and /dev/null differ diff --git a/icons-old/application_split.png b/icons-old/application_split.png deleted file mode 100644 index a91c78a5..00000000 Binary files a/icons-old/application_split.png and /dev/null differ diff --git a/icons-old/application_tile_horizontal.png b/icons-old/application_tile_horizontal.png deleted file mode 100644 index 8a1191c3..00000000 Binary files a/icons-old/application_tile_horizontal.png and /dev/null differ diff --git a/icons-old/application_tile_vertical.png b/icons-old/application_tile_vertical.png deleted file mode 100644 index 1d40383d..00000000 Binary files a/icons-old/application_tile_vertical.png and /dev/null differ diff --git a/icons-old/application_view_columns.png b/icons-old/application_view_columns.png deleted file mode 100644 index dc2e9d5f..00000000 Binary files a/icons-old/application_view_columns.png and /dev/null differ diff --git a/icons-old/application_view_detail.png b/icons-old/application_view_detail.png deleted file mode 100644 index aba044bb..00000000 Binary files a/icons-old/application_view_detail.png and /dev/null differ diff --git a/icons-old/application_view_gallery.png b/icons-old/application_view_gallery.png deleted file mode 100644 index 851950db..00000000 Binary files a/icons-old/application_view_gallery.png and /dev/null differ diff --git a/icons-old/application_view_icons.png b/icons-old/application_view_icons.png deleted file mode 100644 index 6a93cdaa..00000000 Binary files a/icons-old/application_view_icons.png and /dev/null differ diff --git a/icons-old/application_view_list.png b/icons-old/application_view_list.png deleted file mode 100644 index acc30b85..00000000 Binary files a/icons-old/application_view_list.png and /dev/null differ diff --git a/icons-old/application_view_tile.png b/icons-old/application_view_tile.png deleted file mode 100644 index 3bc0bd32..00000000 Binary files a/icons-old/application_view_tile.png and /dev/null differ diff --git a/icons-old/application_xp.png b/icons-old/application_xp.png deleted file mode 100644 index d22860a3..00000000 Binary files a/icons-old/application_xp.png and /dev/null differ diff --git a/icons-old/application_xp_terminal.png b/icons-old/application_xp_terminal.png deleted file mode 100644 index c28dd638..00000000 Binary files a/icons-old/application_xp_terminal.png and /dev/null differ diff --git a/icons-old/arrow_branch.png b/icons-old/arrow_branch.png deleted file mode 100644 index 7542db1d..00000000 Binary files a/icons-old/arrow_branch.png and /dev/null differ diff --git a/icons-old/arrow_divide.png b/icons-old/arrow_divide.png deleted file mode 100644 index 61a7b1d9..00000000 Binary files a/icons-old/arrow_divide.png and /dev/null differ diff --git a/icons-old/arrow_down.png b/icons-old/arrow_down.png deleted file mode 100644 index 2c4e2793..00000000 Binary files a/icons-old/arrow_down.png and /dev/null differ diff --git a/icons-old/arrow_in.png b/icons-old/arrow_in.png deleted file mode 100644 index 745c6513..00000000 Binary files a/icons-old/arrow_in.png and /dev/null differ diff --git a/icons-old/arrow_inout.png b/icons-old/arrow_inout.png deleted file mode 100644 index 1b763672..00000000 Binary files a/icons-old/arrow_inout.png and /dev/null differ diff --git a/icons-old/arrow_join.png b/icons-old/arrow_join.png deleted file mode 100644 index a128413d..00000000 Binary files a/icons-old/arrow_join.png and /dev/null differ diff --git a/icons-old/arrow_left.png b/icons-old/arrow_left.png deleted file mode 100644 index 5dc69678..00000000 Binary files a/icons-old/arrow_left.png and /dev/null differ diff --git a/icons-old/arrow_merge.png b/icons-old/arrow_merge.png deleted file mode 100644 index 7502dbb3..00000000 Binary files a/icons-old/arrow_merge.png and /dev/null differ diff --git a/icons-old/arrow_out.png b/icons-old/arrow_out.png deleted file mode 100644 index 2e9bc42b..00000000 Binary files a/icons-old/arrow_out.png and /dev/null differ diff --git a/icons-old/arrow_redo.png b/icons-old/arrow_redo.png deleted file mode 100644 index fdc394c7..00000000 Binary files a/icons-old/arrow_redo.png and /dev/null differ diff --git a/icons-old/arrow_refresh.png b/icons-old/arrow_refresh.png deleted file mode 100644 index 0de26566..00000000 Binary files a/icons-old/arrow_refresh.png and /dev/null differ diff --git a/icons-old/arrow_refresh_small.png b/icons-old/arrow_refresh_small.png deleted file mode 100644 index d3087dfc..00000000 Binary files a/icons-old/arrow_refresh_small.png and /dev/null differ diff --git a/icons-old/arrow_right.png b/icons-old/arrow_right.png deleted file mode 100644 index b1a18192..00000000 Binary files a/icons-old/arrow_right.png and /dev/null differ diff --git a/icons-old/arrow_rotate_anticlockwise.png b/icons-old/arrow_rotate_anticlockwise.png deleted file mode 100644 index 46c75aa8..00000000 Binary files a/icons-old/arrow_rotate_anticlockwise.png and /dev/null differ diff --git a/icons-old/arrow_rotate_clockwise.png b/icons-old/arrow_rotate_clockwise.png deleted file mode 100644 index aa65210e..00000000 Binary files a/icons-old/arrow_rotate_clockwise.png and /dev/null differ diff --git a/icons-old/arrow_switch.png b/icons-old/arrow_switch.png deleted file mode 100644 index 258c16c6..00000000 Binary files a/icons-old/arrow_switch.png and /dev/null differ diff --git a/icons-old/arrow_turn_left.png b/icons-old/arrow_turn_left.png deleted file mode 100644 index a3d6c9e3..00000000 Binary files a/icons-old/arrow_turn_left.png and /dev/null differ diff --git a/icons-old/arrow_turn_right.png b/icons-old/arrow_turn_right.png deleted file mode 100644 index 629f20d6..00000000 Binary files a/icons-old/arrow_turn_right.png and /dev/null differ diff --git a/icons-old/arrow_undo.png b/icons-old/arrow_undo.png deleted file mode 100644 index 6972c5e5..00000000 Binary files a/icons-old/arrow_undo.png and /dev/null differ diff --git a/icons-old/arrow_up.png b/icons-old/arrow_up.png deleted file mode 100644 index 1ebb1932..00000000 Binary files a/icons-old/arrow_up.png and /dev/null differ diff --git a/icons-old/asterisk_orange.png b/icons-old/asterisk_orange.png deleted file mode 100644 index 1ebebde5..00000000 Binary files a/icons-old/asterisk_orange.png and /dev/null differ diff --git a/icons-old/asterisk_yellow.png b/icons-old/asterisk_yellow.png deleted file mode 100644 index bab7cc9b..00000000 Binary files a/icons-old/asterisk_yellow.png and /dev/null differ diff --git a/icons-old/attach.png b/icons-old/attach.png deleted file mode 100644 index ea897cc9..00000000 Binary files a/icons-old/attach.png and /dev/null differ diff --git a/icons-old/award_star_add.png b/icons-old/award_star_add.png deleted file mode 100644 index 9c4be9be..00000000 Binary files a/icons-old/award_star_add.png and /dev/null differ diff --git a/icons-old/award_star_bronze_1.png b/icons-old/award_star_bronze_1.png deleted file mode 100644 index 658c7117..00000000 Binary files a/icons-old/award_star_bronze_1.png and /dev/null differ diff --git a/icons-old/award_star_bronze_2.png b/icons-old/award_star_bronze_2.png deleted file mode 100644 index e47babd7..00000000 Binary files a/icons-old/award_star_bronze_2.png and /dev/null differ diff --git a/icons-old/award_star_bronze_3.png b/icons-old/award_star_bronze_3.png deleted file mode 100644 index 396e4b3a..00000000 Binary files a/icons-old/award_star_bronze_3.png and /dev/null differ diff --git a/icons-old/award_star_delete.png b/icons-old/award_star_delete.png deleted file mode 100644 index 4721b152..00000000 Binary files a/icons-old/award_star_delete.png and /dev/null differ diff --git a/icons-old/award_star_gold_1.png b/icons-old/award_star_gold_1.png deleted file mode 100644 index 97a22b72..00000000 Binary files a/icons-old/award_star_gold_1.png and /dev/null differ diff --git a/icons-old/award_star_gold_2.png b/icons-old/award_star_gold_2.png deleted file mode 100644 index 0eaa5717..00000000 Binary files a/icons-old/award_star_gold_2.png and /dev/null differ diff --git a/icons-old/award_star_gold_3.png b/icons-old/award_star_gold_3.png deleted file mode 100644 index 124c9914..00000000 Binary files a/icons-old/award_star_gold_3.png and /dev/null differ diff --git a/icons-old/award_star_silver_1.png b/icons-old/award_star_silver_1.png deleted file mode 100644 index 028a5462..00000000 Binary files a/icons-old/award_star_silver_1.png and /dev/null differ diff --git a/icons-old/award_star_silver_2.png b/icons-old/award_star_silver_2.png deleted file mode 100644 index e487c3a1..00000000 Binary files a/icons-old/award_star_silver_2.png and /dev/null differ diff --git a/icons-old/award_star_silver_3.png b/icons-old/award_star_silver_3.png deleted file mode 100644 index 1d72d472..00000000 Binary files a/icons-old/award_star_silver_3.png and /dev/null differ diff --git a/icons-old/basket.png b/icons-old/basket.png deleted file mode 100644 index b0686d78..00000000 Binary files a/icons-old/basket.png and /dev/null differ diff --git a/icons-old/basket_add.png b/icons-old/basket_add.png deleted file mode 100644 index 35543682..00000000 Binary files a/icons-old/basket_add.png and /dev/null differ diff --git a/icons-old/basket_delete.png b/icons-old/basket_delete.png deleted file mode 100644 index 1349974f..00000000 Binary files a/icons-old/basket_delete.png and /dev/null differ diff --git a/icons-old/basket_edit.png b/icons-old/basket_edit.png deleted file mode 100644 index 8138bbdf..00000000 Binary files a/icons-old/basket_edit.png and /dev/null differ diff --git a/icons-old/basket_error.png b/icons-old/basket_error.png deleted file mode 100644 index 3978b292..00000000 Binary files a/icons-old/basket_error.png and /dev/null differ diff --git a/icons-old/basket_go.png b/icons-old/basket_go.png deleted file mode 100644 index ed8b9a57..00000000 Binary files a/icons-old/basket_go.png and /dev/null differ diff --git a/icons-old/basket_put.png b/icons-old/basket_put.png deleted file mode 100644 index be62faaa..00000000 Binary files a/icons-old/basket_put.png and /dev/null differ diff --git a/icons-old/basket_remove.png b/icons-old/basket_remove.png deleted file mode 100644 index 04dd7fd3..00000000 Binary files a/icons-old/basket_remove.png and /dev/null differ diff --git a/icons-old/bell.png b/icons-old/bell.png deleted file mode 100644 index 6e0015df..00000000 Binary files a/icons-old/bell.png and /dev/null differ diff --git a/icons-old/bell_add.png b/icons-old/bell_add.png deleted file mode 100644 index 7db01d62..00000000 Binary files a/icons-old/bell_add.png and /dev/null differ diff --git a/icons-old/bell_delete.png b/icons-old/bell_delete.png deleted file mode 100644 index 23907bbb..00000000 Binary files a/icons-old/bell_delete.png and /dev/null differ diff --git a/icons-old/bell_error.png b/icons-old/bell_error.png deleted file mode 100644 index a0ddc002..00000000 Binary files a/icons-old/bell_error.png and /dev/null differ diff --git a/icons-old/bell_go.png b/icons-old/bell_go.png deleted file mode 100644 index b89bb343..00000000 Binary files a/icons-old/bell_go.png and /dev/null differ diff --git a/icons-old/bell_link.png b/icons-old/bell_link.png deleted file mode 100644 index b8c64af1..00000000 Binary files a/icons-old/bell_link.png and /dev/null differ diff --git a/icons-old/bin.png b/icons-old/bin.png deleted file mode 100644 index ebad933c..00000000 Binary files a/icons-old/bin.png and /dev/null differ diff --git a/icons-old/bin_closed.png b/icons-old/bin_closed.png deleted file mode 100644 index afe22ba9..00000000 Binary files a/icons-old/bin_closed.png and /dev/null differ diff --git a/icons-old/bin_empty.png b/icons-old/bin_empty.png deleted file mode 100644 index 375b8bf6..00000000 Binary files a/icons-old/bin_empty.png and /dev/null differ diff --git a/icons-old/bomb.png b/icons-old/bomb.png deleted file mode 100644 index 1be37974..00000000 Binary files a/icons-old/bomb.png and /dev/null differ diff --git a/icons-old/book.png b/icons-old/book.png deleted file mode 100644 index b0f4dd79..00000000 Binary files a/icons-old/book.png and /dev/null differ diff --git a/icons-old/book_add.png b/icons-old/book_add.png deleted file mode 100644 index e2f08472..00000000 Binary files a/icons-old/book_add.png and /dev/null differ diff --git a/icons-old/book_addresses.png b/icons-old/book_addresses.png deleted file mode 100644 index b73419ba..00000000 Binary files a/icons-old/book_addresses.png and /dev/null differ diff --git a/icons-old/book_delete.png b/icons-old/book_delete.png deleted file mode 100644 index d9a6340d..00000000 Binary files a/icons-old/book_delete.png and /dev/null differ diff --git a/icons-old/book_edit.png b/icons-old/book_edit.png deleted file mode 100644 index 6e756cc8..00000000 Binary files a/icons-old/book_edit.png and /dev/null differ diff --git a/icons-old/book_error.png b/icons-old/book_error.png deleted file mode 100644 index f3fbed0d..00000000 Binary files a/icons-old/book_error.png and /dev/null differ diff --git a/icons-old/book_go.png b/icons-old/book_go.png deleted file mode 100644 index cd4e1964..00000000 Binary files a/icons-old/book_go.png and /dev/null differ diff --git a/icons-old/book_key.png b/icons-old/book_key.png deleted file mode 100644 index d8e23ec9..00000000 Binary files a/icons-old/book_key.png and /dev/null differ diff --git a/icons-old/book_link.png b/icons-old/book_link.png deleted file mode 100644 index dd0820e8..00000000 Binary files a/icons-old/book_link.png and /dev/null differ diff --git a/icons-old/book_next.png b/icons-old/book_next.png deleted file mode 100644 index ff2ea1ab..00000000 Binary files a/icons-old/book_next.png and /dev/null differ diff --git a/icons-old/book_open.png b/icons-old/book_open.png deleted file mode 100644 index 7d863f94..00000000 Binary files a/icons-old/book_open.png and /dev/null differ diff --git a/icons-old/book_previous.png b/icons-old/book_previous.png deleted file mode 100644 index 2e53c698..00000000 Binary files a/icons-old/book_previous.png and /dev/null differ diff --git a/icons-old/box.png b/icons-old/box.png deleted file mode 100644 index 8443c23e..00000000 Binary files a/icons-old/box.png and /dev/null differ diff --git a/icons-old/brick.png b/icons-old/brick.png deleted file mode 100644 index 7851cf34..00000000 Binary files a/icons-old/brick.png and /dev/null differ diff --git a/icons-old/brick_add.png b/icons-old/brick_add.png deleted file mode 100644 index fac186bf..00000000 Binary files a/icons-old/brick_add.png and /dev/null differ diff --git a/icons-old/brick_delete.png b/icons-old/brick_delete.png deleted file mode 100644 index 3a8c3734..00000000 Binary files a/icons-old/brick_delete.png and /dev/null differ diff --git a/icons-old/brick_edit.png b/icons-old/brick_edit.png deleted file mode 100644 index eb06df3b..00000000 Binary files a/icons-old/brick_edit.png and /dev/null differ diff --git a/icons-old/brick_error.png b/icons-old/brick_error.png deleted file mode 100644 index 18ab01eb..00000000 Binary files a/icons-old/brick_error.png and /dev/null differ diff --git a/icons-old/brick_go.png b/icons-old/brick_go.png deleted file mode 100644 index fe0d3356..00000000 Binary files a/icons-old/brick_go.png and /dev/null differ diff --git a/icons-old/brick_link.png b/icons-old/brick_link.png deleted file mode 100644 index 9ebf013a..00000000 Binary files a/icons-old/brick_link.png and /dev/null differ diff --git a/icons-old/bricks.png b/icons-old/bricks.png deleted file mode 100644 index 0905f933..00000000 Binary files a/icons-old/bricks.png and /dev/null differ diff --git a/icons-old/briefcase.png b/icons-old/briefcase.png deleted file mode 100644 index 05c56491..00000000 Binary files a/icons-old/briefcase.png and /dev/null differ diff --git a/icons-old/bug.png b/icons-old/bug.png deleted file mode 100644 index 2d5fb90e..00000000 Binary files a/icons-old/bug.png and /dev/null differ diff --git a/icons-old/bug_add.png b/icons-old/bug_add.png deleted file mode 100644 index ced78174..00000000 Binary files a/icons-old/bug_add.png and /dev/null differ diff --git a/icons-old/bug_delete.png b/icons-old/bug_delete.png deleted file mode 100644 index e81d7571..00000000 Binary files a/icons-old/bug_delete.png and /dev/null differ diff --git a/icons-old/bug_edit.png b/icons-old/bug_edit.png deleted file mode 100644 index e5c7dc05..00000000 Binary files a/icons-old/bug_edit.png and /dev/null differ diff --git a/icons-old/bug_error.png b/icons-old/bug_error.png deleted file mode 100644 index c4e8c280..00000000 Binary files a/icons-old/bug_error.png and /dev/null differ diff --git a/icons-old/bug_go.png b/icons-old/bug_go.png deleted file mode 100644 index 4e4ae99d..00000000 Binary files a/icons-old/bug_go.png and /dev/null differ diff --git a/icons-old/bug_link.png b/icons-old/bug_link.png deleted file mode 100644 index 30e25aba..00000000 Binary files a/icons-old/bug_link.png and /dev/null differ diff --git a/icons-old/building.png b/icons-old/building.png deleted file mode 100644 index 11a017cf..00000000 Binary files a/icons-old/building.png and /dev/null differ diff --git a/icons-old/building_add.png b/icons-old/building_add.png deleted file mode 100644 index d88e2b9a..00000000 Binary files a/icons-old/building_add.png and /dev/null differ diff --git a/icons-old/building_delete.png b/icons-old/building_delete.png deleted file mode 100644 index db6455d2..00000000 Binary files a/icons-old/building_delete.png and /dev/null differ diff --git a/icons-old/building_edit.png b/icons-old/building_edit.png deleted file mode 100644 index 646db36b..00000000 Binary files a/icons-old/building_edit.png and /dev/null differ diff --git a/icons-old/building_error.png b/icons-old/building_error.png deleted file mode 100644 index a342eefc..00000000 Binary files a/icons-old/building_error.png and /dev/null differ diff --git a/icons-old/building_go.png b/icons-old/building_go.png deleted file mode 100644 index cdcbcb38..00000000 Binary files a/icons-old/building_go.png and /dev/null differ diff --git a/icons-old/building_key.png b/icons-old/building_key.png deleted file mode 100644 index 8b79e30e..00000000 Binary files a/icons-old/building_key.png and /dev/null differ diff --git a/icons-old/building_link.png b/icons-old/building_link.png deleted file mode 100644 index a340629a..00000000 Binary files a/icons-old/building_link.png and /dev/null differ diff --git a/icons-old/bullet_add.png b/icons-old/bullet_add.png deleted file mode 100644 index 41ff8335..00000000 Binary files a/icons-old/bullet_add.png and /dev/null differ diff --git a/icons-old/bullet_arrow_bottom.png b/icons-old/bullet_arrow_bottom.png deleted file mode 100644 index 1a28d825..00000000 Binary files a/icons-old/bullet_arrow_bottom.png and /dev/null differ diff --git a/icons-old/bullet_arrow_down.png b/icons-old/bullet_arrow_down.png deleted file mode 100644 index 9b23c06d..00000000 Binary files a/icons-old/bullet_arrow_down.png and /dev/null differ diff --git a/icons-old/bullet_arrow_top.png b/icons-old/bullet_arrow_top.png deleted file mode 100644 index 0ce86d2b..00000000 Binary files a/icons-old/bullet_arrow_top.png and /dev/null differ diff --git a/icons-old/bullet_arrow_up.png b/icons-old/bullet_arrow_up.png deleted file mode 100644 index 24df0f42..00000000 Binary files a/icons-old/bullet_arrow_up.png and /dev/null differ diff --git a/icons-old/bullet_black.png b/icons-old/bullet_black.png deleted file mode 100644 index 57619706..00000000 Binary files a/icons-old/bullet_black.png and /dev/null differ diff --git a/icons-old/bullet_blue.png b/icons-old/bullet_blue.png deleted file mode 100644 index a7651ec8..00000000 Binary files a/icons-old/bullet_blue.png and /dev/null differ diff --git a/icons-old/bullet_delete.png b/icons-old/bullet_delete.png deleted file mode 100644 index bd6271b2..00000000 Binary files a/icons-old/bullet_delete.png and /dev/null differ diff --git a/icons-old/bullet_disk.png b/icons-old/bullet_disk.png deleted file mode 100644 index 209c6a78..00000000 Binary files a/icons-old/bullet_disk.png and /dev/null differ diff --git a/icons-old/bullet_error.png b/icons-old/bullet_error.png deleted file mode 100644 index bca2b491..00000000 Binary files a/icons-old/bullet_error.png and /dev/null differ diff --git a/icons-old/bullet_feed.png b/icons-old/bullet_feed.png deleted file mode 100644 index 1a0e0f18..00000000 Binary files a/icons-old/bullet_feed.png and /dev/null differ diff --git a/icons-old/bullet_go.png b/icons-old/bullet_go.png deleted file mode 100644 index bc4faa70..00000000 Binary files a/icons-old/bullet_go.png and /dev/null differ diff --git a/icons-old/bullet_green.png b/icons-old/bullet_green.png deleted file mode 100644 index 058ad261..00000000 Binary files a/icons-old/bullet_green.png and /dev/null differ diff --git a/icons-old/bullet_key.png b/icons-old/bullet_key.png deleted file mode 100644 index 3d37f2ea..00000000 Binary files a/icons-old/bullet_key.png and /dev/null differ diff --git a/icons-old/bullet_orange.png b/icons-old/bullet_orange.png deleted file mode 100644 index fa63024e..00000000 Binary files a/icons-old/bullet_orange.png and /dev/null differ diff --git a/icons-old/bullet_picture.png b/icons-old/bullet_picture.png deleted file mode 100644 index 386cb302..00000000 Binary files a/icons-old/bullet_picture.png and /dev/null differ diff --git a/icons-old/bullet_pink.png b/icons-old/bullet_pink.png deleted file mode 100644 index 0c9f73e3..00000000 Binary files a/icons-old/bullet_pink.png and /dev/null differ diff --git a/icons-old/bullet_purple.png b/icons-old/bullet_purple.png deleted file mode 100644 index 52ba5036..00000000 Binary files a/icons-old/bullet_purple.png and /dev/null differ diff --git a/icons-old/bullet_red.png b/icons-old/bullet_red.png deleted file mode 100644 index 0cd80311..00000000 Binary files a/icons-old/bullet_red.png and /dev/null differ diff --git a/icons-old/bullet_star.png b/icons-old/bullet_star.png deleted file mode 100644 index fab774a3..00000000 Binary files a/icons-old/bullet_star.png and /dev/null differ diff --git a/icons-old/bullet_toggle_minus.png b/icons-old/bullet_toggle_minus.png deleted file mode 100644 index b47ce55f..00000000 Binary files a/icons-old/bullet_toggle_minus.png and /dev/null differ diff --git a/icons-old/bullet_toggle_plus.png b/icons-old/bullet_toggle_plus.png deleted file mode 100644 index 9ab4a896..00000000 Binary files a/icons-old/bullet_toggle_plus.png and /dev/null differ diff --git a/icons-old/bullet_white.png b/icons-old/bullet_white.png deleted file mode 100644 index a9af8d44..00000000 Binary files a/icons-old/bullet_white.png and /dev/null differ diff --git a/icons-old/bullet_wrench.png b/icons-old/bullet_wrench.png deleted file mode 100644 index 67817e6e..00000000 Binary files a/icons-old/bullet_wrench.png and /dev/null differ diff --git a/icons-old/bullet_yellow.png b/icons-old/bullet_yellow.png deleted file mode 100644 index 6469cea7..00000000 Binary files a/icons-old/bullet_yellow.png and /dev/null differ diff --git a/icons-old/cake.png b/icons-old/cake.png deleted file mode 100644 index 4ef151ae..00000000 Binary files a/icons-old/cake.png and /dev/null differ diff --git a/icons-old/calculator.png b/icons-old/calculator.png deleted file mode 100644 index 701a60a5..00000000 Binary files a/icons-old/calculator.png and /dev/null differ diff --git a/icons-old/calculator_add.png b/icons-old/calculator_add.png deleted file mode 100644 index fd377bde..00000000 Binary files a/icons-old/calculator_add.png and /dev/null differ diff --git a/icons-old/calculator_delete.png b/icons-old/calculator_delete.png deleted file mode 100644 index ac96170e..00000000 Binary files a/icons-old/calculator_delete.png and /dev/null differ diff --git a/icons-old/calculator_edit.png b/icons-old/calculator_edit.png deleted file mode 100644 index 63b06b98..00000000 Binary files a/icons-old/calculator_edit.png and /dev/null differ diff --git a/icons-old/calculator_error.png b/icons-old/calculator_error.png deleted file mode 100644 index 0bc4288a..00000000 Binary files a/icons-old/calculator_error.png and /dev/null differ diff --git a/icons-old/calculator_link.png b/icons-old/calculator_link.png deleted file mode 100644 index a2a8fe69..00000000 Binary files a/icons-old/calculator_link.png and /dev/null differ diff --git a/icons-old/calendar.png b/icons-old/calendar.png deleted file mode 100644 index 65891385..00000000 Binary files a/icons-old/calendar.png and /dev/null differ diff --git a/icons-old/calendar_add.png b/icons-old/calendar_add.png deleted file mode 100644 index 17679db6..00000000 Binary files a/icons-old/calendar_add.png and /dev/null differ diff --git a/icons-old/calendar_delete.png b/icons-old/calendar_delete.png deleted file mode 100644 index 69a3b10a..00000000 Binary files a/icons-old/calendar_delete.png and /dev/null differ diff --git a/icons-old/calendar_edit.png b/icons-old/calendar_edit.png deleted file mode 100644 index d1d2d6e6..00000000 Binary files a/icons-old/calendar_edit.png and /dev/null differ diff --git a/icons-old/calendar_link.png b/icons-old/calendar_link.png deleted file mode 100644 index 6b106b94..00000000 Binary files a/icons-old/calendar_link.png and /dev/null differ diff --git a/icons-old/calendar_view_day.png b/icons-old/calendar_view_day.png deleted file mode 100644 index 9740f76e..00000000 Binary files a/icons-old/calendar_view_day.png and /dev/null differ diff --git a/icons-old/calendar_view_month.png b/icons-old/calendar_view_month.png deleted file mode 100644 index 6cff76c1..00000000 Binary files a/icons-old/calendar_view_month.png and /dev/null differ diff --git a/icons-old/calendar_view_week.png b/icons-old/calendar_view_week.png deleted file mode 100644 index 8fe695f5..00000000 Binary files a/icons-old/calendar_view_week.png and /dev/null differ diff --git a/icons-old/camera.png b/icons-old/camera.png deleted file mode 100644 index 8536d1a7..00000000 Binary files a/icons-old/camera.png and /dev/null differ diff --git a/icons-old/camera_add.png b/icons-old/camera_add.png deleted file mode 100644 index 08b5da98..00000000 Binary files a/icons-old/camera_add.png and /dev/null differ diff --git a/icons-old/camera_delete.png b/icons-old/camera_delete.png deleted file mode 100644 index 3846d74c..00000000 Binary files a/icons-old/camera_delete.png and /dev/null differ diff --git a/icons-old/camera_edit.png b/icons-old/camera_edit.png deleted file mode 100644 index b5015b10..00000000 Binary files a/icons-old/camera_edit.png and /dev/null differ diff --git a/icons-old/camera_error.png b/icons-old/camera_error.png deleted file mode 100644 index 3c1bc95c..00000000 Binary files a/icons-old/camera_error.png and /dev/null differ diff --git a/icons-old/camera_go.png b/icons-old/camera_go.png deleted file mode 100644 index 94ce2b25..00000000 Binary files a/icons-old/camera_go.png and /dev/null differ diff --git a/icons-old/camera_link.png b/icons-old/camera_link.png deleted file mode 100644 index d2ac9f93..00000000 Binary files a/icons-old/camera_link.png and /dev/null differ diff --git a/icons-old/camera_small.png b/icons-old/camera_small.png deleted file mode 100644 index 454b0b01..00000000 Binary files a/icons-old/camera_small.png and /dev/null differ diff --git a/icons-old/cancel.png b/icons-old/cancel.png deleted file mode 100644 index c149c2bc..00000000 Binary files a/icons-old/cancel.png and /dev/null differ diff --git a/icons-old/car.png b/icons-old/car.png deleted file mode 100644 index 4f3a770f..00000000 Binary files a/icons-old/car.png and /dev/null differ diff --git a/icons-old/car_add.png b/icons-old/car_add.png deleted file mode 100644 index 1215a51a..00000000 Binary files a/icons-old/car_add.png and /dev/null differ diff --git a/icons-old/car_delete.png b/icons-old/car_delete.png deleted file mode 100644 index 2803b567..00000000 Binary files a/icons-old/car_delete.png and /dev/null differ diff --git a/icons-old/cart.png b/icons-old/cart.png deleted file mode 100644 index 1baf7b9f..00000000 Binary files a/icons-old/cart.png and /dev/null differ diff --git a/icons-old/cart_add.png b/icons-old/cart_add.png deleted file mode 100644 index 45c29000..00000000 Binary files a/icons-old/cart_add.png and /dev/null differ diff --git a/icons-old/cart_delete.png b/icons-old/cart_delete.png deleted file mode 100644 index ac5bce5c..00000000 Binary files a/icons-old/cart_delete.png and /dev/null differ diff --git a/icons-old/cart_edit.png b/icons-old/cart_edit.png deleted file mode 100644 index b94ff88e..00000000 Binary files a/icons-old/cart_edit.png and /dev/null differ diff --git a/icons-old/cart_error.png b/icons-old/cart_error.png deleted file mode 100644 index 144c8353..00000000 Binary files a/icons-old/cart_error.png and /dev/null differ diff --git a/icons-old/cart_go.png b/icons-old/cart_go.png deleted file mode 100644 index 20ee0584..00000000 Binary files a/icons-old/cart_go.png and /dev/null differ diff --git a/icons-old/cart_put.png b/icons-old/cart_put.png deleted file mode 100644 index 3aec353e..00000000 Binary files a/icons-old/cart_put.png and /dev/null differ diff --git a/icons-old/cart_remove.png b/icons-old/cart_remove.png deleted file mode 100644 index 360217b5..00000000 Binary files a/icons-old/cart_remove.png and /dev/null differ diff --git a/icons-old/cd.png b/icons-old/cd.png deleted file mode 100644 index ef432235..00000000 Binary files a/icons-old/cd.png and /dev/null differ diff --git a/icons-old/cd_add.png b/icons-old/cd_add.png deleted file mode 100644 index b0254eff..00000000 Binary files a/icons-old/cd_add.png and /dev/null differ diff --git a/icons-old/cd_burn.png b/icons-old/cd_burn.png deleted file mode 100644 index 157cb0ba..00000000 Binary files a/icons-old/cd_burn.png and /dev/null differ diff --git a/icons-old/cd_delete.png b/icons-old/cd_delete.png deleted file mode 100644 index 7d7b3d52..00000000 Binary files a/icons-old/cd_delete.png and /dev/null differ diff --git a/icons-old/cd_edit.png b/icons-old/cd_edit.png deleted file mode 100644 index b0dc194b..00000000 Binary files a/icons-old/cd_edit.png and /dev/null differ diff --git a/icons-old/cd_eject.png b/icons-old/cd_eject.png deleted file mode 100644 index 762932f6..00000000 Binary files a/icons-old/cd_eject.png and /dev/null differ diff --git a/icons-old/cd_go.png b/icons-old/cd_go.png deleted file mode 100644 index 13e04991..00000000 Binary files a/icons-old/cd_go.png and /dev/null differ diff --git a/icons-old/chart_bar.png b/icons-old/chart_bar.png deleted file mode 100644 index 9051fbc6..00000000 Binary files a/icons-old/chart_bar.png and /dev/null differ diff --git a/icons-old/chart_bar_add.png b/icons-old/chart_bar_add.png deleted file mode 100644 index d283e846..00000000 Binary files a/icons-old/chart_bar_add.png and /dev/null differ diff --git a/icons-old/chart_bar_delete.png b/icons-old/chart_bar_delete.png deleted file mode 100644 index 259f6868..00000000 Binary files a/icons-old/chart_bar_delete.png and /dev/null differ diff --git a/icons-old/chart_bar_edit.png b/icons-old/chart_bar_edit.png deleted file mode 100644 index df64d97e..00000000 Binary files a/icons-old/chart_bar_edit.png and /dev/null differ diff --git a/icons-old/chart_bar_error.png b/icons-old/chart_bar_error.png deleted file mode 100644 index bdacea5e..00000000 Binary files a/icons-old/chart_bar_error.png and /dev/null differ diff --git a/icons-old/chart_bar_link.png b/icons-old/chart_bar_link.png deleted file mode 100644 index bf18aed4..00000000 Binary files a/icons-old/chart_bar_link.png and /dev/null differ diff --git a/icons-old/chart_curve.png b/icons-old/chart_curve.png deleted file mode 100644 index 01e933a6..00000000 Binary files a/icons-old/chart_curve.png and /dev/null differ diff --git a/icons-old/chart_curve_add.png b/icons-old/chart_curve_add.png deleted file mode 100644 index f9e20504..00000000 Binary files a/icons-old/chart_curve_add.png and /dev/null differ diff --git a/icons-old/chart_curve_delete.png b/icons-old/chart_curve_delete.png deleted file mode 100644 index b411391c..00000000 Binary files a/icons-old/chart_curve_delete.png and /dev/null differ diff --git a/icons-old/chart_curve_edit.png b/icons-old/chart_curve_edit.png deleted file mode 100644 index bd07673b..00000000 Binary files a/icons-old/chart_curve_edit.png and /dev/null differ diff --git a/icons-old/chart_curve_error.png b/icons-old/chart_curve_error.png deleted file mode 100644 index 906dd038..00000000 Binary files a/icons-old/chart_curve_error.png and /dev/null differ diff --git a/icons-old/chart_curve_go.png b/icons-old/chart_curve_go.png deleted file mode 100644 index ac9eda51..00000000 Binary files a/icons-old/chart_curve_go.png and /dev/null differ diff --git a/icons-old/chart_curve_link.png b/icons-old/chart_curve_link.png deleted file mode 100644 index 144eafe0..00000000 Binary files a/icons-old/chart_curve_link.png and /dev/null differ diff --git a/icons-old/chart_line.png b/icons-old/chart_line.png deleted file mode 100644 index 85020f32..00000000 Binary files a/icons-old/chart_line.png and /dev/null differ diff --git a/icons-old/chart_line_add.png b/icons-old/chart_line_add.png deleted file mode 100644 index 5571a5eb..00000000 Binary files a/icons-old/chart_line_add.png and /dev/null differ diff --git a/icons-old/chart_line_delete.png b/icons-old/chart_line_delete.png deleted file mode 100644 index 5b0aa901..00000000 Binary files a/icons-old/chart_line_delete.png and /dev/null differ diff --git a/icons-old/chart_line_edit.png b/icons-old/chart_line_edit.png deleted file mode 100644 index 9cf66073..00000000 Binary files a/icons-old/chart_line_edit.png and /dev/null differ diff --git a/icons-old/chart_line_error.png b/icons-old/chart_line_error.png deleted file mode 100644 index ff23c03a..00000000 Binary files a/icons-old/chart_line_error.png and /dev/null differ diff --git a/icons-old/chart_line_link.png b/icons-old/chart_line_link.png deleted file mode 100644 index f3727d22..00000000 Binary files a/icons-old/chart_line_link.png and /dev/null differ diff --git a/icons-old/chart_organisation.png b/icons-old/chart_organisation.png deleted file mode 100644 index c32d25c1..00000000 Binary files a/icons-old/chart_organisation.png and /dev/null differ diff --git a/icons-old/chart_organisation_add.png b/icons-old/chart_organisation_add.png deleted file mode 100644 index f0dba4ac..00000000 Binary files a/icons-old/chart_organisation_add.png and /dev/null differ diff --git a/icons-old/chart_organisation_delete.png b/icons-old/chart_organisation_delete.png deleted file mode 100644 index 7dc8dcac..00000000 Binary files a/icons-old/chart_organisation_delete.png and /dev/null differ diff --git a/icons-old/chart_pie.png b/icons-old/chart_pie.png deleted file mode 100644 index fe00fa05..00000000 Binary files a/icons-old/chart_pie.png and /dev/null differ diff --git a/icons-old/chart_pie_add.png b/icons-old/chart_pie_add.png deleted file mode 100644 index bf0822ee..00000000 Binary files a/icons-old/chart_pie_add.png and /dev/null differ diff --git a/icons-old/chart_pie_delete.png b/icons-old/chart_pie_delete.png deleted file mode 100644 index 5ab9efd5..00000000 Binary files a/icons-old/chart_pie_delete.png and /dev/null differ diff --git a/icons-old/chart_pie_edit.png b/icons-old/chart_pie_edit.png deleted file mode 100644 index 3debc12d..00000000 Binary files a/icons-old/chart_pie_edit.png and /dev/null differ diff --git a/icons-old/chart_pie_error.png b/icons-old/chart_pie_error.png deleted file mode 100644 index 7344174f..00000000 Binary files a/icons-old/chart_pie_error.png and /dev/null differ diff --git a/icons-old/chart_pie_link.png b/icons-old/chart_pie_link.png deleted file mode 100644 index c072f8e0..00000000 Binary files a/icons-old/chart_pie_link.png and /dev/null differ diff --git a/icons-old/clear-icon.png b/icons-old/clear-icon.png deleted file mode 100755 index 37c28e7f..00000000 Binary files a/icons-old/clear-icon.png and /dev/null differ diff --git a/icons-old/clock.png b/icons-old/clock.png deleted file mode 100644 index e2672c20..00000000 Binary files a/icons-old/clock.png and /dev/null differ diff --git a/icons-old/clock_add.png b/icons-old/clock_add.png deleted file mode 100644 index 598b839b..00000000 Binary files a/icons-old/clock_add.png and /dev/null differ diff --git a/icons-old/clock_delete.png b/icons-old/clock_delete.png deleted file mode 100644 index 8bf9efe4..00000000 Binary files a/icons-old/clock_delete.png and /dev/null differ diff --git a/icons-old/clock_edit.png b/icons-old/clock_edit.png deleted file mode 100644 index 7d357188..00000000 Binary files a/icons-old/clock_edit.png and /dev/null differ diff --git a/icons-old/clock_error.png b/icons-old/clock_error.png deleted file mode 100644 index a7c461ba..00000000 Binary files a/icons-old/clock_error.png and /dev/null differ diff --git a/icons-old/clock_go.png b/icons-old/clock_go.png deleted file mode 100644 index a1a24d3f..00000000 Binary files a/icons-old/clock_go.png and /dev/null differ diff --git a/icons-old/clock_link.png b/icons-old/clock_link.png deleted file mode 100644 index 481cf04c..00000000 Binary files a/icons-old/clock_link.png and /dev/null differ diff --git a/icons-old/clock_pause.png b/icons-old/clock_pause.png deleted file mode 100644 index ba747258..00000000 Binary files a/icons-old/clock_pause.png and /dev/null differ diff --git a/icons-old/clock_play.png b/icons-old/clock_play.png deleted file mode 100644 index fb4ebc85..00000000 Binary files a/icons-old/clock_play.png and /dev/null differ diff --git a/icons-old/clock_red.png b/icons-old/clock_red.png deleted file mode 100644 index 2842cc33..00000000 Binary files a/icons-old/clock_red.png and /dev/null differ diff --git a/icons-old/clock_stop.png b/icons-old/clock_stop.png deleted file mode 100644 index 6fe8a6f9..00000000 Binary files a/icons-old/clock_stop.png and /dev/null differ diff --git a/icons-old/cog.png b/icons-old/cog.png deleted file mode 100644 index 67de2c6c..00000000 Binary files a/icons-old/cog.png and /dev/null differ diff --git a/icons-old/cog_add.png b/icons-old/cog_add.png deleted file mode 100644 index 04f22bad..00000000 Binary files a/icons-old/cog_add.png and /dev/null differ diff --git a/icons-old/cog_delete.png b/icons-old/cog_delete.png deleted file mode 100644 index 8ce71c47..00000000 Binary files a/icons-old/cog_delete.png and /dev/null differ diff --git a/icons-old/cog_edit.png b/icons-old/cog_edit.png deleted file mode 100644 index 47b75a45..00000000 Binary files a/icons-old/cog_edit.png and /dev/null differ diff --git a/icons-old/cog_error.png b/icons-old/cog_error.png deleted file mode 100644 index 47667430..00000000 Binary files a/icons-old/cog_error.png and /dev/null differ diff --git a/icons-old/cog_go.png b/icons-old/cog_go.png deleted file mode 100644 index 3262767c..00000000 Binary files a/icons-old/cog_go.png and /dev/null differ diff --git a/icons-old/coins.png b/icons-old/coins.png deleted file mode 100644 index 0ca9074d..00000000 Binary files a/icons-old/coins.png and /dev/null differ diff --git a/icons-old/coins_add.png b/icons-old/coins_add.png deleted file mode 100644 index cdff5d3d..00000000 Binary files a/icons-old/coins_add.png and /dev/null differ diff --git a/icons-old/coins_delete.png b/icons-old/coins_delete.png deleted file mode 100644 index 18e0c0fd..00000000 Binary files a/icons-old/coins_delete.png and /dev/null differ diff --git a/icons-old/color_swatch.png b/icons-old/color_swatch.png deleted file mode 100644 index 6e6e8521..00000000 Binary files a/icons-old/color_swatch.png and /dev/null differ diff --git a/icons-old/color_wheel.png b/icons-old/color_wheel.png deleted file mode 100644 index 809fb00e..00000000 Binary files a/icons-old/color_wheel.png and /dev/null differ diff --git a/icons-old/comment.png b/icons-old/comment.png deleted file mode 100644 index 7bc9233e..00000000 Binary files a/icons-old/comment.png and /dev/null differ diff --git a/icons-old/comment_add.png b/icons-old/comment_add.png deleted file mode 100644 index 75e78ded..00000000 Binary files a/icons-old/comment_add.png and /dev/null differ diff --git a/icons-old/comment_delete.png b/icons-old/comment_delete.png deleted file mode 100644 index 643fdbe8..00000000 Binary files a/icons-old/comment_delete.png and /dev/null differ diff --git a/icons-old/comment_edit.png b/icons-old/comment_edit.png deleted file mode 100644 index 73db110d..00000000 Binary files a/icons-old/comment_edit.png and /dev/null differ diff --git a/icons-old/comments.png b/icons-old/comments.png deleted file mode 100644 index 39433cf7..00000000 Binary files a/icons-old/comments.png and /dev/null differ diff --git a/icons-old/comments_add.png b/icons-old/comments_add.png deleted file mode 100644 index b3256344..00000000 Binary files a/icons-old/comments_add.png and /dev/null differ diff --git a/icons-old/comments_delete.png b/icons-old/comments_delete.png deleted file mode 100644 index 6df7376d..00000000 Binary files a/icons-old/comments_delete.png and /dev/null differ diff --git a/icons-old/compress.png b/icons-old/compress.png deleted file mode 100644 index 8606ff0f..00000000 Binary files a/icons-old/compress.png and /dev/null differ diff --git a/icons-old/computer.png b/icons-old/computer.png deleted file mode 100644 index 9bc37dce..00000000 Binary files a/icons-old/computer.png and /dev/null differ diff --git a/icons-old/computer_add.png b/icons-old/computer_add.png deleted file mode 100644 index db604ee3..00000000 Binary files a/icons-old/computer_add.png and /dev/null differ diff --git a/icons-old/computer_delete.png b/icons-old/computer_delete.png deleted file mode 100644 index 5e9b2683..00000000 Binary files a/icons-old/computer_delete.png and /dev/null differ diff --git a/icons-old/computer_edit.png b/icons-old/computer_edit.png deleted file mode 100644 index 34c72fe5..00000000 Binary files a/icons-old/computer_edit.png and /dev/null differ diff --git a/icons-old/computer_error.png b/icons-old/computer_error.png deleted file mode 100644 index b2c3ed57..00000000 Binary files a/icons-old/computer_error.png and /dev/null differ diff --git a/icons-old/computer_go.png b/icons-old/computer_go.png deleted file mode 100644 index 0b26144d..00000000 Binary files a/icons-old/computer_go.png and /dev/null differ diff --git a/icons-old/computer_key.png b/icons-old/computer_key.png deleted file mode 100644 index eca54301..00000000 Binary files a/icons-old/computer_key.png and /dev/null differ diff --git a/icons-old/computer_link.png b/icons-old/computer_link.png deleted file mode 100644 index 3859db21..00000000 Binary files a/icons-old/computer_link.png and /dev/null differ diff --git a/icons-old/connect.png b/icons-old/connect.png deleted file mode 100644 index 024138eb..00000000 Binary files a/icons-old/connect.png and /dev/null differ diff --git a/icons-old/contrast.png b/icons-old/contrast.png deleted file mode 100644 index adcc0046..00000000 Binary files a/icons-old/contrast.png and /dev/null differ diff --git a/icons-old/contrast_decrease.png b/icons-old/contrast_decrease.png deleted file mode 100644 index 0155bf5c..00000000 Binary files a/icons-old/contrast_decrease.png and /dev/null differ diff --git a/icons-old/contrast_high.png b/icons-old/contrast_high.png deleted file mode 100644 index d87c8cbb..00000000 Binary files a/icons-old/contrast_high.png and /dev/null differ diff --git a/icons-old/contrast_increase.png b/icons-old/contrast_increase.png deleted file mode 100644 index a3e7f520..00000000 Binary files a/icons-old/contrast_increase.png and /dev/null differ diff --git a/icons-old/contrast_low.png b/icons-old/contrast_low.png deleted file mode 100644 index dc9f4b10..00000000 Binary files a/icons-old/contrast_low.png and /dev/null differ diff --git a/icons-old/control_eject.png b/icons-old/control_eject.png deleted file mode 100644 index 924d817b..00000000 Binary files a/icons-old/control_eject.png and /dev/null differ diff --git a/icons-old/control_eject_blue.png b/icons-old/control_eject_blue.png deleted file mode 100644 index 2bd49638..00000000 Binary files a/icons-old/control_eject_blue.png and /dev/null differ diff --git a/icons-old/control_end.png b/icons-old/control_end.png deleted file mode 100644 index 036e04dc..00000000 Binary files a/icons-old/control_end.png and /dev/null differ diff --git a/icons-old/control_end_blue.png b/icons-old/control_end_blue.png deleted file mode 100644 index 72079357..00000000 Binary files a/icons-old/control_end_blue.png and /dev/null differ diff --git a/icons-old/control_equalizer.png b/icons-old/control_equalizer.png deleted file mode 100644 index 46060872..00000000 Binary files a/icons-old/control_equalizer.png and /dev/null differ diff --git a/icons-old/control_equalizer_blue.png b/icons-old/control_equalizer_blue.png deleted file mode 100644 index 1b2e6a37..00000000 Binary files a/icons-old/control_equalizer_blue.png and /dev/null differ diff --git a/icons-old/control_fastforward.png b/icons-old/control_fastforward.png deleted file mode 100644 index 31f7fd3a..00000000 Binary files a/icons-old/control_fastforward.png and /dev/null differ diff --git a/icons-old/control_fastforward_blue.png b/icons-old/control_fastforward_blue.png deleted file mode 100644 index 4a2f9d4e..00000000 Binary files a/icons-old/control_fastforward_blue.png and /dev/null differ diff --git a/icons-old/control_pause.png b/icons-old/control_pause.png deleted file mode 100644 index 2d9ce9c4..00000000 Binary files a/icons-old/control_pause.png and /dev/null differ diff --git a/icons-old/control_pause_blue.png b/icons-old/control_pause_blue.png deleted file mode 100644 index ec61099b..00000000 Binary files a/icons-old/control_pause_blue.png and /dev/null differ diff --git a/icons-old/control_play.png b/icons-old/control_play.png deleted file mode 100644 index 0846555d..00000000 Binary files a/icons-old/control_play.png and /dev/null differ diff --git a/icons-old/control_play_blue.png b/icons-old/control_play_blue.png deleted file mode 100644 index f8c8ec68..00000000 Binary files a/icons-old/control_play_blue.png and /dev/null differ diff --git a/icons-old/control_repeat.png b/icons-old/control_repeat.png deleted file mode 100644 index 1c4f57a1..00000000 Binary files a/icons-old/control_repeat.png and /dev/null differ diff --git a/icons-old/control_repeat_blue.png b/icons-old/control_repeat_blue.png deleted file mode 100644 index 406ec333..00000000 Binary files a/icons-old/control_repeat_blue.png and /dev/null differ diff --git a/icons-old/control_rewind.png b/icons-old/control_rewind.png deleted file mode 100644 index c0294477..00000000 Binary files a/icons-old/control_rewind.png and /dev/null differ diff --git a/icons-old/control_rewind_blue.png b/icons-old/control_rewind_blue.png deleted file mode 100644 index 15d1584b..00000000 Binary files a/icons-old/control_rewind_blue.png and /dev/null differ diff --git a/icons-old/control_start.png b/icons-old/control_start.png deleted file mode 100644 index 7dd1c07f..00000000 Binary files a/icons-old/control_start.png and /dev/null differ diff --git a/icons-old/control_start_blue.png b/icons-old/control_start_blue.png deleted file mode 100644 index 6f11fcb0..00000000 Binary files a/icons-old/control_start_blue.png and /dev/null differ diff --git a/icons-old/control_stop.png b/icons-old/control_stop.png deleted file mode 100644 index 893bb60e..00000000 Binary files a/icons-old/control_stop.png and /dev/null differ diff --git a/icons-old/control_stop_blue.png b/icons-old/control_stop_blue.png deleted file mode 100644 index e6f75d23..00000000 Binary files a/icons-old/control_stop_blue.png and /dev/null differ diff --git a/icons-old/controller.png b/icons-old/controller.png deleted file mode 100644 index 5cf76ed0..00000000 Binary files a/icons-old/controller.png and /dev/null differ diff --git a/icons-old/controller_add.png b/icons-old/controller_add.png deleted file mode 100644 index efecb387..00000000 Binary files a/icons-old/controller_add.png and /dev/null differ diff --git a/icons-old/controller_delete.png b/icons-old/controller_delete.png deleted file mode 100644 index 3d83bc7b..00000000 Binary files a/icons-old/controller_delete.png and /dev/null differ diff --git a/icons-old/controller_error.png b/icons-old/controller_error.png deleted file mode 100644 index 7f17c0cb..00000000 Binary files a/icons-old/controller_error.png and /dev/null differ diff --git a/icons-old/creditcards.png b/icons-old/creditcards.png deleted file mode 100644 index 4eae583e..00000000 Binary files a/icons-old/creditcards.png and /dev/null differ diff --git a/icons-old/cross.png b/icons-old/cross.png deleted file mode 100644 index 1514d51a..00000000 Binary files a/icons-old/cross.png and /dev/null differ diff --git a/icons-old/css.png b/icons-old/css.png deleted file mode 100644 index 23f31018..00000000 Binary files a/icons-old/css.png and /dev/null differ diff --git a/icons-old/css_add.png b/icons-old/css_add.png deleted file mode 100644 index e8ea10fc..00000000 Binary files a/icons-old/css_add.png and /dev/null differ diff --git a/icons-old/css_delete.png b/icons-old/css_delete.png deleted file mode 100644 index 326aba44..00000000 Binary files a/icons-old/css_delete.png and /dev/null differ diff --git a/icons-old/css_go.png b/icons-old/css_go.png deleted file mode 100644 index 6cdf38c3..00000000 Binary files a/icons-old/css_go.png and /dev/null differ diff --git a/icons-old/css_valid.png b/icons-old/css_valid.png deleted file mode 100644 index 4c72ca5a..00000000 Binary files a/icons-old/css_valid.png and /dev/null differ diff --git a/icons-old/cup.png b/icons-old/cup.png deleted file mode 100644 index b7bfcd15..00000000 Binary files a/icons-old/cup.png and /dev/null differ diff --git a/icons-old/cup_add.png b/icons-old/cup_add.png deleted file mode 100644 index 4ecaece2..00000000 Binary files a/icons-old/cup_add.png and /dev/null differ diff --git a/icons-old/cup_delete.png b/icons-old/cup_delete.png deleted file mode 100644 index 59a6d9c6..00000000 Binary files a/icons-old/cup_delete.png and /dev/null differ diff --git a/icons-old/cup_edit.png b/icons-old/cup_edit.png deleted file mode 100644 index 0b8f1e1e..00000000 Binary files a/icons-old/cup_edit.png and /dev/null differ diff --git a/icons-old/cup_error.png b/icons-old/cup_error.png deleted file mode 100644 index 68798748..00000000 Binary files a/icons-old/cup_error.png and /dev/null differ diff --git a/icons-old/cup_go.png b/icons-old/cup_go.png deleted file mode 100644 index 9527efbe..00000000 Binary files a/icons-old/cup_go.png and /dev/null differ diff --git a/icons-old/cup_key.png b/icons-old/cup_key.png deleted file mode 100644 index 7ae160ce..00000000 Binary files a/icons-old/cup_key.png and /dev/null differ diff --git a/icons-old/cup_link.png b/icons-old/cup_link.png deleted file mode 100644 index 41d1ace1..00000000 Binary files a/icons-old/cup_link.png and /dev/null differ diff --git a/icons-old/cursor.png b/icons-old/cursor.png deleted file mode 100644 index 532f532d..00000000 Binary files a/icons-old/cursor.png and /dev/null differ diff --git a/icons-old/cut.png b/icons-old/cut.png deleted file mode 100644 index f215d6f6..00000000 Binary files a/icons-old/cut.png and /dev/null differ diff --git a/icons-old/cut_red.png b/icons-old/cut_red.png deleted file mode 100644 index 85bb2f0f..00000000 Binary files a/icons-old/cut_red.png and /dev/null differ diff --git a/icons-old/database.png b/icons-old/database.png deleted file mode 100644 index 3d09261a..00000000 Binary files a/icons-old/database.png and /dev/null differ diff --git a/icons-old/database_add.png b/icons-old/database_add.png deleted file mode 100644 index 802bd6cd..00000000 Binary files a/icons-old/database_add.png and /dev/null differ diff --git a/icons-old/database_connect.png b/icons-old/database_connect.png deleted file mode 100644 index 3a111977..00000000 Binary files a/icons-old/database_connect.png and /dev/null differ diff --git a/icons-old/database_delete.png b/icons-old/database_delete.png deleted file mode 100644 index cce652e8..00000000 Binary files a/icons-old/database_delete.png and /dev/null differ diff --git a/icons-old/database_edit.png b/icons-old/database_edit.png deleted file mode 100644 index e501b668..00000000 Binary files a/icons-old/database_edit.png and /dev/null differ diff --git a/icons-old/database_error.png b/icons-old/database_error.png deleted file mode 100644 index 578221aa..00000000 Binary files a/icons-old/database_error.png and /dev/null differ diff --git a/icons-old/database_gear.png b/icons-old/database_gear.png deleted file mode 100644 index 7c0ab2b4..00000000 Binary files a/icons-old/database_gear.png and /dev/null differ diff --git a/icons-old/database_go.png b/icons-old/database_go.png deleted file mode 100644 index 61a8556c..00000000 Binary files a/icons-old/database_go.png and /dev/null differ diff --git a/icons-old/database_key.png b/icons-old/database_key.png deleted file mode 100644 index 33341476..00000000 Binary files a/icons-old/database_key.png and /dev/null differ diff --git a/icons-old/database_lightning.png b/icons-old/database_lightning.png deleted file mode 100644 index d9eefc22..00000000 Binary files a/icons-old/database_lightning.png and /dev/null differ diff --git a/icons-old/database_link.png b/icons-old/database_link.png deleted file mode 100644 index 4c8204af..00000000 Binary files a/icons-old/database_link.png and /dev/null differ diff --git a/icons-old/database_refresh.png b/icons-old/database_refresh.png deleted file mode 100644 index ff803be1..00000000 Binary files a/icons-old/database_refresh.png and /dev/null differ diff --git a/icons-old/database_save.png b/icons-old/database_save.png deleted file mode 100644 index 44c06ddd..00000000 Binary files a/icons-old/database_save.png and /dev/null differ diff --git a/icons-old/database_table.png b/icons-old/database_table.png deleted file mode 100644 index 693709cb..00000000 Binary files a/icons-old/database_table.png and /dev/null differ diff --git a/icons-old/date.png b/icons-old/date.png deleted file mode 100644 index 783c8335..00000000 Binary files a/icons-old/date.png and /dev/null differ diff --git a/icons-old/date_add.png b/icons-old/date_add.png deleted file mode 100644 index 6a7ae025..00000000 Binary files a/icons-old/date_add.png and /dev/null differ diff --git a/icons-old/date_delete.png b/icons-old/date_delete.png deleted file mode 100644 index 969a6b72..00000000 Binary files a/icons-old/date_delete.png and /dev/null differ diff --git a/icons-old/date_edit.png b/icons-old/date_edit.png deleted file mode 100644 index e6810650..00000000 Binary files a/icons-old/date_edit.png and /dev/null differ diff --git a/icons-old/date_error.png b/icons-old/date_error.png deleted file mode 100644 index 442cd97d..00000000 Binary files a/icons-old/date_error.png and /dev/null differ diff --git a/icons-old/date_go.png b/icons-old/date_go.png deleted file mode 100644 index 52dd9f3a..00000000 Binary files a/icons-old/date_go.png and /dev/null differ diff --git a/icons-old/date_link.png b/icons-old/date_link.png deleted file mode 100644 index 9f0aada7..00000000 Binary files a/icons-old/date_link.png and /dev/null differ diff --git a/icons-old/date_magnify.png b/icons-old/date_magnify.png deleted file mode 100644 index cd05f190..00000000 Binary files a/icons-old/date_magnify.png and /dev/null differ diff --git a/icons-old/date_next.png b/icons-old/date_next.png deleted file mode 100644 index 48d740ab..00000000 Binary files a/icons-old/date_next.png and /dev/null differ diff --git a/icons-old/date_previous.png b/icons-old/date_previous.png deleted file mode 100644 index e117a837..00000000 Binary files a/icons-old/date_previous.png and /dev/null differ diff --git a/icons-old/delete.png b/icons-old/delete.png deleted file mode 100644 index 08f24936..00000000 Binary files a/icons-old/delete.png and /dev/null differ diff --git a/icons-old/disconnect.png b/icons-old/disconnect.png deleted file mode 100644 index b335cb11..00000000 Binary files a/icons-old/disconnect.png and /dev/null differ diff --git a/icons-old/disk.png b/icons-old/disk.png deleted file mode 100644 index 99d532e8..00000000 Binary files a/icons-old/disk.png and /dev/null differ diff --git a/icons-old/disk_multiple.png b/icons-old/disk_multiple.png deleted file mode 100644 index fc5a52f5..00000000 Binary files a/icons-old/disk_multiple.png and /dev/null differ diff --git a/icons-old/door.png b/icons-old/door.png deleted file mode 100644 index 369fc46e..00000000 Binary files a/icons-old/door.png and /dev/null differ diff --git a/icons-old/door_in.png b/icons-old/door_in.png deleted file mode 100644 index 41676a0a..00000000 Binary files a/icons-old/door_in.png and /dev/null differ diff --git a/icons-old/door_open.png b/icons-old/door_open.png deleted file mode 100644 index 64bab57d..00000000 Binary files a/icons-old/door_open.png and /dev/null differ diff --git a/icons-old/door_out.png b/icons-old/door_out.png deleted file mode 100644 index 2541d2bc..00000000 Binary files a/icons-old/door_out.png and /dev/null differ diff --git a/icons-old/drink.png b/icons-old/drink.png deleted file mode 100644 index d98359c2..00000000 Binary files a/icons-old/drink.png and /dev/null differ diff --git a/icons-old/drink_empty.png b/icons-old/drink_empty.png deleted file mode 100644 index a40211ed..00000000 Binary files a/icons-old/drink_empty.png and /dev/null differ diff --git a/icons-old/drive.png b/icons-old/drive.png deleted file mode 100644 index 37b7c9b2..00000000 Binary files a/icons-old/drive.png and /dev/null differ diff --git a/icons-old/drive_add.png b/icons-old/drive_add.png deleted file mode 100644 index 29a35d5a..00000000 Binary files a/icons-old/drive_add.png and /dev/null differ diff --git a/icons-old/drive_burn.png b/icons-old/drive_burn.png deleted file mode 100644 index 80fd79f9..00000000 Binary files a/icons-old/drive_burn.png and /dev/null differ diff --git a/icons-old/drive_cd.png b/icons-old/drive_cd.png deleted file mode 100644 index 1850b701..00000000 Binary files a/icons-old/drive_cd.png and /dev/null differ diff --git a/icons-old/drive_cd_empty.png b/icons-old/drive_cd_empty.png deleted file mode 100644 index 8df38d9e..00000000 Binary files a/icons-old/drive_cd_empty.png and /dev/null differ diff --git a/icons-old/drive_delete.png b/icons-old/drive_delete.png deleted file mode 100644 index e6eb1866..00000000 Binary files a/icons-old/drive_delete.png and /dev/null differ diff --git a/icons-old/drive_disk.png b/icons-old/drive_disk.png deleted file mode 100644 index 5a51e819..00000000 Binary files a/icons-old/drive_disk.png and /dev/null differ diff --git a/icons-old/drive_edit.png b/icons-old/drive_edit.png deleted file mode 100644 index 7923fada..00000000 Binary files a/icons-old/drive_edit.png and /dev/null differ diff --git a/icons-old/drive_error.png b/icons-old/drive_error.png deleted file mode 100644 index 309f6396..00000000 Binary files a/icons-old/drive_error.png and /dev/null differ diff --git a/icons-old/drive_go.png b/icons-old/drive_go.png deleted file mode 100644 index fc53379e..00000000 Binary files a/icons-old/drive_go.png and /dev/null differ diff --git a/icons-old/drive_key.png b/icons-old/drive_key.png deleted file mode 100644 index d0b3c673..00000000 Binary files a/icons-old/drive_key.png and /dev/null differ diff --git a/icons-old/drive_link.png b/icons-old/drive_link.png deleted file mode 100644 index 8679c4b5..00000000 Binary files a/icons-old/drive_link.png and /dev/null differ diff --git a/icons-old/drive_magnify.png b/icons-old/drive_magnify.png deleted file mode 100644 index 0f0f4446..00000000 Binary files a/icons-old/drive_magnify.png and /dev/null differ diff --git a/icons-old/drive_network.png b/icons-old/drive_network.png deleted file mode 100644 index 63d2d5d5..00000000 Binary files a/icons-old/drive_network.png and /dev/null differ diff --git a/icons-old/drive_rename.png b/icons-old/drive_rename.png deleted file mode 100644 index 2a9f38b4..00000000 Binary files a/icons-old/drive_rename.png and /dev/null differ diff --git a/icons-old/drive_user.png b/icons-old/drive_user.png deleted file mode 100644 index 0b4751ce..00000000 Binary files a/icons-old/drive_user.png and /dev/null differ diff --git a/icons-old/drive_web.png b/icons-old/drive_web.png deleted file mode 100644 index 8850a835..00000000 Binary files a/icons-old/drive_web.png and /dev/null differ diff --git a/icons-old/dvd.png b/icons-old/dvd.png deleted file mode 100644 index 9d94de5d..00000000 Binary files a/icons-old/dvd.png and /dev/null differ diff --git a/icons-old/dvd_add.png b/icons-old/dvd_add.png deleted file mode 100644 index 517d1121..00000000 Binary files a/icons-old/dvd_add.png and /dev/null differ diff --git a/icons-old/dvd_delete.png b/icons-old/dvd_delete.png deleted file mode 100644 index 87bed221..00000000 Binary files a/icons-old/dvd_delete.png and /dev/null differ diff --git a/icons-old/dvd_edit.png b/icons-old/dvd_edit.png deleted file mode 100644 index d6330aa9..00000000 Binary files a/icons-old/dvd_edit.png and /dev/null differ diff --git a/icons-old/dvd_error.png b/icons-old/dvd_error.png deleted file mode 100644 index 8f6d4bee..00000000 Binary files a/icons-old/dvd_error.png and /dev/null differ diff --git a/icons-old/dvd_go.png b/icons-old/dvd_go.png deleted file mode 100644 index ef6959f7..00000000 Binary files a/icons-old/dvd_go.png and /dev/null differ diff --git a/icons-old/dvd_key.png b/icons-old/dvd_key.png deleted file mode 100644 index da9307f6..00000000 Binary files a/icons-old/dvd_key.png and /dev/null differ diff --git a/icons-old/dvd_link.png b/icons-old/dvd_link.png deleted file mode 100644 index caad7263..00000000 Binary files a/icons-old/dvd_link.png and /dev/null differ diff --git a/icons-old/email.png b/icons-old/email.png deleted file mode 100644 index 7348aed7..00000000 Binary files a/icons-old/email.png and /dev/null differ diff --git a/icons-old/email_add.png b/icons-old/email_add.png deleted file mode 100644 index 6c933681..00000000 Binary files a/icons-old/email_add.png and /dev/null differ diff --git a/icons-old/email_attach.png b/icons-old/email_attach.png deleted file mode 100644 index 1f994851..00000000 Binary files a/icons-old/email_attach.png and /dev/null differ diff --git a/icons-old/email_delete.png b/icons-old/email_delete.png deleted file mode 100644 index a9932b1a..00000000 Binary files a/icons-old/email_delete.png and /dev/null differ diff --git a/icons-old/email_edit.png b/icons-old/email_edit.png deleted file mode 100644 index 244f04ae..00000000 Binary files a/icons-old/email_edit.png and /dev/null differ diff --git a/icons-old/email_error.png b/icons-old/email_error.png deleted file mode 100644 index 8bdd3304..00000000 Binary files a/icons-old/email_error.png and /dev/null differ diff --git a/icons-old/email_go.png b/icons-old/email_go.png deleted file mode 100644 index 4a6c5d39..00000000 Binary files a/icons-old/email_go.png and /dev/null differ diff --git a/icons-old/email_link.png b/icons-old/email_link.png deleted file mode 100644 index 2c49f78a..00000000 Binary files a/icons-old/email_link.png and /dev/null differ diff --git a/icons-old/email_open.png b/icons-old/email_open.png deleted file mode 100644 index 7b6f9813..00000000 Binary files a/icons-old/email_open.png and /dev/null differ diff --git a/icons-old/email_open_image.png b/icons-old/email_open_image.png deleted file mode 100644 index e588e2fb..00000000 Binary files a/icons-old/email_open_image.png and /dev/null differ diff --git a/icons-old/emoticon_evilgrin.png b/icons-old/emoticon_evilgrin.png deleted file mode 100644 index 817bd509..00000000 Binary files a/icons-old/emoticon_evilgrin.png and /dev/null differ diff --git a/icons-old/emoticon_grin.png b/icons-old/emoticon_grin.png deleted file mode 100644 index fc60c5e1..00000000 Binary files a/icons-old/emoticon_grin.png and /dev/null differ diff --git a/icons-old/emoticon_happy.png b/icons-old/emoticon_happy.png deleted file mode 100644 index 6b7336e1..00000000 Binary files a/icons-old/emoticon_happy.png and /dev/null differ diff --git a/icons-old/emoticon_smile.png b/icons-old/emoticon_smile.png deleted file mode 100644 index ade43185..00000000 Binary files a/icons-old/emoticon_smile.png and /dev/null differ diff --git a/icons-old/emoticon_surprised.png b/icons-old/emoticon_surprised.png deleted file mode 100644 index 4520cfc5..00000000 Binary files a/icons-old/emoticon_surprised.png and /dev/null differ diff --git a/icons-old/emoticon_tongue.png b/icons-old/emoticon_tongue.png deleted file mode 100644 index ecafd2ff..00000000 Binary files a/icons-old/emoticon_tongue.png and /dev/null differ diff --git a/icons-old/emoticon_unhappy.png b/icons-old/emoticon_unhappy.png deleted file mode 100644 index fd5d030e..00000000 Binary files a/icons-old/emoticon_unhappy.png and /dev/null differ diff --git a/icons-old/emoticon_waii.png b/icons-old/emoticon_waii.png deleted file mode 100644 index 458f9361..00000000 Binary files a/icons-old/emoticon_waii.png and /dev/null differ diff --git a/icons-old/emoticon_wink.png b/icons-old/emoticon_wink.png deleted file mode 100644 index a631949b..00000000 Binary files a/icons-old/emoticon_wink.png and /dev/null differ diff --git a/icons-old/error.png b/icons-old/error.png deleted file mode 100644 index 628cf2da..00000000 Binary files a/icons-old/error.png and /dev/null differ diff --git a/icons-old/error_add.png b/icons-old/error_add.png deleted file mode 100644 index 4c974840..00000000 Binary files a/icons-old/error_add.png and /dev/null differ diff --git a/icons-old/error_delete.png b/icons-old/error_delete.png deleted file mode 100644 index 7f78bcc8..00000000 Binary files a/icons-old/error_delete.png and /dev/null differ diff --git a/icons-old/error_go.png b/icons-old/error_go.png deleted file mode 100644 index caa1838d..00000000 Binary files a/icons-old/error_go.png and /dev/null differ diff --git a/icons-old/exclamation.png b/icons-old/exclamation.png deleted file mode 100644 index c37bd062..00000000 Binary files a/icons-old/exclamation.png and /dev/null differ diff --git a/icons-old/eye.png b/icons-old/eye.png deleted file mode 100644 index 564a1a97..00000000 Binary files a/icons-old/eye.png and /dev/null differ diff --git a/icons-old/feed.png b/icons-old/feed.png deleted file mode 100644 index 315c4f4f..00000000 Binary files a/icons-old/feed.png and /dev/null differ diff --git a/icons-old/feed_add.png b/icons-old/feed_add.png deleted file mode 100644 index e77d46e8..00000000 Binary files a/icons-old/feed_add.png and /dev/null differ diff --git a/icons-old/feed_delete.png b/icons-old/feed_delete.png deleted file mode 100644 index 5e332b4c..00000000 Binary files a/icons-old/feed_delete.png and /dev/null differ diff --git a/icons-old/feed_disk.png b/icons-old/feed_disk.png deleted file mode 100644 index a158c998..00000000 Binary files a/icons-old/feed_disk.png and /dev/null differ diff --git a/icons-old/feed_edit.png b/icons-old/feed_edit.png deleted file mode 100644 index f1fde7a9..00000000 Binary files a/icons-old/feed_edit.png and /dev/null differ diff --git a/icons-old/feed_error.png b/icons-old/feed_error.png deleted file mode 100644 index c0a801c7..00000000 Binary files a/icons-old/feed_error.png and /dev/null differ diff --git a/icons-old/feed_go.png b/icons-old/feed_go.png deleted file mode 100644 index f2eed1ec..00000000 Binary files a/icons-old/feed_go.png and /dev/null differ diff --git a/icons-old/feed_key.png b/icons-old/feed_key.png deleted file mode 100644 index 156bfa97..00000000 Binary files a/icons-old/feed_key.png and /dev/null differ diff --git a/icons-old/feed_link.png b/icons-old/feed_link.png deleted file mode 100644 index c45a5345..00000000 Binary files a/icons-old/feed_link.png and /dev/null differ diff --git a/icons-old/feed_magnify.png b/icons-old/feed_magnify.png deleted file mode 100644 index 3023695d..00000000 Binary files a/icons-old/feed_magnify.png and /dev/null differ diff --git a/icons-old/female.png b/icons-old/female.png deleted file mode 100644 index f92958e6..00000000 Binary files a/icons-old/female.png and /dev/null differ diff --git a/icons-old/film.png b/icons-old/film.png deleted file mode 100644 index b0ce7bb1..00000000 Binary files a/icons-old/film.png and /dev/null differ diff --git a/icons-old/film_add.png b/icons-old/film_add.png deleted file mode 100644 index 40d681fe..00000000 Binary files a/icons-old/film_add.png and /dev/null differ diff --git a/icons-old/film_delete.png b/icons-old/film_delete.png deleted file mode 100644 index 23a2508c..00000000 Binary files a/icons-old/film_delete.png and /dev/null differ diff --git a/icons-old/film_edit.png b/icons-old/film_edit.png deleted file mode 100644 index af66b73f..00000000 Binary files a/icons-old/film_edit.png and /dev/null differ diff --git a/icons-old/film_error.png b/icons-old/film_error.png deleted file mode 100644 index 88f3d69b..00000000 Binary files a/icons-old/film_error.png and /dev/null differ diff --git a/icons-old/film_go.png b/icons-old/film_go.png deleted file mode 100644 index dd0168ea..00000000 Binary files a/icons-old/film_go.png and /dev/null differ diff --git a/icons-old/film_key.png b/icons-old/film_key.png deleted file mode 100644 index 58921624..00000000 Binary files a/icons-old/film_key.png and /dev/null differ diff --git a/icons-old/film_link.png b/icons-old/film_link.png deleted file mode 100644 index 0f24e86e..00000000 Binary files a/icons-old/film_link.png and /dev/null differ diff --git a/icons-old/film_save.png b/icons-old/film_save.png deleted file mode 100644 index bc8c0d35..00000000 Binary files a/icons-old/film_save.png and /dev/null differ diff --git a/icons-old/find.png b/icons-old/find.png deleted file mode 100644 index 15474796..00000000 Binary files a/icons-old/find.png and /dev/null differ diff --git a/icons-old/flag_blue.png b/icons-old/flag_blue.png deleted file mode 100644 index 003924f5..00000000 Binary files a/icons-old/flag_blue.png and /dev/null differ diff --git a/icons-old/flag_green.png b/icons-old/flag_green.png deleted file mode 100644 index e4bc611f..00000000 Binary files a/icons-old/flag_green.png and /dev/null differ diff --git a/icons-old/flag_orange.png b/icons-old/flag_orange.png deleted file mode 100644 index e6320242..00000000 Binary files a/icons-old/flag_orange.png and /dev/null differ diff --git a/icons-old/flag_pink.png b/icons-old/flag_pink.png deleted file mode 100644 index 5f15e526..00000000 Binary files a/icons-old/flag_pink.png and /dev/null differ diff --git a/icons-old/flag_purple.png b/icons-old/flag_purple.png deleted file mode 100644 index d0698664..00000000 Binary files a/icons-old/flag_purple.png and /dev/null differ diff --git a/icons-old/flag_red.png b/icons-old/flag_red.png deleted file mode 100644 index e8a602da..00000000 Binary files a/icons-old/flag_red.png and /dev/null differ diff --git a/icons-old/flag_yellow.png b/icons-old/flag_yellow.png deleted file mode 100644 index 14c89a54..00000000 Binary files a/icons-old/flag_yellow.png and /dev/null differ diff --git a/icons-old/folder-from-jstree-default-dark-32px.png b/icons-old/folder-from-jstree-default-dark-32px.png deleted file mode 100755 index eb3e1f51..00000000 Binary files a/icons-old/folder-from-jstree-default-dark-32px.png and /dev/null differ diff --git a/icons-old/folder.png b/icons-old/folder.png deleted file mode 100644 index 784e8fa4..00000000 Binary files a/icons-old/folder.png and /dev/null differ diff --git a/icons-old/folder_add.png b/icons-old/folder_add.png deleted file mode 100644 index 529fe8fe..00000000 Binary files a/icons-old/folder_add.png and /dev/null differ diff --git a/icons-old/folder_bell.png b/icons-old/folder_bell.png deleted file mode 100644 index d04dd7f5..00000000 Binary files a/icons-old/folder_bell.png and /dev/null differ diff --git a/icons-old/folder_brick.png b/icons-old/folder_brick.png deleted file mode 100644 index 5dea9769..00000000 Binary files a/icons-old/folder_brick.png and /dev/null differ diff --git a/icons-old/folder_bug.png b/icons-old/folder_bug.png deleted file mode 100644 index 4f791b68..00000000 Binary files a/icons-old/folder_bug.png and /dev/null differ diff --git a/icons-old/folder_camera.png b/icons-old/folder_camera.png deleted file mode 100644 index c9519416..00000000 Binary files a/icons-old/folder_camera.png and /dev/null differ diff --git a/icons-old/folder_database.png b/icons-old/folder_database.png deleted file mode 100644 index 5193e2ef..00000000 Binary files a/icons-old/folder_database.png and /dev/null differ diff --git a/icons-old/folder_delete.png b/icons-old/folder_delete.png deleted file mode 100644 index 112b0163..00000000 Binary files a/icons-old/folder_delete.png and /dev/null differ diff --git a/icons-old/folder_edit.png b/icons-old/folder_edit.png deleted file mode 100644 index ad669cc7..00000000 Binary files a/icons-old/folder_edit.png and /dev/null differ diff --git a/icons-old/folder_error.png b/icons-old/folder_error.png deleted file mode 100644 index 1af88095..00000000 Binary files a/icons-old/folder_error.png and /dev/null differ diff --git a/icons-old/folder_explore.png b/icons-old/folder_explore.png deleted file mode 100644 index 0ba93918..00000000 Binary files a/icons-old/folder_explore.png and /dev/null differ diff --git a/icons-old/folder_feed.png b/icons-old/folder_feed.png deleted file mode 100644 index d06ee51e..00000000 Binary files a/icons-old/folder_feed.png and /dev/null differ diff --git a/icons-old/folder_find.png b/icons-old/folder_find.png deleted file mode 100644 index c64e2ee6..00000000 Binary files a/icons-old/folder_find.png and /dev/null differ diff --git a/icons-old/folder_go.png b/icons-old/folder_go.png deleted file mode 100644 index 34a736f7..00000000 Binary files a/icons-old/folder_go.png and /dev/null differ diff --git a/icons-old/folder_heart.png b/icons-old/folder_heart.png deleted file mode 100644 index 56d7da1d..00000000 Binary files a/icons-old/folder_heart.png and /dev/null differ diff --git a/icons-old/folder_image.png b/icons-old/folder_image.png deleted file mode 100644 index d5df75bb..00000000 Binary files a/icons-old/folder_image.png and /dev/null differ diff --git a/icons-old/folder_key.png b/icons-old/folder_key.png deleted file mode 100644 index fb9b4c2b..00000000 Binary files a/icons-old/folder_key.png and /dev/null differ diff --git a/icons-old/folder_lightbulb.png b/icons-old/folder_lightbulb.png deleted file mode 100644 index f367a511..00000000 Binary files a/icons-old/folder_lightbulb.png and /dev/null differ diff --git a/icons-old/folder_link.png b/icons-old/folder_link.png deleted file mode 100644 index b9b75f6c..00000000 Binary files a/icons-old/folder_link.png and /dev/null differ diff --git a/icons-old/folder_magnify.png b/icons-old/folder_magnify.png deleted file mode 100644 index 0a3e7985..00000000 Binary files a/icons-old/folder_magnify.png and /dev/null differ diff --git a/icons-old/folder_page.png b/icons-old/folder_page.png deleted file mode 100644 index 1ef6e114..00000000 Binary files a/icons-old/folder_page.png and /dev/null differ diff --git a/icons-old/folder_page_white.png b/icons-old/folder_page_white.png deleted file mode 100644 index 14d6b618..00000000 Binary files a/icons-old/folder_page_white.png and /dev/null differ diff --git a/icons-old/folder_palette.png b/icons-old/folder_palette.png deleted file mode 100644 index ba12fe8a..00000000 Binary files a/icons-old/folder_palette.png and /dev/null differ diff --git a/icons-old/folder_picture.png b/icons-old/folder_picture.png deleted file mode 100644 index 052b3363..00000000 Binary files a/icons-old/folder_picture.png and /dev/null differ diff --git a/icons-old/folder_star.png b/icons-old/folder_star.png deleted file mode 100644 index 448e46fd..00000000 Binary files a/icons-old/folder_star.png and /dev/null differ diff --git a/icons-old/folder_table.png b/icons-old/folder_table.png deleted file mode 100644 index 473cee35..00000000 Binary files a/icons-old/folder_table.png and /dev/null differ diff --git a/icons-old/folder_user.png b/icons-old/folder_user.png deleted file mode 100644 index f021c3e1..00000000 Binary files a/icons-old/folder_user.png and /dev/null differ diff --git a/icons-old/folder_wrench.png b/icons-old/folder_wrench.png deleted file mode 100644 index ea3404e0..00000000 Binary files a/icons-old/folder_wrench.png and /dev/null differ diff --git a/icons-old/font.png b/icons-old/font.png deleted file mode 100644 index b7960db9..00000000 Binary files a/icons-old/font.png and /dev/null differ diff --git a/icons-old/font_add.png b/icons-old/font_add.png deleted file mode 100644 index b709ebae..00000000 Binary files a/icons-old/font_add.png and /dev/null differ diff --git a/icons-old/font_delete.png b/icons-old/font_delete.png deleted file mode 100644 index 1d6124d6..00000000 Binary files a/icons-old/font_delete.png and /dev/null differ diff --git a/icons-old/font_go.png b/icons-old/font_go.png deleted file mode 100644 index 75eba80d..00000000 Binary files a/icons-old/font_go.png and /dev/null differ diff --git a/icons-old/group.png b/icons-old/group.png deleted file mode 100644 index 7fb4e1f1..00000000 Binary files a/icons-old/group.png and /dev/null differ diff --git a/icons-old/group_add.png b/icons-old/group_add.png deleted file mode 100644 index 06c5350c..00000000 Binary files a/icons-old/group_add.png and /dev/null differ diff --git a/icons-old/group_delete.png b/icons-old/group_delete.png deleted file mode 100644 index 4489ca23..00000000 Binary files a/icons-old/group_delete.png and /dev/null differ diff --git a/icons-old/group_edit.png b/icons-old/group_edit.png deleted file mode 100644 index c88b945b..00000000 Binary files a/icons-old/group_edit.png and /dev/null differ diff --git a/icons-old/group_error.png b/icons-old/group_error.png deleted file mode 100644 index 7364a13c..00000000 Binary files a/icons-old/group_error.png and /dev/null differ diff --git a/icons-old/group_gear.png b/icons-old/group_gear.png deleted file mode 100644 index 2544f2e6..00000000 Binary files a/icons-old/group_gear.png and /dev/null differ diff --git a/icons-old/group_go.png b/icons-old/group_go.png deleted file mode 100644 index 1f523330..00000000 Binary files a/icons-old/group_go.png and /dev/null differ diff --git a/icons-old/group_key.png b/icons-old/group_key.png deleted file mode 100644 index 257f111c..00000000 Binary files a/icons-old/group_key.png and /dev/null differ diff --git a/icons-old/group_link.png b/icons-old/group_link.png deleted file mode 100644 index c77ed881..00000000 Binary files a/icons-old/group_link.png and /dev/null differ diff --git a/icons-old/heart.png b/icons-old/heart.png deleted file mode 100644 index d9ee53e5..00000000 Binary files a/icons-old/heart.png and /dev/null differ diff --git a/icons-old/heart_add.png b/icons-old/heart_add.png deleted file mode 100644 index d4195ff8..00000000 Binary files a/icons-old/heart_add.png and /dev/null differ diff --git a/icons-old/heart_delete.png b/icons-old/heart_delete.png deleted file mode 100644 index ce523e34..00000000 Binary files a/icons-old/heart_delete.png and /dev/null differ diff --git a/icons-old/help.png b/icons-old/help.png deleted file mode 100644 index 5c870176..00000000 Binary files a/icons-old/help.png and /dev/null differ diff --git a/icons-old/hourglass.png b/icons-old/hourglass.png deleted file mode 100644 index 57b03ce7..00000000 Binary files a/icons-old/hourglass.png and /dev/null differ diff --git a/icons-old/hourglass_add.png b/icons-old/hourglass_add.png deleted file mode 100644 index 170dfff1..00000000 Binary files a/icons-old/hourglass_add.png and /dev/null differ diff --git a/icons-old/hourglass_delete.png b/icons-old/hourglass_delete.png deleted file mode 100644 index 4b1337be..00000000 Binary files a/icons-old/hourglass_delete.png and /dev/null differ diff --git a/icons-old/hourglass_go.png b/icons-old/hourglass_go.png deleted file mode 100644 index b2d3a98b..00000000 Binary files a/icons-old/hourglass_go.png and /dev/null differ diff --git a/icons-old/hourglass_link.png b/icons-old/hourglass_link.png deleted file mode 100644 index ecc59b0a..00000000 Binary files a/icons-old/hourglass_link.png and /dev/null differ diff --git a/icons-old/house.png b/icons-old/house.png deleted file mode 100644 index fed62219..00000000 Binary files a/icons-old/house.png and /dev/null differ diff --git a/icons-old/house_go.png b/icons-old/house_go.png deleted file mode 100644 index 5457dbd3..00000000 Binary files a/icons-old/house_go.png and /dev/null differ diff --git a/icons-old/house_link.png b/icons-old/house_link.png deleted file mode 100644 index be2c2719..00000000 Binary files a/icons-old/house_link.png and /dev/null differ diff --git a/icons-old/html.png b/icons-old/html.png deleted file mode 100644 index 55d1072e..00000000 Binary files a/icons-old/html.png and /dev/null differ diff --git a/icons-old/html_add.png b/icons-old/html_add.png deleted file mode 100644 index f1c08b7d..00000000 Binary files a/icons-old/html_add.png and /dev/null differ diff --git a/icons-old/html_delete.png b/icons-old/html_delete.png deleted file mode 100644 index 1bd28489..00000000 Binary files a/icons-old/html_delete.png and /dev/null differ diff --git a/icons-old/html_go.png b/icons-old/html_go.png deleted file mode 100644 index a95cede1..00000000 Binary files a/icons-old/html_go.png and /dev/null differ diff --git a/icons-old/html_valid.png b/icons-old/html_valid.png deleted file mode 100644 index 71cec924..00000000 Binary files a/icons-old/html_valid.png and /dev/null differ diff --git a/icons-old/icon128.png b/icons-old/icon128.png deleted file mode 100755 index 79b452b4..00000000 Binary files a/icons-old/icon128.png and /dev/null differ diff --git a/icons-old/icon16.png b/icons-old/icon16.png deleted file mode 100755 index 6664ee4b..00000000 Binary files a/icons-old/icon16.png and /dev/null differ diff --git a/icons-old/icon19.png b/icons-old/icon19.png deleted file mode 100755 index 68aa4be7..00000000 Binary files a/icons-old/icon19.png and /dev/null differ diff --git a/icons-old/icon48.png b/icons-old/icon48.png deleted file mode 100755 index 4a016d7d..00000000 Binary files a/icons-old/icon48.png and /dev/null differ diff --git a/icons-old/image.png b/icons-old/image.png deleted file mode 100644 index fc3c393c..00000000 Binary files a/icons-old/image.png and /dev/null differ diff --git a/icons-old/image_add.png b/icons-old/image_add.png deleted file mode 100644 index fc5d6139..00000000 Binary files a/icons-old/image_add.png and /dev/null differ diff --git a/icons-old/image_delete.png b/icons-old/image_delete.png deleted file mode 100644 index c260e1d9..00000000 Binary files a/icons-old/image_delete.png and /dev/null differ diff --git a/icons-old/image_edit.png b/icons-old/image_edit.png deleted file mode 100644 index 0aa4cc65..00000000 Binary files a/icons-old/image_edit.png and /dev/null differ diff --git a/icons-old/image_link.png b/icons-old/image_link.png deleted file mode 100644 index 4bdb3541..00000000 Binary files a/icons-old/image_link.png and /dev/null differ diff --git a/icons-old/images.png b/icons-old/images.png deleted file mode 100644 index 184860d1..00000000 Binary files a/icons-old/images.png and /dev/null differ diff --git a/icons-old/information.png b/icons-old/information.png deleted file mode 100644 index 12cd1aef..00000000 Binary files a/icons-old/information.png and /dev/null differ diff --git a/icons-old/ipod.png b/icons-old/ipod.png deleted file mode 100644 index 3f768da5..00000000 Binary files a/icons-old/ipod.png and /dev/null differ diff --git a/icons-old/ipod_cast.png b/icons-old/ipod_cast.png deleted file mode 100644 index 6f6d3406..00000000 Binary files a/icons-old/ipod_cast.png and /dev/null differ diff --git a/icons-old/ipod_cast_add.png b/icons-old/ipod_cast_add.png deleted file mode 100644 index c3257f5f..00000000 Binary files a/icons-old/ipod_cast_add.png and /dev/null differ diff --git a/icons-old/ipod_cast_delete.png b/icons-old/ipod_cast_delete.png deleted file mode 100644 index 377ab695..00000000 Binary files a/icons-old/ipod_cast_delete.png and /dev/null differ diff --git a/icons-old/ipod_sound.png b/icons-old/ipod_sound.png deleted file mode 100644 index fef6e8ba..00000000 Binary files a/icons-old/ipod_sound.png and /dev/null differ diff --git a/icons-old/joystick.png b/icons-old/joystick.png deleted file mode 100644 index 62168f56..00000000 Binary files a/icons-old/joystick.png and /dev/null differ diff --git a/icons-old/joystick_add.png b/icons-old/joystick_add.png deleted file mode 100644 index 77e71077..00000000 Binary files a/icons-old/joystick_add.png and /dev/null differ diff --git a/icons-old/joystick_delete.png b/icons-old/joystick_delete.png deleted file mode 100644 index 5d44b592..00000000 Binary files a/icons-old/joystick_delete.png and /dev/null differ diff --git a/icons-old/joystick_error.png b/icons-old/joystick_error.png deleted file mode 100644 index b32149e2..00000000 Binary files a/icons-old/joystick_error.png and /dev/null differ diff --git a/icons-old/key.png b/icons-old/key.png deleted file mode 100644 index 4ec1a928..00000000 Binary files a/icons-old/key.png and /dev/null differ diff --git a/icons-old/key_add.png b/icons-old/key_add.png deleted file mode 100644 index d4074039..00000000 Binary files a/icons-old/key_add.png and /dev/null differ diff --git a/icons-old/key_delete.png b/icons-old/key_delete.png deleted file mode 100644 index 00dec80d..00000000 Binary files a/icons-old/key_delete.png and /dev/null differ diff --git a/icons-old/key_go.png b/icons-old/key_go.png deleted file mode 100644 index 30b0dc31..00000000 Binary files a/icons-old/key_go.png and /dev/null differ diff --git a/icons-old/keyboard.png b/icons-old/keyboard.png deleted file mode 100644 index 898d402d..00000000 Binary files a/icons-old/keyboard.png and /dev/null differ diff --git a/icons-old/keyboard_add.png b/icons-old/keyboard_add.png deleted file mode 100644 index 26938dd0..00000000 Binary files a/icons-old/keyboard_add.png and /dev/null differ diff --git a/icons-old/keyboard_delete.png b/icons-old/keyboard_delete.png deleted file mode 100644 index 1786ed5b..00000000 Binary files a/icons-old/keyboard_delete.png and /dev/null differ diff --git a/icons-old/keyboard_magnify.png b/icons-old/keyboard_magnify.png deleted file mode 100644 index 928fc17b..00000000 Binary files a/icons-old/keyboard_magnify.png and /dev/null differ diff --git a/icons-old/layers.png b/icons-old/layers.png deleted file mode 100644 index 00818f63..00000000 Binary files a/icons-old/layers.png and /dev/null differ diff --git a/icons-old/layout.png b/icons-old/layout.png deleted file mode 100644 index ea086b04..00000000 Binary files a/icons-old/layout.png and /dev/null differ diff --git a/icons-old/layout_add.png b/icons-old/layout_add.png deleted file mode 100644 index 62037221..00000000 Binary files a/icons-old/layout_add.png and /dev/null differ diff --git a/icons-old/layout_content.png b/icons-old/layout_content.png deleted file mode 100644 index b4aaad9a..00000000 Binary files a/icons-old/layout_content.png and /dev/null differ diff --git a/icons-old/layout_delete.png b/icons-old/layout_delete.png deleted file mode 100644 index 4bd45f13..00000000 Binary files a/icons-old/layout_delete.png and /dev/null differ diff --git a/icons-old/layout_edit.png b/icons-old/layout_edit.png deleted file mode 100644 index ab3100b5..00000000 Binary files a/icons-old/layout_edit.png and /dev/null differ diff --git a/icons-old/layout_error.png b/icons-old/layout_error.png deleted file mode 100644 index 5b5acea9..00000000 Binary files a/icons-old/layout_error.png and /dev/null differ diff --git a/icons-old/layout_header.png b/icons-old/layout_header.png deleted file mode 100644 index c6ea7f23..00000000 Binary files a/icons-old/layout_header.png and /dev/null differ diff --git a/icons-old/layout_link.png b/icons-old/layout_link.png deleted file mode 100644 index 3445d420..00000000 Binary files a/icons-old/layout_link.png and /dev/null differ diff --git a/icons-old/layout_sidebar.png b/icons-old/layout_sidebar.png deleted file mode 100644 index 3be27bb9..00000000 Binary files a/icons-old/layout_sidebar.png and /dev/null differ diff --git a/icons-old/lightbulb.png b/icons-old/lightbulb.png deleted file mode 100644 index d22fde8b..00000000 Binary files a/icons-old/lightbulb.png and /dev/null differ diff --git a/icons-old/lightbulb_add.png b/icons-old/lightbulb_add.png deleted file mode 100644 index 0dd848bd..00000000 Binary files a/icons-old/lightbulb_add.png and /dev/null differ diff --git a/icons-old/lightbulb_delete.png b/icons-old/lightbulb_delete.png deleted file mode 100644 index f4781daa..00000000 Binary files a/icons-old/lightbulb_delete.png and /dev/null differ diff --git a/icons-old/lightbulb_off.png b/icons-old/lightbulb_off.png deleted file mode 100644 index e95b8c5b..00000000 Binary files a/icons-old/lightbulb_off.png and /dev/null differ diff --git a/icons-old/lightning.png b/icons-old/lightning.png deleted file mode 100644 index 9680afd1..00000000 Binary files a/icons-old/lightning.png and /dev/null differ diff --git a/icons-old/lightning_add.png b/icons-old/lightning_add.png deleted file mode 100644 index dac3c905..00000000 Binary files a/icons-old/lightning_add.png and /dev/null differ diff --git a/icons-old/lightning_delete.png b/icons-old/lightning_delete.png deleted file mode 100644 index dfe27705..00000000 Binary files a/icons-old/lightning_delete.png and /dev/null differ diff --git a/icons-old/lightning_go.png b/icons-old/lightning_go.png deleted file mode 100644 index 29039e6a..00000000 Binary files a/icons-old/lightning_go.png and /dev/null differ diff --git a/icons-old/link.png b/icons-old/link.png deleted file mode 100644 index 25eacb7c..00000000 Binary files a/icons-old/link.png and /dev/null differ diff --git a/icons-old/link_add.png b/icons-old/link_add.png deleted file mode 100644 index 00be352c..00000000 Binary files a/icons-old/link_add.png and /dev/null differ diff --git a/icons-old/link_break.png b/icons-old/link_break.png deleted file mode 100644 index 52357530..00000000 Binary files a/icons-old/link_break.png and /dev/null differ diff --git a/icons-old/link_delete.png b/icons-old/link_delete.png deleted file mode 100644 index f66e2974..00000000 Binary files a/icons-old/link_delete.png and /dev/null differ diff --git a/icons-old/link_edit.png b/icons-old/link_edit.png deleted file mode 100644 index 5b3aed09..00000000 Binary files a/icons-old/link_edit.png and /dev/null differ diff --git a/icons-old/link_error.png b/icons-old/link_error.png deleted file mode 100644 index ab694b1a..00000000 Binary files a/icons-old/link_error.png and /dev/null differ diff --git a/icons-old/link_go.png b/icons-old/link_go.png deleted file mode 100644 index ae8cae80..00000000 Binary files a/icons-old/link_go.png and /dev/null differ diff --git a/icons-old/lock.png b/icons-old/lock.png deleted file mode 100644 index 2ebc4f6f..00000000 Binary files a/icons-old/lock.png and /dev/null differ diff --git a/icons-old/lock_add.png b/icons-old/lock_add.png deleted file mode 100644 index a7b566b1..00000000 Binary files a/icons-old/lock_add.png and /dev/null differ diff --git a/icons-old/lock_break.png b/icons-old/lock_break.png deleted file mode 100644 index 13578ab5..00000000 Binary files a/icons-old/lock_break.png and /dev/null differ diff --git a/icons-old/lock_delete.png b/icons-old/lock_delete.png deleted file mode 100644 index ecb50a93..00000000 Binary files a/icons-old/lock_delete.png and /dev/null differ diff --git a/icons-old/lock_edit.png b/icons-old/lock_edit.png deleted file mode 100644 index 116aa5b7..00000000 Binary files a/icons-old/lock_edit.png and /dev/null differ diff --git a/icons-old/lock_go.png b/icons-old/lock_go.png deleted file mode 100644 index 8c7c89b2..00000000 Binary files a/icons-old/lock_go.png and /dev/null differ diff --git a/icons-old/lock_open.png b/icons-old/lock_open.png deleted file mode 100644 index a471765f..00000000 Binary files a/icons-old/lock_open.png and /dev/null differ diff --git a/icons-old/lorry.png b/icons-old/lorry.png deleted file mode 100644 index 8f95f5a5..00000000 Binary files a/icons-old/lorry.png and /dev/null differ diff --git a/icons-old/lorry_add.png b/icons-old/lorry_add.png deleted file mode 100644 index a2c51249..00000000 Binary files a/icons-old/lorry_add.png and /dev/null differ diff --git a/icons-old/lorry_delete.png b/icons-old/lorry_delete.png deleted file mode 100644 index 66217f52..00000000 Binary files a/icons-old/lorry_delete.png and /dev/null differ diff --git a/icons-old/lorry_error.png b/icons-old/lorry_error.png deleted file mode 100644 index 3619ead9..00000000 Binary files a/icons-old/lorry_error.png and /dev/null differ diff --git a/icons-old/lorry_flatbed.png b/icons-old/lorry_flatbed.png deleted file mode 100644 index 8b20f550..00000000 Binary files a/icons-old/lorry_flatbed.png and /dev/null differ diff --git a/icons-old/lorry_go.png b/icons-old/lorry_go.png deleted file mode 100644 index 1c296a6a..00000000 Binary files a/icons-old/lorry_go.png and /dev/null differ diff --git a/icons-old/lorry_link.png b/icons-old/lorry_link.png deleted file mode 100644 index 5e6663e5..00000000 Binary files a/icons-old/lorry_link.png and /dev/null differ diff --git a/icons-old/magifier_zoom_out.png b/icons-old/magifier_zoom_out.png deleted file mode 100644 index 81f28199..00000000 Binary files a/icons-old/magifier_zoom_out.png and /dev/null differ diff --git a/icons-old/magnifier.png b/icons-old/magnifier.png deleted file mode 100644 index cf3d97f7..00000000 Binary files a/icons-old/magnifier.png and /dev/null differ diff --git a/icons-old/magnifier_zoom_in.png b/icons-old/magnifier_zoom_in.png deleted file mode 100644 index af4fe074..00000000 Binary files a/icons-old/magnifier_zoom_in.png and /dev/null differ diff --git a/icons-old/male.png b/icons-old/male.png deleted file mode 100644 index 25d6ea91..00000000 Binary files a/icons-old/male.png and /dev/null differ diff --git a/icons-old/map.png b/icons-old/map.png deleted file mode 100644 index f90ef25e..00000000 Binary files a/icons-old/map.png and /dev/null differ diff --git a/icons-old/map_add.png b/icons-old/map_add.png deleted file mode 100644 index 2b72da06..00000000 Binary files a/icons-old/map_add.png and /dev/null differ diff --git a/icons-old/map_delete.png b/icons-old/map_delete.png deleted file mode 100644 index e74402f9..00000000 Binary files a/icons-old/map_delete.png and /dev/null differ diff --git a/icons-old/map_edit.png b/icons-old/map_edit.png deleted file mode 100644 index 93d4d7e5..00000000 Binary files a/icons-old/map_edit.png and /dev/null differ diff --git a/icons-old/map_go.png b/icons-old/map_go.png deleted file mode 100644 index 11eab26d..00000000 Binary files a/icons-old/map_go.png and /dev/null differ diff --git a/icons-old/map_magnify.png b/icons-old/map_magnify.png deleted file mode 100644 index 7184c9dd..00000000 Binary files a/icons-old/map_magnify.png and /dev/null differ diff --git a/icons-old/medal_bronze_1.png b/icons-old/medal_bronze_1.png deleted file mode 100644 index 5f8a6d65..00000000 Binary files a/icons-old/medal_bronze_1.png and /dev/null differ diff --git a/icons-old/medal_bronze_2.png b/icons-old/medal_bronze_2.png deleted file mode 100644 index 623d68c5..00000000 Binary files a/icons-old/medal_bronze_2.png and /dev/null differ diff --git a/icons-old/medal_bronze_3.png b/icons-old/medal_bronze_3.png deleted file mode 100644 index ed3f43eb..00000000 Binary files a/icons-old/medal_bronze_3.png and /dev/null differ diff --git a/icons-old/medal_bronze_add.png b/icons-old/medal_bronze_add.png deleted file mode 100644 index 8487b2c1..00000000 Binary files a/icons-old/medal_bronze_add.png and /dev/null differ diff --git a/icons-old/medal_bronze_delete.png b/icons-old/medal_bronze_delete.png deleted file mode 100644 index d32aed72..00000000 Binary files a/icons-old/medal_bronze_delete.png and /dev/null differ diff --git a/icons-old/medal_gold_1.png b/icons-old/medal_gold_1.png deleted file mode 100644 index 87584dc9..00000000 Binary files a/icons-old/medal_gold_1.png and /dev/null differ diff --git a/icons-old/medal_gold_2.png b/icons-old/medal_gold_2.png deleted file mode 100644 index fa3a15dd..00000000 Binary files a/icons-old/medal_gold_2.png and /dev/null differ diff --git a/icons-old/medal_gold_3.png b/icons-old/medal_gold_3.png deleted file mode 100644 index ef1b08b9..00000000 Binary files a/icons-old/medal_gold_3.png and /dev/null differ diff --git a/icons-old/medal_gold_add.png b/icons-old/medal_gold_add.png deleted file mode 100644 index dcade0d8..00000000 Binary files a/icons-old/medal_gold_add.png and /dev/null differ diff --git a/icons-old/medal_gold_delete.png b/icons-old/medal_gold_delete.png deleted file mode 100644 index 84b06d5b..00000000 Binary files a/icons-old/medal_gold_delete.png and /dev/null differ diff --git a/icons-old/medal_silver_1.png b/icons-old/medal_silver_1.png deleted file mode 100644 index 75a64da3..00000000 Binary files a/icons-old/medal_silver_1.png and /dev/null differ diff --git a/icons-old/medal_silver_2.png b/icons-old/medal_silver_2.png deleted file mode 100644 index 2e0fe75c..00000000 Binary files a/icons-old/medal_silver_2.png and /dev/null differ diff --git a/icons-old/medal_silver_3.png b/icons-old/medal_silver_3.png deleted file mode 100644 index e385b546..00000000 Binary files a/icons-old/medal_silver_3.png and /dev/null differ diff --git a/icons-old/medal_silver_add.png b/icons-old/medal_silver_add.png deleted file mode 100644 index b0633fa0..00000000 Binary files a/icons-old/medal_silver_add.png and /dev/null differ diff --git a/icons-old/medal_silver_delete.png b/icons-old/medal_silver_delete.png deleted file mode 100644 index 06cab467..00000000 Binary files a/icons-old/medal_silver_delete.png and /dev/null differ diff --git a/icons-old/money.png b/icons-old/money.png deleted file mode 100644 index 42c52d05..00000000 Binary files a/icons-old/money.png and /dev/null differ diff --git a/icons-old/money_add.png b/icons-old/money_add.png deleted file mode 100644 index 588fa9d0..00000000 Binary files a/icons-old/money_add.png and /dev/null differ diff --git a/icons-old/money_delete.png b/icons-old/money_delete.png deleted file mode 100644 index eae2c524..00000000 Binary files a/icons-old/money_delete.png and /dev/null differ diff --git a/icons-old/money_dollar.png b/icons-old/money_dollar.png deleted file mode 100644 index 59af1638..00000000 Binary files a/icons-old/money_dollar.png and /dev/null differ diff --git a/icons-old/money_euro.png b/icons-old/money_euro.png deleted file mode 100644 index b322ba92..00000000 Binary files a/icons-old/money_euro.png and /dev/null differ diff --git a/icons-old/money_pound.png b/icons-old/money_pound.png deleted file mode 100644 index b7113646..00000000 Binary files a/icons-old/money_pound.png and /dev/null differ diff --git a/icons-old/money_yen.png b/icons-old/money_yen.png deleted file mode 100644 index 228a6778..00000000 Binary files a/icons-old/money_yen.png and /dev/null differ diff --git a/icons-old/monitor.png b/icons-old/monitor.png deleted file mode 100644 index d040bd02..00000000 Binary files a/icons-old/monitor.png and /dev/null differ diff --git a/icons-old/monitor_add.png b/icons-old/monitor_add.png deleted file mode 100644 index a8180664..00000000 Binary files a/icons-old/monitor_add.png and /dev/null differ diff --git a/icons-old/monitor_delete.png b/icons-old/monitor_delete.png deleted file mode 100644 index 37332563..00000000 Binary files a/icons-old/monitor_delete.png and /dev/null differ diff --git a/icons-old/monitor_edit.png b/icons-old/monitor_edit.png deleted file mode 100644 index f772c562..00000000 Binary files a/icons-old/monitor_edit.png and /dev/null differ diff --git a/icons-old/monitor_error.png b/icons-old/monitor_error.png deleted file mode 100644 index 270c5018..00000000 Binary files a/icons-old/monitor_error.png and /dev/null differ diff --git a/icons-old/monitor_go.png b/icons-old/monitor_go.png deleted file mode 100644 index 8af3eda9..00000000 Binary files a/icons-old/monitor_go.png and /dev/null differ diff --git a/icons-old/monitor_lightning.png b/icons-old/monitor_lightning.png deleted file mode 100644 index 06e53a9d..00000000 Binary files a/icons-old/monitor_lightning.png and /dev/null differ diff --git a/icons-old/monitor_link.png b/icons-old/monitor_link.png deleted file mode 100644 index a014b025..00000000 Binary files a/icons-old/monitor_link.png and /dev/null differ diff --git a/icons-old/mouse.png b/icons-old/mouse.png deleted file mode 100644 index 63a92fa9..00000000 Binary files a/icons-old/mouse.png and /dev/null differ diff --git a/icons-old/mouse_add.png b/icons-old/mouse_add.png deleted file mode 100644 index 65bcab52..00000000 Binary files a/icons-old/mouse_add.png and /dev/null differ diff --git a/icons-old/mouse_delete.png b/icons-old/mouse_delete.png deleted file mode 100644 index 72865668..00000000 Binary files a/icons-old/mouse_delete.png and /dev/null differ diff --git a/icons-old/mouse_error.png b/icons-old/mouse_error.png deleted file mode 100644 index bcc15623..00000000 Binary files a/icons-old/mouse_error.png and /dev/null differ diff --git a/icons-old/music.png b/icons-old/music.png deleted file mode 100644 index a8b3ede3..00000000 Binary files a/icons-old/music.png and /dev/null differ diff --git a/icons-old/new.png b/icons-old/new.png deleted file mode 100644 index 6a9bf037..00000000 Binary files a/icons-old/new.png and /dev/null differ diff --git a/icons-old/newspaper.png b/icons-old/newspaper.png deleted file mode 100644 index 6a2ecce1..00000000 Binary files a/icons-old/newspaper.png and /dev/null differ diff --git a/icons-old/newspaper_add.png b/icons-old/newspaper_add.png deleted file mode 100644 index 8140e8c1..00000000 Binary files a/icons-old/newspaper_add.png and /dev/null differ diff --git a/icons-old/newspaper_delete.png b/icons-old/newspaper_delete.png deleted file mode 100644 index bde96ce1..00000000 Binary files a/icons-old/newspaper_delete.png and /dev/null differ diff --git a/icons-old/newspaper_go.png b/icons-old/newspaper_go.png deleted file mode 100644 index fd614287..00000000 Binary files a/icons-old/newspaper_go.png and /dev/null differ diff --git a/icons-old/newspaper_link.png b/icons-old/newspaper_link.png deleted file mode 100644 index 99e57cba..00000000 Binary files a/icons-old/newspaper_link.png and /dev/null differ diff --git a/icons-old/note.png b/icons-old/note.png deleted file mode 100644 index 244e6ca0..00000000 Binary files a/icons-old/note.png and /dev/null differ diff --git a/icons-old/note_add.png b/icons-old/note_add.png deleted file mode 100644 index abdad91e..00000000 Binary files a/icons-old/note_add.png and /dev/null differ diff --git a/icons-old/note_delete.png b/icons-old/note_delete.png deleted file mode 100644 index 8a1f0ff5..00000000 Binary files a/icons-old/note_delete.png and /dev/null differ diff --git a/icons-old/note_edit.png b/icons-old/note_edit.png deleted file mode 100644 index 291bfc76..00000000 Binary files a/icons-old/note_edit.png and /dev/null differ diff --git a/icons-old/note_error.png b/icons-old/note_error.png deleted file mode 100644 index 896dadfd..00000000 Binary files a/icons-old/note_error.png and /dev/null differ diff --git a/icons-old/note_go.png b/icons-old/note_go.png deleted file mode 100644 index 49e54fd8..00000000 Binary files a/icons-old/note_go.png and /dev/null differ diff --git a/icons-old/overlays.png b/icons-old/overlays.png deleted file mode 100644 index ab3100b5..00000000 Binary files a/icons-old/overlays.png and /dev/null differ diff --git a/icons-old/package.png b/icons-old/package.png deleted file mode 100644 index da3c2a2d..00000000 Binary files a/icons-old/package.png and /dev/null differ diff --git a/icons-old/package_add.png b/icons-old/package_add.png deleted file mode 100644 index 9c8a9da4..00000000 Binary files a/icons-old/package_add.png and /dev/null differ diff --git a/icons-old/package_delete.png b/icons-old/package_delete.png deleted file mode 100644 index 86f7fbc2..00000000 Binary files a/icons-old/package_delete.png and /dev/null differ diff --git a/icons-old/package_go.png b/icons-old/package_go.png deleted file mode 100644 index aace63ad..00000000 Binary files a/icons-old/package_go.png and /dev/null differ diff --git a/icons-old/package_green.png b/icons-old/package_green.png deleted file mode 100644 index 25b28bb6..00000000 Binary files a/icons-old/package_green.png and /dev/null differ diff --git a/icons-old/package_link.png b/icons-old/package_link.png deleted file mode 100644 index 48e7ab55..00000000 Binary files a/icons-old/package_link.png and /dev/null differ diff --git a/icons-old/page.png b/icons-old/page.png deleted file mode 100644 index 03ddd799..00000000 Binary files a/icons-old/page.png and /dev/null differ diff --git a/icons-old/page_add.png b/icons-old/page_add.png deleted file mode 100644 index d5bfa071..00000000 Binary files a/icons-old/page_add.png and /dev/null differ diff --git a/icons-old/page_attach.png b/icons-old/page_attach.png deleted file mode 100644 index 89ee2da0..00000000 Binary files a/icons-old/page_attach.png and /dev/null differ diff --git a/icons-old/page_code.png b/icons-old/page_code.png deleted file mode 100644 index f7ea9041..00000000 Binary files a/icons-old/page_code.png and /dev/null differ diff --git a/icons-old/page_copy.png b/icons-old/page_copy.png deleted file mode 100644 index 195dc6d6..00000000 Binary files a/icons-old/page_copy.png and /dev/null differ diff --git a/icons-old/page_delete.png b/icons-old/page_delete.png deleted file mode 100644 index 3141467c..00000000 Binary files a/icons-old/page_delete.png and /dev/null differ diff --git a/icons-old/page_edit.png b/icons-old/page_edit.png deleted file mode 100644 index 046811ed..00000000 Binary files a/icons-old/page_edit.png and /dev/null differ diff --git a/icons-old/page_error.png b/icons-old/page_error.png deleted file mode 100644 index f07f449a..00000000 Binary files a/icons-old/page_error.png and /dev/null differ diff --git a/icons-old/page_excel.png b/icons-old/page_excel.png deleted file mode 100644 index eb6158eb..00000000 Binary files a/icons-old/page_excel.png and /dev/null differ diff --git a/icons-old/page_find.png b/icons-old/page_find.png deleted file mode 100644 index 2f193889..00000000 Binary files a/icons-old/page_find.png and /dev/null differ diff --git a/icons-old/page_gear.png b/icons-old/page_gear.png deleted file mode 100644 index 8e83281c..00000000 Binary files a/icons-old/page_gear.png and /dev/null differ diff --git a/icons-old/page_go.png b/icons-old/page_go.png deleted file mode 100644 index 80fe1ed0..00000000 Binary files a/icons-old/page_go.png and /dev/null differ diff --git a/icons-old/page_green.png b/icons-old/page_green.png deleted file mode 100644 index de8e003f..00000000 Binary files a/icons-old/page_green.png and /dev/null differ diff --git a/icons-old/page_key.png b/icons-old/page_key.png deleted file mode 100644 index d6626cb0..00000000 Binary files a/icons-old/page_key.png and /dev/null differ diff --git a/icons-old/page_lightning.png b/icons-old/page_lightning.png deleted file mode 100644 index 7e568703..00000000 Binary files a/icons-old/page_lightning.png and /dev/null differ diff --git a/icons-old/page_link.png b/icons-old/page_link.png deleted file mode 100644 index 312eab09..00000000 Binary files a/icons-old/page_link.png and /dev/null differ diff --git a/icons-old/page_paintbrush.png b/icons-old/page_paintbrush.png deleted file mode 100644 index 246a2f0b..00000000 Binary files a/icons-old/page_paintbrush.png and /dev/null differ diff --git a/icons-old/page_paste.png b/icons-old/page_paste.png deleted file mode 100644 index 968f073f..00000000 Binary files a/icons-old/page_paste.png and /dev/null differ diff --git a/icons-old/page_red.png b/icons-old/page_red.png deleted file mode 100644 index 0b18247d..00000000 Binary files a/icons-old/page_red.png and /dev/null differ diff --git a/icons-old/page_refresh.png b/icons-old/page_refresh.png deleted file mode 100644 index cf347c7d..00000000 Binary files a/icons-old/page_refresh.png and /dev/null differ diff --git a/icons-old/page_save.png b/icons-old/page_save.png deleted file mode 100644 index caea546a..00000000 Binary files a/icons-old/page_save.png and /dev/null differ diff --git a/icons-old/page_white.png b/icons-old/page_white.png deleted file mode 100644 index 8b8b1ca0..00000000 Binary files a/icons-old/page_white.png and /dev/null differ diff --git a/icons-old/page_white_acrobat.png b/icons-old/page_white_acrobat.png deleted file mode 100644 index 8f8095e4..00000000 Binary files a/icons-old/page_white_acrobat.png and /dev/null differ diff --git a/icons-old/page_white_actionscript.png b/icons-old/page_white_actionscript.png deleted file mode 100644 index 159b2407..00000000 Binary files a/icons-old/page_white_actionscript.png and /dev/null differ diff --git a/icons-old/page_white_add.png b/icons-old/page_white_add.png deleted file mode 100644 index aa23dde3..00000000 Binary files a/icons-old/page_white_add.png and /dev/null differ diff --git a/icons-old/page_white_c.png b/icons-old/page_white_c.png deleted file mode 100644 index 34a05ccc..00000000 Binary files a/icons-old/page_white_c.png and /dev/null differ diff --git a/icons-old/page_white_camera.png b/icons-old/page_white_camera.png deleted file mode 100644 index f501a593..00000000 Binary files a/icons-old/page_white_camera.png and /dev/null differ diff --git a/icons-old/page_white_cd.png b/icons-old/page_white_cd.png deleted file mode 100644 index 848bdaf3..00000000 Binary files a/icons-old/page_white_cd.png and /dev/null differ diff --git a/icons-old/page_white_code.png b/icons-old/page_white_code.png deleted file mode 100644 index 0c76bd12..00000000 Binary files a/icons-old/page_white_code.png and /dev/null differ diff --git a/icons-old/page_white_code_red.png b/icons-old/page_white_code_red.png deleted file mode 100644 index 87a69145..00000000 Binary files a/icons-old/page_white_code_red.png and /dev/null differ diff --git a/icons-old/page_white_coldfusion.png b/icons-old/page_white_coldfusion.png deleted file mode 100644 index c66011fb..00000000 Binary files a/icons-old/page_white_coldfusion.png and /dev/null differ diff --git a/icons-old/page_white_compressed.png b/icons-old/page_white_compressed.png deleted file mode 100644 index 2b6b1007..00000000 Binary files a/icons-old/page_white_compressed.png and /dev/null differ diff --git a/icons-old/page_white_copy.png b/icons-old/page_white_copy.png deleted file mode 100644 index a9f31a27..00000000 Binary files a/icons-old/page_white_copy.png and /dev/null differ diff --git a/icons-old/page_white_cplusplus.png b/icons-old/page_white_cplusplus.png deleted file mode 100644 index a87cf847..00000000 Binary files a/icons-old/page_white_cplusplus.png and /dev/null differ diff --git a/icons-old/page_white_csharp.png b/icons-old/page_white_csharp.png deleted file mode 100644 index ffb8fc93..00000000 Binary files a/icons-old/page_white_csharp.png and /dev/null differ diff --git a/icons-old/page_white_cup.png b/icons-old/page_white_cup.png deleted file mode 100644 index 0a7d6f4a..00000000 Binary files a/icons-old/page_white_cup.png and /dev/null differ diff --git a/icons-old/page_white_database.png b/icons-old/page_white_database.png deleted file mode 100644 index bddba1f9..00000000 Binary files a/icons-old/page_white_database.png and /dev/null differ diff --git a/icons-old/page_white_delete.png b/icons-old/page_white_delete.png deleted file mode 100644 index af1ecaf2..00000000 Binary files a/icons-old/page_white_delete.png and /dev/null differ diff --git a/icons-old/page_white_dvd.png b/icons-old/page_white_dvd.png deleted file mode 100644 index 4cc537af..00000000 Binary files a/icons-old/page_white_dvd.png and /dev/null differ diff --git a/icons-old/page_white_edit.png b/icons-old/page_white_edit.png deleted file mode 100644 index b93e7760..00000000 Binary files a/icons-old/page_white_edit.png and /dev/null differ diff --git a/icons-old/page_white_error.png b/icons-old/page_white_error.png deleted file mode 100644 index 9fc5a0a1..00000000 Binary files a/icons-old/page_white_error.png and /dev/null differ diff --git a/icons-old/page_white_excel.png b/icons-old/page_white_excel.png deleted file mode 100644 index b977d7e5..00000000 Binary files a/icons-old/page_white_excel.png and /dev/null differ diff --git a/icons-old/page_white_find.png b/icons-old/page_white_find.png deleted file mode 100644 index 58184363..00000000 Binary files a/icons-old/page_white_find.png and /dev/null differ diff --git a/icons-old/page_white_flash.png b/icons-old/page_white_flash.png deleted file mode 100644 index 5769120b..00000000 Binary files a/icons-old/page_white_flash.png and /dev/null differ diff --git a/icons-old/page_white_freehand.png b/icons-old/page_white_freehand.png deleted file mode 100644 index 8d719df5..00000000 Binary files a/icons-old/page_white_freehand.png and /dev/null differ diff --git a/icons-old/page_white_gear.png b/icons-old/page_white_gear.png deleted file mode 100644 index 106f5aa3..00000000 Binary files a/icons-old/page_white_gear.png and /dev/null differ diff --git a/icons-old/page_white_get.png b/icons-old/page_white_get.png deleted file mode 100644 index e4a1ecba..00000000 Binary files a/icons-old/page_white_get.png and /dev/null differ diff --git a/icons-old/page_white_go.png b/icons-old/page_white_go.png deleted file mode 100644 index 7e62a924..00000000 Binary files a/icons-old/page_white_go.png and /dev/null differ diff --git a/icons-old/page_white_h.png b/icons-old/page_white_h.png deleted file mode 100644 index e902abb0..00000000 Binary files a/icons-old/page_white_h.png and /dev/null differ diff --git a/icons-old/page_white_horizontal.png b/icons-old/page_white_horizontal.png deleted file mode 100644 index 1d2d0a49..00000000 Binary files a/icons-old/page_white_horizontal.png and /dev/null differ diff --git a/icons-old/page_white_key.png b/icons-old/page_white_key.png deleted file mode 100644 index d6164845..00000000 Binary files a/icons-old/page_white_key.png and /dev/null differ diff --git a/icons-old/page_white_lightning.png b/icons-old/page_white_lightning.png deleted file mode 100644 index 7215d1e8..00000000 Binary files a/icons-old/page_white_lightning.png and /dev/null differ diff --git a/icons-old/page_white_link.png b/icons-old/page_white_link.png deleted file mode 100644 index bf7bd1c9..00000000 Binary files a/icons-old/page_white_link.png and /dev/null differ diff --git a/icons-old/page_white_magnify.png b/icons-old/page_white_magnify.png deleted file mode 100644 index f6b74cc4..00000000 Binary files a/icons-old/page_white_magnify.png and /dev/null differ diff --git a/icons-old/page_white_medal.png b/icons-old/page_white_medal.png deleted file mode 100644 index d3fffb6d..00000000 Binary files a/icons-old/page_white_medal.png and /dev/null differ diff --git a/icons-old/page_white_office.png b/icons-old/page_white_office.png deleted file mode 100644 index a65bcb3e..00000000 Binary files a/icons-old/page_white_office.png and /dev/null differ diff --git a/icons-old/page_white_paint.png b/icons-old/page_white_paint.png deleted file mode 100644 index 23a37b89..00000000 Binary files a/icons-old/page_white_paint.png and /dev/null differ diff --git a/icons-old/page_white_paintbrush.png b/icons-old/page_white_paintbrush.png deleted file mode 100644 index f907e44b..00000000 Binary files a/icons-old/page_white_paintbrush.png and /dev/null differ diff --git a/icons-old/page_white_paste.png b/icons-old/page_white_paste.png deleted file mode 100644 index 5b2cbb3f..00000000 Binary files a/icons-old/page_white_paste.png and /dev/null differ diff --git a/icons-old/page_white_php.png b/icons-old/page_white_php.png deleted file mode 100644 index 7868a259..00000000 Binary files a/icons-old/page_white_php.png and /dev/null differ diff --git a/icons-old/page_white_picture.png b/icons-old/page_white_picture.png deleted file mode 100644 index 134b6693..00000000 Binary files a/icons-old/page_white_picture.png and /dev/null differ diff --git a/icons-old/page_white_powerpoint.png b/icons-old/page_white_powerpoint.png deleted file mode 100644 index c4eff038..00000000 Binary files a/icons-old/page_white_powerpoint.png and /dev/null differ diff --git a/icons-old/page_white_put.png b/icons-old/page_white_put.png deleted file mode 100644 index 884ffd6f..00000000 Binary files a/icons-old/page_white_put.png and /dev/null differ diff --git a/icons-old/page_white_red_banner.png b/icons-old/page_white_red_banner.png deleted file mode 100755 index 680037fd..00000000 Binary files a/icons-old/page_white_red_banner.png and /dev/null differ diff --git a/icons-old/page_white_ruby.png b/icons-old/page_white_ruby.png deleted file mode 100644 index f59b7c43..00000000 Binary files a/icons-old/page_white_ruby.png and /dev/null differ diff --git a/icons-old/page_white_stack.png b/icons-old/page_white_stack.png deleted file mode 100644 index 44084add..00000000 Binary files a/icons-old/page_white_stack.png and /dev/null differ diff --git a/icons-old/page_white_star.png b/icons-old/page_white_star.png deleted file mode 100644 index 3a1441c9..00000000 Binary files a/icons-old/page_white_star.png and /dev/null differ diff --git a/icons-old/page_white_swoosh.png b/icons-old/page_white_swoosh.png deleted file mode 100644 index e7708292..00000000 Binary files a/icons-old/page_white_swoosh.png and /dev/null differ diff --git a/icons-old/page_white_text.png b/icons-old/page_white_text.png deleted file mode 100644 index 813f712f..00000000 Binary files a/icons-old/page_white_text.png and /dev/null differ diff --git a/icons-old/page_white_text_width.png b/icons-old/page_white_text_width.png deleted file mode 100644 index d9cf1325..00000000 Binary files a/icons-old/page_white_text_width.png and /dev/null differ diff --git a/icons-old/page_white_tux.png b/icons-old/page_white_tux.png deleted file mode 100644 index 52699bfe..00000000 Binary files a/icons-old/page_white_tux.png and /dev/null differ diff --git a/icons-old/page_white_vector.png b/icons-old/page_white_vector.png deleted file mode 100644 index 4a05955b..00000000 Binary files a/icons-old/page_white_vector.png and /dev/null differ diff --git a/icons-old/page_white_visualstudio.png b/icons-old/page_white_visualstudio.png deleted file mode 100644 index a0a433df..00000000 Binary files a/icons-old/page_white_visualstudio.png and /dev/null differ diff --git a/icons-old/page_white_width.png b/icons-old/page_white_width.png deleted file mode 100644 index 1eb88094..00000000 Binary files a/icons-old/page_white_width.png and /dev/null differ diff --git a/icons-old/page_white_word.png b/icons-old/page_white_word.png deleted file mode 100644 index ae8ecbf4..00000000 Binary files a/icons-old/page_white_word.png and /dev/null differ diff --git a/icons-old/page_white_world.png b/icons-old/page_white_world.png deleted file mode 100644 index 6ed2490e..00000000 Binary files a/icons-old/page_white_world.png and /dev/null differ diff --git a/icons-old/page_white_wrench.png b/icons-old/page_white_wrench.png deleted file mode 100644 index fecadd08..00000000 Binary files a/icons-old/page_white_wrench.png and /dev/null differ diff --git a/icons-old/page_white_zip.png b/icons-old/page_white_zip.png deleted file mode 100644 index fd4bbccd..00000000 Binary files a/icons-old/page_white_zip.png and /dev/null differ diff --git a/icons-old/page_word.png b/icons-old/page_word.png deleted file mode 100644 index 834cdfaf..00000000 Binary files a/icons-old/page_word.png and /dev/null differ diff --git a/icons-old/page_world.png b/icons-old/page_world.png deleted file mode 100644 index b8895dde..00000000 Binary files a/icons-old/page_world.png and /dev/null differ diff --git a/icons-old/paintbrush.png b/icons-old/paintbrush.png deleted file mode 100644 index a3ecf877..00000000 Binary files a/icons-old/paintbrush.png and /dev/null differ diff --git a/icons-old/paintcan.png b/icons-old/paintcan.png deleted file mode 100644 index f82a8865..00000000 Binary files a/icons-old/paintcan.png and /dev/null differ diff --git a/icons-old/palette.png b/icons-old/palette.png deleted file mode 100644 index 73c5b3f2..00000000 Binary files a/icons-old/palette.png and /dev/null differ diff --git a/icons-old/paste_plain.png b/icons-old/paste_plain.png deleted file mode 100644 index c0490eb7..00000000 Binary files a/icons-old/paste_plain.png and /dev/null differ diff --git a/icons-old/paste_word.png b/icons-old/paste_word.png deleted file mode 100644 index f6b87f82..00000000 Binary files a/icons-old/paste_word.png and /dev/null differ diff --git a/icons-old/pencil.png b/icons-old/pencil.png deleted file mode 100644 index 0bfecd50..00000000 Binary files a/icons-old/pencil.png and /dev/null differ diff --git a/icons-old/pencil_add.png b/icons-old/pencil_add.png deleted file mode 100644 index 902bbe61..00000000 Binary files a/icons-old/pencil_add.png and /dev/null differ diff --git a/icons-old/pencil_delete.png b/icons-old/pencil_delete.png deleted file mode 100644 index d8944e6e..00000000 Binary files a/icons-old/pencil_delete.png and /dev/null differ diff --git a/icons-old/pencil_go.png b/icons-old/pencil_go.png deleted file mode 100644 index 937bded9..00000000 Binary files a/icons-old/pencil_go.png and /dev/null differ diff --git a/icons-old/phone.png b/icons-old/phone.png deleted file mode 100644 index c39f162f..00000000 Binary files a/icons-old/phone.png and /dev/null differ diff --git a/icons-old/phone_add.png b/icons-old/phone_add.png deleted file mode 100644 index d3555e02..00000000 Binary files a/icons-old/phone_add.png and /dev/null differ diff --git a/icons-old/phone_delete.png b/icons-old/phone_delete.png deleted file mode 100644 index bbe4f8ab..00000000 Binary files a/icons-old/phone_delete.png and /dev/null differ diff --git a/icons-old/phone_sound.png b/icons-old/phone_sound.png deleted file mode 100644 index 7fdf1c58..00000000 Binary files a/icons-old/phone_sound.png and /dev/null differ diff --git a/icons-old/photo.png b/icons-old/photo.png deleted file mode 100644 index 6c2aaaaa..00000000 Binary files a/icons-old/photo.png and /dev/null differ diff --git a/icons-old/photo_add.png b/icons-old/photo_add.png deleted file mode 100644 index 63cc355c..00000000 Binary files a/icons-old/photo_add.png and /dev/null differ diff --git a/icons-old/photo_delete.png b/icons-old/photo_delete.png deleted file mode 100644 index 18b67df4..00000000 Binary files a/icons-old/photo_delete.png and /dev/null differ diff --git a/icons-old/photo_link.png b/icons-old/photo_link.png deleted file mode 100644 index e6bb35fb..00000000 Binary files a/icons-old/photo_link.png and /dev/null differ diff --git a/icons-old/photos.png b/icons-old/photos.png deleted file mode 100644 index 8836fe6c..00000000 Binary files a/icons-old/photos.png and /dev/null differ diff --git a/icons-old/picture.png b/icons-old/picture.png deleted file mode 100644 index 4a158fef..00000000 Binary files a/icons-old/picture.png and /dev/null differ diff --git a/icons-old/picture_add.png b/icons-old/picture_add.png deleted file mode 100644 index d6d3f856..00000000 Binary files a/icons-old/picture_add.png and /dev/null differ diff --git a/icons-old/picture_delete.png b/icons-old/picture_delete.png deleted file mode 100644 index cca9f535..00000000 Binary files a/icons-old/picture_delete.png and /dev/null differ diff --git a/icons-old/picture_edit.png b/icons-old/picture_edit.png deleted file mode 100644 index 9a70c349..00000000 Binary files a/icons-old/picture_edit.png and /dev/null differ diff --git a/icons-old/picture_empty.png b/icons-old/picture_empty.png deleted file mode 100644 index abd2b9bb..00000000 Binary files a/icons-old/picture_empty.png and /dev/null differ diff --git a/icons-old/picture_error.png b/icons-old/picture_error.png deleted file mode 100644 index d41d90d6..00000000 Binary files a/icons-old/picture_error.png and /dev/null differ diff --git a/icons-old/picture_go.png b/icons-old/picture_go.png deleted file mode 100644 index 27c63c5a..00000000 Binary files a/icons-old/picture_go.png and /dev/null differ diff --git a/icons-old/picture_key.png b/icons-old/picture_key.png deleted file mode 100644 index 667086c0..00000000 Binary files a/icons-old/picture_key.png and /dev/null differ diff --git a/icons-old/picture_link.png b/icons-old/picture_link.png deleted file mode 100644 index 42dca744..00000000 Binary files a/icons-old/picture_link.png and /dev/null differ diff --git a/icons-old/picture_save.png b/icons-old/picture_save.png deleted file mode 100644 index 777fb5d2..00000000 Binary files a/icons-old/picture_save.png and /dev/null differ diff --git a/icons-old/pictures.png b/icons-old/pictures.png deleted file mode 100644 index d9591c13..00000000 Binary files a/icons-old/pictures.png and /dev/null differ diff --git a/icons-old/pilcrow.png b/icons-old/pilcrow.png deleted file mode 100644 index 95704fba..00000000 Binary files a/icons-old/pilcrow.png and /dev/null differ diff --git a/icons-old/pill.png b/icons-old/pill.png deleted file mode 100644 index f2bdef6b..00000000 Binary files a/icons-old/pill.png and /dev/null differ diff --git a/icons-old/pill_add.png b/icons-old/pill_add.png deleted file mode 100644 index ac9c2df6..00000000 Binary files a/icons-old/pill_add.png and /dev/null differ diff --git a/icons-old/pill_delete.png b/icons-old/pill_delete.png deleted file mode 100644 index c61592e8..00000000 Binary files a/icons-old/pill_delete.png and /dev/null differ diff --git a/icons-old/pill_go.png b/icons-old/pill_go.png deleted file mode 100644 index e5c07d41..00000000 Binary files a/icons-old/pill_go.png and /dev/null differ diff --git a/icons-old/plugin.png b/icons-old/plugin.png deleted file mode 100644 index 6187b15a..00000000 Binary files a/icons-old/plugin.png and /dev/null differ diff --git a/icons-old/plugin_add.png b/icons-old/plugin_add.png deleted file mode 100644 index ae43690e..00000000 Binary files a/icons-old/plugin_add.png and /dev/null differ diff --git a/icons-old/plugin_delete.png b/icons-old/plugin_delete.png deleted file mode 100644 index d9c3376d..00000000 Binary files a/icons-old/plugin_delete.png and /dev/null differ diff --git a/icons-old/plugin_disabled.png b/icons-old/plugin_disabled.png deleted file mode 100644 index f4f6be59..00000000 Binary files a/icons-old/plugin_disabled.png and /dev/null differ diff --git a/icons-old/plugin_edit.png b/icons-old/plugin_edit.png deleted file mode 100644 index b6cb0ecf..00000000 Binary files a/icons-old/plugin_edit.png and /dev/null differ diff --git a/icons-old/plugin_error.png b/icons-old/plugin_error.png deleted file mode 100644 index cff65d7f..00000000 Binary files a/icons-old/plugin_error.png and /dev/null differ diff --git a/icons-old/plugin_go.png b/icons-old/plugin_go.png deleted file mode 100644 index 41da9913..00000000 Binary files a/icons-old/plugin_go.png and /dev/null differ diff --git a/icons-old/plugin_link.png b/icons-old/plugin_link.png deleted file mode 100644 index 445c1886..00000000 Binary files a/icons-old/plugin_link.png and /dev/null differ diff --git a/icons-old/printer.png b/icons-old/printer.png deleted file mode 100644 index a350d187..00000000 Binary files a/icons-old/printer.png and /dev/null differ diff --git a/icons-old/printer_add.png b/icons-old/printer_add.png deleted file mode 100644 index d228d058..00000000 Binary files a/icons-old/printer_add.png and /dev/null differ diff --git a/icons-old/printer_delete.png b/icons-old/printer_delete.png deleted file mode 100644 index 1d8605f2..00000000 Binary files a/icons-old/printer_delete.png and /dev/null differ diff --git a/icons-old/printer_empty.png b/icons-old/printer_empty.png deleted file mode 100644 index 94e8c161..00000000 Binary files a/icons-old/printer_empty.png and /dev/null differ diff --git a/icons-old/printer_error.png b/icons-old/printer_error.png deleted file mode 100644 index 279ebb0e..00000000 Binary files a/icons-old/printer_error.png and /dev/null differ diff --git a/icons-old/rainbow.png b/icons-old/rainbow.png deleted file mode 100644 index 5ede989a..00000000 Binary files a/icons-old/rainbow.png and /dev/null differ diff --git a/icons-old/report.png b/icons-old/report.png deleted file mode 100644 index 779ad58e..00000000 Binary files a/icons-old/report.png and /dev/null differ diff --git a/icons-old/report_add.png b/icons-old/report_add.png deleted file mode 100644 index d5eac9bc..00000000 Binary files a/icons-old/report_add.png and /dev/null differ diff --git a/icons-old/report_delete.png b/icons-old/report_delete.png deleted file mode 100644 index dcce0b64..00000000 Binary files a/icons-old/report_delete.png and /dev/null differ diff --git a/icons-old/report_disk.png b/icons-old/report_disk.png deleted file mode 100644 index 1c856cd6..00000000 Binary files a/icons-old/report_disk.png and /dev/null differ diff --git a/icons-old/report_edit.png b/icons-old/report_edit.png deleted file mode 100644 index c61a6d84..00000000 Binary files a/icons-old/report_edit.png and /dev/null differ diff --git a/icons-old/report_go.png b/icons-old/report_go.png deleted file mode 100644 index f35a9793..00000000 Binary files a/icons-old/report_go.png and /dev/null differ diff --git a/icons-old/report_key.png b/icons-old/report_key.png deleted file mode 100644 index 90b758e8..00000000 Binary files a/icons-old/report_key.png and /dev/null differ diff --git a/icons-old/report_link.png b/icons-old/report_link.png deleted file mode 100644 index 23f2611e..00000000 Binary files a/icons-old/report_link.png and /dev/null differ diff --git a/icons-old/report_magnify.png b/icons-old/report_magnify.png deleted file mode 100644 index aeaa8895..00000000 Binary files a/icons-old/report_magnify.png and /dev/null differ diff --git a/icons-old/report_picture.png b/icons-old/report_picture.png deleted file mode 100644 index 3a9a7e5e..00000000 Binary files a/icons-old/report_picture.png and /dev/null differ diff --git a/icons-old/report_user.png b/icons-old/report_user.png deleted file mode 100644 index 7766edd7..00000000 Binary files a/icons-old/report_user.png and /dev/null differ diff --git a/icons-old/report_word.png b/icons-old/report_word.png deleted file mode 100644 index 99513424..00000000 Binary files a/icons-old/report_word.png and /dev/null differ diff --git a/icons-old/resultset_first.png b/icons-old/resultset_first.png deleted file mode 100644 index b03eaf8b..00000000 Binary files a/icons-old/resultset_first.png and /dev/null differ diff --git a/icons-old/resultset_last.png b/icons-old/resultset_last.png deleted file mode 100644 index 8ec89478..00000000 Binary files a/icons-old/resultset_last.png and /dev/null differ diff --git a/icons-old/resultset_next.png b/icons-old/resultset_next.png deleted file mode 100644 index e252606d..00000000 Binary files a/icons-old/resultset_next.png and /dev/null differ diff --git a/icons-old/resultset_previous.png b/icons-old/resultset_previous.png deleted file mode 100644 index 18f9cc10..00000000 Binary files a/icons-old/resultset_previous.png and /dev/null differ diff --git a/icons-old/rosette.png b/icons-old/rosette.png deleted file mode 100644 index f233bc77..00000000 Binary files a/icons-old/rosette.png and /dev/null differ diff --git a/icons-old/rss.png b/icons-old/rss.png deleted file mode 100644 index 1dc6ff30..00000000 Binary files a/icons-old/rss.png and /dev/null differ diff --git a/icons-old/rss_add.png b/icons-old/rss_add.png deleted file mode 100644 index b590beb7..00000000 Binary files a/icons-old/rss_add.png and /dev/null differ diff --git a/icons-old/rss_delete.png b/icons-old/rss_delete.png deleted file mode 100644 index 9deb738d..00000000 Binary files a/icons-old/rss_delete.png and /dev/null differ diff --git a/icons-old/rss_go.png b/icons-old/rss_go.png deleted file mode 100644 index 43a86bff..00000000 Binary files a/icons-old/rss_go.png and /dev/null differ diff --git a/icons-old/rss_valid.png b/icons-old/rss_valid.png deleted file mode 100644 index a6d0b0e8..00000000 Binary files a/icons-old/rss_valid.png and /dev/null differ diff --git a/icons-old/ruby.png b/icons-old/ruby.png deleted file mode 100644 index f763a168..00000000 Binary files a/icons-old/ruby.png and /dev/null differ diff --git a/icons-old/ruby_add.png b/icons-old/ruby_add.png deleted file mode 100644 index a2cd648f..00000000 Binary files a/icons-old/ruby_add.png and /dev/null differ diff --git a/icons-old/ruby_delete.png b/icons-old/ruby_delete.png deleted file mode 100644 index 30022630..00000000 Binary files a/icons-old/ruby_delete.png and /dev/null differ diff --git a/icons-old/ruby_gear.png b/icons-old/ruby_gear.png deleted file mode 100644 index 4a10590b..00000000 Binary files a/icons-old/ruby_gear.png and /dev/null differ diff --git a/icons-old/ruby_get.png b/icons-old/ruby_get.png deleted file mode 100644 index f5203c7e..00000000 Binary files a/icons-old/ruby_get.png and /dev/null differ diff --git a/icons-old/ruby_go.png b/icons-old/ruby_go.png deleted file mode 100644 index d8d276e3..00000000 Binary files a/icons-old/ruby_go.png and /dev/null differ diff --git a/icons-old/ruby_key.png b/icons-old/ruby_key.png deleted file mode 100644 index 451cfebe..00000000 Binary files a/icons-old/ruby_key.png and /dev/null differ diff --git a/icons-old/ruby_link.png b/icons-old/ruby_link.png deleted file mode 100644 index bf4be526..00000000 Binary files a/icons-old/ruby_link.png and /dev/null differ diff --git a/icons-old/ruby_put.png b/icons-old/ruby_put.png deleted file mode 100644 index e026323c..00000000 Binary files a/icons-old/ruby_put.png and /dev/null differ diff --git a/icons-old/script.png b/icons-old/script.png deleted file mode 100644 index 0f9ed4d4..00000000 Binary files a/icons-old/script.png and /dev/null differ diff --git a/icons-old/script_add.png b/icons-old/script_add.png deleted file mode 100644 index d650552d..00000000 Binary files a/icons-old/script_add.png and /dev/null differ diff --git a/icons-old/script_code.png b/icons-old/script_code.png deleted file mode 100644 index 63fe6cef..00000000 Binary files a/icons-old/script_code.png and /dev/null differ diff --git a/icons-old/script_code_red.png b/icons-old/script_code_red.png deleted file mode 100644 index 8fcf0f09..00000000 Binary files a/icons-old/script_code_red.png and /dev/null differ diff --git a/icons-old/script_delete.png b/icons-old/script_delete.png deleted file mode 100644 index e6500ced..00000000 Binary files a/icons-old/script_delete.png and /dev/null differ diff --git a/icons-old/script_edit.png b/icons-old/script_edit.png deleted file mode 100644 index b4d31ce2..00000000 Binary files a/icons-old/script_edit.png and /dev/null differ diff --git a/icons-old/script_error.png b/icons-old/script_error.png deleted file mode 100644 index 04919548..00000000 Binary files a/icons-old/script_error.png and /dev/null differ diff --git a/icons-old/script_gear.png b/icons-old/script_gear.png deleted file mode 100644 index 56fcf84a..00000000 Binary files a/icons-old/script_gear.png and /dev/null differ diff --git a/icons-old/script_go.png b/icons-old/script_go.png deleted file mode 100644 index 8e154e23..00000000 Binary files a/icons-old/script_go.png and /dev/null differ diff --git a/icons-old/script_key.png b/icons-old/script_key.png deleted file mode 100644 index 49bb24d7..00000000 Binary files a/icons-old/script_key.png and /dev/null differ diff --git a/icons-old/script_lightning.png b/icons-old/script_lightning.png deleted file mode 100644 index b3fa18ce..00000000 Binary files a/icons-old/script_lightning.png and /dev/null differ diff --git a/icons-old/script_link.png b/icons-old/script_link.png deleted file mode 100644 index bdeb9852..00000000 Binary files a/icons-old/script_link.png and /dev/null differ diff --git a/icons-old/script_palette.png b/icons-old/script_palette.png deleted file mode 100644 index 6d46962d..00000000 Binary files a/icons-old/script_palette.png and /dev/null differ diff --git a/icons-old/script_save.png b/icons-old/script_save.png deleted file mode 100644 index 36216d82..00000000 Binary files a/icons-old/script_save.png and /dev/null differ diff --git a/icons-old/server.png b/icons-old/server.png deleted file mode 100644 index 720a237c..00000000 Binary files a/icons-old/server.png and /dev/null differ diff --git a/icons-old/server_add.png b/icons-old/server_add.png deleted file mode 100644 index 3f10a3a9..00000000 Binary files a/icons-old/server_add.png and /dev/null differ diff --git a/icons-old/server_chart.png b/icons-old/server_chart.png deleted file mode 100644 index 1128d3f3..00000000 Binary files a/icons-old/server_chart.png and /dev/null differ diff --git a/icons-old/server_compressed.png b/icons-old/server_compressed.png deleted file mode 100644 index bf49fad9..00000000 Binary files a/icons-old/server_compressed.png and /dev/null differ diff --git a/icons-old/server_connect.png b/icons-old/server_connect.png deleted file mode 100644 index 49b26914..00000000 Binary files a/icons-old/server_connect.png and /dev/null differ diff --git a/icons-old/server_database.png b/icons-old/server_database.png deleted file mode 100644 index b24e826c..00000000 Binary files a/icons-old/server_database.png and /dev/null differ diff --git a/icons-old/server_delete.png b/icons-old/server_delete.png deleted file mode 100644 index 61e740fe..00000000 Binary files a/icons-old/server_delete.png and /dev/null differ diff --git a/icons-old/server_edit.png b/icons-old/server_edit.png deleted file mode 100644 index dc762537..00000000 Binary files a/icons-old/server_edit.png and /dev/null differ diff --git a/icons-old/server_error.png b/icons-old/server_error.png deleted file mode 100644 index f6402563..00000000 Binary files a/icons-old/server_error.png and /dev/null differ diff --git a/icons-old/server_go.png b/icons-old/server_go.png deleted file mode 100644 index 540c8e26..00000000 Binary files a/icons-old/server_go.png and /dev/null differ diff --git a/icons-old/server_key.png b/icons-old/server_key.png deleted file mode 100644 index ecd51742..00000000 Binary files a/icons-old/server_key.png and /dev/null differ diff --git a/icons-old/server_lightning.png b/icons-old/server_lightning.png deleted file mode 100644 index b0f4e46c..00000000 Binary files a/icons-old/server_lightning.png and /dev/null differ diff --git a/icons-old/server_link.png b/icons-old/server_link.png deleted file mode 100644 index e8821dfd..00000000 Binary files a/icons-old/server_link.png and /dev/null differ diff --git a/icons-old/server_uncompressed.png b/icons-old/server_uncompressed.png deleted file mode 100644 index 86e8325b..00000000 Binary files a/icons-old/server_uncompressed.png and /dev/null differ diff --git a/icons-old/shading.png b/icons-old/shading.png deleted file mode 100644 index 09275f9c..00000000 Binary files a/icons-old/shading.png and /dev/null differ diff --git a/icons-old/shape_align_bottom.png b/icons-old/shape_align_bottom.png deleted file mode 100644 index 55d26940..00000000 Binary files a/icons-old/shape_align_bottom.png and /dev/null differ diff --git a/icons-old/shape_align_center.png b/icons-old/shape_align_center.png deleted file mode 100644 index efe9a98e..00000000 Binary files a/icons-old/shape_align_center.png and /dev/null differ diff --git a/icons-old/shape_align_left.png b/icons-old/shape_align_left.png deleted file mode 100644 index aaedc41b..00000000 Binary files a/icons-old/shape_align_left.png and /dev/null differ diff --git a/icons-old/shape_align_middle.png b/icons-old/shape_align_middle.png deleted file mode 100644 index d350dd88..00000000 Binary files a/icons-old/shape_align_middle.png and /dev/null differ diff --git a/icons-old/shape_align_right.png b/icons-old/shape_align_right.png deleted file mode 100644 index ff556b6a..00000000 Binary files a/icons-old/shape_align_right.png and /dev/null differ diff --git a/icons-old/shape_align_top.png b/icons-old/shape_align_top.png deleted file mode 100644 index 1181b43f..00000000 Binary files a/icons-old/shape_align_top.png and /dev/null differ diff --git a/icons-old/shape_flip_horizontal.png b/icons-old/shape_flip_horizontal.png deleted file mode 100644 index 8667c81f..00000000 Binary files a/icons-old/shape_flip_horizontal.png and /dev/null differ diff --git a/icons-old/shape_flip_vertical.png b/icons-old/shape_flip_vertical.png deleted file mode 100644 index 0bd66d19..00000000 Binary files a/icons-old/shape_flip_vertical.png and /dev/null differ diff --git a/icons-old/shape_group.png b/icons-old/shape_group.png deleted file mode 100644 index bb2ff516..00000000 Binary files a/icons-old/shape_group.png and /dev/null differ diff --git a/icons-old/shape_handles.png b/icons-old/shape_handles.png deleted file mode 100644 index ce27fe3a..00000000 Binary files a/icons-old/shape_handles.png and /dev/null differ diff --git a/icons-old/shape_move_back.png b/icons-old/shape_move_back.png deleted file mode 100644 index a216ffd3..00000000 Binary files a/icons-old/shape_move_back.png and /dev/null differ diff --git a/icons-old/shape_move_backwards.png b/icons-old/shape_move_backwards.png deleted file mode 100644 index ee3f9b27..00000000 Binary files a/icons-old/shape_move_backwards.png and /dev/null differ diff --git a/icons-old/shape_move_forwards.png b/icons-old/shape_move_forwards.png deleted file mode 100644 index cfe44932..00000000 Binary files a/icons-old/shape_move_forwards.png and /dev/null differ diff --git a/icons-old/shape_move_front.png b/icons-old/shape_move_front.png deleted file mode 100644 index b4a4e3b7..00000000 Binary files a/icons-old/shape_move_front.png and /dev/null differ diff --git a/icons-old/shape_rotate_anticlockwise.png b/icons-old/shape_rotate_anticlockwise.png deleted file mode 100644 index 07a30206..00000000 Binary files a/icons-old/shape_rotate_anticlockwise.png and /dev/null differ diff --git a/icons-old/shape_rotate_clockwise.png b/icons-old/shape_rotate_clockwise.png deleted file mode 100644 index b99db7d7..00000000 Binary files a/icons-old/shape_rotate_clockwise.png and /dev/null differ diff --git a/icons-old/shape_square.png b/icons-old/shape_square.png deleted file mode 100644 index 33af0460..00000000 Binary files a/icons-old/shape_square.png and /dev/null differ diff --git a/icons-old/shape_square_add.png b/icons-old/shape_square_add.png deleted file mode 100644 index 31edfce5..00000000 Binary files a/icons-old/shape_square_add.png and /dev/null differ diff --git a/icons-old/shape_square_delete.png b/icons-old/shape_square_delete.png deleted file mode 100644 index ede912de..00000000 Binary files a/icons-old/shape_square_delete.png and /dev/null differ diff --git a/icons-old/shape_square_edit.png b/icons-old/shape_square_edit.png deleted file mode 100644 index d28dc6b1..00000000 Binary files a/icons-old/shape_square_edit.png and /dev/null differ diff --git a/icons-old/shape_square_error.png b/icons-old/shape_square_error.png deleted file mode 100644 index 0d0dcfa9..00000000 Binary files a/icons-old/shape_square_error.png and /dev/null differ diff --git a/icons-old/shape_square_go.png b/icons-old/shape_square_go.png deleted file mode 100644 index 5a2ad901..00000000 Binary files a/icons-old/shape_square_go.png and /dev/null differ diff --git a/icons-old/shape_square_key.png b/icons-old/shape_square_key.png deleted file mode 100644 index c34b982a..00000000 Binary files a/icons-old/shape_square_key.png and /dev/null differ diff --git a/icons-old/shape_square_link.png b/icons-old/shape_square_link.png deleted file mode 100644 index b885fcc6..00000000 Binary files a/icons-old/shape_square_link.png and /dev/null differ diff --git a/icons-old/shape_ungroup.png b/icons-old/shape_ungroup.png deleted file mode 100644 index 3a6f369a..00000000 Binary files a/icons-old/shape_ungroup.png and /dev/null differ diff --git a/icons-old/shield.png b/icons-old/shield.png deleted file mode 100644 index 3cb4e257..00000000 Binary files a/icons-old/shield.png and /dev/null differ diff --git a/icons-old/shield_add.png b/icons-old/shield_add.png deleted file mode 100644 index e20a1b4a..00000000 Binary files a/icons-old/shield_add.png and /dev/null differ diff --git a/icons-old/shield_delete.png b/icons-old/shield_delete.png deleted file mode 100644 index 22823a70..00000000 Binary files a/icons-old/shield_delete.png and /dev/null differ diff --git a/icons-old/shield_go.png b/icons-old/shield_go.png deleted file mode 100644 index e9bd8522..00000000 Binary files a/icons-old/shield_go.png and /dev/null differ diff --git a/icons-old/sitemap.png b/icons-old/sitemap.png deleted file mode 100644 index ca779f32..00000000 Binary files a/icons-old/sitemap.png and /dev/null differ diff --git a/icons-old/sitemap_color.png b/icons-old/sitemap_color.png deleted file mode 100644 index c64582bc..00000000 Binary files a/icons-old/sitemap_color.png and /dev/null differ diff --git a/icons-old/sound.png b/icons-old/sound.png deleted file mode 100644 index 6056d234..00000000 Binary files a/icons-old/sound.png and /dev/null differ diff --git a/icons-old/sound_add.png b/icons-old/sound_add.png deleted file mode 100644 index 965c503c..00000000 Binary files a/icons-old/sound_add.png and /dev/null differ diff --git a/icons-old/sound_delete.png b/icons-old/sound_delete.png deleted file mode 100644 index ab9577aa..00000000 Binary files a/icons-old/sound_delete.png and /dev/null differ diff --git a/icons-old/sound_low.png b/icons-old/sound_low.png deleted file mode 100644 index 4d918633..00000000 Binary files a/icons-old/sound_low.png and /dev/null differ diff --git a/icons-old/sound_mute.png b/icons-old/sound_mute.png deleted file mode 100644 index b652d2a7..00000000 Binary files a/icons-old/sound_mute.png and /dev/null differ diff --git a/icons-old/sound_none.png b/icons-old/sound_none.png deleted file mode 100644 index b497ebd5..00000000 Binary files a/icons-old/sound_none.png and /dev/null differ diff --git a/icons-old/spellcheck.png b/icons-old/spellcheck.png deleted file mode 100644 index ebc632d9..00000000 Binary files a/icons-old/spellcheck.png and /dev/null differ diff --git a/icons-old/sport_8ball.png b/icons-old/sport_8ball.png deleted file mode 100644 index 4f627b76..00000000 Binary files a/icons-old/sport_8ball.png and /dev/null differ diff --git a/icons-old/sport_basketball.png b/icons-old/sport_basketball.png deleted file mode 100644 index f7a000b9..00000000 Binary files a/icons-old/sport_basketball.png and /dev/null differ diff --git a/icons-old/sport_football.png b/icons-old/sport_football.png deleted file mode 100644 index 199f0f7f..00000000 Binary files a/icons-old/sport_football.png and /dev/null differ diff --git a/icons-old/sport_golf.png b/icons-old/sport_golf.png deleted file mode 100644 index e21fa44c..00000000 Binary files a/icons-old/sport_golf.png and /dev/null differ diff --git a/icons-old/sport_raquet.png b/icons-old/sport_raquet.png deleted file mode 100644 index f5e0f0c2..00000000 Binary files a/icons-old/sport_raquet.png and /dev/null differ diff --git a/icons-old/sport_shuttlecock.png b/icons-old/sport_shuttlecock.png deleted file mode 100644 index 917287fa..00000000 Binary files a/icons-old/sport_shuttlecock.png and /dev/null differ diff --git a/icons-old/sport_soccer.png b/icons-old/sport_soccer.png deleted file mode 100644 index 3eb1828b..00000000 Binary files a/icons-old/sport_soccer.png and /dev/null differ diff --git a/icons-old/sport_tennis.png b/icons-old/sport_tennis.png deleted file mode 100644 index e88a6efa..00000000 Binary files a/icons-old/sport_tennis.png and /dev/null differ diff --git a/icons-old/star.png b/icons-old/star.png deleted file mode 100644 index b88c8578..00000000 Binary files a/icons-old/star.png and /dev/null differ diff --git a/icons-old/status_away.png b/icons-old/status_away.png deleted file mode 100644 index 70bcbcca..00000000 Binary files a/icons-old/status_away.png and /dev/null differ diff --git a/icons-old/status_busy.png b/icons-old/status_busy.png deleted file mode 100644 index 987c806f..00000000 Binary files a/icons-old/status_busy.png and /dev/null differ diff --git a/icons-old/status_offline.png b/icons-old/status_offline.png deleted file mode 100644 index a88261a6..00000000 Binary files a/icons-old/status_offline.png and /dev/null differ diff --git a/icons-old/status_online.png b/icons-old/status_online.png deleted file mode 100644 index 947bd4b6..00000000 Binary files a/icons-old/status_online.png and /dev/null differ diff --git a/icons-old/stop.png b/icons-old/stop.png deleted file mode 100644 index 0cfd5859..00000000 Binary files a/icons-old/stop.png and /dev/null differ diff --git a/icons-old/style.png b/icons-old/style.png deleted file mode 100644 index 81e41de7..00000000 Binary files a/icons-old/style.png and /dev/null differ diff --git a/icons-old/style_add.png b/icons-old/style_add.png deleted file mode 100644 index e0369c6b..00000000 Binary files a/icons-old/style_add.png and /dev/null differ diff --git a/icons-old/style_delete.png b/icons-old/style_delete.png deleted file mode 100644 index 640f187e..00000000 Binary files a/icons-old/style_delete.png and /dev/null differ diff --git a/icons-old/style_edit.png b/icons-old/style_edit.png deleted file mode 100644 index 25bb5b67..00000000 Binary files a/icons-old/style_edit.png and /dev/null differ diff --git a/icons-old/style_go.png b/icons-old/style_go.png deleted file mode 100644 index 25d6181a..00000000 Binary files a/icons-old/style_go.png and /dev/null differ diff --git a/icons-old/sum.png b/icons-old/sum.png deleted file mode 100644 index fd7b32e4..00000000 Binary files a/icons-old/sum.png and /dev/null differ diff --git a/icons-old/tab.png b/icons-old/tab.png deleted file mode 100644 index 3d8207fd..00000000 Binary files a/icons-old/tab.png and /dev/null differ diff --git a/icons-old/tab_add.png b/icons-old/tab_add.png deleted file mode 100644 index d3b99364..00000000 Binary files a/icons-old/tab_add.png and /dev/null differ diff --git a/icons-old/tab_delete.png b/icons-old/tab_delete.png deleted file mode 100644 index 100da2f1..00000000 Binary files a/icons-old/tab_delete.png and /dev/null differ diff --git a/icons-old/tab_edit.png b/icons-old/tab_edit.png deleted file mode 100644 index 4c09c0fd..00000000 Binary files a/icons-old/tab_edit.png and /dev/null differ diff --git a/icons-old/tab_go.png b/icons-old/tab_go.png deleted file mode 100644 index 844ce04b..00000000 Binary files a/icons-old/tab_go.png and /dev/null differ diff --git a/icons-old/table.png b/icons-old/table.png deleted file mode 100644 index abcd9368..00000000 Binary files a/icons-old/table.png and /dev/null differ diff --git a/icons-old/table_add.png b/icons-old/table_add.png deleted file mode 100644 index 2a3e5c4d..00000000 Binary files a/icons-old/table_add.png and /dev/null differ diff --git a/icons-old/table_delete.png b/icons-old/table_delete.png deleted file mode 100644 index b85916d9..00000000 Binary files a/icons-old/table_delete.png and /dev/null differ diff --git a/icons-old/table_edit.png b/icons-old/table_edit.png deleted file mode 100644 index bfcb0249..00000000 Binary files a/icons-old/table_edit.png and /dev/null differ diff --git a/icons-old/table_error.png b/icons-old/table_error.png deleted file mode 100644 index 589e92b5..00000000 Binary files a/icons-old/table_error.png and /dev/null differ diff --git a/icons-old/table_gear.png b/icons-old/table_gear.png deleted file mode 100644 index cfc2702a..00000000 Binary files a/icons-old/table_gear.png and /dev/null differ diff --git a/icons-old/table_go.png b/icons-old/table_go.png deleted file mode 100644 index 0528dfa2..00000000 Binary files a/icons-old/table_go.png and /dev/null differ diff --git a/icons-old/table_key.png b/icons-old/table_key.png deleted file mode 100644 index 34e23e24..00000000 Binary files a/icons-old/table_key.png and /dev/null differ diff --git a/icons-old/table_lightning.png b/icons-old/table_lightning.png deleted file mode 100644 index 612612b5..00000000 Binary files a/icons-old/table_lightning.png and /dev/null differ diff --git a/icons-old/table_link.png b/icons-old/table_link.png deleted file mode 100644 index decac8a6..00000000 Binary files a/icons-old/table_link.png and /dev/null differ diff --git a/icons-old/table_multiple.png b/icons-old/table_multiple.png deleted file mode 100644 index d76448e3..00000000 Binary files a/icons-old/table_multiple.png and /dev/null differ diff --git a/icons-old/table_refresh.png b/icons-old/table_refresh.png deleted file mode 100644 index ab92010c..00000000 Binary files a/icons-old/table_refresh.png and /dev/null differ diff --git a/icons-old/table_relationship.png b/icons-old/table_relationship.png deleted file mode 100644 index 28b8505c..00000000 Binary files a/icons-old/table_relationship.png and /dev/null differ diff --git a/icons-old/table_row_delete.png b/icons-old/table_row_delete.png deleted file mode 100644 index 54c69691..00000000 Binary files a/icons-old/table_row_delete.png and /dev/null differ diff --git a/icons-old/table_row_insert.png b/icons-old/table_row_insert.png deleted file mode 100644 index ff5925ef..00000000 Binary files a/icons-old/table_row_insert.png and /dev/null differ diff --git a/icons-old/table_save.png b/icons-old/table_save.png deleted file mode 100644 index 25b74d18..00000000 Binary files a/icons-old/table_save.png and /dev/null differ diff --git a/icons-old/table_sort.png b/icons-old/table_sort.png deleted file mode 100644 index ed6785a6..00000000 Binary files a/icons-old/table_sort.png and /dev/null differ diff --git a/icons-old/tag.png b/icons-old/tag.png deleted file mode 100644 index e093032a..00000000 Binary files a/icons-old/tag.png and /dev/null differ diff --git a/icons-old/tag_blue.png b/icons-old/tag_blue.png deleted file mode 100644 index 9757fc6e..00000000 Binary files a/icons-old/tag_blue.png and /dev/null differ diff --git a/icons-old/tag_blue_add.png b/icons-old/tag_blue_add.png deleted file mode 100644 index f135248f..00000000 Binary files a/icons-old/tag_blue_add.png and /dev/null differ diff --git a/icons-old/tag_blue_delete.png b/icons-old/tag_blue_delete.png deleted file mode 100644 index 9fbae672..00000000 Binary files a/icons-old/tag_blue_delete.png and /dev/null differ diff --git a/icons-old/tag_blue_edit.png b/icons-old/tag_blue_edit.png deleted file mode 100644 index 2a9f6266..00000000 Binary files a/icons-old/tag_blue_edit.png and /dev/null differ diff --git a/icons-old/tag_green.png b/icons-old/tag_green.png deleted file mode 100644 index 83ec984b..00000000 Binary files a/icons-old/tag_green.png and /dev/null differ diff --git a/icons-old/tag_orange.png b/icons-old/tag_orange.png deleted file mode 100644 index 454a59f3..00000000 Binary files a/icons-old/tag_orange.png and /dev/null differ diff --git a/icons-old/tag_pink.png b/icons-old/tag_pink.png deleted file mode 100644 index 76e2296c..00000000 Binary files a/icons-old/tag_pink.png and /dev/null differ diff --git a/icons-old/tag_purple.png b/icons-old/tag_purple.png deleted file mode 100644 index ebaf0e87..00000000 Binary files a/icons-old/tag_purple.png and /dev/null differ diff --git a/icons-old/tag_red.png b/icons-old/tag_red.png deleted file mode 100644 index 6ebb37d2..00000000 Binary files a/icons-old/tag_red.png and /dev/null differ diff --git a/icons-old/tag_yellow.png b/icons-old/tag_yellow.png deleted file mode 100644 index 83d12924..00000000 Binary files a/icons-old/tag_yellow.png and /dev/null differ diff --git a/icons-old/telephone.png b/icons-old/telephone.png deleted file mode 100644 index cecc436f..00000000 Binary files a/icons-old/telephone.png and /dev/null differ diff --git a/icons-old/telephone_add.png b/icons-old/telephone_add.png deleted file mode 100644 index 5591cfc4..00000000 Binary files a/icons-old/telephone_add.png and /dev/null differ diff --git a/icons-old/telephone_delete.png b/icons-old/telephone_delete.png deleted file mode 100644 index 0013268e..00000000 Binary files a/icons-old/telephone_delete.png and /dev/null differ diff --git a/icons-old/telephone_edit.png b/icons-old/telephone_edit.png deleted file mode 100644 index bcf6d7ec..00000000 Binary files a/icons-old/telephone_edit.png and /dev/null differ diff --git a/icons-old/telephone_error.png b/icons-old/telephone_error.png deleted file mode 100644 index d3ec3a11..00000000 Binary files a/icons-old/telephone_error.png and /dev/null differ diff --git a/icons-old/telephone_go.png b/icons-old/telephone_go.png deleted file mode 100644 index 395c8fbf..00000000 Binary files a/icons-old/telephone_go.png and /dev/null differ diff --git a/icons-old/telephone_key.png b/icons-old/telephone_key.png deleted file mode 100644 index cef5dec4..00000000 Binary files a/icons-old/telephone_key.png and /dev/null differ diff --git a/icons-old/telephone_link.png b/icons-old/telephone_link.png deleted file mode 100644 index ef1ee5dd..00000000 Binary files a/icons-old/telephone_link.png and /dev/null differ diff --git a/icons-old/television.png b/icons-old/television.png deleted file mode 100644 index 1738a4f1..00000000 Binary files a/icons-old/television.png and /dev/null differ diff --git a/icons-old/television_add.png b/icons-old/television_add.png deleted file mode 100644 index 2baaad99..00000000 Binary files a/icons-old/television_add.png and /dev/null differ diff --git a/icons-old/television_delete.png b/icons-old/television_delete.png deleted file mode 100644 index b9a58602..00000000 Binary files a/icons-old/television_delete.png and /dev/null differ diff --git a/icons-old/text_align_center.png b/icons-old/text_align_center.png deleted file mode 100644 index 57beb381..00000000 Binary files a/icons-old/text_align_center.png and /dev/null differ diff --git a/icons-old/text_align_justify.png b/icons-old/text_align_justify.png deleted file mode 100644 index 2fbdd692..00000000 Binary files a/icons-old/text_align_justify.png and /dev/null differ diff --git a/icons-old/text_align_left.png b/icons-old/text_align_left.png deleted file mode 100644 index 6c8fcc11..00000000 Binary files a/icons-old/text_align_left.png and /dev/null differ diff --git a/icons-old/text_align_right.png b/icons-old/text_align_right.png deleted file mode 100644 index a1502571..00000000 Binary files a/icons-old/text_align_right.png and /dev/null differ diff --git a/icons-old/text_allcaps.png b/icons-old/text_allcaps.png deleted file mode 100644 index 280fd442..00000000 Binary files a/icons-old/text_allcaps.png and /dev/null differ diff --git a/icons-old/text_bold.png b/icons-old/text_bold.png deleted file mode 100644 index 889ae80e..00000000 Binary files a/icons-old/text_bold.png and /dev/null differ diff --git a/icons-old/text_columns.png b/icons-old/text_columns.png deleted file mode 100644 index 97b2e035..00000000 Binary files a/icons-old/text_columns.png and /dev/null differ diff --git a/icons-old/text_dropcaps.png b/icons-old/text_dropcaps.png deleted file mode 100644 index dd65786a..00000000 Binary files a/icons-old/text_dropcaps.png and /dev/null differ diff --git a/icons-old/text_heading_1.png b/icons-old/text_heading_1.png deleted file mode 100644 index 9c122e91..00000000 Binary files a/icons-old/text_heading_1.png and /dev/null differ diff --git a/icons-old/text_heading_2.png b/icons-old/text_heading_2.png deleted file mode 100644 index fbd87657..00000000 Binary files a/icons-old/text_heading_2.png and /dev/null differ diff --git a/icons-old/text_heading_3.png b/icons-old/text_heading_3.png deleted file mode 100644 index c7836cf0..00000000 Binary files a/icons-old/text_heading_3.png and /dev/null differ diff --git a/icons-old/text_heading_4.png b/icons-old/text_heading_4.png deleted file mode 100644 index 4e929eaf..00000000 Binary files a/icons-old/text_heading_4.png and /dev/null differ diff --git a/icons-old/text_heading_5.png b/icons-old/text_heading_5.png deleted file mode 100644 index 30cabebf..00000000 Binary files a/icons-old/text_heading_5.png and /dev/null differ diff --git a/icons-old/text_heading_6.png b/icons-old/text_heading_6.png deleted file mode 100644 index 058170a2..00000000 Binary files a/icons-old/text_heading_6.png and /dev/null differ diff --git a/icons-old/text_horizontalrule.png b/icons-old/text_horizontalrule.png deleted file mode 100644 index 8dd1da1c..00000000 Binary files a/icons-old/text_horizontalrule.png and /dev/null differ diff --git a/icons-old/text_indent.png b/icons-old/text_indent.png deleted file mode 100644 index 93645323..00000000 Binary files a/icons-old/text_indent.png and /dev/null differ diff --git a/icons-old/text_indent_remove.png b/icons-old/text_indent_remove.png deleted file mode 100644 index 1651b074..00000000 Binary files a/icons-old/text_indent_remove.png and /dev/null differ diff --git a/icons-old/text_italic.png b/icons-old/text_italic.png deleted file mode 100644 index 8482ac8c..00000000 Binary files a/icons-old/text_italic.png and /dev/null differ diff --git a/icons-old/text_kerning.png b/icons-old/text_kerning.png deleted file mode 100644 index 377def64..00000000 Binary files a/icons-old/text_kerning.png and /dev/null differ diff --git a/icons-old/text_letter_omega.png b/icons-old/text_letter_omega.png deleted file mode 100644 index 5075ec6b..00000000 Binary files a/icons-old/text_letter_omega.png and /dev/null differ diff --git a/icons-old/text_letterspacing.png b/icons-old/text_letterspacing.png deleted file mode 100644 index 41390f54..00000000 Binary files a/icons-old/text_letterspacing.png and /dev/null differ diff --git a/icons-old/text_linespacing.png b/icons-old/text_linespacing.png deleted file mode 100644 index 1a91cbdd..00000000 Binary files a/icons-old/text_linespacing.png and /dev/null differ diff --git a/icons-old/text_list_bullets.png b/icons-old/text_list_bullets.png deleted file mode 100644 index 4a8672bd..00000000 Binary files a/icons-old/text_list_bullets.png and /dev/null differ diff --git a/icons-old/text_list_numbers.png b/icons-old/text_list_numbers.png deleted file mode 100644 index 33b0b8df..00000000 Binary files a/icons-old/text_list_numbers.png and /dev/null differ diff --git a/icons-old/text_lowercase.png b/icons-old/text_lowercase.png deleted file mode 100644 index 382a102e..00000000 Binary files a/icons-old/text_lowercase.png and /dev/null differ diff --git a/icons-old/text_padding_bottom.png b/icons-old/text_padding_bottom.png deleted file mode 100644 index 4880c43a..00000000 Binary files a/icons-old/text_padding_bottom.png and /dev/null differ diff --git a/icons-old/text_padding_left.png b/icons-old/text_padding_left.png deleted file mode 100644 index b55482ee..00000000 Binary files a/icons-old/text_padding_left.png and /dev/null differ diff --git a/icons-old/text_padding_right.png b/icons-old/text_padding_right.png deleted file mode 100644 index 106edae5..00000000 Binary files a/icons-old/text_padding_right.png and /dev/null differ diff --git a/icons-old/text_padding_top.png b/icons-old/text_padding_top.png deleted file mode 100644 index c5c45b2d..00000000 Binary files a/icons-old/text_padding_top.png and /dev/null differ diff --git a/icons-old/text_replace.png b/icons-old/text_replace.png deleted file mode 100644 index 877f82fe..00000000 Binary files a/icons-old/text_replace.png and /dev/null differ diff --git a/icons-old/text_signature.png b/icons-old/text_signature.png deleted file mode 100644 index c72fd808..00000000 Binary files a/icons-old/text_signature.png and /dev/null differ diff --git a/icons-old/text_smallcaps.png b/icons-old/text_smallcaps.png deleted file mode 100644 index 5b98a6e1..00000000 Binary files a/icons-old/text_smallcaps.png and /dev/null differ diff --git a/icons-old/text_strikethrough.png b/icons-old/text_strikethrough.png deleted file mode 100644 index 612058a7..00000000 Binary files a/icons-old/text_strikethrough.png and /dev/null differ diff --git a/icons-old/text_subscript.png b/icons-old/text_subscript.png deleted file mode 100644 index 1a2b0101..00000000 Binary files a/icons-old/text_subscript.png and /dev/null differ diff --git a/icons-old/text_superscript.png b/icons-old/text_superscript.png deleted file mode 100644 index 2fb2a7c7..00000000 Binary files a/icons-old/text_superscript.png and /dev/null differ diff --git a/icons-old/text_underline.png b/icons-old/text_underline.png deleted file mode 100644 index 90d0df28..00000000 Binary files a/icons-old/text_underline.png and /dev/null differ diff --git a/icons-old/text_uppercase.png b/icons-old/text_uppercase.png deleted file mode 100644 index 8dcc2dbb..00000000 Binary files a/icons-old/text_uppercase.png and /dev/null differ diff --git a/icons-old/textfield.png b/icons-old/textfield.png deleted file mode 100644 index d37e7304..00000000 Binary files a/icons-old/textfield.png and /dev/null differ diff --git a/icons-old/textfield_add.png b/icons-old/textfield_add.png deleted file mode 100644 index 204de723..00000000 Binary files a/icons-old/textfield_add.png and /dev/null differ diff --git a/icons-old/textfield_delete.png b/icons-old/textfield_delete.png deleted file mode 100644 index c7bd58b2..00000000 Binary files a/icons-old/textfield_delete.png and /dev/null differ diff --git a/icons-old/textfield_key.png b/icons-old/textfield_key.png deleted file mode 100644 index a9d5e4f8..00000000 Binary files a/icons-old/textfield_key.png and /dev/null differ diff --git a/icons-old/textfield_rename.png b/icons-old/textfield_rename.png deleted file mode 100644 index 4e3688ed..00000000 Binary files a/icons-old/textfield_rename.png and /dev/null differ diff --git a/icons-old/thumb_down.png b/icons-old/thumb_down.png deleted file mode 100644 index 3c832d4c..00000000 Binary files a/icons-old/thumb_down.png and /dev/null differ diff --git a/icons-old/thumb_up.png b/icons-old/thumb_up.png deleted file mode 100644 index 2bd16ccf..00000000 Binary files a/icons-old/thumb_up.png and /dev/null differ diff --git a/icons-old/tick.png b/icons-old/tick.png deleted file mode 100644 index a9925a06..00000000 Binary files a/icons-old/tick.png and /dev/null differ diff --git a/icons-old/time.png b/icons-old/time.png deleted file mode 100644 index 911da3f1..00000000 Binary files a/icons-old/time.png and /dev/null differ diff --git a/icons-old/time_add.png b/icons-old/time_add.png deleted file mode 100644 index dcc45cb2..00000000 Binary files a/icons-old/time_add.png and /dev/null differ diff --git a/icons-old/time_delete.png b/icons-old/time_delete.png deleted file mode 100644 index 5bf8313c..00000000 Binary files a/icons-old/time_delete.png and /dev/null differ diff --git a/icons-old/time_go.png b/icons-old/time_go.png deleted file mode 100644 index d451ee06..00000000 Binary files a/icons-old/time_go.png and /dev/null differ diff --git a/icons-old/timeline_marker.png b/icons-old/timeline_marker.png deleted file mode 100644 index a3fbddf8..00000000 Binary files a/icons-old/timeline_marker.png and /dev/null differ diff --git a/icons-old/transmit.png b/icons-old/transmit.png deleted file mode 100644 index f54bf736..00000000 Binary files a/icons-old/transmit.png and /dev/null differ diff --git a/icons-old/transmit_add.png b/icons-old/transmit_add.png deleted file mode 100644 index b7fd4e68..00000000 Binary files a/icons-old/transmit_add.png and /dev/null differ diff --git a/icons-old/transmit_blue.png b/icons-old/transmit_blue.png deleted file mode 100644 index 7b1142fc..00000000 Binary files a/icons-old/transmit_blue.png and /dev/null differ diff --git a/icons-old/transmit_delete.png b/icons-old/transmit_delete.png deleted file mode 100644 index 3d72be2a..00000000 Binary files a/icons-old/transmit_delete.png and /dev/null differ diff --git a/icons-old/transmit_edit.png b/icons-old/transmit_edit.png deleted file mode 100644 index eb9a3dd5..00000000 Binary files a/icons-old/transmit_edit.png and /dev/null differ diff --git a/icons-old/transmit_error.png b/icons-old/transmit_error.png deleted file mode 100644 index fd1d4499..00000000 Binary files a/icons-old/transmit_error.png and /dev/null differ diff --git a/icons-old/transmit_go.png b/icons-old/transmit_go.png deleted file mode 100644 index 10137e55..00000000 Binary files a/icons-old/transmit_go.png and /dev/null differ diff --git a/icons-old/tux.png b/icons-old/tux.png deleted file mode 100644 index bbefe2ec..00000000 Binary files a/icons-old/tux.png and /dev/null differ diff --git a/icons-old/user.png b/icons-old/user.png deleted file mode 100644 index 79f35ccb..00000000 Binary files a/icons-old/user.png and /dev/null differ diff --git a/icons-old/user_add.png b/icons-old/user_add.png deleted file mode 100644 index deae99bc..00000000 Binary files a/icons-old/user_add.png and /dev/null differ diff --git a/icons-old/user_comment.png b/icons-old/user_comment.png deleted file mode 100644 index e54ebeba..00000000 Binary files a/icons-old/user_comment.png and /dev/null differ diff --git a/icons-old/user_delete.png b/icons-old/user_delete.png deleted file mode 100644 index acbb5630..00000000 Binary files a/icons-old/user_delete.png and /dev/null differ diff --git a/icons-old/user_edit.png b/icons-old/user_edit.png deleted file mode 100644 index c1974cda..00000000 Binary files a/icons-old/user_edit.png and /dev/null differ diff --git a/icons-old/user_female.png b/icons-old/user_female.png deleted file mode 100644 index 7c71de03..00000000 Binary files a/icons-old/user_female.png and /dev/null differ diff --git a/icons-old/user_go.png b/icons-old/user_go.png deleted file mode 100644 index 0468cf08..00000000 Binary files a/icons-old/user_go.png and /dev/null differ diff --git a/icons-old/user_gray.png b/icons-old/user_gray.png deleted file mode 100644 index 8fd539e9..00000000 Binary files a/icons-old/user_gray.png and /dev/null differ diff --git a/icons-old/user_green.png b/icons-old/user_green.png deleted file mode 100644 index 30383c2d..00000000 Binary files a/icons-old/user_green.png and /dev/null differ diff --git a/icons-old/user_orange.png b/icons-old/user_orange.png deleted file mode 100644 index b818127d..00000000 Binary files a/icons-old/user_orange.png and /dev/null differ diff --git a/icons-old/user_red.png b/icons-old/user_red.png deleted file mode 100644 index c6f66e8b..00000000 Binary files a/icons-old/user_red.png and /dev/null differ diff --git a/icons-old/user_suit.png b/icons-old/user_suit.png deleted file mode 100644 index b3454e15..00000000 Binary files a/icons-old/user_suit.png and /dev/null differ diff --git a/icons-old/vcard.png b/icons-old/vcard.png deleted file mode 100644 index c02f315d..00000000 Binary files a/icons-old/vcard.png and /dev/null differ diff --git a/icons-old/vcard_add.png b/icons-old/vcard_add.png deleted file mode 100644 index 2a684538..00000000 Binary files a/icons-old/vcard_add.png and /dev/null differ diff --git a/icons-old/vcard_delete.png b/icons-old/vcard_delete.png deleted file mode 100644 index b194b971..00000000 Binary files a/icons-old/vcard_delete.png and /dev/null differ diff --git a/icons-old/vcard_edit.png b/icons-old/vcard_edit.png deleted file mode 100644 index ab0f6e73..00000000 Binary files a/icons-old/vcard_edit.png and /dev/null differ diff --git a/icons-old/vector.png b/icons-old/vector.png deleted file mode 100644 index a1291c2d..00000000 Binary files a/icons-old/vector.png and /dev/null differ diff --git a/icons-old/vector_add.png b/icons-old/vector_add.png deleted file mode 100644 index 988770f4..00000000 Binary files a/icons-old/vector_add.png and /dev/null differ diff --git a/icons-old/vector_delete.png b/icons-old/vector_delete.png deleted file mode 100644 index ca139e0f..00000000 Binary files a/icons-old/vector_delete.png and /dev/null differ diff --git a/icons-old/wand.png b/icons-old/wand.png deleted file mode 100644 index 44ccbf81..00000000 Binary files a/icons-old/wand.png and /dev/null differ diff --git a/icons-old/weather_clouds.png b/icons-old/weather_clouds.png deleted file mode 100644 index 3f73eaa1..00000000 Binary files a/icons-old/weather_clouds.png and /dev/null differ diff --git a/icons-old/weather_cloudy.png b/icons-old/weather_cloudy.png deleted file mode 100644 index 5856e1d0..00000000 Binary files a/icons-old/weather_cloudy.png and /dev/null differ diff --git a/icons-old/weather_lightning.png b/icons-old/weather_lightning.png deleted file mode 100644 index 1d42b367..00000000 Binary files a/icons-old/weather_lightning.png and /dev/null differ diff --git a/icons-old/weather_rain.png b/icons-old/weather_rain.png deleted file mode 100644 index cb3d54d0..00000000 Binary files a/icons-old/weather_rain.png and /dev/null differ diff --git a/icons-old/weather_snow.png b/icons-old/weather_snow.png deleted file mode 100644 index 45bbdf19..00000000 Binary files a/icons-old/weather_snow.png and /dev/null differ diff --git a/icons-old/weather_sun.png b/icons-old/weather_sun.png deleted file mode 100644 index 0156c266..00000000 Binary files a/icons-old/weather_sun.png and /dev/null differ diff --git a/icons-old/webcam.png b/icons-old/webcam.png deleted file mode 100644 index af71c306..00000000 Binary files a/icons-old/webcam.png and /dev/null differ diff --git a/icons-old/webcam_add.png b/icons-old/webcam_add.png deleted file mode 100644 index f02fcfa9..00000000 Binary files a/icons-old/webcam_add.png and /dev/null differ diff --git a/icons-old/webcam_delete.png b/icons-old/webcam_delete.png deleted file mode 100644 index bd6277f5..00000000 Binary files a/icons-old/webcam_delete.png and /dev/null differ diff --git a/icons-old/webcam_error.png b/icons-old/webcam_error.png deleted file mode 100644 index 2faa7067..00000000 Binary files a/icons-old/webcam_error.png and /dev/null differ diff --git a/icons-old/world.png b/icons-old/world.png deleted file mode 100644 index 68f21d30..00000000 Binary files a/icons-old/world.png and /dev/null differ diff --git a/icons-old/world_add.png b/icons-old/world_add.png deleted file mode 100644 index 6d0d7f74..00000000 Binary files a/icons-old/world_add.png and /dev/null differ diff --git a/icons-old/world_delete.png b/icons-old/world_delete.png deleted file mode 100644 index ffcd1156..00000000 Binary files a/icons-old/world_delete.png and /dev/null differ diff --git a/icons-old/world_edit.png b/icons-old/world_edit.png deleted file mode 100644 index 00794d40..00000000 Binary files a/icons-old/world_edit.png and /dev/null differ diff --git a/icons-old/world_go.png b/icons-old/world_go.png deleted file mode 100644 index aee9c97f..00000000 Binary files a/icons-old/world_go.png and /dev/null differ diff --git a/icons-old/world_link.png b/icons-old/world_link.png deleted file mode 100644 index b8edc126..00000000 Binary files a/icons-old/world_link.png and /dev/null differ diff --git a/icons-old/wrench.png b/icons-old/wrench.png deleted file mode 100644 index 5c8213fe..00000000 Binary files a/icons-old/wrench.png and /dev/null differ diff --git a/icons-old/wrench_orange.png b/icons-old/wrench_orange.png deleted file mode 100644 index 565a9330..00000000 Binary files a/icons-old/wrench_orange.png and /dev/null differ diff --git a/icons-old/xhtml.png b/icons-old/xhtml.png deleted file mode 100644 index da5dbf20..00000000 Binary files a/icons-old/xhtml.png and /dev/null differ diff --git a/icons-old/xhtml_add.png b/icons-old/xhtml_add.png deleted file mode 100644 index bbaf784f..00000000 Binary files a/icons-old/xhtml_add.png and /dev/null differ diff --git a/icons-old/xhtml_delete.png b/icons-old/xhtml_delete.png deleted file mode 100644 index 157b5201..00000000 Binary files a/icons-old/xhtml_delete.png and /dev/null differ diff --git a/icons-old/xhtml_go.png b/icons-old/xhtml_go.png deleted file mode 100644 index 43cf8144..00000000 Binary files a/icons-old/xhtml_go.png and /dev/null differ diff --git a/icons-old/xhtml_valid.png b/icons-old/xhtml_valid.png deleted file mode 100644 index d2e1cfbe..00000000 Binary files a/icons-old/xhtml_valid.png and /dev/null differ diff --git a/icons-old/zoom.png b/icons-old/zoom.png deleted file mode 100644 index 908612e3..00000000 Binary files a/icons-old/zoom.png and /dev/null differ diff --git a/icons-old/zoom_in.png b/icons-old/zoom_in.png deleted file mode 100644 index cdf0a52f..00000000 Binary files a/icons-old/zoom_in.png and /dev/null differ diff --git a/icons-old/zoom_out.png b/icons-old/zoom_out.png deleted file mode 100644 index 07bf98a7..00000000 Binary files a/icons-old/zoom_out.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 index 9a99eada..9250ee52 --- a/package-lock.json +++ b/package-lock.json @@ -1,4676 +1,48 @@ { "name": "tabfern", - "version": "0.1.16.1337", + "version": "0.1.17.1337", "lockfileVersion": 1, "requires": true, "dependencies": { - "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", - "dev": true - }, - "acorn": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz", - "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "4.0.13" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, - "ajv": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", - "dev": true, - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" - } - }, - "ajv-keywords": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.0.tgz", - "integrity": "sha1-opbhf3v658HOT34N5T0pyzIWLfA=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "dev": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.3" - } - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", - "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "asynquence": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/asynquence/-/asynquence-0.10.0.tgz", "integrity": "sha1-Tsdmo4dgFuw9FPe6g09mDViffDE=" }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000738", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "benchmark": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", - "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", - "dev": true, - "requires": { - "lodash": "4.17.4", - "platform": "1.3.5" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", - "dev": true - }, "blake2s-js": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/blake2s-js/-/blake2s-js-1.2.3.tgz", "integrity": "sha512-Q/zJLggr8szFyiYMcYXq2NNGJM3Xk3UMufY4/zlBwFvWcf0NI5KandolIIUtolZt5p4QSRUlpXk6XuRfzJ+28Q==" }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", - "integrity": "sha512-WYCMOT/PtGTlpOKFht0YJFYcPy6pLCR98CtWfzK13zoynLlBMvAdEMSRGmgnJCw2M2j/5qxBkinZQFobieM8dQ==", - "dev": true, - "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "1.0.8", - "browserify-des": "1.0.0", - "evp_bytestokey": "1.0.3" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.5" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "elliptic": "6.4.0", - "inherits": "2.0.3", - "parse-asn1": "5.1.0" - } - }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "requires": { - "pako": "0.2.9" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000738", - "electron-to-chromium": "1.3.22" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000738", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" - } - }, - "caniuse-db": { - "version": "1.0.30000738", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000738.tgz", - "integrity": "sha1-hICavEmjkOWowiSrk2nT+NAaogI=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - }, - "dependencies": { - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "1.1.3" - } - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - } - }, - "clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", - "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", - "dev": true - }, - "clone-deep": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz", - "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=", - "dev": true, - "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "3.2.2", - "shallow-clone": "0.1.2" - }, - "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "1.0.2", - "color-convert": "1.9.0", - "color-string": "0.3.0" - } - }, - "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "0.11.4", - "css-color-names": "0.0.4", - "has": "1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "sha.js": "2.4.9" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.1.3", - "inherits": "2.0.3", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.9" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "shebang-command": "1.2.0", - "which": "1.3.0" - } - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - } - } - }, - "crypto-browserify": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", - "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", - "dev": true, - "requires": { - "browserify-cipher": "1.0.0", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.0", - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "diffie-hellman": "5.0.2", - "inherits": "2.0.3", - "pbkdf2": "3.0.14", - "public-encrypt": "4.0.0", - "randombytes": "2.0.5" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-loader": { - "version": "0.28.7", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz", - "integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "icss-utils": "2.1.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.17", - "postcss-modules-extract-imports": "1.1.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "postcss-value-parser": "3.3.0", - "source-list-map": "2.0.0" - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.17", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "1.0.2" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.30" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.0", - "randombytes": "2.0.5" - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "electron-to-chromium": { - "version": "1.3.22", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.22.tgz", - "integrity": "sha1-QyLVLBUUBuPq73StAmdog+hBZBg=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.3", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "object-assign": "4.1.1", - "tapable": "0.2.8" - } - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "dev": true, - "requires": { - "prr": "0.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "0.2.1" - } - }, - "es5-ext": { - "version": "0.10.30", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz", - "integrity": "sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk=", - "dev": true, - "requires": { - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.30" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "extract-text-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.0.tgz", - "integrity": "sha1-kMqnkHvESfM1AF46x1MrQbAN5hI=", - "dev": true, - "requires": { - "async": "2.5.0", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0", - "webpack-sources": "1.0.1" - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "dev": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "gaze": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", - "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=", - "dev": true, - "requires": { - "globule": "1.2.0" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "1.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "globule": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", - "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=", - "dev": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.4", - "minimatch": "3.0.4" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "5.2.3", - "har-schema": "2.0.0" - } - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.0" - } - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.0.2" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "1.1.3", - "minimalistic-assert": "1.0.0", - "minimalistic-crypto-utils": "1.0.1" - } - }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", - "dev": true - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", - "dev": true - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "6.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "postcss": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.12.tgz", - "integrity": "sha512-K6SLofXEK43FBSyZ6/ExQV7ji24OEw4tEY6x1CAf7+tcoMWJoO24Rf3rVFVpk+5IQL1e1Cy3sTKfg7hXuLzafg==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - } - } - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "interpret": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", - "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.10.0" - } - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "1.1.1" - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "1.1.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "jasmine2-custom-message": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/jasmine2-custom-message/-/jasmine2-custom-message-0.8.4.tgz", - "integrity": "sha1-c8/TMU2o00ZbnNd98yys6/xXs3g=", - "dev": true - }, - "js-base64": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", - "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "1.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" - } - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.mergewith": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", - "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", - "dev": true - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" - } - }, - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", - "dev": true, - "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" - } - }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - } - } - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "1.1.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - } - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "miller-rabin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", - "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" - } - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", - "dev": true - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "dev": true, - "requires": { - "mime-db": "1.30.0" - } - }, - "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", - "dev": true - }, - "node-gyp": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", - "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=", - "dev": true, - "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.4", - "request": "2.82.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.0" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } - } - }, - "node-libs-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", - "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.1.4", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.11.1", - "domain-browser": "1.1.7", - "events": "1.1.1", - "https-browserify": "0.0.1", - "os-browserify": "0.2.1", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.7.2", - "string_decoder": "0.10.31", - "timers-browserify": "2.0.4", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "node-sass": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.3.tgz", - "integrity": "sha1-0JydEXlkEjnRuX/8YjH9zsU+FWg=", - "dev": true, - "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.2", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.0", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.7.0", - "node-gyp": "3.6.2", - "npmlog": "4.1.2", - "request": "2.82.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0" - }, - "dependencies": { - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "requires": { - "lru-cache": "4.1.1", - "which": "1.3.0" - } - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1.1.0" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "2.5.0", - "is-builtin-module": "1.0.0", - "semver": "5.4.1", - "validate-npm-package-license": "3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "2.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "1.1.0" - } - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "4.9.1", - "browserify-aes": "1.0.8", - "create-hash": "1.1.3", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.14" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "1.3.1" - } - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "2.3.0" - } - }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", - "dev": true, - "requires": { - "create-hash": "1.1.3", - "create-hmac": "1.1.6", - "ripemd160": "2.0.1", - "safe-buffer": "5.1.1", - "sha.js": "2.4.9" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "platform": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", - "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==", - "dev": true - }, - "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", - "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.3.2", - "source-map": "0.5.7", - "supports-color": "3.2.3" - }, - "dependencies": { - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "1.1.2", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "uniqs": "2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "uniqid": "4.1.1" - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.17", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.17", - "postcss-selector-parser": "2.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", - "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", - "dev": true, - "requires": { - "postcss": "6.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "postcss": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.12.tgz", - "integrity": "sha512-K6SLofXEK43FBSyZ6/ExQV7ji24OEw4tEY6x1CAf7+tcoMWJoO24Rf3rVFVpk+5IQL1e1Cy3sTKfg7hXuLzafg==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "postcss": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.12.tgz", - "integrity": "sha512-K6SLofXEK43FBSyZ6/ExQV7ji24OEw4tEY6x1CAf7+tcoMWJoO24Rf3rVFVpk+5IQL1e1Cy3sTKfg7hXuLzafg==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "postcss": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.12.tgz", - "integrity": "sha512-K6SLofXEK43FBSyZ6/ExQV7ji24OEw4tEY6x1CAf7+tcoMWJoO24Rf3rVFVpk+5IQL1e1Cy3sTKfg7hXuLzafg==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.12" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "postcss": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.12.tgz", - "integrity": "sha512-K6SLofXEK43FBSyZ6/ExQV7ji24OEw4tEY6x1CAf7+tcoMWJoO24Rf3rVFVpk+5IQL1e1Cy3sTKfg7hXuLzafg==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.5.7", - "supports-color": "4.4.0" - } - } - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "5.2.17" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.17", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.17", - "uniqs": "2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.17", - "uniqs": "2.0.0" - } - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.1.3", - "parse-asn1": "5.1.0", - "randombytes": "2.0.5" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", - "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "randombytes": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", - "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "raw-loader": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", - "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "request": { - "version": "2.82.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.82.0.tgz", - "integrity": "sha512-/QWqfmyTfQ4OYs6EhB1h2wQsX9ZxbuNePCvCm0Mdz/mxw73mjdg0D4QdIl0TQBFs35CZmMXLjk0iCGK395CUDg==", - "dev": true, - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, "requirejs": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz", "integrity": "sha512-svnO+aNcR/an9Dpi44C7KSAy5fFGLtmPbaaCeQaklUz8BQhS64tWWIIlvEA5jrWICzlO/X9KSzSeXFnZdBu8nw==" }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "dev": true, - "requires": { - "hash-base": "2.0.2", - "inherits": "2.0.3" - } - }, "rmodal": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/rmodal/-/rmodal-1.0.28.tgz", "integrity": "sha512-f4g2iIKkfEXhyMSsEr5Snv0jujZhco6XwpEHZXgZlEhmsWOJf7wgG620f9gSYmLWsI13HbDNnSadoVgBdpK7Cw==" }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "sass": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.0.0-beta.2.tgz", - "integrity": "sha1-kQBx8W3c3guUY5Kq36EtNHEo610=", - "dev": true - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "dev": true, - "requires": { - "glob": "7.1.2", - "lodash": "4.17.4", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - } - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "1.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "requires": { - "camelcase": "3.0.0" - } - } - } - }, - "sass-loader": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz", - "integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==", - "dev": true, - "requires": { - "async": "2.5.0", - "clone-deep": "0.3.0", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "pify": "3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "5.2.3" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "requires": { - "js-base64": "2.3.2", - "source-map": "0.4.4" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "seedrandom": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz", - "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=", - "dev": true - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "sha.js": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", - "integrity": "sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A==", - "dev": true, - "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.1" - } - }, - "shallow-clone": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", - "integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=", - "dev": true, - "requires": { - "is-extendable": "0.1.1", - "kind-of": "2.0.1", - "lazy-cache": "0.2.7", - "mixin-object": "2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", - "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=", - "dev": true - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sntp": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", - "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", - "dev": true, - "requires": { - "hoek": "4.2.0" - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "1.2.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, "spectrum-colorpicker": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/spectrum-colorpicker/-/spectrum-colorpicker-1.8.0.tgz", "integrity": "sha1-uSbPUALAp3hgtfg1HhwJPGUgAQc=" }, + "spin.js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spin.js/-/spin.js-4.0.0.tgz", + "integrity": "sha512-uJN9Q4/I4I7PRZAp1c5reoY89thAfvFB5thbJncAwkPjPhqCfUrCooJc8GMN8q8WKFGIMy+JMf477RW7deCInA==" + }, "split.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.3.5.tgz", "integrity": "sha1-YuLOZtLPkcx3SqXwdJ/yUTgDn1A=" }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "dev": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "stdout-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz", - "integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - } - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "4.0.1" - } - }, - "style-loader": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.18.2.tgz", - "integrity": "sha512-WPpJPZGUxWYHWIUMNNOYqql7zh85zGmr84FdTVWq52WTIkqlW9xSxD3QYWi/T31cqn9UNSsietVEgGn2aaSCzw==", - "dev": true, - "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" - } - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" - } - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, "tinycolor2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "dev": true, - "requires": { - "punycode": "1.4.1" - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "0.5.7", - "uglify-js": "2.8.29", - "webpack-sources": "1.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "1.0.2", - "spdx-expression-parse": "1.0.4" - } - }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "watchpack": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", - "dev": true, - "requires": { - "async": "2.5.0", - "chokidar": "1.7.0", - "graceful-fs": "4.1.11" - } - }, - "webpack": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.6.0.tgz", - "integrity": "sha512-OsHT3D0W0KmPPh60tC7asNnOmST6bKTiR90UyEdT9QYoaJ4OYN4Gg7WK1k3VxHK07ZoiYWPsKvlS/gAjwL/vRA==", - "dev": true, - "requires": { - "acorn": "5.1.2", - "acorn-dynamic-import": "2.0.2", - "ajv": "5.2.3", - "ajv-keywords": "2.1.0", - "async": "2.5.0", - "enhanced-resolve": "3.4.1", - "escope": "3.6.0", - "interpret": "1.0.4", - "json-loader": "0.5.7", - "json5": "0.5.1", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "mkdirp": "0.5.1", - "node-libs-browser": "2.0.0", - "source-map": "0.5.7", - "supports-color": "4.4.0", - "tapable": "0.2.8", - "uglifyjs-webpack-plugin": "0.4.6", - "watchpack": "1.4.0", - "webpack-sources": "1.0.1", - "yargs": "8.0.2" - } - }, - "webpack-sources": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz", - "integrity": "sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw==", - "dev": true, - "requires": { - "source-list-map": "2.0.0", - "source-map": "0.5.7" - } - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "requires": { - "string-width": "1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "4.1.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "read-pkg-up": "2.0.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - } - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } } } } diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 7859bede..254c6653 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tabfern", - "version": "0.1.16.1337", + "version": "0.1.17.1337", "description": "Google Chrome extension for displaying, saving, and managing tabs", "main": "src/view/main.js", "directories": { @@ -33,20 +33,13 @@ "requirejs": "^2.3.5", "rmodal": "^1.0.28", "spectrum-colorpicker": "^1.8.0", + "spin.js": "^4.0.0", "split.js": "^1.3.5", "tinycolor2": "^1.4.1" }, "devDependencies": { "benchmark": "^2.1.4", - "css-loader": "^0.28.7", - "extract-text-webpack-plugin": "^3.0.0", "jasmine2-custom-message": "^0.8.4", - "node-sass": "^4.5.3", - "raw-loader": "^0.5.1", - "sass": "^1.0.0-beta.2", - "sass-loader": "^6.0.6", - "seedrandom": "^2.4.3", - "style-loader": "^0.18.2", - "webpack": "^3.6.0" + "seedrandom": "^2.4.3" } } diff --git a/tabfern/_locales/README-patching.txt b/tabfern/_locales/README-patching.txt new file mode 100644 index 00000000..12d1ab22 --- /dev/null +++ b/tabfern/_locales/README-patching.txt @@ -0,0 +1,18 @@ +To copy new text from the en file to another file: + +First, grab the en file from the last commit in which the other file +was updated: +git show :./en/messages.json > en/90dmsgs.json + +Then, apply the changes: +git merge-file ru/messages.json en/90dmsgs.json en/messages.json + ^ File to be changed + ^ Base file ^ Current file + +Edit the other file (e.g., ru/messages.json) to clear any conflict +markers (<<<< / >>>>). + +Then, check to make sure it looks OK: +git diff diff --word-diff=color --ignore-space-at-eol --patience -b -w --ignore-blank-lines --no-index en/messages.json ru/messages.json + +The only changes you should see are the translations. diff --git a/tabfern/_locales/de/messages.json b/tabfern/_locales/de/messages.json new file mode 100755 index 00000000..8609f660 --- /dev/null +++ b/tabfern/_locales/de/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- Grüße!", "description": "de" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern Registerkarten-Manager und Backup-Tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Einstellungen" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Notiz hinzufügen oder bearbeiten" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Löschen (schließen; nicht speichern)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Umbenennen" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Schließen und speichern" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Löschen (schließen; nicht speichern)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "Neuer Fenstername?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Notiz für Tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Fenster \"$TITLE$\" löschen?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Registerkarte \"$TITLE$\" löschen?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Registerkarte \"$TITLE$\" und ihre Fenster löschen?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "Ja" + ,"description":"HTML code for a 'Yes' dialog button. Includes a Nein" + ,"description":"HTML code for a 'No' dialog button. Includes a Abbrechen" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a note)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (...) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Nicht gespeichert" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Gespeicherte Registerkarten" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Gerettete Registerkarten" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (gerettete)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unbekannter Titel ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Registerkarte" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Neuladen" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Online-Informationen" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Einstellungen und Offline-Hilfe" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Retten das letzte-gelöschte Fenster" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Backup speichern" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Lade den Inhalt eines Backups" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Sortieren" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Öffne Fenster nach oben" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Alle erweitern" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Alle reduzieren" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Oberen Rand umschalten" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Notiz hinzufügen oder bearbeiten" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Umbenennen" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Vergessen aber nicht schließen" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Speichern dieses Fenster nicht, wenn es geschlossen wird" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Erinnern" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Speichern dieses Fenster nicht, wenn es geschlossen wird" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Schließen und erinnern" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Löschen" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "Fehler: I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "Fehler: I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "Fehler: I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "Fehler: I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "Fehler: File $FILENAME$ is not something I can understand as a TabFern save file. Pars error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Fehler: Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/tabfern/_locales/en/messages.json b/tabfern/_locales/en/messages.json index a1b9cd07..fc30c4a2 100755 --- a/tabfern/_locales/en/messages.json +++ b/tabfern/_locales/en/messages.json @@ -1,66 +1,326 @@ -{ - "l10nTabName": { - "message":"Localization" - ,"description":"name of the localization tab" - } - ,"l10nHeader": { - "message":"It does localization too! (this whole tab is, actually)" - ,"description":"Header text for the localization section" - } - ,"l10nIntro": { - "message":"'L10n' refers to 'Localization' - 'L' an 'n' are obvious, and 10 comes from the number of letters between those two. It is the process/whatever of displaying something in the language of choice. It uses 'I18n', 'Internationalization', which refers to the tools / framework supporting L10n. I.e., something is internationalized if it has I18n support, and can be localized. Something is localized for you if it is in your language / dialect." - ,"description":"introduce the basic idea." - } - ,"l10nProd": { - "message":"You are planning to allow localization, right? You have no idea who will be using your extension! You have no idea who will be translating it! At least support the basics, it's not hard, and having the framework in place will let you transition much more easily later on." - ,"description":"drive the point home. It's good for you." - } - ,"l10nFirstParagraph": { - "message":"When the options page loads, elements decorated with data-l10n will automatically be localized!" - ,"description":"inform that elements will be localized on load" - } - ,"l10nSecondParagraph": { - "message":"If you need more complex localization, you can also define data-l10n-args. This should contain $containerType$ filled with $dataType$, which will be passed into Chrome's i18n API as $functionArgs$. In fact, this paragraph does just that, and wraps the args in mono-space font. Easy!" - ,"description":"introduce the data-l10n-args attribute. End on a lame note." - ,"placeholders": { - "containerType": { - "content":"$1" - ,"example":"'array', 'list', or something similar" - ,"description":"type of the args container" - } - ,"dataType": { - "content":"$2" - ,"example":"string" - ,"description":"type of data in each array index" - } - ,"functionArgs": { - "content":"$3" - ,"example":"arguments" - ,"description":"whatever you call what you pass into a function/method. args, params, etc." - } - } - } - ,"l10nThirdParagraph": { - "message":"Message contents are passed right into innerHTML without processing - include any tags (or even scripts) that you feel like. If you have an input field, the placeholder will be set instead, and buttons will have the value attribute set." - ,"description":"inform that we handle placeholders, buttons, and direct HTML input" - } - ,"l10nButtonsBefore": { - "message":"Different types of buttons are handled as well. <button> elements have their html set:" - } - ,"l10nButton": { - "message":"in a button" - } - ,"l10nButtonsBetween": { - "message":"while <input type='submit'> and <input type='button'> get their 'value' set (note: no HTML):" - } - ,"l10nSubmit": { - "message":"a submit value" - } - ,"l10nButtonsAfter": { - "message":"Awesome, no?" - } - ,"l10nExtras": { - "message":"You can even set data-l10n on things like the <title> tag, which lets you have translatable page titles, or fieldset <legend> tags, or anywhere else - the default Boil.localize() behavior will check every tag in the document, not just the body." - ,"description":"inform about places which may not be obvious, like , etc" - } -} +{ "encoding": { "message": "-*- coding: utf-8 -*- схема", "description": "en" } + + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern tab manager and backup tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Settings" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Add/edit label" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Edit text" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Close and save" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "New window name?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note for tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Delete window \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Delete tab \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Delete tab \"$TITLE$\" and its window?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">Y</span>es" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "y" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>o" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">C</span>ancel" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "c" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Don't ask again (<abbr title=\"You can change this in Settings | Behaviour\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Unsaved" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Saved tabs" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Recovered tabs" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Recovered)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unknown title ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Tab" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Reload" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Online info" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Settings and offline help" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restore last deleted" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Backup now" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Load contents of a backup" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Sort" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Open windows to top" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Expand all" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Collapse all" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Toggle top border" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Add/edit a note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Rename" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Forget but don't close" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Do not save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Remember" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Close and remember" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Delete" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "File $FILENAME$ is not something I can understand as a TabFern save file. Parse error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/tabfern/_locales/fr/messages.json b/tabfern/_locales/fr/messages.json new file mode 100755 index 00000000..f0ae8b37 --- /dev/null +++ b/tabfern/_locales/fr/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- drôles d'œufs abîmés", "description": "fr" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern gestionnaire d'onglet et outil de sauvegarde" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Paramètres" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Ajouter/éditer étiquette" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Supprimer (fermer; ne pas sauver)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Editer texte" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Fermer et sauver" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Supprimer (fermer; ne pas sauver)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "Nom de la nouvelle fenêtre ?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note pour l'onglet \"$TITLE$\" ?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Supprimer la fenêtre \"$TITLE$\" ?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Supprimer l'onglet \"$TITLE$\" ?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Supprimer l'onglet \"$TITLE$\" et sa fenêtre ?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">O</span>ui" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "o" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>on" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">A</span>nnuler" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "a" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Ne plus demander (<abbr title=\"Vous pouvez changer ceci dans Paramètres | Comportement\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Non enregistré" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Onglets enregistrés" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Onglets récupérés" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Récupéré)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Titre inconnu ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Onglet" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Exécuter Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Recharger" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Info en ligne" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Paramètres et aide hors ligne" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restaurer dernier effacement" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Sauvegarder backup maintenant" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Charger le contenu d'un backup" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Trier" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Ouvrir les fenêtres en haut" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Développer tout" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Cacher tout" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Alterner affichage bordure supérieure" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Ajouter/éditer une note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Renommer" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Oublier mais ne pas fermer" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Ne pas sauver cette fenêtre quand elle est fermée" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Se souvenir" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Sauver cette fenêtre quand elle est fermée" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Fermer et se souvenir" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Supprimer" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "Je n'ai pas pu sauver l'arborescence: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "Je n'ai pas pu ouvrir la fenêtre: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "Je n'ai pas pu créer la fenêtre: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "Je n'ai pas pu charger $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "Je ne comprend pas le fichier $FILENAME$ comme étant un fichier de sauvegarde de TabFern. L'erreur d'analyse est: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Erreur inattendue pendant que j'essayais de charger le fichier. Le code d'erreur est: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/tabfern/_locales/ru/messages.json b/tabfern/_locales/ru/messages.json new file mode 100755 index 00000000..f1cf968a --- /dev/null +++ b/tabfern/_locales/ru/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- схема", "description": "ru" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern tab manager and backup tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Settings" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Add/edit label" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Edit text" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Close and save" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "New window name?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note for tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Delete window \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Delete tab \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Delete tab \"$TITLE$\" and its window?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">Y</span>es" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "y" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>o" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">C</span>ancel" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "c" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Don't ask again (<abbr title=\"You can change this in Settings | Behaviour\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "несохронённые вешалки" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Сохронённые вешалки" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Recovered tabs" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Recovered)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unknown title ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Tab" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Reload" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "онлайновая информация" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "установки и автономная помощь" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restore last deleted" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "резервировать сейчас" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "загрузить содержания резерва" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "сортировка" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Open windows to top" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "увеличивать содержания" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "уменьшить содержания" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Toggle top border" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Add/edit a note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Rename" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Forget but don't close" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Do not save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Remember" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Close and remember" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Delete" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "File $FILENAME$ is not something I can understand as a TabFern save file. Pars error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/tabfern/assets/css/icons.css b/tabfern/assets/css/icons.css index 390905a1..6c54a6aa 100755 --- a/tabfern/assets/css/icons.css +++ b/tabfern/assets/css/icons.css @@ -1,17 +1,17 @@ /* icons.css: Common icon definitions */ .jstree-themeicon-custom.tf-window.tfs-saved > a > i { - background-image: url(/assets/icons/folder-from-jstree-default-dark-32px.png); + background-image: url("/assets/icons/folder-from-jstree-default-dark-32px.png"); } /* For Chrome */ .jstree-themeicon-custom.visible-window-icon, .jstree-themeicon-custom.tf-window.tfs-open > a > i { - background-image: url(/assets/icons/monitor.png); /* or folder.png? */ + background-image: url("/assets/icons/monitor.png"); /* or folder.png? */ } .jstree-themeicon-custom.visible-saved-window-icon, .jstree-themeicon-custom.tf-window.tfs-open.tfs-saved > a > i { - background-image: url(/assets/icons/monitor_add.png); /* or folder.png? */ + background-image: url("/assets/icons/monitor_add.png"); /* or folder.png? */ } /* For Firefox */ @@ -28,36 +28,36 @@ /* Back to cross-browser */ .jstree-themeicon-custom.fff-page /*, .tf-tab > i*/ { - background-image: url(/assets/icons/page_white.png); + background-image: url("/assets/icons/page_white.png"); } .jstree-themeicon-custom.fff-monitor-add { - background-image: url(/assets/icons/monitor_add.png); + background-image: url("/assets/icons/monitor_add.png"); } .jstree-themeicon-custom.fff-monitor { - background-image: url(/assets/icons/monitor.png); + background-image: url("/assets/icons/monitor.png"); } .jstree-themeicon-custom.fff-page-white-with-red-banner { - background-image: url(/assets/icons/page_white_red_banner.png); + background-image: url("/assets/icons/page_white_red_banner.png"); } .jstree-themeicon-custom.fff-pencil, .vakata-context .fff-pencil { - background-image: url(/assets/icons/pencil.png); + background-image: url("/assets/icons/pencil.png"); } .jstree-themeicon-custom.fff-cross, .vakata-context .fff-cross { - background-image: url(/assets/icons/cross.png); + background-image: url("/assets/icons/cross.png"); } .jstree-themeicon-custom.fff-picture-delete, .vakata-context .fff-picture-delete { - background-image: url(/assets/icons/picture_delete.png); + background-image: url("/assets/icons/picture_delete.png"); } .jstree-themeicon-custom.fff-text-padding-top, .vakata-context .fff-text-padding-top { - background-image: url(/assets/icons/text_padding_top.png); + background-image: url("/assets/icons/text_padding_top.png"); } /* Class for icons with no content. Used in jstree.set_icon() when the @@ -69,17 +69,26 @@ } /* Icons for actions, which do use content rather than background-image. - * That way we do not have to set the size of the <i> manually. */ -.tf-action-button.fff-pencil { - content: url(/assets/icons/pencil.png); + * That way we do not have to set the size of the <i> manually. + * For Firefox compatibility, we use content in the ::after pseudo-elements, + * since Chrome supports content in regular elements but Firefox doesn't. */ + +.tf-action-button { + position: relative; + display: inline-block; + height: 16px; /* must match icon height */ +} + +.tf-action-button.fff-pencil::after { + content: url("/assets/icons/pencil.png"); } -.tf-action-button.fff-picture-delete { - content: url(/assets/icons/picture_delete.png); +.tf-action-button.fff-picture-delete::after { + content: url("/assets/icons/picture_delete.png"); } -.tf-action-button.fff-cross { - content: url(/assets/icons/cross.png); +.tf-action-button.fff-cross::after { + content: url("/assets/icons/cross.png"); } /* Background sizes in context menu are different. TODO fix this --- it is diff --git a/tabfern/assets/css/spinjs.css b/tabfern/assets/css/spinjs.css new file mode 100755 index 00000000..083d2761 --- /dev/null +++ b/tabfern/assets/css/spinjs.css @@ -0,0 +1,26 @@ +@keyframes spinner-line-fade-more { + 0%, 100% { + opacity: 0; /* minimum opacity */ + } + 1% { + opacity: 1; + } +} + +@keyframes spinner-line-fade-quick { + 0%, 39%, 100% { + opacity: 0.25; /* minimum opacity */ + } + 40% { + opacity: 1; + } +} + +@keyframes spinner-line-fade-default { + 0%, 100% { + opacity: 0.22; /* minimum opacity */ + } + 1% { + opacity: 1; + } +} diff --git a/tabfern/js/jstree-actions.js b/tabfern/js/jstree-actions.js index 92a7dc85..8e4ff8f4 100644 --- a/tabfern/js/jstree-actions.js +++ b/tabfern/js/jstree-actions.js @@ -1,371 +1,377 @@ (function (factory) { - "use strict"; - if (typeof define === 'function' && define.amd) { - define('jstree-actions',['jquery', 'jstree'], factory); - } - else if(typeof module !== 'undefined' && module.exports) { - module.exports = factory(require('jquery'), require('jstree')); - } - else { - factory(jQuery, jQuery.jstree); - } + "use strict"; + if (typeof define === 'function' && define.amd) { + define('jstree-actions',['jquery', 'jstree'], factory); + } + else if(typeof module !== 'undefined' && module.exports) { + module.exports = factory(require('jquery'), require('jstree')); + } + else { + factory(jQuery, jQuery.jstree); + } }(function ($, _jstree_unused, undefined) { - "use strict"; - - if($.jstree.plugins.actions) { return; } - - /** - * stores all defaults for the actions plugin - * @name $.jstree.defaults.actions - * @plugin actions - */ - $.jstree.defaults.actions = { - /** - * How event propagation should be controlled after a click - * on an action button. - * - * - Set to `stop` to call `stopPropagation` after the callback - * - Set to `immediate` to call `stopImmediatePropagation` - * after the callback - * - Any other value (e.g., the default of `normal` will not - * change the propagation. - * - * @name $.jstree.defaults.actions.propagation - * @plugin actions - */ - propagation: 'normal' - }; - - $.jstree.plugins.actions = function (options, parent) { - - this._actions = {}; // indexed by node id - this._group_parms = {}; // The parameters of the group divs, indexed by node id. - - /** Make a group to hold grouped actions. Call this before calling add_action() - * with grouped: true. - * @param node_id <- the ID of the pertinent node - * @param opts <- a structure with option fields. - * @return the new group div's jquery object, or null - * - * possible opts are: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * class <- class(es) to apply to the new div - * text <- text to put in the new div - */ - this.make_group = function (node_id, opts) { - var after = (typeof opts.after == 'undefined') ? true : opts.after; - if (typeof opts.selector == 'undefined') return null; // TODO report error? - - // Note: when this is called, the DOM object may not yet exist. - - // Regularize and stash the parms - var parms = this._group_parms[node_id] = Object.assign({}, opts); - parms.class = parms.class || ''; - parms.text = parms.text || ''; - parms.selector = parms.selector || null; - // any falsy value => null, for regularity and to permit - // distinguishing undefined from null should you need to. - } - - /** Add a DOM element based on the selector, child, and after options. - * @param node_el {DOM Element} The root element of the node, generally <li>. - * Must have non-null `parentNode`. - * @param to_add_el {DOM Element} The element to add - * @param opts {object} Where to add the element. Has optional selector, - * optional child, and optional after, as described - * with reference to this._make_group(). - */ - this._add_dom_element = function(node_el, to_add_el, opts) { - var place; - if(opts.selector) { - place = node_el.querySelector(opts.selector); - } else { - place = node_el; - } - - if (opts.child) { - place.appendChild(to_add_el); - } else if (opts.after) { - place.parentNode.insertBefore(to_add_el, place.nextSibling); - } else { //before - place.parentNode.insertBefore(to_add_el, place); - } - }; - - /** Create the group div for a node. Returns the DOM object, or null. - * Uses the provided node_el because this.get_node(node_id, true) - * doesn't always succeed during redraw. - * @param node_id {string} The node - * @param node_el {DOM element} The current element for this node. - */ - this._create_group_for = function (node_id, node_el) { - if(!(node_id in this._group_parms)) return null; // TODO report error? - - var opts = this._group_parms[node_id]; - var group_el = document.createElement("div"); - group_el.className = opts.class; - group_el.textContent = opts.text; - - this._add_dom_element(node_el, group_el, opts); - - return group_el; - }; - - /** Add an action to a node or node(s). - * @param node_id Can be a single node id or an array of node ids. - * @param action An object representing the action that should be added to <node>. - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action will be added to all nodes. - * - * The action object can contain the following keys: - * id <- string An ID which identifies the action. The same ID can be shared across different nodes - * text <- string The action's text - * html <- string The action's html; used in preference to text if both are provided - * class <- string (a string containing all the classes you want to add to the action (space separated) - * event <- string The event on which the trigger will be called - * callback <- function that will be called when the action is clicked - * dataset <- optional object of key-value pairs that will be added as - * data-* attributes of the action's element. The values - * must be strings or support toString(). - * - * The action object can contain one of two types of location information: - * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. - * 2. If grouped is not true, the following can be used: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * - * NOTES: Please keep in mind that: - * - the id's are strictly compared (===) - * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar - */ - this.add_action = function (node_id, action) { - var self = this; - node_id = typeof node_id === 'object' ? node_id : [node_id]; - - for (var i = 0; i < node_id.length; i++) { - var _node_id = node_id[i]; - var actions = self._actions[_node_id] = self._actions[_node_id] || []; - - var should_redraw = false; - - if (!self._has_action(_node_id, action.id)) { - if(action.grouped && !(_node_id in this._group_parms)) { - continue; // TODO report error? - } - - var the_action = Object.assign({}, action); //our own copy - the_action.selector = the_action.selector || null; - actions.push(the_action); - should_redraw = true; - } - - if(should_redraw) this.redraw_node(_node_id); - - } - }; - - /** - * @param node_id Can be a single node id or an array of node ids - * @param action_id The ID of the action to be removed - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action_id will be removed from all nodes. - * - * The action_id is the unique identifier for each action. - * A special value "all" is allowed, in which case all the actions of node_id will be removed. - */ - this.remove_action = function (node_id, action_id) { - var self = this; - var node_ids = typeof node_id === 'object' ? node_id : - node_id === "all" ? Object.keys(this._actions).concat('all') : - [node_id]; - - for (var i = 0; i < node_ids.length; i++) { - node_id = node_ids[i]; - var actions = self._actions[node_id] || []; - var new_actions = []; - - for (var j = 0; j < actions.length; j++) { - var action = actions[j]; - if(action.id !== action_id && action_id !== "all") { - new_actions.push(action); - } - } - var ids = actions.map(function(x) { return x.id; }); - var new_ids = new_actions.map(function(x) { return x.id; }); - if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { - self._actions[node_id] = new_actions; - this.redraw_node(node_id); - } - } - }; - - /** - * Create the element for an action button. - * @param node_id {string} The node's ID - * @param action_id {string} The action's ID - */ - this._create_action = function (node_id, action_id) { - var self = this; - var action = this._get_action(node_id, action_id); - if (action === null) return null; - - var action_el = document.createElement("i"); - action_el.className = action.class; - if(action.html) { - action_el.innerHTML = action.html; - } else { - action_el.textContent = action.text || ''; - } - - // Set up element data-* values, if any - if( action_el.dataset && action.dataset && - (typeof action.dataset === 'object') && - (action.dataset !== null) - ) { - for(var key in action.dataset) { - action_el.dataset[key] = '' + action.dataset[key]; - } - } - - $(action_el).click(function(event) { - var node = self.get_node(action_el); - action.callback(node_id, node, - action_id, action_el, event); - - if(self.settings.actions.propagation==='stop') { - event.stopPropagation(); - } else if(self.settings.actions.propagation === 'immediate') { - event.stopImmediatePropagation(); - } - }); - - return { - "action": action, - "action_el": action_el - }; - }; - - /** - * Find an action of a node. - * @param node_id {string} The node's ID, or "all" - * @param action_id {string} The action's ID - * @return The action's data, or `null` if the action wasn't found. - */ - this._get_action = function (node_id, action_id) { - var actions = this._actions[node_id] || []; - var v = null; - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (action.id === action_id) { - //TODO: fill empty fields with default values? - v = action; - } - } - return v; - }; - - /** - * Add the given action to the DOM. - * @param node_el {DOM element} The node - * @param action The action's data record, including the - * already-created DOM element of the action button - */ - this._set_action = function (node_el, action) { - if (action === null) return; - - this._add_dom_element(node_el, action.action_el, action.action); - }; - - this._has_action = function (node_id, action_id) { - var found = false; - var actions = this._actions; - - if (actions.hasOwnProperty(node_id)) { - for (var i = 0; i < actions[node_id].length; i++) { - if (actions[node_id][i].id === action_id) found = true; - } - } - - if (this._actions.hasOwnProperty('all')) { - for (i = 0; i < actions['all'].length; i++) { - if (actions['all'][i].id === action_id) found = true; - } - } - - return found; - }; - - /** - * @param obj The node to redraw - */ - this.redraw_node = function (obj, deep, callback, force_draw) { - var self = this; - var node_id = typeof obj === "object" ? obj.id : obj; - var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); - if (node_el) { - //Check if we have any specific actions for this node - var actions = this._actions[node_id] || []; - var actions_all = this._actions["all"] || []; - - // Create the group if necessary - var group_el; - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } - - for (var i = 0; (!group_el) && (i < actions_all.length); i++) { - if(actions_all[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } - - // Populate the actions - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping action of node ' + node_id); - //console.log(actions[i]); - continue; - } - var _action = self._create_action(node_id, actions[i].id); - - if(actions[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } - - // Populate the "all" actions - for (i = 0; i < actions_all.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping "all" action at node ' + node_id); - //console.log(actions[i]); - continue; - } - _action = self._create_action("all", actions_all[i].id); - - if(actions_all[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } - - } //endif node_el - return node_el; - }; - - } + "use strict"; + + if($.jstree.plugins.actions) { return; } + + /** + * stores all defaults for the actions plugin + * @name $.jstree.defaults.actions + * @plugin actions + */ + $.jstree.defaults.actions = { + /** + * How event propagation should be controlled after a click + * on an action button. + * + * - Set to `stop` to call `stopPropagation` after the callback + * - Set to `immediate` to call `stopImmediatePropagation` + * after the callback + * - Any other value (e.g., the default of `normal` will not + * change the propagation. + * + * @name $.jstree.defaults.actions.propagation + * @plugin actions + */ + propagation: 'normal' + }; + + $.jstree.plugins.actions = function (options, parent) { + + this._actions = {}; // indexed by node id + this._group_parms = {}; // The parameters of the group divs, indexed by node id. + + /** Make a group to hold grouped actions. Call this before calling add_action() + * with grouped: true. + * @param node_id <- the ID of the pertinent node + * @param opts <- a structure with option fields. + * @return the new group div's jquery object, or null + * + * possible opts are: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * class <- class(es) to apply to the new div + * text <- text to put in the new div + */ + this.make_group = function (node_id, opts) { + var after = (typeof opts.after == 'undefined') ? true : opts.after; + if (typeof opts.selector == 'undefined') return null; // TODO report error? + + // Note: when this is called, the DOM object may not yet exist. + + // Regularize and stash the parms + var parms = this._group_parms[node_id] = Object.assign({}, opts); + parms.class = parms.class || ''; + parms.text = parms.text || ''; + parms.selector = parms.selector || null; + // any falsy value => null, for regularity and to permit + // distinguishing undefined from null should you need to. + } + + /** Add a DOM element based on the selector, child, and after options. + * @param node_el {DOM Element} The root element of the node, generally <li>. + * Must have non-null `parentNode`. + * @param to_add_el {DOM Element} The element to add + * @param opts {object} Where to add the element. Has optional selector, + * optional child, and optional after, as described + * with reference to this._make_group(). + */ + this._add_dom_element = function(node_el, to_add_el, opts) { + var place; + if(opts.selector) { + place = node_el.querySelector(opts.selector); + } else { + place = node_el; + } + + if (opts.child) { + place.appendChild(to_add_el); + } else if (opts.after) { + place.parentNode.insertBefore(to_add_el, place.nextSibling); + } else { //before + place.parentNode.insertBefore(to_add_el, place); + } + }; + + /** Create the group div for a node. Returns the DOM object, or null. + * Uses the provided node_el because this.get_node(node_id, true) + * doesn't always succeed during redraw. + * @param node_id {string} The node + * @param node_el {DOM element} The current element for this node. + */ + this._create_group_for = function (node_id, node_el) { + if(!(node_id in this._group_parms)) return null; // TODO report error? + + var opts = this._group_parms[node_id]; + var group_el = document.createElement("div"); + group_el.className = opts.class; + group_el.textContent = opts.text; + + this._add_dom_element(node_el, group_el, opts); + + return group_el; + }; + + /** Add an action to a node or node(s). + * @param node_id Can be a single node id or an array of node ids. + * @param action An object representing the action that should be added to <node>. + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action will be added to all nodes. + * + * The action object can contain the following keys: + * id <- string An ID which identifies the action. The same ID can be shared across different nodes + * text <- string The action's text + * html <- string The action's html; used in preference to text if both are provided + * class <- string (a string containing all the classes you want to add to the action (space separated) + * event <- string The event on which the trigger will be called + * callback <- function that will be called when the action is clicked + * dataset <- optional object of key-value pairs that will be added as + * data-* attributes of the action's element. The values + * must be strings or support toString(). + * + * The action object can contain one of two types of location information: + * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. + * 2. If grouped is not true, the following can be used: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * + * NOTES: Please keep in mind that: + * - the id's are strictly compared (===) + * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar + */ + this.add_action = function (node_id, action) { + var self = this; + node_id = typeof node_id === 'object' ? node_id : [node_id]; + + for (var i = 0; i < node_id.length; i++) { + var _node_id = node_id[i]; + var actions = self._actions[_node_id] = self._actions[_node_id] || []; + + var should_redraw = false; + + if (!self._has_action(_node_id, action.id)) { + if(action.grouped && !(_node_id in this._group_parms)) { + continue; // TODO report error? + } + + var the_action = Object.assign({}, action); //our own copy + the_action.selector = the_action.selector || null; + actions.push(the_action); + should_redraw = true; + } + + if(should_redraw) this.redraw_node(_node_id); + + } + }; + + /** + * @param node_id Can be a single node id or an array of node ids + * @param action_id The ID of the action to be removed + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action_id will be removed from all nodes. + * + * The action_id is the unique identifier for each action. + * A special value "all" is allowed, in which case all the actions of node_id will be removed. + */ + this.remove_action = function (node_id, action_id) { + var self = this; + var node_ids = typeof node_id === 'object' ? node_id : + node_id === "all" ? Object.keys(this._actions).concat('all') : + [node_id]; + + for (var i = 0; i < node_ids.length; i++) { + node_id = node_ids[i]; + var actions = self._actions[node_id] || []; + var new_actions = []; + + for (var j = 0; j < actions.length; j++) { + var action = actions[j]; + if(action.id !== action_id && action_id !== "all") { + new_actions.push(action); + } + } + var ids = actions.map(function(x) { return x.id; }); + var new_ids = new_actions.map(function(x) { return x.id; }); + if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { + self._actions[node_id] = new_actions; + this.redraw_node(node_id); + } + } + }; + + /** + * Create the element for an action button. + * @param node_id {string} The node's ID + * @param action_id {string} The action's ID + */ + this._create_action = function (node_id, action_id) { + var self = this; + var action = this._get_action(node_id, action_id); + if (action === null) return null; + + var action_el = document.createElement("i"); + action_el.className = action.class; + if(action.html) { + action_el.innerHTML = action.html; + } else { + action_el.textContent = action.text || ''; + } + + // Set up element data-* values, if any + if( action_el.dataset && action.dataset && + (typeof action.dataset === 'object') && + (action.dataset !== null) + ) { + for(var key in action.dataset) { + action_el.dataset[key] = '' + action.dataset[key]; + } + } + + // Title + if(action.title) { + action_el.title = action.title; + } + + $(action_el).click(function(event) { + var node = self.get_node(action_el); + action.callback(node_id, node, + action_id, action_el, event); + + if(self.settings.actions.propagation==='stop') { + event.stopPropagation(); + } else if(self.settings.actions.propagation === 'immediate') { + event.stopImmediatePropagation(); + } + }); + + return { + "action": action, + "action_el": action_el + }; + }; + + /** + * Find an action of a node. + * @param node_id {string} The node's ID, or "all" + * @param action_id {string} The action's ID + * @return The action's data, or `null` if the action wasn't found. + */ + this._get_action = function (node_id, action_id) { + var actions = this._actions[node_id] || []; + var v = null; + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; + if (action.id === action_id) { + //TODO: fill empty fields with default values? + v = action; + } + } + return v; + }; + + /** + * Add the given action to the DOM. + * @param node_el {DOM element} The node + * @param action The action's data record, including the + * already-created DOM element of the action button + */ + this._set_action = function (node_el, action) { + if (action === null) return; + + this._add_dom_element(node_el, action.action_el, action.action); + }; + + this._has_action = function (node_id, action_id) { + var found = false; + var actions = this._actions; + + if (actions.hasOwnProperty(node_id)) { + for (var i = 0; i < actions[node_id].length; i++) { + if (actions[node_id][i].id === action_id) found = true; + } + } + + if (this._actions.hasOwnProperty('all')) { + for (i = 0; i < actions['all'].length; i++) { + if (actions['all'][i].id === action_id) found = true; + } + } + + return found; + }; + + /** + * @param obj The node to redraw + */ + this.redraw_node = function (obj, deep, callback, force_draw) { + var self = this; + var node_id = typeof obj === "object" ? obj.id : obj; + var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); + if (node_el) { + //Check if we have any specific actions for this node + var actions = this._actions[node_id] || []; + var actions_all = this._actions["all"] || []; + + // Create the group if necessary + var group_el; + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } + + for (var i = 0; (!group_el) && (i < actions_all.length); i++) { + if(actions_all[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } + + // Populate the actions + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping action of node ' + node_id); + //console.log(actions[i]); + continue; + } + var _action = self._create_action(node_id, actions[i].id); + + if(actions[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } + + // Populate the "all" actions + for (i = 0; i < actions_all.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping "all" action at node ' + node_id); + //console.log(actions[i]); + continue; + } + _action = self._create_action("all", actions_all[i].id); + + if(actions_all[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } + + } //endif node_el + return node_el; + }; + + } })); +// vi: set ts=4 sts=4 sw=4 et ai: // diff --git a/tabfern/js/multidex.js b/tabfern/js/multidex.js index 268e44b7..d6c3e051 100644 --- a/tabfern/js/multidex.js +++ b/tabfern/js/multidex.js @@ -256,7 +256,7 @@ } let val = idx[key_val]; - if( (field_name===null) || + if( (field_name == null) || (typeof field_name === 'undefined') ) { return val; } else if(field_name in val) { diff --git a/tabfern/js/spin-packed.js b/tabfern/js/spin-packed.js new file mode 100644 index 00000000..8a5c2d05 --- /dev/null +++ b/tabfern/js/spin-packed.js @@ -0,0 +1,295 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var spin_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); + +window.Spinner = spin_js__WEBPACK_IMPORTED_MODULE_0__["Spinner"]; + + +/***/ }), +/* 1 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Spinner", function() { return Spinner; }); +var __assign = (undefined && undefined.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +var defaults = { + lines: 12, + length: 7, + width: 5, + radius: 10, + scale: 1.0, + corners: 1, + color: '#000', + fadeColor: 'transparent', + animation: 'spinner-line-fade-default', + rotate: 0, + direction: 1, + speed: 1, + zIndex: 2e9, + className: 'spinner', + top: '50%', + left: '50%', + shadow: '0 0 1px transparent', + position: 'absolute', +}; +var Spinner = /** @class */ (function () { + function Spinner(opts) { + if (opts === void 0) { opts = {}; } + this.opts = __assign({}, defaults, opts); + } + /** + * Adds the spinner to the given target element. If this instance is already + * spinning, it is automatically removed from its previous target by calling + * stop() internally. + */ + Spinner.prototype.spin = function (target) { + this.stop(); + this.el = document.createElement('div'); + this.el.className = this.opts.className; + this.el.setAttribute('role', 'progressbar'); + css(this.el, { + position: this.opts.position, + width: 0, + zIndex: this.opts.zIndex, + left: this.opts.left, + top: this.opts.top, + transform: "scale(" + this.opts.scale + ")", + }); + if (target) { + target.insertBefore(this.el, target.firstChild || null); + } + drawLines(this.el, this.opts); + return this; + }; + /** + * Stops and removes the Spinner. + * Stopped spinners may be reused by calling spin() again. + */ + Spinner.prototype.stop = function () { + if (this.el) { + if (typeof requestAnimationFrame !== 'undefined') { + cancelAnimationFrame(this.animateId); + } + else { + clearTimeout(this.animateId); + } + if (this.el.parentNode) { + this.el.parentNode.removeChild(this.el); + } + this.el = undefined; + } + return this; + }; + return Spinner; +}()); + +/** + * Sets multiple style properties at once. + */ +function css(el, props) { + for (var prop in props) { + el.style[prop] = props[prop]; + } + return el; +} +/** + * Returns the line color from the given string or array. + */ +function getColor(color, idx) { + return typeof color == 'string' ? color : color[idx % color.length]; +} +/** + * Internal method that draws the individual lines. + */ +function drawLines(el, opts) { + var borderRadius = (Math.round(opts.corners * opts.width * 500) / 1000) + 'px'; + var shadow = 'none'; + if (opts.shadow === true) { + shadow = '0 2px 4px #000'; // default shadow + } + else if (typeof opts.shadow === 'string') { + shadow = opts.shadow; + } + var shadows = parseBoxShadow(shadow); + for (var i = 0; i < opts.lines; i++) { + var degrees = ~~(360 / opts.lines * i + opts.rotate); + var backgroundLine = css(document.createElement('div'), { + position: 'absolute', + top: -opts.width / 2 + "px", + width: (opts.length + opts.width) + 'px', + height: opts.width + 'px', + background: getColor(opts.fadeColor, i), + borderRadius: borderRadius, + transformOrigin: 'left', + transform: "rotate(" + degrees + "deg) translateX(" + opts.radius + "px)", + }); + var delay = i * opts.direction / opts.lines / opts.speed; + delay -= 1 / opts.speed; // so initial animation state will include trail + var line = css(document.createElement('div'), { + width: '100%', + height: '100%', + background: getColor(opts.color, i), + borderRadius: borderRadius, + boxShadow: normalizeShadow(shadows, degrees), + animation: 1 / opts.speed + "s linear " + delay + "s infinite " + opts.animation, + }); + backgroundLine.appendChild(line); + el.appendChild(backgroundLine); + } +} +function parseBoxShadow(boxShadow) { + var regex = /^\s*([a-zA-Z]+\s+)?(-?\d+(\.\d+)?)([a-zA-Z]*)\s+(-?\d+(\.\d+)?)([a-zA-Z]*)(.*)$/; + var shadows = []; + for (var _i = 0, _a = boxShadow.split(','); _i < _a.length; _i++) { + var shadow = _a[_i]; + var matches = shadow.match(regex); + if (matches === null) { + continue; // invalid syntax + } + var x = +matches[2]; + var y = +matches[5]; + var xUnits = matches[4]; + var yUnits = matches[7]; + if (x === 0 && !xUnits) { + xUnits = yUnits; + } + if (y === 0 && !yUnits) { + yUnits = xUnits; + } + if (xUnits !== yUnits) { + continue; // units must match to use as coordinates + } + shadows.push({ + prefix: matches[1] || '', + x: x, + y: y, + xUnits: xUnits, + yUnits: yUnits, + end: matches[8], + }); + } + return shadows; +} +/** + * Modify box-shadow x/y offsets to counteract rotation + */ +function normalizeShadow(shadows, degrees) { + var normalized = []; + for (var _i = 0, shadows_1 = shadows; _i < shadows_1.length; _i++) { + var shadow = shadows_1[_i]; + var xy = convertOffset(shadow.x, shadow.y, degrees); + normalized.push(shadow.prefix + xy[0] + shadow.xUnits + ' ' + xy[1] + shadow.yUnits + shadow.end); + } + return normalized.join(', '); +} +function convertOffset(x, y, degrees) { + var radians = degrees * Math.PI / 180; + var sin = Math.sin(radians); + var cos = Math.cos(radians); + return [ + Math.round((x * cos + y * sin) * 1000) / 1000, + Math.round((-x * sin + y * cos) * 1000) / 1000, + ]; +} + + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/tabfern/manifest.json b/tabfern/manifest.json index 7ea1213f..6d9bacdc 100755 --- a/tabfern/manifest.json +++ b/tabfern/manifest.json @@ -1,8 +1,8 @@ { - "name": "TabFern tab manager and backup tool", - "short_name": "TabFern", - "version": "0.1.16.1337", - "version_name": "0.1.16", + "name": "__MSG_wsLongName__", + "short_name": "__MSG_wsShortName__", + "version": "0.1.17.1337", + "version_name": "0.1.17", "offline_enabled": true, "manifest_version": 2, "minimum_chrome_version": "54", @@ -40,5 +40,10 @@ ], "web_accessible_resources": [ "assets/*" - ] + ], + "commands": { + "reveal-view": { + "description": "Open the TabFern window" + } + } } diff --git a/tabfern/src/bg/background.js b/tabfern/src/bg/background.js index d6242db0..1ef6e284 100755 --- a/tabfern/src/bg/background.js +++ b/tabfern/src/bg/background.js @@ -60,7 +60,7 @@ function moveTabFernViewToWindow(reference_cwin) // https://stackoverflow.com/users/930675/sean-bannister // When the icon is clicked in Chrome -chrome.browserAction.onClicked.addListener(function(tab) { +let onClickedListener = function(tab) { // If viewWindowID is undefined then there isn't a popup currently open. if (typeof viewWindowID === "undefined") { // Open the popup @@ -72,7 +72,7 @@ chrome.browserAction.onClicked.addListener(function(tab) { // Set a timer to bring the window to the front on another click // that follows fairly shortly. - { + if(tab) { let clickListener = function(tab) { if(viewWindowID && tab.windowId) { chrome.windows.get(tab.windowId, moveTabFernViewToWindow); @@ -89,7 +89,17 @@ chrome.browserAction.onClicked.addListener(function(tab) { chrome.browserAction.onClicked.addListener(clickListener); } -}); +} //onClickedListener() + +chrome.browserAction.onClicked.addListener(onClickedListener); + +let onCommandListener = function(cmd) { + console.log("Received command " + cmd); + if(cmd == 'reveal-view') { + onClickedListener(null); // null => no tab, so no summon + } +} //onCommandListener() +chrome.commands.onCommand.addListener(onCommandListener); // When a window is closed chrome.windows.onRemoved.addListener(function(windowId) { diff --git a/tabfern/src/common/common.js b/tabfern/src/common/common.js index a301ef75..9e6c98c8 100755 --- a/tabfern/src/common/common.js +++ b/tabfern/src/common/common.js @@ -11,7 +11,7 @@ console.log('TabFern common.js loading'); /// The TabFern extension friendly version number. Displayed in the /// title bar of the popup window, so lowercase (no shouting!). -const TABFERN_VERSION='0.1.16' //' alpha \u26a0' +const TABFERN_VERSION='0.1.17'; // When you change this, also update: // - manifest.json: both the version and version_name // - package.json @@ -39,47 +39,126 @@ const MSG_EDIT_TAB_NOTE = 'editTabNote'; ////////////////////////////////////////////////////////////////////////// // Names of settings, and their defaults // +/// An array to build the defaults in. Every property must have a default, +/// since the defaults array is also used to identify properties to be +/// saved/loaded. The JS types of the defaults must match the types +/// of the properties. +let _DEF = { __proto__: null }; + +/// An array of validators, used when loading settings. Each is a function +/// that returns a valid value for that setting, or `undefined` to use the +/// default. NOTE: returning "undefined" will trigger a warning on the console. +let _VAL = { __proto__: null }; +let _vbool = (v)=>{ return ((typeof v === 'boolean')?v:undefined)}; + // Booleans const CFG_ENB_CONTEXT_MENU = 'ContextMenu.Enabled'; +_DEF[CFG_ENB_CONTEXT_MENU] = true; +_VAL[CFG_ENB_CONTEXT_MENU] = _vbool; + const CFG_RESTORE_ON_LAST_DELETED = 'open-tree-on-restore-last-deleted'; +_DEF[CFG_RESTORE_ON_LAST_DELETED] = false; +_VAL[CFG_RESTORE_ON_LAST_DELETED] = _vbool; + const CFG_JUMP_WITH_SORT_OPEN_TOP = 'jump-to-top-when-sort-open-to-top'; +_DEF[CFG_JUMP_WITH_SORT_OPEN_TOP] = true; +_VAL[CFG_JUMP_WITH_SORT_OPEN_TOP] = _vbool; + const CFG_COLLAPSE_ON_STARTUP = 'collapse-trees-on-startup'; +_DEF[CFG_COLLAPSE_ON_STARTUP] = true; +_VAL[CFG_COLLAPSE_ON_STARTUP] = _vbool; + const CFG_OPEN_TOP_ON_STARTUP = 'open-to-top-on-startup'; +_DEF[CFG_OPEN_TOP_ON_STARTUP] = false; +_VAL[CFG_OPEN_TOP_ON_STARTUP] = _vbool; + const CFG_HIDE_HORIZONTAL_SCROLLBARS = 'hide-horizontal-scrollbars'; +_DEF[CFG_HIDE_HORIZONTAL_SCROLLBARS] = true; +_VAL[CFG_HIDE_HORIZONTAL_SCROLLBARS] = _vbool; + const CFG_SKINNY_SCROLLBARS = 'skinny-scrollbars'; +_DEF[CFG_SKINNY_SCROLLBARS] = false; +_VAL[CFG_SKINNY_SCROLLBARS] = _vbool; + const CFG_NEW_WINS_AT_TOP = 'open-new-windows-at-top'; +_DEF[CFG_NEW_WINS_AT_TOP] = true; +_VAL[CFG_NEW_WINS_AT_TOP] = _vbool; + const CFG_SHOW_TREE_LINES = 'show-tree-lines'; +_DEF[CFG_SHOW_TREE_LINES] = false; +_VAL[CFG_SHOW_TREE_LINES] = _vbool; + const CFG_CONFIRM_DEL_OF_SAVED = 'confirm-del-of-saved-wins'; +_DEF[CFG_CONFIRM_DEL_OF_SAVED] = true; +_VAL[CFG_CONFIRM_DEL_OF_SAVED] = _vbool; + const CFG_CONFIRM_DEL_OF_UNSAVED = 'confirm-del-of-unsaved-wins'; +_DEF[CFG_CONFIRM_DEL_OF_UNSAVED] = false; +_VAL[CFG_CONFIRM_DEL_OF_UNSAVED] = _vbool; + const CFG_CONFIRM_DEL_OF_SAVED_TABS = 'confirm-del-of-saved-tabs'; +_DEF[CFG_CONFIRM_DEL_OF_SAVED_TABS] = true; +_VAL[CFG_CONFIRM_DEL_OF_SAVED_TABS] = _vbool; + const CFG_CONFIRM_DEL_OF_UNSAVED_TABS = 'confirm-del-of-unsaved-tabs'; +_DEF[CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = false; +_VAL[CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = _vbool; + +const CFG_URL_IN_TOOLTIP = 'tooltip-has-url'; +_DEF[CFG_URL_IN_TOOLTIP] = false; +_VAL[CFG_URL_IN_TOOLTIP] = _vbool; -// Strings +const CFG_TITLE_IN_TOOLTIP = 'tooltip-has-title'; +_DEF[CFG_TITLE_IN_TOOLTIP] = false; +_VAL[CFG_TITLE_IN_TOOLTIP] = _vbool; + +const CFG_PRUNE_NEW_WINDOWS = 'prune-new-windows'; +_DEF[CFG_PRUNE_NEW_WINDOWS] = false; +_VAL[CFG_PRUNE_NEW_WINDOWS] = _vbool; + +/// Not actually a setting, but an indicator that we loaded settings OK. +/// Used by src/settings/main.js. +const SETTINGS_LOADED_OK = '__settings_loaded_OK'; +_DEF[SETTINGS_LOADED_OK] = false; +_VAL[SETTINGS_LOADED_OK] = ()=>{return undefined;} + + + +// Not yet implemented - pending #35. Whether to open closed tabs when +// you click on the tree item for a partially-open window. +//const CFG_OPEN_REST_ON_CLICK = 'open-rest-on-win-click', +// CFG_OROC_DO = true, +// CFG_OROC_DO_NOT = false; +//_DEF[CFG_OPEN_REST_ON_CLICK] = CFG_OROC_DO_NOT; + +// Strings, including limited-choice controls such as radio buttons and dropdowns. const CFGS_BACKGROUND = 'window-background'; -const CFGS_SCROLLBAR_COLOR = 'skinny-scrollbar-color'; +_DEF[CFGS_BACKGROUND] = ''; +_VAL[CFGS_BACKGROUND] = (v)=>{ + if(!v) return ''; + if(Validation.isValidColor(v)) return v; + if(Validation.isValidURL(v, + ['file', 'https', 'data', 'chrome-extension'])) return v; + return undefined; +}; -// Other const CFGS_THEME_NAME = 'theme-name'; +_DEF[CFGS_THEME_NAME] = 'default-dark'; +_VAL[CFGS_THEME_NAME] = (v)=>{ + return (( v === 'default-dark' || v === 'default') ? v : undefined); +}; -const CFG_DEFAULTS = { - __proto__: null, - [CFG_ENB_CONTEXT_MENU]: true, - [CFG_RESTORE_ON_LAST_DELETED]: false, - [CFG_JUMP_WITH_SORT_OPEN_TOP]: true, - [CFG_COLLAPSE_ON_STARTUP]: true, - [CFG_OPEN_TOP_ON_STARTUP]: false, - [CFG_HIDE_HORIZONTAL_SCROLLBARS]: true, - [CFG_SKINNY_SCROLLBARS]: false, - [CFG_NEW_WINS_AT_TOP]: true, - [CFG_SHOW_TREE_LINES]: false, - [CFG_CONFIRM_DEL_OF_SAVED]: true, - [CFG_CONFIRM_DEL_OF_UNSAVED]: false, - [CFG_CONFIRM_DEL_OF_SAVED_TABS]: true, - [CFG_CONFIRM_DEL_OF_UNSAVED_TABS]: false, - [CFGS_THEME_NAME]: 'default-dark', - [CFGS_SCROLLBAR_COLOR]: '', // none by default +const CFGS_SCROLLBAR_COLOR = 'skinny-scrollbar-color'; +_DEF[CFGS_SCROLLBAR_COLOR] = ''; +_VAL[CFGS_SCROLLBAR_COLOR] = (v)=>{ + if(!v) return ''; + return ((Validation.isValidColor(v)) ? v : undefined); }; +/// The default values for the configuration settings. +const CFG_DEFAULTS = Object.seal(_DEF); +const CFG_VALIDATORS = Object.seal(_VAL); + ////////////////////////////////////////////////////////////////////////// // Test for Firefox // // Not sure if I need this, but I'm playing it safe for now. Firefox returns @@ -88,6 +167,8 @@ const CFG_DEFAULTS = { // Chrome code. Hopefully in the future I can test for null/undefined // in either browser, and get rid of this block. +BROWSER_TYPE=null; // unknown + (function(win){ let isLastError_chrome = ()=>{return (typeof(chrome.runtime.lastError) !== 'undefined');}; @@ -100,6 +181,7 @@ const CFG_DEFAULTS = { (info)=>{ // fullfillment if(info.name === 'Firefox') { win.isLastError = isLastError_firefox; + BROWSER_TYPE = 'ff'; } else { win.isLastError = isLastError_chrome; } @@ -110,6 +192,7 @@ const CFG_DEFAULTS = { } ); } else { // Chrome + BROWSER_TYPE = 'chrome'; win.isLastError = isLastError_chrome; } })(window); @@ -119,6 +202,12 @@ const CFG_DEFAULTS = { const SETTING_PREFIX = 'store.settings.'; +/// Get the raw value of a setting. Returns null if the key doesn't exist. +function getRawSetting(setting_name) +{ + return localStorage.getItem(SETTING_PREFIX + setting_name); +} //getSetting + /// Get the string value of a setting, if it is a string. function getStringSetting(setting_name, default_value = undefined) { @@ -177,10 +266,11 @@ function haveSetting(setting_name) /// @param setting_value {mixed} The value, which must be JSON.stringifiable. function setSetting(setting_name, setting_value) { + // TODO handle exceptions in some reasonable way. localStorage.setItem( SETTING_PREFIX + setting_name, JSON.stringify(setting_value) - ); + ); // JSON stringify so we can store more than just strings. } //setSetting /// Set a setting only if it's not already there. Parameters are as @@ -259,6 +349,9 @@ function loadCSS(doc, url, before) { ////////////////////////////////////////////////////////////////////////// // Miscellaneous functions // +/// Shortcut for i18n. Call _T("name") to pull the localized "name". +var _T = chrome.i18n.getMessage; + /// Ignore a Chrome callback error, and suppress Chrome's /// `runtime.lastError` diagnostic. Use this as a Chrome callback. function ignore_chrome_error() { void chrome.runtime.lastError; } diff --git a/tabfern/src/common/validation.js b/tabfern/src/common/validation.js index 800ec845..1f3fb53e 100755 --- a/tabfern/src/common/validation.js +++ b/tabfern/src/common/validation.js @@ -1,4 +1,5 @@ -// validation.js: Data-validation routines +/// validation.js: Data-validation routines. +/// NOTE: does NOT use common.js routines, so that common.js can use it. (function (root, factory) { let imports=[]; @@ -44,7 +45,25 @@ const RE = /^(rgb|hsl)a?\((-?[\d\.]+%?(deg|rad|grad|turn)?[,\s]+){2,3}[\s\/]*[\d\.]+%?\)$/i; return RE.test(color); } - } //isValidColor + }; //isValidColor + + /// Validate a URL. + /// @param test_url The URL to test + /// @param allowed_schemes {Optional array} If provided, only those schemes + /// (no colons) are allowed. + module.isValidURL = function(test_url, allowed_schemes) { + try { + let url = new URL(String(test_url)); + + if(Array.isArray(allowed_schemes)) { + let scheme = url.protocol.replace(/:$/,''); + if(allowed_schemes.indexOf(scheme) === -1) return false; + } + + return true; + } catch(e) { } // nop + return false; + }; //isValidURL return module; })); diff --git a/tabfern/src/settings/index.html b/tabfern/src/settings/index.html index 37f9e698..78796d2b 100755 --- a/tabfern/src/settings/index.html +++ b/tabfern/src/settings/index.html @@ -18,11 +18,17 @@ <link rel="stylesheet" href="custom.css" media="screen"> <link rel="stylesheet" href="/assets/css/icons.css" media="screen"> <link rel="stylesheet" href="/assets/css/spectrum.css" media="screen"> + <link rel="stylesheet" href="/assets/css/spinjs.css" media="screen"> <!-- JavaScripts --> + <script src="../common/validation.js"></script> <script src="../common/common.js"></script> + <script src="/js/loglevel.js"></script> + <script src="/js/spin-packed.js"></script> <script src="/js/tinycolor.js"></script> + <script src="/js/import-file.js"></script> + <script src="/js/export-file.js"></script> <script src="lib/mootools-core.js"></script> <!-- now $ is mootools --> <script src="lib/store.js"></script> @@ -40,7 +46,7 @@ <script src="/js/jquery-noconflict.js"></script> <!-- now $ is back to mootools - use jQuery() instead of $ --> - <script src="settings.js"></script> + <script src="main.js"></script> </head> <body class="no-select"> <div id="sidebar" class="fancy"> diff --git a/tabfern/src/settings/main.js b/tabfern/src/settings/main.js new file mode 100755 index 00000000..6cb993db --- /dev/null +++ b/tabfern/src/settings/main.js @@ -0,0 +1,302 @@ +/// An object to hold the settings for later programmatic access +let settingsobj; + +/// jQuery alias, since $ is mootools +let $$ = jQuery; + +// Color picker //////////////////////////////////////////////////// {{{1 + +/// Create the color picker for the scrollbar color. +let createPicker = function createPicker() { + let picker = $$('#scrollbar-color-picker-label'); + + let orig_color = getStringSetting(CFGS_SCROLLBAR_COLOR); + if(!Validation.isValidColor(orig_color)) { + orig_color = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; + } + + // Replace the manifest entry with the color picker + $$(picker).spectrum({ + showInput: true, + allowEmpty:true, + showInitial: true, + preferredFormat: 'hex', + color: orig_color, + }); + + // Add the text that would otherwise have gone in the manifest + let newlabel = $$('<span>').text(i18n.get( + 'Skinny-scrollbar color ("X" for the default): ')) + .addClass('setting label'); + $$(picker).before(newlabel); + + // Handle updates + $$(picker).on('change.spectrum', (e, newcolor)=>{ + let colorstring; + if(!newcolor || !newcolor.toString) { + console.log('New color: default'); + colorstring = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; + } else { + console.log({'New color': newcolor.toString()}); + colorstring = String(newcolor.toString()); + } + + if(!colorstring || Validation.isValidColor(colorstring)) { + setSetting(CFGS_SCROLLBAR_COLOR, colorstring); + } else { + console.log('Invalid color'); + $$(picker).spectrum('set',orig_color); + } + }); +}; //createPicker + +// }}}1 +// Export/Import Settings ////////////////////////////////////////// {{{1 + +/// Pack the settings into an object to export. +/// TODO automate keeping this in sync with common.js. +function saveSettingsToObject() +{ + let retval = { __proto__: null }; + for(let key in CFG_DEFAULTS) { + retval[key] = getRawSetting(key); + } + return Object.seal(retval); +} //saveSettingsToObject + +/// Export the settings +function exportSettings(evt_unused) +{ + let date_tag = new Date().toISOString().replace(/:/g,'.'); + // DOS filenames can't include colons. + // TODO use local time - maybe + // https://www.npmjs.com/package/dateformat ? + let filename = 'TabFern settings backup ' + date_tag + '.tabfern_settings'; + + let saved_info = saveSettingsToObject(); + Fileops.Export(document, JSON.stringify(saved_info), filename); +} //exportSettings() + +/// Assign settings from an object we have loaded. +/// TODO automate keeping this in sync with common.js. +function loadSettingsFromObject(obj) { + let ok = true; + let errmsgs = ''; + function stash(m) { errmsgs += `<li>${m}</li>`; } + + log.info({'Loading settings from':obj}); + + for(let key in CFG_DEFAULTS) { + if(!obj[key]) { + log.info(`Setting ${key} not found`); + continue; // not an error + } + + // Get the value + let val; + try { + val = JSON.parse(String(obj[key])); + } catch(e) { + let m = `Non-JSON value for ${key} - skipping`; + log.warn(m); + stash(m); + ok = false; + continue; + } + + // Confirm its type + if(typeof val !== typeof CFG_DEFAULTS[key]) { + let m = `Setting ${key}: value is a ${typeof val} but should be `+ + `a ${typeof CFG_DEFAULTS[key]} - skipping`; + log.warn(m); + stash(m); + ok = false; + continue; + } + + // Run value-specific checks, e.g., for well-formedness. + if(CFG_VALIDATORS[key]) { + let val_output = CFG_VALIDATORS[key](val); + if(val_output === undefined) { + let m = `Setting ${key}: Value ${val} failed validation`; + log.warn(m); + //stash(m); // Not user-facing + val = CFG_DEFAULTS[key]; + } else { + val = val_output; + } + } + + // Set the value + if(typeof val === 'boolean' || typeof val === 'string') { + // We already checked that val is of the correct type above, + // so we can go ahead and set it. + setSetting(key, val); + } else { // This shouldn't happen, so it's a log.error if it does. + let m = `Unexpected type ${typeof val} for ${key} - skipping`; + log.error(m); + stash(m); + ok = false; + continue; + } + + } //foreach key + + return {ok, errmsgs}; +} //loadSettingsFromObject + +/// Import the settings +function importSettings(evt_unused) +{ + function processFile(text, filename) { + let spinner; + try { + spinner = new Spinner().spin( + $$('#import-settings').parent()[0] + ); + let parsed = JSON.parse(text); + let {ok, errmsgs} = loadSettingsFromObject(parsed); + if(!ok) { + let elem = $$('<div>').html( + '<p>I encountered error(s) while loading the file ' + + `'${filename}':</p><ul>${errmsgs}</ul>`); + $$('#import-settings').after(elem); + + } else { // success + // Let ourselves know, after reload, that it worked + setSetting(SETTINGS_LOADED_OK, true); + + // refresh all the controls by reloading + window.location.reload(true); + } + + } catch(e) { + window.alert("File " + filename + ' is not something I can '+ + 'understand as a TabFern settings file. Parse error code was: ' + + e); + } + if(spinner) spinner.stop(); + } //processFile() + + setSetting(SETTINGS_LOADED_OK, false); + let importer = Fileops.Importer(document, '.tabfern_settings'); + importer.getFileAsString(processFile); +} //importSettings() + +// }}}1 +// Main //////////////////////////////////////////////////////////// {{{1 + +function main() +{ + // Option 1: Use the manifest: + new FancySettings.initWithManifest(function (settings) { + $$('#settings-label').text(_T('wsSettings')); + + settingsobj = settings; + //settings.manifest.myButton.addEvent("action", function () { + // alert("You clicked me!"); + //}); + + // ---------------------------- + // Finish creating the page + createPicker(); // Skinny-scrollbar color picker + + // ---------------------------- + // Hook up events + $$('#import-settings').on('click', importSettings); + $$('#export-settings').on('click', exportSettings); + + if(getBoolSetting(SETTINGS_LOADED_OK)) { + let elem = $$('<div>').text("Settings loaded"); + $$('#import-settings').after(elem); + setSetting(SETTINGS_LOADED_OK, false); + } + // ---------------------------- + // open tab specified in a query parm, if known. + // See https://stackoverflow.com/a/12151322/2877364 + // Use location.hash instead of location.search since Chrome doesn't + // seem to navigate to chrome-extension://...&... . + let searchParams = new URLSearchParams(window.location.hash.slice(1)); + if(searchParams.has('open')) { + let whichtab = -1; // If other than -1, select that tab + + let openval = String(searchParams.get('open')); // Do we need the explicit String()? + let tabNames = Object.keys(settingsobj.tabs); + // These come out in definition order, as far as I know + + // Check for a tab number + let tabnum = Number(openval); + if(!isNaN(tabnum) && (tabnum|0)>=0 && (tabnum|0)<tabNames.length) { + whichtab = (tabnum|0); + } + + // Check for "last" as a special value + if(whichtab === -1 && openval.toLowerCase()==='last') { + whichtab = tabNames.length-1; + } + + // Jump to that tab. + if(whichtab !== -1) { + settingsobj.tabs[tabNames[whichtab]].bundle.activate(); + } + + } //endif &open=... parameter specified + }); + + // Option 2: Do everything manually: + /* + var settings = new FancySettings("My Extension", "icon.png"); + + var username = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "username", + "type": "text", + "label": i18n.get("username"), + "text": i18n.get("x-characters") + }); + + var password = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "password", + "type": "text", + "label": i18n.get("password"), + "text": i18n.get("x-characters-pw"), + "masked": true + }); + + var myDescription = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "myDescription", + "type": "description", + "text": i18n.get("description") + }); + + var myButton = settings.create({ + "tab": "Information", + "group": "Logout", + "name": "myButton", + "type": "button", + "label": "Disconnect:", + "text": "Logout" + }); + + // ... + + myButton.addEvent("action", function () { + alert("You clicked me!"); + }); + + settings.align([ + username, + password + ]); + */ +} //main() + +window.addEvent("domready", main); +// }}}1 + +// vi: set ts=4 sts=4 sw=4 et ai foldmethod=marker: // diff --git a/tabfern/src/settings/manifest.js b/tabfern/src/settings/manifest.js index a0aa1f3a..f3c07646 100755 --- a/tabfern/src/settings/manifest.js +++ b/tabfern/src/settings/manifest.js @@ -5,7 +5,7 @@ (function(root){ // Shortcuts for frequently-used items function icon(cls) { return `<i class="${cls}"></i>`; } - function issue(num) { return `(<a href="https://github.com/cxw42/TabFern/issues/${num|0}">#${num|0}</a>)`; } + function issue(num, noparen) { return `${noparen ? '' : '('}<a href="https://github.com/cxw42/TabFern/issues/${num|0}">#${num|0}</a>${noparen ? '' : ')'}`; } function brplain(text){return `<br/><span class="plain">${text}</span>`;} let ham = icon('fa fa-bars'); @@ -13,10 +13,12 @@ let settings = `${ham} ${gt} Settings ${gt}`; let refresh_message = " (refresh the TabFern window after you change this to make the change take effect)" -// Settings {{{2 + // Settings {{{2 // Assign the settings root.manifest = { - "name": "Settings - ver. "+TABFERN_VERSION+' - TabFern', + "name": + `${_T('wsSettings')} - ${_T('wsShortName')} (v${TABFERN_VERSION})`, + "icon": "/assets/fern16.png", "settings": [ @@ -35,6 +37,22 @@ ' of the list, information about recent feature additions'+ ' or changes.</p>' }, + { + "tab": i18n.get("Welcome / Help"), + "group": i18n.get("Import/Export"), + "name": "export-settings", + "id": "export-settings", + "type": "button", + "text": "Save settings to a file" + }, + { + "tab": i18n.get("Welcome / Help"), + "group": i18n.get("Import/Export"), + "name": "import-settings", + "id": "import-settings", + "type": "button", + "text": "Load settings from a file" + }, // Behaviour. Yeah, there's a "u" in there! { @@ -107,6 +125,7 @@ "type": "checkbox", "label": i18n.get('Prompt for confirmation before deleting <b>unsaved</b> windows'), }, + { "tab": i18n.get("Behaviour"), "group": i18n.get("Deleting tabs"), @@ -122,7 +141,28 @@ "label": i18n.get('Prompt for confirmation before deleting <b>tabs</b> in <b>unsaved</b> windows'), }, + { + "tab": i18n.get("Behaviour"), + "group": i18n.get("When Chrome..."), + "name": CFG_PRUNE_NEW_WINDOWS, + "type": "checkbox", + "label": i18n.get("Adds extra tabs to a new window I've just opened, get rid of them!"), + }, + { + "tab": i18n.get("Behaviour"), + "group": i18n.get("When Chrome..."), + 'group_html':true, + "type": "description", + "text": i18n.get("\u26a0 use this option only if you need it — it may not behave exactly as you would expect."), + }, + // Appearance + { + "tab": i18n.get("Appearance"), + "group": '', + "type": "description", + "text": i18n.get("Refresh the TabFern window to apply changes to these options. To refresh, click TabFern's title bar and hit F5."), + }, { "tab": i18n.get("Appearance"), "group": i18n.get("Scrollbars"), @@ -147,20 +187,15 @@ // placeholder - settings.js adds the actual control // after this. }, - { - "tab": i18n.get("Appearance"), - "group": i18n.get("Scrollbars"), - "type": "description", - "text": i18n.get("Refresh the TabFern window to apply changes to these options."), - }, - // Maybe add some theming options here? { "tab": i18n.get("Appearance"), "group": i18n.get("Tree"), "name": CFG_SHOW_TREE_LINES, "type": "checkbox", - "label": i18n.get('Show connecting lines between nodes' + refresh_message), + "label": i18n.get('Show connecting lines between nodes'), }, + + // Theming options { "tab": i18n.get("Appearance"), "group": i18n.get("Theme"), @@ -184,8 +219,7 @@ "group": i18n.get("Theme"), "type": "description", "text": -`${refresh_message}<br/> -The background can be specified as a CSS color name, rgb(r,g,b), hsl(h,s,l), +`The background can be specified as a CSS color name, rgb(r,g,b), hsl(h,s,l), or a URL (data, https, chrome-extension, or file). To use images from your local disk (file): <ul> @@ -195,6 +229,21 @@ bar (it will start with "file://")</li> <li>Paste the "file://..." URL into the box above.</li>` }, + { + "tab": i18n.get("Appearance"), + "group": i18n.get("Tooltips"), + "name": CFG_URL_IN_TOOLTIP, + "type": "checkbox", + "label": i18n.get("Show URL in each item's tooltip"), + }, + { + "tab": i18n.get("Appearance"), + "group": i18n.get("Tooltips"), + "name": CFG_TITLE_IN_TOOLTIP, + "type": "checkbox", + "label": i18n.get("Show page title in each item's tooltip"), + }, + // Features { "tab": "Features", @@ -257,8 +306,81 @@ bar (it will start with "file://")</li> "text": "X or + or don't show when these are dynamic" }, -// }}}2 + // }}}2 + // Credits {{{2 + { + "tab": i18n.get("Credits and thanks"), + "group": 'TabFern', + 'group_html':true, + "type": "description", + "text": +`TabFern is by Chris White (<a href="https://devwrench.wordpress.com">blog</a>, +<a href="https://github.com/cxw42/">GitHub</a>). I greatly appreciate +the following contributors! If I have accidentally missed you, please let +me know so I can correct the omission. All names below are in alphabetical +order.` + }, + + { + "tab": i18n.get("Credits and thanks"), + "group": i18n.get("Programming"), + 'group_html':true, + "type": "description", + "text": +`<ul><li><a href="https://github.com/r4j4h/">Jasmine Hegman</a></li></ul>` + }, + { + "tab": i18n.get("Credits and thanks"), + "group": i18n.get("Translation"), + 'group_html':true, + "type": "description", + "text": +`<ul> +<li><a href="https://github.com/Procyon-b/">Procyon-b</a> (French)</li> +<li><a href="https://github.com/rwexmd/">rwexmd</a> (Russian)</li> +</ul>` + }, + + // }}}2 // Changelog {{{1 + { + "tab": i18n.get("What's new?"), + "group": `Version 0.1.17${brplain('2018-09-02')}`, + 'group_html':true, + "type": "description", + "text": +`<ul> +<li class="gold-star">TabFern now has <b>500</b> users!!! +<b>Thank you</b> for using TabFern and helping the project!</li> +<li>The first version of TabFern was released one year ago today. +\u{1F382}</li> +<li>Partial translations into French and Russian. My thanks to the +translators! Please see the new Credits tab. ${issue(135)}</li> +<li>Tooltips on the action buttons. ${issue(117)}</li> +<li>${settings} Appearance ${gt} Tooltips now has options to show the +URL and title of each item in a tooltip on that item. This way you can +see URLs in the tree, and you can see long titles without having +to scroll. ${issue(104)}</li> +<li>You can now save and load settings from the ${i18n.get("Welcome / Help")} +tab. ${issue(92)}</li> +<li>When you open the TF window, it moves back to its last position +more quickly. ${issue(134)}</li> +<li><a href="https://vivaldi.com/">Vivaldi</a> support: +Basic TabFern functionality is now also available on the Vivaldi +browser. Vivaldi uses the +Chrome Web Store just like Chrome itself does, so installation in Vivaldi +is the same as installation in Chrome. ${issue(123)}</li> +<li><a href="https://getfirefox.com">Firefox Quantum</a> support: +If you're a developer, you can now load TabFern as a temporary add-on +and get at least the basic save/load/tab-switching. (Note that you can't +manipulate <tt>about:debugging</tt> because it's special to Firefox.) +${issue(100)}</li> +<li>There is now a "Reload" option on the menu +(${ham} ${gt} ${icon('fa fa-refresh')} Reload), in case TabFern +and Chrome get out of sync. ${issue(127)}</li> +<li>Bugfixes: ${issue(128,true)}, ${issue(129,true)}</li> +</ul>` + }, { "tab": i18n.get("What's new?"), "group": `Version 0.1.16${brplain('2018-03-08')}`, diff --git a/tabfern/src/settings/settings.js b/tabfern/src/settings/settings.js deleted file mode 100755 index a96c425c..00000000 --- a/tabfern/src/settings/settings.js +++ /dev/null @@ -1,135 +0,0 @@ -/// An object to hold the settings for later programmatic access -let settingsobj; - -function createPicker($$) -{ - let picker = $$('#scrollbar-color-picker-label'); - - // Replace the manifest entry with the color picker - $$(picker).spectrum({ - showInput: true, - allowEmpty:true, - showInitial: true, - color: getStringSetting(CFGS_SCROLLBAR_COLOR), - }); - - // Add the text that would otherwise have gone in the manifest - let newlabel = $$('<span>').text(i18n.get( - 'Skinny-scrollbar color ("X" for the default): ')) - .addClass('setting label'); - $$(picker).before(newlabel); - - // Handle updates - $$(picker).on('change.spectrum', (e, newcolor)=>{ - let colorstring; - if(!newcolor || !newcolor.toString) { - console.log('New color: default'); - colorstring = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; - } else { - console.log({'New color': newcolor.toString()}); - colorstring = String(newcolor.toString()); - } - - setSetting(CFGS_SCROLLBAR_COLOR, colorstring); - }); -} //createPicker - -window.addEvent("domready", function () { - let $$ = jQuery; // since $ is mootools - - // Option 1: Use the manifest: - new FancySettings.initWithManifest(function (settings) { - settingsobj = settings; - //settings.manifest.myButton.addEvent("action", function () { - // alert("You clicked me!"); - //}); - - // ---------------------------- - // Create the color-picker for the skinny scrollbars - createPicker($$); - - // ---------------------------- - // open tab specified in a query parm, if known. - // See https://stackoverflow.com/a/12151322/2877364 - // Use location.hash instead of location.search since Chrome doesn't - // seem to navigate to chrome-extension://...&... . - let searchParams = new URLSearchParams(window.location.hash.slice(1)); - if(searchParams.has('open')) { - let whichtab = -1; // If other than -1, select that tab - - let openval = String(searchParams.get('open')); // Do we need the explicit String()? - let tabNames = Object.keys(settingsobj.tabs); - // These come out in definition order, as far as I know - - // Check for a tab number - let tabnum = Number(openval); - if(!isNaN(tabnum) && (tabnum|0)>=0 && (tabnum|0)<tabNames.length) { - whichtab = (tabnum|0); - } - - // Check for "last" as a special value - if(whichtab === -1 && openval.toLowerCase()==='last') { - whichtab = tabNames.length-1; - } - - // Jump to that tab. - if(whichtab !== -1) { - settingsobj.tabs[tabNames[whichtab]].bundle.activate(); - } - - } //endif &open=... parameter specified - }); - - // Option 2: Do everything manually: - /* - var settings = new FancySettings("My Extension", "icon.png"); - - var username = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "username", - "type": "text", - "label": i18n.get("username"), - "text": i18n.get("x-characters") - }); - - var password = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "password", - "type": "text", - "label": i18n.get("password"), - "text": i18n.get("x-characters-pw"), - "masked": true - }); - - var myDescription = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "myDescription", - "type": "description", - "text": i18n.get("description") - }); - - var myButton = settings.create({ - "tab": "Information", - "group": "Logout", - "name": "myButton", - "type": "button", - "label": "Disconnect:", - "text": "Logout" - }); - - // ... - - myButton.addEvent("action", function () { - alert("You clicked me!"); - }); - - settings.align([ - username, - password - ]); - */ -}); -// vi: set ts=4 sts=4 sw=4 et ai: // diff --git a/tabfern/src/view/bundle_tree.js b/tabfern/src/view/bundle_tree.js index 92237267..aa932014 100644 --- a/tabfern/src/view/bundle_tree.js +++ b/tabfern/src/view/bundle_tree.js @@ -8463,376 +8463,382 @@ // tabfern/js/jstree-actions.js (function (factory) { - "use strict"; - if (typeof define === 'function' && define.amd) { - define('jstree-actions',['jquery', 'jstree'], factory); - } - else if(typeof module !== 'undefined' && module.exports) { - module.exports = factory(require('jquery'), require('jstree')); - } - else { - factory(jQuery, jQuery.jstree); - } + "use strict"; + if (typeof define === 'function' && define.amd) { + define('jstree-actions',['jquery', 'jstree'], factory); + } + else if(typeof module !== 'undefined' && module.exports) { + module.exports = factory(require('jquery'), require('jstree')); + } + else { + factory(jQuery, jQuery.jstree); + } }(function ($, _jstree_unused, undefined) { - "use strict"; - - if($.jstree.plugins.actions) { return; } - - /** - * stores all defaults for the actions plugin - * @name $.jstree.defaults.actions - * @plugin actions - */ - $.jstree.defaults.actions = { - /** - * How event propagation should be controlled after a click - * on an action button. - * - * - Set to `stop` to call `stopPropagation` after the callback - * - Set to `immediate` to call `stopImmediatePropagation` - * after the callback - * - Any other value (e.g., the default of `normal` will not - * change the propagation. - * - * @name $.jstree.defaults.actions.propagation - * @plugin actions - */ - propagation: 'normal' - }; + "use strict"; - $.jstree.plugins.actions = function (options, parent) { + if($.jstree.plugins.actions) { return; } - this._actions = {}; // indexed by node id - this._group_parms = {}; // The parameters of the group divs, indexed by node id. + /** + * stores all defaults for the actions plugin + * @name $.jstree.defaults.actions + * @plugin actions + */ + $.jstree.defaults.actions = { + /** + * How event propagation should be controlled after a click + * on an action button. + * + * - Set to `stop` to call `stopPropagation` after the callback + * - Set to `immediate` to call `stopImmediatePropagation` + * after the callback + * - Any other value (e.g., the default of `normal` will not + * change the propagation. + * + * @name $.jstree.defaults.actions.propagation + * @plugin actions + */ + propagation: 'normal' + }; - /** Make a group to hold grouped actions. Call this before calling add_action() - * with grouped: true. - * @param node_id <- the ID of the pertinent node - * @param opts <- a structure with option fields. - * @return the new group div's jquery object, or null - * - * possible opts are: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * class <- class(es) to apply to the new div - * text <- text to put in the new div - */ - this.make_group = function (node_id, opts) { - var after = (typeof opts.after == 'undefined') ? true : opts.after; - if (typeof opts.selector == 'undefined') return null; // TODO report error? - - // Note: when this is called, the DOM object may not yet exist. - - // Regularize and stash the parms - var parms = this._group_parms[node_id] = Object.assign({}, opts); - parms.class = parms.class || ''; - parms.text = parms.text || ''; - parms.selector = parms.selector || null; - // any falsy value => null, for regularity and to permit - // distinguishing undefined from null should you need to. - } + $.jstree.plugins.actions = function (options, parent) { + + this._actions = {}; // indexed by node id + this._group_parms = {}; // The parameters of the group divs, indexed by node id. + + /** Make a group to hold grouped actions. Call this before calling add_action() + * with grouped: true. + * @param node_id <- the ID of the pertinent node + * @param opts <- a structure with option fields. + * @return the new group div's jquery object, or null + * + * possible opts are: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * class <- class(es) to apply to the new div + * text <- text to put in the new div + */ + this.make_group = function (node_id, opts) { + var after = (typeof opts.after == 'undefined') ? true : opts.after; + if (typeof opts.selector == 'undefined') return null; // TODO report error? + + // Note: when this is called, the DOM object may not yet exist. + + // Regularize and stash the parms + var parms = this._group_parms[node_id] = Object.assign({}, opts); + parms.class = parms.class || ''; + parms.text = parms.text || ''; + parms.selector = parms.selector || null; + // any falsy value => null, for regularity and to permit + // distinguishing undefined from null should you need to. + } - /** Add a DOM element based on the selector, child, and after options. - * @param node_el {DOM Element} The root element of the node, generally <li>. - * Must have non-null `parentNode`. - * @param to_add_el {DOM Element} The element to add - * @param opts {object} Where to add the element. Has optional selector, - * optional child, and optional after, as described - * with reference to this._make_group(). - */ - this._add_dom_element = function(node_el, to_add_el, opts) { - var place; - if(opts.selector) { - place = node_el.querySelector(opts.selector); - } else { - place = node_el; - } + /** Add a DOM element based on the selector, child, and after options. + * @param node_el {DOM Element} The root element of the node, generally <li>. + * Must have non-null `parentNode`. + * @param to_add_el {DOM Element} The element to add + * @param opts {object} Where to add the element. Has optional selector, + * optional child, and optional after, as described + * with reference to this._make_group(). + */ + this._add_dom_element = function(node_el, to_add_el, opts) { + var place; + if(opts.selector) { + place = node_el.querySelector(opts.selector); + } else { + place = node_el; + } - if (opts.child) { - place.appendChild(to_add_el); - } else if (opts.after) { - place.parentNode.insertBefore(to_add_el, place.nextSibling); - } else { //before - place.parentNode.insertBefore(to_add_el, place); - } - }; + if (opts.child) { + place.appendChild(to_add_el); + } else if (opts.after) { + place.parentNode.insertBefore(to_add_el, place.nextSibling); + } else { //before + place.parentNode.insertBefore(to_add_el, place); + } + }; - /** Create the group div for a node. Returns the DOM object, or null. - * Uses the provided node_el because this.get_node(node_id, true) - * doesn't always succeed during redraw. - * @param node_id {string} The node - * @param node_el {DOM element} The current element for this node. - */ - this._create_group_for = function (node_id, node_el) { - if(!(node_id in this._group_parms)) return null; // TODO report error? + /** Create the group div for a node. Returns the DOM object, or null. + * Uses the provided node_el because this.get_node(node_id, true) + * doesn't always succeed during redraw. + * @param node_id {string} The node + * @param node_el {DOM element} The current element for this node. + */ + this._create_group_for = function (node_id, node_el) { + if(!(node_id in this._group_parms)) return null; // TODO report error? - var opts = this._group_parms[node_id]; - var group_el = document.createElement("div"); - group_el.className = opts.class; - group_el.textContent = opts.text; + var opts = this._group_parms[node_id]; + var group_el = document.createElement("div"); + group_el.className = opts.class; + group_el.textContent = opts.text; - this._add_dom_element(node_el, group_el, opts); + this._add_dom_element(node_el, group_el, opts); - return group_el; - }; + return group_el; + }; - /** Add an action to a node or node(s). - * @param node_id Can be a single node id or an array of node ids. - * @param action An object representing the action that should be added to <node>. - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action will be added to all nodes. - * - * The action object can contain the following keys: - * id <- string An ID which identifies the action. The same ID can be shared across different nodes - * text <- string The action's text - * html <- string The action's html; used in preference to text if both are provided - * class <- string (a string containing all the classes you want to add to the action (space separated) - * event <- string The event on which the trigger will be called - * callback <- function that will be called when the action is clicked - * dataset <- optional object of key-value pairs that will be added as - * data-* attributes of the action's element. The values - * must be strings or support toString(). - * - * The action object can contain one of two types of location information: - * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. - * 2. If grouped is not true, the following can be used: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * - * NOTES: Please keep in mind that: - * - the id's are strictly compared (===) - * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar - */ - this.add_action = function (node_id, action) { - var self = this; - node_id = typeof node_id === 'object' ? node_id : [node_id]; + /** Add an action to a node or node(s). + * @param node_id Can be a single node id or an array of node ids. + * @param action An object representing the action that should be added to <node>. + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action will be added to all nodes. + * + * The action object can contain the following keys: + * id <- string An ID which identifies the action. The same ID can be shared across different nodes + * text <- string The action's text + * html <- string The action's html; used in preference to text if both are provided + * class <- string (a string containing all the classes you want to add to the action (space separated) + * event <- string The event on which the trigger will be called + * callback <- function that will be called when the action is clicked + * dataset <- optional object of key-value pairs that will be added as + * data-* attributes of the action's element. The values + * must be strings or support toString(). + * + * The action object can contain one of two types of location information: + * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. + * 2. If grouped is not true, the following can be used: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * + * NOTES: Please keep in mind that: + * - the id's are strictly compared (===) + * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar + */ + this.add_action = function (node_id, action) { + var self = this; + node_id = typeof node_id === 'object' ? node_id : [node_id]; - for (var i = 0; i < node_id.length; i++) { - var _node_id = node_id[i]; - var actions = self._actions[_node_id] = self._actions[_node_id] || []; + for (var i = 0; i < node_id.length; i++) { + var _node_id = node_id[i]; + var actions = self._actions[_node_id] = self._actions[_node_id] || []; - var should_redraw = false; + var should_redraw = false; - if (!self._has_action(_node_id, action.id)) { - if(action.grouped && !(_node_id in this._group_parms)) { - continue; // TODO report error? - } + if (!self._has_action(_node_id, action.id)) { + if(action.grouped && !(_node_id in this._group_parms)) { + continue; // TODO report error? + } - var the_action = Object.assign({}, action); //our own copy - the_action.selector = the_action.selector || null; - actions.push(the_action); - should_redraw = true; - } + var the_action = Object.assign({}, action); //our own copy + the_action.selector = the_action.selector || null; + actions.push(the_action); + should_redraw = true; + } - if(should_redraw) this.redraw_node(_node_id); + if(should_redraw) this.redraw_node(_node_id); - } - }; + } + }; - /** - * @param node_id Can be a single node id or an array of node ids - * @param action_id The ID of the action to be removed - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action_id will be removed from all nodes. - * - * The action_id is the unique identifier for each action. - * A special value "all" is allowed, in which case all the actions of node_id will be removed. - */ - this.remove_action = function (node_id, action_id) { - var self = this; - var node_ids = typeof node_id === 'object' ? node_id : - node_id === "all" ? Object.keys(this._actions).concat('all') : - [node_id]; - - for (var i = 0; i < node_ids.length; i++) { - node_id = node_ids[i]; - var actions = self._actions[node_id] || []; - var new_actions = []; - - for (var j = 0; j < actions.length; j++) { - var action = actions[j]; - if(action.id !== action_id && action_id !== "all") { - new_actions.push(action); - } - } - var ids = actions.map(function(x) { return x.id; }); - var new_ids = new_actions.map(function(x) { return x.id; }); - if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { - self._actions[node_id] = new_actions; - this.redraw_node(node_id); - } - } - }; + /** + * @param node_id Can be a single node id or an array of node ids + * @param action_id The ID of the action to be removed + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action_id will be removed from all nodes. + * + * The action_id is the unique identifier for each action. + * A special value "all" is allowed, in which case all the actions of node_id will be removed. + */ + this.remove_action = function (node_id, action_id) { + var self = this; + var node_ids = typeof node_id === 'object' ? node_id : + node_id === "all" ? Object.keys(this._actions).concat('all') : + [node_id]; + + for (var i = 0; i < node_ids.length; i++) { + node_id = node_ids[i]; + var actions = self._actions[node_id] || []; + var new_actions = []; + + for (var j = 0; j < actions.length; j++) { + var action = actions[j]; + if(action.id !== action_id && action_id !== "all") { + new_actions.push(action); + } + } + var ids = actions.map(function(x) { return x.id; }); + var new_ids = new_actions.map(function(x) { return x.id; }); + if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { + self._actions[node_id] = new_actions; + this.redraw_node(node_id); + } + } + }; - /** - * Create the element for an action button. - * @param node_id {string} The node's ID - * @param action_id {string} The action's ID - */ - this._create_action = function (node_id, action_id) { - var self = this; - var action = this._get_action(node_id, action_id); - if (action === null) return null; + /** + * Create the element for an action button. + * @param node_id {string} The node's ID + * @param action_id {string} The action's ID + */ + this._create_action = function (node_id, action_id) { + var self = this; + var action = this._get_action(node_id, action_id); + if (action === null) return null; + + var action_el = document.createElement("i"); + action_el.className = action.class; + if(action.html) { + action_el.innerHTML = action.html; + } else { + action_el.textContent = action.text || ''; + } - var action_el = document.createElement("i"); - action_el.className = action.class; - if(action.html) { - action_el.innerHTML = action.html; - } else { - action_el.textContent = action.text || ''; - } + // Set up element data-* values, if any + if( action_el.dataset && action.dataset && + (typeof action.dataset === 'object') && + (action.dataset !== null) + ) { + for(var key in action.dataset) { + action_el.dataset[key] = '' + action.dataset[key]; + } + } - // Set up element data-* values, if any - if( action_el.dataset && action.dataset && - (typeof action.dataset === 'object') && - (action.dataset !== null) - ) { - for(var key in action.dataset) { - action_el.dataset[key] = '' + action.dataset[key]; - } - } + // Title + if(action.title) { + action_el.title = action.title; + } - $(action_el).click(function(event) { - var node = self.get_node(action_el); - action.callback(node_id, node, - action_id, action_el, event); + $(action_el).click(function(event) { + var node = self.get_node(action_el); + action.callback(node_id, node, + action_id, action_el, event); - if(self.settings.actions.propagation==='stop') { - event.stopPropagation(); - } else if(self.settings.actions.propagation === 'immediate') { - event.stopImmediatePropagation(); - } - }); + if(self.settings.actions.propagation==='stop') { + event.stopPropagation(); + } else if(self.settings.actions.propagation === 'immediate') { + event.stopImmediatePropagation(); + } + }); - return { - "action": action, - "action_el": action_el - }; - }; + return { + "action": action, + "action_el": action_el + }; + }; - /** - * Find an action of a node. - * @param node_id {string} The node's ID, or "all" - * @param action_id {string} The action's ID - * @return The action's data, or `null` if the action wasn't found. - */ - this._get_action = function (node_id, action_id) { - var actions = this._actions[node_id] || []; - var v = null; - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (action.id === action_id) { - //TODO: fill empty fields with default values? - v = action; - } - } - return v; - }; + /** + * Find an action of a node. + * @param node_id {string} The node's ID, or "all" + * @param action_id {string} The action's ID + * @return The action's data, or `null` if the action wasn't found. + */ + this._get_action = function (node_id, action_id) { + var actions = this._actions[node_id] || []; + var v = null; + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; + if (action.id === action_id) { + //TODO: fill empty fields with default values? + v = action; + } + } + return v; + }; - /** - * Add the given action to the DOM. - * @param node_el {DOM element} The node - * @param action The action's data record, including the - * already-created DOM element of the action button - */ - this._set_action = function (node_el, action) { - if (action === null) return; + /** + * Add the given action to the DOM. + * @param node_el {DOM element} The node + * @param action The action's data record, including the + * already-created DOM element of the action button + */ + this._set_action = function (node_el, action) { + if (action === null) return; - this._add_dom_element(node_el, action.action_el, action.action); - }; + this._add_dom_element(node_el, action.action_el, action.action); + }; - this._has_action = function (node_id, action_id) { - var found = false; - var actions = this._actions; + this._has_action = function (node_id, action_id) { + var found = false; + var actions = this._actions; - if (actions.hasOwnProperty(node_id)) { - for (var i = 0; i < actions[node_id].length; i++) { - if (actions[node_id][i].id === action_id) found = true; - } - } + if (actions.hasOwnProperty(node_id)) { + for (var i = 0; i < actions[node_id].length; i++) { + if (actions[node_id][i].id === action_id) found = true; + } + } - if (this._actions.hasOwnProperty('all')) { - for (i = 0; i < actions['all'].length; i++) { - if (actions['all'][i].id === action_id) found = true; - } - } + if (this._actions.hasOwnProperty('all')) { + for (i = 0; i < actions['all'].length; i++) { + if (actions['all'][i].id === action_id) found = true; + } + } - return found; - }; + return found; + }; - /** - * @param obj The node to redraw - */ - this.redraw_node = function (obj, deep, callback, force_draw) { - var self = this; - var node_id = typeof obj === "object" ? obj.id : obj; - var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); - if (node_el) { - //Check if we have any specific actions for this node - var actions = this._actions[node_id] || []; - var actions_all = this._actions["all"] || []; - - // Create the group if necessary - var group_el; - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } + /** + * @param obj The node to redraw + */ + this.redraw_node = function (obj, deep, callback, force_draw) { + var self = this; + var node_id = typeof obj === "object" ? obj.id : obj; + var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); + if (node_el) { + //Check if we have any specific actions for this node + var actions = this._actions[node_id] || []; + var actions_all = this._actions["all"] || []; + + // Create the group if necessary + var group_el; + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } - for (var i = 0; (!group_el) && (i < actions_all.length); i++) { - if(actions_all[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } + for (var i = 0; (!group_el) && (i < actions_all.length); i++) { + if(actions_all[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } - // Populate the actions - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping action of node ' + node_id); - //console.log(actions[i]); - continue; - } - var _action = self._create_action(node_id, actions[i].id); + // Populate the actions + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping action of node ' + node_id); + //console.log(actions[i]); + continue; + } + var _action = self._create_action(node_id, actions[i].id); - if(actions[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } + if(actions[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } - // Populate the "all" actions - for (i = 0; i < actions_all.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping "all" action at node ' + node_id); - //console.log(actions[i]); - continue; - } - _action = self._create_action("all", actions_all[i].id); + // Populate the "all" actions + for (i = 0; i < actions_all.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping "all" action at node ' + node_id); + //console.log(actions[i]); + continue; + } + _action = self._create_action("all", actions_all[i].id); - if(actions_all[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } + if(actions_all[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } - } //endif node_el - return node_el; - }; + } //endif node_el + return node_el; + }; - } + } })); +// vi: set ts=4 sts=4 sw=4 et ai: // // Begin bundled file /////////////////////////////////////////////////// // tabfern/js/jstree-flagnode.js @@ -10039,7 +10045,7 @@ } let val = idx[key_val]; - if( (field_name===null) || + if( (field_name == null) || (typeof field_name === 'undefined') ) { return val; } else if(field_name in val) { diff --git a/tabfern/src/view/const.js b/tabfern/src/view/const.js index 56c3b79a..3b5fd0e3 100755 --- a/tabfern/src/view/const.js +++ b/tabfern/src/view/const.js @@ -53,6 +53,12 @@ INIT_TIME_ALLOWED_MS: 3000, // After this time, if init isn't done, // display an error message. INIT_MSG_SEL: 'div#init-incomplete', // Selector for that message + INIT_PROGRESS_SEL: 'div#init-progress', // Selector for the x/y progress indicator + + /// How long to wait after a winOnCreated or tabOnCreated before + /// checking whether pruning is required. This is to give merge + /// detection priority over pruning. + WIN_PRUNE_TIMER_MS: 200, ACTION_GROUP_WIN_CLASS: 'tf-action-group', // Class on action-group div ACTION_BUTTON_WIN_CLASS: 'tf-action-button', // Class on action buttons (<i>) @@ -99,28 +105,68 @@ /// Open a new window with a given URL. Also remove the default /// tab that appears because we are letting the window open at the /// default size. Yes, this is a bit like a FOUC, but oh well. - module.openWindowForURL = function(url) { + /// @return An ASQ instance + module.openWindowForURL = function(desired_url) { let win_id; // TODO is there a better way to pass data down // the sequence? - let tab0_id; ///< The tab automatically created with the window +// let tab0_id; ///< The tab automatically created with the window + let seq = ASQH.NowCC((cc)=>{ chrome.windows.create(cc); }) .then(function open_tab(done, new_win){ win_id = new_win.id; - tab0_id = new_win.tabs[0].id; - chrome.tabs.create({windowId: win_id, url: url}, ASQH.CC(done)); + // Chrome automatically creates a tab in a window, but + // Vivaldi does not. Handle either. +// if(new_win.tabs && new_win.tabs.length) { +// tab0_id = new_win.tabs[0].id; +// } + chrome.tabs.create({windowId: win_id, url: desired_url}, + ASQH.CC(done)); + }) + .then(function get_all_tabs(done){ + chrome.windows.get(win_id, {populate: true}, ASQH.CC(done)); }) - .then(function remove_old_tab(done){ - chrome.tabs.remove(tab0_id, ASQH.CC(done)); + .then(function remove_all_other_tabs(done, cwin){ + if(!cwin) { + done.fail("Could not query tabs"); + return; + } + + let gates = []; + + for(let tab of cwin.tabs) { + if(tab.url != desired_url) { + gates.push((done_gate)=>{ + log.info({'Pruning undesired tab': tab}); + chrome.tabs.remove(tab.id, ASQH.CC(done_gate)); + }); + } + } //foreach tab + +// if(tab0_id) { +// chrome.tabs.remove(tab0_id, ASQH.CC(done)); +// } else { +// done(); +// } + if(gates.length === 0) { + done(); + return; + } else { + let gate_seq = ASQ(); + gate_seq.all.apply(gate_seq, gates); + gate_seq.pipe(done); + } }) - .or(function(err){log_orig.error({'Load error':err, url});}) + .or(function(err){log_orig.error({'Load error':err, desired_url});}) ; // To start the sequence paused, use `let seq = ASQ().duplicate()` above // instead of just ASQ(). Then, to fire it off, `seq.unpause();`. + return seq; + } //openWindowForURL return Object.freeze(module); // all fields constant diff --git a/tabfern/src/view/item_details.js b/tabfern/src/view/item_details.js index bd09bfd1..76eb37c3 100755 --- a/tabfern/src/view/item_details.js +++ b/tabfern/src/view/item_details.js @@ -35,8 +35,10 @@ let module = {}; /// Map between open-tab IDs and node IDs. - /// Design decision: no fields named "parent" so I can distinguish - /// jstree node records from multidex values. + /// Design decisions: + /// - No fields named `parent` so I can distinguish jstree node + /// records from multidex values. + /// - All types of records have `raw_title` and `isOpen` fields. module.tabs = multidex( K.IT_TAB, //type [ //keys @@ -47,6 +49,7 @@ 'win_id', // from Chrome 'index', // in the current window 'tab', // the actual Tab record from Chrome + // TODO remove this --- tab_id should be enough 'being_opened', // true if we are manually opening the Chrome tab 'raw_url', // the tab's URL 'raw_title', // the tab's title. null => default. @@ -77,11 +80,13 @@ ], [ //other data 'win', // the actual Window record from chrome + // TODO? remove this --- win_id should be enough 'raw_title', // the window's title (e.g., "Window") 'isOpen', // whether the window is open or not 'keep', // whether the window should be saved or not //'raw_bullet', // User-provided text (brief). null => none // Not currently used. + 'prune_data', // {timer_id,cwin} of a setTimeout used for pruning ]); /// Find a node's value in the model, regardless of type. diff --git a/tabfern/src/view/item_tree.js b/tabfern/src/view/item_tree.js index d4a32eb9..d5da5421 100755 --- a/tabfern/src/view/item_tree.js +++ b/tabfern/src/view/item_tree.js @@ -194,6 +194,7 @@ jstreeTypes[K.IT_WIN] = { li_attr: { 'class': WIN_CLASS }, + //a_attr: { 'title': 'Unsaved window' }, //maybe? //icon: 'clear-icon', // We will overlay the actual icon in the CSS }; diff --git a/tabfern/src/view/main.js b/tabfern/src/view/main.js index 24dce99a..a07404a3 100755 --- a/tabfern/src/view/main.js +++ b/tabfern/src/view/main.js @@ -64,7 +64,7 @@ function initMain() W = window.frames[0]; // Thanks to https://stackoverflow.com/a/13913943/2877364 by // https://stackoverflow.com/users/1105384/shank - document.title = `TabFern (v${TABFERN_VERSION})`; + document.title = `${_T('wsShortName')} (v${TABFERN_VERSION})`; } //initMain ////////////////////////////////////////////////////////////////////////// diff --git a/tabfern/src/view/model.js b/tabfern/src/view/model.js index 09df4166..b512556d 100755 --- a/tabfern/src/view/model.js +++ b/tabfern/src/view/model.js @@ -63,6 +63,8 @@ /// Otherwise, all types will be checked. /// @return {Object} {val, node_id}. `val` is falsy if the /// given vorny was not found. + /// If #item_type was specified and the given item wasn't + /// of that type, also returns falsy. module.vn_by_vorny = function(val_or_nodey, item_type) { if(!val_or_nodey) return module.VN_NONE; @@ -92,8 +94,11 @@ return module.VN_NONE; } + if(item_type && (val.ty !== item_type)) { + return module.VN_NONE; + } return {val, node_id}; - } //vn_by_vorny + }; //vn_by_vorny() /// Determine whether a model has given subtype(s). /// @param vorny {mixed} The item @@ -110,62 +115,71 @@ if(!T.treeobj.has_multitype(node_id, ty)) return false; } return true; - } //has_subtype + }; //has_subtype() // }}}1 // Data-access routines //////////////////////////////////////////// {{{1 - /// Find a node's value in the model, regardless of type. - /// @param node_ref {mixed} If a string, the node id; otherwise, anything - /// that can be passed to jstree.get_node. + /// Find a node's value in the model, regardless of type (DEPRECATED). + /// TODO replace calls to this (currently only in tree.js) with calls + /// to module.vn_by_vorny(). + /// @param vorny {mixed} A vorny for the node /// @return ret {object} the value, or ===false if the node wasn't found. - module.get_node_val = function(node_ref) + module.get_node_val = function(vorny) { - // Get the node ID - let node_id; - - if(typeof node_ref === 'string') { - node_id = node_ref; - } else { - let node = T.treeobj.get_node(node_ref); - if(node === false) return false; - node_id = node.id; - } - - return D.val_by_node_id(node_id); + let {val} = module.vn_by_vorny(vorny); + return val || false; }; //get_node_val() - /// Get the textual version of raw_title for a window's value - module.get_win_raw_text = function(val) + /// Get the textual version of raw_title for an item. + /// @param vorny {mixed} the item + /// @return {string} + module.get_raw_text = function(vorny) { - if(val.raw_title !== null) { + let {val} = module.vn_by_vorny(vorny); + if(!val) return '** UNKNOWN **'; + // TODO throw? + + if(val.raw_title !== null) { // window or tab return val.raw_title; - } else if(val.keep) { - return 'Saved tabs'; - } else { - return 'Unsaved'; + } else if(val.keep === K.WIN_KEEP) { // default title for saved window + return _T('labelSavedTabs'); + } else if(val.ty === K.IT_WIN) { // def. title for ephem. win. + return _T('labelUnsaved'); + } else { // e.g., tabs with no raw_title. + // TODO see if this makes sense. + return "** no title **"; } - }; //get_win_raw_text() + }; //get_raw_text() - /// Mark window item #val as unsaved (forget #val). - /// @param val {Object} the item + /// Mark window item #vorny as unsaved (forget #vorny). + /// @param vorny {mixed} the item /// @param adjust_title {Boolean=true} Add unsaved markers if truthy /// @return {Boolean} true on success; false on error - module.mark_win_as_unsaved = function(val, adjust_title=true) { - if(!val || val.ty !== K.IT_WIN || !val.node_id) return false; + module.mark_win_as_unsaved = function(vorny, adjust_title=true) { + let {val, node_id} = module.vn_by_vorny(vorny, K.IT_WIN); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; val.keep = K.WIN_NOKEEP; - T.treeobj.del_multitype(val.node_id, K.NST_SAVED); + T.treeobj.del_multitype(node, K.NST_SAVED); if(adjust_title && (val.raw_title !== null)) { - val.raw_title = module.remove_unsaved_markers(val.raw_title) + - ' (Unsaved)'; + if(val.raw_title === _T('labelSavedTabs')) { + val.raw_title = _T('labelUnsaved'); + } else { + val.raw_title = + module.remove_unsaved_markers(val.raw_title) + + ` (${_T('labelUnsaved')})`; + } } - // If raw_title is null, get_win_raw_text() will return 'Unsaved', + // If raw_title is null, get_raw_text() will return _T('Unsaved'), // so we don't need to manually assign text here. module.refresh_label(val.node_id); module.refresh_icon(val); + module.refresh_tooltip(val); + return true; }; //mark_as_unsaved() @@ -177,29 +191,43 @@ module.remove_unsaved_markers = function(str) { if(!str) return str; str = str.toString(); - let re = /(\s+\(Unsaved\)){1,}\s*$/i; + let re = new RegExp( + `((${_T('labelUnsaved')})|` + // Just the "Unsaved" text + `(\\s+\\(${_T('labelUnsaved')}\\)){1,})\\s*$`, // Postfix "(Unsaved)" + 'u'); // u=> unicode + // Not using 'i' anymore per + // https://mathiasbynens.be/notes/es6-unicode-regex#recommendations + let matches = str.match(re); if(matches && matches.index > 0) { - return str.slice(0,matches.index); + return str.slice(0, matches.index); } else { return str; } - }; + }; //remove_unsaved_markers() /// Get the HTML for the node's label. The output can be passed /// directly to jstree.rename_node(). - /// @param val The multidex value for the item of interest + /// @param vorny {mixed} The item of interest, which + /// can be a window or a tab. /// @return A string - module.get_html_label = function(val) { + module.get_html_label = function(vorny) { + let {val} = module.vn_by_vorny(vorny); + if(!val) return false; + let retval = ''; if(val.isPinned) { // TODO make this optional? + // Note: for windows, isPinned is nonexistent, thus falsy. retval += '📌 '; // PUSHPIN } - let raw_text = module.get_win_raw_text(val); + let raw_text = module.get_raw_text(val); // raw_title, or default + + // Add the bullet, for tabs only. For windows, raw_bullet is + // always falsy. if(val.raw_bullet && typeof val.raw_bullet === 'string') { - // The first condition checks for null/undefined/&c., and also for - // empty strings. + // The first condition checks for null/undefined/&c., and also + // for empty strings. retval += '<span class="' + K.BULLET_CLASS + '">'; retval += Esc.escape(val.raw_bullet); @@ -211,26 +239,53 @@ } retval += '</span>'; - } + } //endif there's a raw_bullet retval += Esc.escape(raw_text); return retval; - }; + }; //get_html_label() // }}}1 // Item manipulation /////////////////////////////////////////////// {{{1 - /// Update the tree-node text for an item. + /// Update the tooltip for an item. + /// @param vorny {mixed} the item + /// @return {Boolean} truthy on success; falsy on failure. + module.refresh_tooltip = function(vorny) { + let {val, node_id} = module.vn_by_vorny(vorny); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; + + let strs = []; + if(getBoolSetting(CFG_TITLE_IN_TOOLTIP)) { + let raw_text = module.get_raw_text(val); // raw_title, or default + strs.push(raw_text); + } + if(getBoolSetting(CFG_URL_IN_TOOLTIP) && (val.ty === K.IT_TAB) ) { + strs.push(val.raw_url); + } + + let tooltip = strs.join('\n'); // '' if no tooltips + + if(tooltip !== node.li_attr.title) { + node.li_attr.title = tooltip; + T.treeobj.redraw_node(node); + } + + return true; + }; //refresh_tooltip() + + /// Update the tree-node text for an item from its details record. /// @param node_id {string} the node's ID (which doubles as the item's id) /// @return truthy on success, falsy on failure. - module.refresh_label = function(node_id) { - if(!node_id) return false; - let val = D.val_by_node_id(node_id); - if(!val) return false; - let retval = T.treeobj.rename_node(node_id, module.get_html_label(val)); + module.refresh_label = function(vorny) { + let {val, node_id} = module.vn_by_vorny(vorny); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return; + let retval = T.treeobj.rename_node(node, module.get_html_label(val)); return retval; - }; + }; //refresh_label() /// Update the icon of #vorny /// @param vorny {Mixed} The item @@ -238,7 +293,7 @@ module.refresh_icon = function(vorny) { let {val, node_id} = module.vn_by_vorny(vorny); let node = T.treeobj.get_node(node_id); - if(!val || !node_id || !node) return false; + if(!val || !node) return false; let icon; @@ -273,28 +328,39 @@ // generic page icon so we don't keep hitting the favIconUrl. return true; - } //refresh_icon + }; //refresh_icon() /// Mark the window identified by #win_node_id as to be kept. - /// @param win_node_id {string} The window node ID + /// @param win_vorny {mixed} The window node /// @param cleanup_title {optional boolean, default true} /// If true, remove unsaved markers from the raw_title. /// @return {Boolean} true on success; false on error - module.remember = function(win_node_id, cleanup_title = true) { - if(!win_node_id) return false; - let val = D.windows.by_node_id(win_node_id); - if(!val) return false; + module.remember = function(win_vorny, cleanup_title = true) { + let {val, node_id} = module.vn_by_vorny(win_vorny, K.IT_WIN); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; val.keep = K.WIN_KEEP; - T.treeobj.add_multitype(win_node_id, K.NST_SAVED); + T.treeobj.add_multitype(node, K.NST_SAVED); if(cleanup_title) { - val.raw_title = module.remove_unsaved_markers( - module.get_win_raw_text(val)); + let new_title = + module.remove_unsaved_markers(module.get_raw_text(val)); + if( new_title === _T('labelSavedTabs') || + new_title === _T('labelUnsaved') + ) { + // If it's the default text, don't treat it as a user entry. + // It will be labelUnsaved if the user right-clicked on a + // fresh unsaved window and selected Remember. + val.raw_title = null; + } else { + val.raw_title = new_title; + } } - module.refresh_label(win_node_id); + module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; }; //remember() @@ -321,7 +387,7 @@ blake.update(databuf); } return blake.hexDigest(); - } //orderedHashOfStrings + }; //orderedHashOfStrings() /// Update the given node's ordered_url_hash to reflect its current children. /// @return {Boolean} True if the ordered_url_hash was set or was @@ -396,7 +462,8 @@ raw_title: null, raw_bullet: null, isOpen: false, - keep: undefined + keep: undefined, + prune_data: undefined }); if(!val) { @@ -406,9 +473,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return {val, node_id}; - } //vnRezWin + }; //vnRezWin() /// Add a model node/item for a tab, with the given parent. /// Does not process Chrome widgets. Instead, assumes the tab is @@ -451,9 +519,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return {val, node_id}; - } //vnRezTab + }; //vnRezTab() // }}}1 // Updating model items //////////////////////////////////////////// {{{1 @@ -473,7 +542,7 @@ // TODO report failure to add a type? } return true; - } //add_subtype + }; //add_subtype() /// Remove a subtype (K.NST_*) from an item. /// @param vorny {mixed} The item @@ -490,7 +559,31 @@ // TODO report failure to remove a type? } return true; - } //add_subtype + }; //del_subtype() + + // TODO? implement this? +// /// Modify #vorny with all the changes from #deltas in one go. +// module.change = function(vorny, deltas) { +// let {val, node_id} = module.vn_by_vorny(vorny); +// let node = T.treeobj.get_node(node_id); +// if(!deltas || (typeof deltas !== 'object') || !val || !node) return; +// +// /// A list of functions of this module to be called after +// /// the values are changed. +// let updates = []; +// +// // Update the internal data +// for(let k in deltas) { +// let v = deltas[k]; +// //TODO update val +// //TODO push the appropriate update +// } +// +// // Commit the updates to the visible tree +// for(let fn of updates) { +// module[fn](vorny); +// } +// }; //change() // }}}1 // Attaching Chrome widgets to model items ///////////////////////// {{{1 @@ -530,9 +623,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; - } //markWinAsOpen + }; //markWinAsOpen() /// Attach a Chrome tab to an existing tab item. /// Updates the item, but does not touch the Chrome tab. @@ -549,7 +643,7 @@ if(!val || !node_id) return false; if(val.isOpen || val.tab) { - log.info({'Refusing to re-mark already-open tab as open':val}); + log.info({'Refusing to re-mark already-open tab as open at ctab':ctab,val}); return false; } @@ -573,13 +667,14 @@ module.refresh_label(node_id); module.refresh_icon(val); // since favicon may have changed + module.refresh_tooltip(val); // Design decision: tree items for open windows always start expanded. // No one has requested any other behaviour, as of the time of writing. T.treeobj.open_node(node.parent); return true; - } //markTabAsOpen + }; //markTabAsOpen() // }}}1 // Removing Chrome widgets from model items //////////////////////// {{{1 @@ -611,9 +706,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; - } //markWinAsClosed + }; //markWinAsClosed() /// Remove the connection between #tab_vorny and its Chrome tab. /// Use this when the Chrome tab has been closed. @@ -649,9 +745,11 @@ module.refresh_label(node_id); // TODO is this necessary? // Don't change icon - keep favicon + // Don't change tooltip - closing a tab doesn't affect the + // raw_title or raw_url. return true; - } //markTabAsClosed + }; //markTabAsClosed() // }}}1 // Removing model items //////////////////////////////////////////// {{{1 @@ -674,7 +772,7 @@ T.treeobj.delete_node(node_id); return true; - } //eraseTab + }; //eraseTab() /// Delete a window from the tree and the details. This will also /// erase any remaining child nodes of #win_vorny from the @@ -704,7 +802,7 @@ T.treeobj.delete_node(node_id); return true; - } //eraseWin + }; //eraseWin() // }}}1 diff --git a/tabfern/src/view/tree.css b/tabfern/src/view/tree.css index f852e9f4..f32d3ccb 100755 --- a/tabfern/src/view/tree.css +++ b/tabfern/src/view/tree.css @@ -53,6 +53,11 @@ color: #f33; } +#init-progress { + display: none; /* initially */ + color: #f33; +} + li.ephemeral-recovered, .tfs-recovered { background: darkslategray !important; } diff --git a/tabfern/src/view/tree.html b/tabfern/src/view/tree.html index 7ab36337..2961c124 100644 --- a/tabfern/src/view/tree.html +++ b/tabfern/src/view/tree.html @@ -22,6 +22,7 @@ <!-- this one should always be the last stylesheet listed --> <!-- Scripts, except for those loaded by require.js ~~~~~~~~~~~~~~~~~~~ --> + <script src="../common/validation.js" type="text/javascript"></script> <!-- not requirejs --> <script src="../common/common.js" type="text/javascript"></script> <!-- not requirejs --> <script src="/conf/require-config.js"></script> <!-- load before require.js --> @@ -34,9 +35,12 @@ <div id="tabfern-container"> + <!-- Error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <div id="init-incomplete">Initialization not complete (or not yet complete). Changes will not be saved. - </div><!-- hidden if init does complete. --> + </div> + <div id="init-progress"></div> + <!-- the above two divs are removed if init does complete. --> <!-- Content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <div id="maintree"></div><!-- the window tree --> @@ -46,7 +50,8 @@ <!-- Modal dialog ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- rmodal sets display:none on .modal automatically, so we don't - have to here. --> + have to here. Note that the text below will be replaced from + the localized messages.json. --> <div id="confirm-dialog" class="modal"> <div class="modal-dialog"> <form class="form-horizontal" method="get"> @@ -58,7 +63,7 @@ <input type="checkbox" name="cbNotAgain" id="cbNotAgain" class="form-control col-0p5" /> - <label for="cbNotAgain" + <label id="labelNotAgain" for="cbNotAgain" class="control-label col-xs-4 col-5"> Don't ask again (<abbr title="You can change this in Settings | Behaviour">note</abbr>) @@ -66,16 +71,17 @@ <!-- note: don't use <button type=submit>, which reloads the page. --> <div class="col-3"> </div> - <button data-which="yes" + <button id="confirm-dialog-yes" data-which="yes" class="btn btn-primary col-1" type="button" accesskey="y"> <span class="accel">Y</span>es </button> - <button data-which="no" class="btn col-1" + <button id="confirm-dialog-no" data-which="no" + class="btn col-1" type="button" accesskey="n"> <span class="accel">N</span>o </button> - <button data-which="cancel" + <button id="confirm-dialog-cancel" data-which="cancel" class="btn btn-default col-1" type="button" accesskey="c"> <span class="accel">C</span>ancel diff --git a/tabfern/src/view/tree.js b/tabfern/src/view/tree.js index 9c0d564b..21659db7 100755 --- a/tabfern/src/view/tree.js +++ b/tabfern/src/view/tree.js @@ -4,7 +4,8 @@ // TODO break more of this into separate modules -console.log('Loading TabFern ' + TABFERN_VERSION); +console.log(`============================================================= +Loading TabFern ${TABFERN_VERSION}`); ////////////////////////////////////////////////////////////////////////// // Modules // {{{1 @@ -60,7 +61,7 @@ let Module_dependencies = [ // Modules of TabFern itself 'view/const', 'view/item_details', 'view/sorts', 'view/item_tree', - 'view/model', 'common/validation' + 'view/model', ]; /// Make short names in Modules for some modules. shortname => longname @@ -80,8 +81,13 @@ var my_winid; ///< window ID of this popup window var currently_focused_winid = null; /// HACK to avoid creating extra tree items. +/// TODO? change this to an object with win node ID's as keys. Use one of the +/// tabs to carry the node ID for confirmation. var window_is_being_restored = false; +/// HACK to not prune windows we create! +var do_not_prune_right_now = false; + /// The size of the last-closed window, to be used as the /// size of newly-opened windows (whence the name). /// Should always have a valid value. @@ -100,6 +106,11 @@ var ShowWhatIsNew = false; /// Array of URLs of the last-deleted window var lastDeletedWindow; +/// Node ID of the last-closed saved window --- merging is prohibited with +/// this node. It's the last-closed saved and not the last-closed overall +/// because nodes for unsaved windows disappear with their windows. +var lastSavedClosedWindow_node_id = undefined; + /// Did initialization complete successfully? var did_init_complete = false; @@ -120,6 +131,41 @@ var Bypasser; ////////////////////////////////////////////////////////////////////////// }}}1 // Module setup, and utilities // {{{1 +// Initialization support +let init_step_num = 0; +const LAST_INIT_STEP = 13; + +/// Update the initialization status counter +let next_init_step = function(where) { + ++init_step_num; + + let msg = + `${init_step_num}/${LAST_INIT_STEP}${where ? (': ' + where): ''}`; + log.info({Initialization:msg}) + + let elem = $(K.INIT_PROGRESS_SEL); + if(elem.css('display') !== 'block') return; + + elem.text(msg); + + elem.hide().show(1); + // Force redraw - thanks to + // https://stackoverflow.com/users/122543/juank and + // https://stackoverflow.com/users/402517/l-poellabauer + // at https://stackoverflow.com/a/8840703/2877364 . + // I used (1) instead of (0) since I saw it not work once with (0). + +}; //next_init_step + +/// Tell me if I forgot to update LAST_INIT_STEP +let check_init_step_count = function() { + if(init_step_num !== LAST_INIT_STEP) { + let msg = `Step count was ${init_step_num} - update LAST_INIT_STEP`; + log.warn(msg); + window.alert(msg); + } +}; //check_init_step_count + /// Init those of our globals that don't require any data to be loaded. /// Call after Modules has been populated. function local_init() @@ -244,6 +290,95 @@ function unflagAllWindows() { ); }; +////////////////////////////////////////////////////////////////////////// }}}1 +// Chrome-related utilities // {{{1 + +/// Check the tabs in the window to make sure they match what we expect, +/// and prune any we don't expect. +/// This is because sometimes Chrome opens extra tabs but doesn't tell the +/// chrome.windows.create callback about them. +/// See https://bugs.chromium.org/p/chromium/issues/detail?id=762951 . +/// See also commit 70638829e5c35a8c86b556dc62ddb3920e4a5e92, +/// at tabfern/src/view/tree.js:997. +/// +/// @param cwin The Chrome window +/// @param expected_tab_count {Number} How many tabs to keep, or 0 to leave +/// the window empty except for a chrome://newtab. +function pruneWindow(cwin, expected_tab_count) +{ + if(!cwin || +expected_tab_count < 0 || do_not_prune_right_now) return; + let count = +expected_tab_count || 1; // 0 => one tab + + ASQH.NowCC( (cc)=>{ chrome.windows.get(cwin.id, {populate: true}, cc); } ) + + .or((err)=>{ + L.log.warn({'Could not get tabs in window': cwin, err}); + }) + + .then( (done, inner_cwin)=>{ + + if(inner_cwin.tabs.length === 0) { + //chrome.tabs.create( {windowId: inner_cwin.id, index: 0} ); + done.fail({"I can't check for pruning in zero-tab window":inner_cwin}); + return; + } + + let win_val = D.windows.by_win_id(inner_cwin.id); + + // Make sure the first tab is a new tab, if desired. + // However, if this is a saved window, don't mess with tab 0. + if( expected_tab_count === 0 && + !(win_val && win_val.keep === K.WIN_KEEP) + ) { + // Note: this is problematic if the last window to be open was a + // saved window, since this races against the merge check in + // tabOnCreated. That is the reason for pruneWindowSetTimer(). + let tabid = inner_cwin.tabs[0].id; + + let seq = ASQ(); // for error reporting + // Don't change chrome: URLs, since those might be things + // like History (Ctl+H). + if(!inner_cwin.tabs[0].url.match(/^chrome:/i)) { + log.info(`Setting tab ${tabid} to chrome:newtab`); + chrome.tabs.update(tabid, {url: 'chrome://newtab'}, + ASQH.CCgo(seq)); + } + // Currently we don't have anything else to do, so there's no + // seq.then() calls. However, using seq still causes asynquence + // to report any errors in the chrome.tabs.update() call. + + } //endif the caller wanted tab 0 === chrome://newtab + + if(inner_cwin.tabs.length <= count) { + done(); // No need to prune --- we're done + return; + } + + L.log.warn('Win ' + inner_cwin.id + ': expected ' + + count + ' tabs; got ' + + inner_cwin.tabs.length + ' tabs --- pruning.'); + + let to_prune=[]; + + // Assume that the unexpected tabs are all at the right-hand + // end, since that's the only behaviour I've observed. + + for(let tab_idx = count; + tab_idx < inner_cwin.tabs.length; + ++tab_idx) { + to_prune.push(inner_cwin.tabs[tab_idx].id); + } //foreach extra tab + + L.log.warn('Pruning ' + to_prune); + chrome.tabs.remove(to_prune, ASQH.CC(done)); + }) + + .or((err)=>{ + L.log.warn({'Could not prune tabs in window': cwin, err}); + }) + ; +} //pruneWindow + ////////////////////////////////////////////////////////////////////////// }}}1 // UI Controls // {{{1 @@ -262,7 +397,25 @@ function showConfirmationModalDialog(message_html) { let cleanup_cbk = cleanup.errfcb(); // pause the sequence // Modified from the rmodal sample at https://rmodal.js.org/ - $('#confirm-dialog-question').html(message_html); + let jqdlg = $('#confirm-dialog'); + jqdlg.find('#confirm-dialog-question').html(message_html); + + // i18n + const KEY_YES = _T('dlgYesAccelerator'); + const KEY_NO = _T('dlgNoAccelerator'); + const KEY_CANCEL = _T('dlgCancelAccelerator'); + + jqdlg.find('#confirm-dialog-yes') + .attr('accesskey', KEY_YES) + .html(_T('dlgYesHTML')); + jqdlg.find('#confirm-dialog-no') + .attr('accesskey', KEY_NO) + .html(_T('dlgNoHTML')); + jqdlg.find('#confirm-dialog-cancel') + .attr('accesskey', KEY_CANCEL) + .html(_T('dlgCancelHTML')); + jqdlg.find('#labelNotAgain') + .html(_T('dlgDoNotAskAgainHTML')); dlg = new (Modules['rmodal'])( document.getElementById('confirm-dialog'), @@ -307,11 +460,11 @@ function showConfirmationModalDialog(message_html) { ev.key.toLowerCase(): null; // Send the events on the next cycle. Not sure if this is required. - if(key === 'y') { + if(key === KEY_YES) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='yes']").click();}); - } else if(key === 'n') { + } else if(key === KEY_NO) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='no']").click();}); - } else if(key === 'c') { + } else if(key === KEY_CANCEL) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='cancel']").click();}); } else { dlg.keydown(ev); @@ -424,14 +577,14 @@ function saveTree(save_ephemeral_windows = true, cbk = undefined) chrome.storage.local.set(to_save, function() { if(!isLastError()) { + log.debug('Saved tree'); if(typeof cbk === 'function') { cbk(null, to_save[K.STORAGE_KEY]); } return; // Saved OK } //else there was an error - let msg = "TabFern: couldn't save: " + - chrome.runtime.lastError.toString(); + let msg = _T('errCouldNotSave', chrome.runtime.lastError.toString()); log.error(msg); window.alert(msg); // The user needs to know if(typeof cbk === 'function') cbk(new Error(msg)); @@ -461,14 +614,14 @@ function actionRenameWindow(node_id, node, unused_action_id, unused_action_el) if(!win_val) return; // TODO replace window.prompt with an in-DOM GUI. - let win_name = window.prompt('New window name?', - M.remove_unsaved_markers(M.get_win_raw_text(win_val))); + let win_name = window.prompt(_T('dlgpNewWindowName'), + M.remove_unsaved_markers(M.get_raw_text(win_val))); if(win_name === null) return; // user cancelled // A bit of a hack --- if the user hits OK on the default text for a // no-name window, change it to "Saved tabs." TODO find a better way. - if(win_name === 'Unsaved') { - win_val.raw_title = 'Saved tabs'; + if(win_name === _T('labelUnsaved') || win_name === '') { + win_val.raw_title = _T('labelSavedTabs'); } else { win_val.raw_title = win_name; } @@ -540,7 +693,7 @@ function actionCloseWindowButDoNotSave(node_id, node, unused_action_id, unused_a // the window is already gone, so the remove() throws. // See https://stackoverflow.com/a/45871870/2877364 by cxw - // TODO RESUME HERE --- don't actually proceed until + // TODO don't actually proceed until // the window is gone. Test case: window.beforeonunload=()=>true; } @@ -614,7 +767,7 @@ function actionDeleteWindow(node_id, node, unused_action_id, unused_action_el, // Remove the window's node and value let scrollOffsets = [window.scrollX, window.scrollY]; T.treeobj.delete_node(node_id); //also deletes child nodes - // TODO RESUME HERE --- don't actually delete the node until + // TODO don't actually delete the node until // the window is gone. Test case: window.beforeonunload=()=>true; window.scrollTo(...scrollOffsets); @@ -637,19 +790,24 @@ function actionDeleteWindow(node_id, node, unused_action_id, unused_action_el, } //doDeletion() // Prompt for confirmation, if necessary - if( is_internal || + let no_confirmation = !!is_internal; + + no_confirmation = no_confirmation || (win_val.keep === K.WIN_KEEP && - !getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)) || + !getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)); + + no_confirmation = no_confirmation || (win_val.keep === K.WIN_NOKEEP && !getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED)) - ) { // No confirmation required - just do it + + if(no_confirmation) { // No confirmation required - just do it doDeletion(); } else { // Confirmation required - showConfirmationModalDialog( - 'Delete window "' + M.get_win_raw_text(win_val) + '"?' - ) + (showConfirmationModalDialog( + _T('dlgpDeleteWindow', M.get_raw_text(win_val)) + )) // Processing after the dialog closes .val((result)=>{ @@ -704,22 +862,29 @@ function actionEditTabBullet(node_id, node, unused_action_id, unused_action_el) if(!val || val.ty !== K.IT_TAB) return; // TODO replace window.prompt with an in-DOM GUI. - let question = `Note for tab "${val.raw_title}"?`; - //DEBUG - //new_bullet='42'; + let question = _T('dlgpTabNote', val.raw_title); let new_bullet = window.prompt(question, val.raw_bullet || ''); if(new_bullet === null) return; // user cancelled val.raw_bullet = new_bullet; M.refresh_label(node_id); + // TODO if window is currently ephemeral, only remember if + // new_bullet is nonempty. M.remember(node.parent); - // assume that a user who bothered to add a note + // Assume that a user who bothered to add a note // wants to keep the window the note is in. saveTree(); } //actionEditTabBullet +///// Close the tab and save - NOT YET IMPLEMENTED +//function actionCloseTabAndSave(node_id, node, unused_action_id, unused_action_el) +//{ +// // TODO actionCloseTabButDoNotSave(node_id, node, unused_action_id, unused_action_el); +// saveTree(); +//} //actionCloseTabAndSave + /// Delete a tab's entry in the tree. /// @param node_id {string} the ID of the node to delete /// @param node the node to delete @@ -759,22 +924,38 @@ function actionDeleteTab(node_id, node, unused_action_id, unused_action_el, } //doDeletion() // Prompt for confirmation, if necessary + let prompt_name = 'dlgpDeleteTab'; // in messages.json let is_keep = (parent_val.keep === K.WIN_KEEP); let is_nokeep = (parent_val.keep === K.WIN_NOKEEP); + + // General rule... let need_confirmation = ( (is_keep && getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED_TABS)) || (is_nokeep && getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED_TABS)) ); - if( !need_confirmation || - (/^((chrome:\/\/newtab\/?)|(about:blank))$/i.test(tab_val.raw_url)) - ) { + // ... but we don't usually need confirmation for empty tabs... + if(/^((chrome:\/\/newtab\/?)|(about:blank))$/i.test(tab_val.raw_url)) { + need_confirmation = false; + } + + // ... except when such a tab (or any tab being deleted) is the last + // tab in its window. In that case, check the window settings as well. + if(parent_node.children.length === 1) { + if( (is_keep && getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)) || + (is_nokeep && getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED)) ) { + need_confirmation = true; + prompt_name = 'dlgpDeleteTabAndWindow'; + } + } + + if(!need_confirmation) { // No confirmation required - just do it doDeletion(); } else { // Confirmation required - showConfirmationModalDialog( - 'Delete tab "' + M.get_html_label(tab_val) + '"?' + (showConfirmationModalDialog( + _T(prompt_name, M.get_html_label(tab_val))) ) // Processing after the dialog closes @@ -863,15 +1044,27 @@ function addTabNodeActions(tab_node_id) // "Actually" event check in treeOnSelect. //html: `<img src="/assets/icons/pencil.png" class=${K.ACTION_BUTTON_WIN_CLASS} />`, grouped: true, + title: _T('ttEditTab'), callback: actionEditTabBullet, dataset: { action: 'editBullet' } }); +// T.treeobj.add_action(tab_node_id, { +// id: 'closeTab', +// class: 'fff-picture-delete ' + K.ACTION_BUTTON_WIN_CLASS, +// text: '\xa0', +// grouped: true, +// title: "Close and save", +// callback: actionCloseTabAndSave, +// dataset: { action: 'closeTab' } +// }); + T.treeobj.add_action(tab_node_id, { id: 'deleteTab', class: 'fff-cross ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttDeleteTab'), callback: actionDeleteTab, dataset: { action: 'deleteTab' } }); @@ -884,8 +1077,13 @@ function addTabNodeActions(tab_node_id) function createNodeForTab(ctab, parent_node_id) { let {node_id, val} = M.vnRezTab(parent_node_id); - if(!node_id) return false; - M.markTabAsOpen(val, ctab); + if(!node_id) { + log.debug({"<M> Could not create record for ctab":ctab,parent_node_id}); + return false; + } + if(!M.markTabAsOpen(val, ctab)) { + log.debug({"<M> Could not mark tab as open":ctab,val}); + } addTabNodeActions(node_id); @@ -899,7 +1097,10 @@ function createNodeForTab(ctab, parent_node_id) function createNodeForClosedTabV1(tab_data_v1, parent_node_id) { let {node_id, val} = M.vnRezTab(parent_node_id); - if(!node_id) return false; + if(!node_id) { + log.debug({"<M> Could not create record for closed tab":tab_data_v1,parent_node_id}); + return false; + } // Copy properties into the details copyTruthyProperties(val, tab_data_v1, @@ -909,6 +1110,7 @@ function createNodeForClosedTabV1(tab_data_v1, parent_node_id) M.refresh_label(node_id); M.refresh_icon(val); + M.refresh_tooltip(val); if(tab_data_v1.bordered) M.add_subtype(val, K.NST_TOP_BORDER); @@ -932,6 +1134,7 @@ function addWindowNodeActions(win_node_id) class: 'fff-pencil ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttEditWin'), callback: actionRenameWindow, dataset: { action: 'renameWindow' } }); @@ -941,6 +1144,7 @@ function addWindowNodeActions(win_node_id) class: 'fff-picture-delete ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttCloseWin'), callback: actionCloseWindowAndSave, dataset: { action: 'closeWindow' } }); @@ -950,15 +1154,54 @@ function addWindowNodeActions(win_node_id) class: 'fff-cross ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttDeleteWin'), callback: actionDeleteWindow, dataset: { action: 'deleteWindow' } }); } //addWindowNodeActions +/// Set a timer to prune the window later. +/// @param win_val The value for the window +/// @param cwin (Optional) The Chrome window. If not provided, the +/// one stashed in win_val (if any) will be used. +let pruneWindowSetTimer = function(win_val, cwin) { + log.debug({"Bump of prune timer requested for":win_val, cwin}); + if(!win_val.prune_data) { + win_val.prune_data = {timer_id: undefined, cwin: cwin || undefined}; + } + + if(win_val.prune_data.timer_id) { + window.clearTimeout(win_val.prune_data.timer_id); + win_val.prune_data.timer_id = undefined; + } + + if(do_not_prune_right_now) return; + + log.debug({" Bumping prune timer for":win_val, cwin}); + win_val.prune_data.timer_id = window.setTimeout( + ()=>{ + if(!win_val.prune_data.cwin) return; + if(!did_init_complete) { + log.info({'Still initializing, so skipping prune-to-0 check of': + win_val.prune_data.cwin}); + return; + } + + if(do_not_prune_right_now) return; + log.info({'Checking for pruning-to-0 of':win_val.prune_data.cwin}); + pruneWindow(win_val.prune_data.cwin, 0); + // 0 => only leave a chrome://newtab there + win_val.prune_data = undefined; + // Once pruning is done, don't leave stale data around. + }, + K.WIN_PRUNE_TIMER_MS // UGLY HACK - give the merge check time to run + ); +} //pruneWindowSetTimer + /// Create a tree node for open Chrome window #cwin. /// @returns the tree-node ID, or falsy on error. -function createNodeForWindow(cwin, keep) +function createNodeForWindow(cwin, keep, no_prune) { if(!cwin || !cwin.id) return; @@ -967,10 +1210,13 @@ function createNodeForWindow(cwin, keep) let is_first = (!!cwin && getBoolSetting(CFG_NEW_WINS_AT_TOP)); let {node_id, val} = M.vnRezWin(is_first); - if(!node_id) return false; //sanity check + if(!node_id) { //sanity check + log.debug({"<M> Could not create tree node for open cwin":cwin,keep,no_prune}); + return false; + } M.markWinAsOpen(val, cwin); - if(keep) { + if(keep === K.WIN_KEEP) { M.remember(node_id, false); } else { M.mark_win_as_unsaved(val, false); @@ -985,6 +1231,13 @@ function createNodeForWindow(cwin, keep) } } + // Remove extra tabs if the user wants + if(!no_prune && !do_not_prune_right_now + && getBoolSetting(CFG_PRUNE_NEW_WINDOWS) + ) { + pruneWindowSetTimer(val, cwin); + } + return node_id; } //createNodeForWindow @@ -1001,22 +1254,30 @@ function createNodeForClosedWindowV1(win_data_v1) // Make a node for a closed window. The node is marked KEEP. // TODO don't mark it keep if it's ephemeral and still open. let {node_id, val} = M.vnRezWin(); - if(!node_id) return false; - M.remember(node_id, false); // Closed windows are KEEP by design + if(!node_id) { + log.debug({"<M> Could not create node for closed window":win_data_v1}); + return false; + } + + if(!M.remember(node_id, false)) { // Closed windows are KEEP by design + log.debug({"<M> Could not mark closed window as KEEP":win_data_v1, node_id, val}); + } // TODO restore ordered_url_hash // Mark recovered windows if(is_ephemeral) { - M.add_subtype(node_id, K.NST_RECOVERED); + if(!M.add_subtype(node_id, K.NST_RECOVERED)) { + log.debug({"<M> Could not add subtype RECOVERED":node_id, val, win_data_v1}); + } } // Update the item details let new_title; if( is_ephemeral && (typeof win_data_v1.raw_title !== 'string') ) { - new_title = 'Recovered tabs'; + new_title = _T('labelRecoveredTabs'); } else if(is_ephemeral) { // and raw_title is a string - new_title = String(win_data_v1.raw_title) + ' (Recovered)'; + new_title = String(win_data_v1.raw_title) + _T('labelRecoveredTabsPostfix'); } else { // not ephemeral let n = win_data_v1.raw_title; new_title = (typeof n === 'string') ? n : null; @@ -1026,6 +1287,7 @@ function createNodeForClosedWindowV1(win_data_v1) M.refresh_label(node_id); M.refresh_icon(val); + M.refresh_tooltip(val); addWindowNodeActions(node_id); @@ -1046,13 +1308,19 @@ function createNodeForClosedWindowV1(win_data_v1) /// Update #existing_win to connect to #cwin. Also hooks up all the /// ctabs. +/// Requires cwin.tabs.length === existing_win.node.children.length. +/// /// @param cwin {Chrome Window} The open window, populated with tabs. /// @param existing_win {object} An object with {val, node}, e.g., from /// winAlreadyExistsInTree(). -/// @param during_init {Boolean=false} If truthy, failures correspond to init failure. -/// \pre cwin.tabs.length === existing_win.node.children.length. -function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=false) +/// @param options {object={}} Options. Presently: +/// - during_init {Boolean=false} If truthy, failures correspond to init failure. +/// - +/// +/// @return truthy on success; falsy on failure +function connectChromeWindowToTreeWindowItem(cwin, existing_win, options = {}) { + let during_init = !!options.during_init; // Attach the open window to the saved window log.info({[`Attaching window ${cwin.id} to existing window in the tree`]: existing_win}); @@ -1078,10 +1346,11 @@ function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=fal }); if(during_init) was_loading_error = true; - return; // TODO handle this better + return false; // TODO handle this better } // If we reach here, cwin.tabs.length === existing_win.node.children.length. + for(let idx=0; idx < cwin.tabs.length; ++idx) { let tab_node_id = existing_win.node.children[idx]; let tab_val = D.tabs.by_node_id(tab_node_id); @@ -1090,15 +1359,29 @@ function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=fal let ctab = cwin.tabs[idx]; // Do we need these? - ctab.url = ctab.url || 'about:blank'; - ctab.title = ctab.title || '## Unknown title ##'; + ctab.url = ctab.url || tab_val.raw_url || 'about:blank'; + ctab.title = ctab.title || tab_val.raw_title || _T('labelUnknownTitle'); + let pinned = tab_val.isPinned; M.markTabAsOpen(tab_node_id, ctab); + + // Apply changes from the tab_val to the ctab if requested. + // New tabs start out unpinned, so we may need to repin. + if(options.repin && pinned) { + chrome.tabs.update(ctab.id, {pinned: true}, ignore_chrome_error); + } } //foreach tab // Note: We do not need to update existing_win.val.ordered_url_hash. // Since we got here, we know that it was a match. -} //attachChromeWindowToSavedWindowItem() + + T.treeobj.redraw_node(existing_win.node); + + // Prune any extra tabs Chrome may have created due to bugs. + pruneWindow(cwin, existing_win.node.children.length); + + return true; +} //connectChromeWindowToTreeWindowItem() ////////////////////////////////////////////////////////////////////////// }}}1 // Loading // {{{1 @@ -1141,7 +1424,7 @@ function winAlreadyExistsInTree(cwin) /// Design decision: TabFern SHALL always be able to load older save files. /// Never remove a loader from this function. /// @post The new windows are added after any existing windows in the tree -/// @param {mixed} data The save data, parsed (i.e., not a JSON string) +/// @param {mixed} data The save data, parsed (i.e., not a JSON-encoded string) /// @return {number} The number of new windows, or ===false on failure. /// ** Note: 0 is a valid number of windows to load! var loadSavedWindowsFromData = (function(){ @@ -1193,7 +1476,9 @@ var loadSavedWindowsFromData = (function(){ let versionLoaders = { 0: loadSaveDataV0, 1: loadSaveDataV1 }; /// Populate the tree from the save data. - return function(data) + /// TODO throw on failure, so that the caller can report the details of + /// the error if desired. + return function loadSavedWindowsFromData_inner(data) { let succeeded = false; let loader_retval; // # of wins loaded @@ -1231,14 +1516,17 @@ var loadSavedWindowsFromData = (function(){ succeeded = true; } return (succeeded ? loader_retval : false); - } + } //loadSavedWindowsFromData_inner })(); //loadSavedWindowsFromData /// Load the saved windows from local storage - used as part of initialization. /// @param {function} next_action If provided, will be called when loading /// is complete. function loadSavedWindowsIntoTree(next_action) { + next_init_step('Load saved windows'); // TODO _T() the step names + chrome.storage.local.get(K.STORAGE_KEY, function(items) { + next_init_step('Got save data'); READIT: if(isLastError()) { @@ -1397,6 +1685,8 @@ function treeOnSelect(_evt_unused, evt_data) // Tabs case 'editBullet': actionEditTabBullet(node.id, node, null, null); break; +// case 'closeTab': +// actionCloseTabAndSave(node.id, node, null, null); case 'deleteTab': actionDeleteTab(node.id, node, null, null, evt_data.event); // as deleteWindow, above. @@ -1428,10 +1718,22 @@ function treeOnSelect(_evt_unused, evt_data) } if(is_tab && node_val.isOpen) { // An open tab - chrome.tabs.highlight({ - windowId: node_val.win_id, - tabs: [node_val.index] // Jump to the clicked-on tab - }, ignore_chrome_error); + + if(BROWSER_TYPE === 'ff') { + ASQ().promise( + browser.tabs.update(node_val.tab_id,{active:true}) + ) + .or((err)=>{ + log.warn({"Could not highlight tab":node_val,err}); + }); + + } else { //Chrome + chrome.tabs.highlight({ + windowId: node_val.win_id, + tabs: [node_val.index] // Jump to the clicked-on tab + }, ignore_chrome_error); + } + //log.info('flagging treeonselect' + node.id); T.treeobj.flag_node(node); win_id = node_val.win_id; @@ -1440,6 +1742,10 @@ function treeOnSelect(_evt_unused, evt_data) win_id = node_val.win_id; } else if( open_new_window ) { + // Ignore attempts to open two windows at once, since we currently + // can't handle them. + if(window_is_being_restored) return; + // A closed window or tab. Make sure we have the window. let win_node; let win_val; @@ -1467,123 +1773,64 @@ function treeOnSelect(_evt_unused, evt_data) for(let child_id of win_node.children) { let child_val = D.tabs.by_node_id(child_id); urls.push(child_val.raw_url); + // TODO: in Firefox, you can't call window.create with a URL of + // about:addons or about:debugging. } // Open the window window_is_being_restored = true; - chrome.windows.create( - { - url: urls - , focused: true - , left: newWinSize.left - , top: newWinSize.top - , width: newWinSize.width - , height: newWinSize.height - }, - function(win) { - // Update the tree and node mappings - log.info('Adding nodeid map for winid ' + win.id); - D.windows.change_key(win_val, 'win_id', win.id); - - win_val.isOpen = true; - win_val.keep = K.WIN_KEEP; // just in case - win_val.win = win; - //T.treeobj.set_type(win_node.id, K.NT_WIN_ELVISH); - M.add_subtype(win_node.id, K.NST_OPEN); - M.refresh_label(win_node.id); - M.refresh_icon(win_node); - - T.treeobj.open_node(win_node); - T.treeobj.redraw_node(win_node); - // Because open_node() doesn't redraw the parent, only - // its children, and opening the node changes the - // settings at this time. - - // In Chrome 61, with v0.1.4, I observed strange behaviour: - // the window would open extra tabs that were copies of - // items listed in `urls`. However, win.tabs.length sometimes - // would, and sometimes would not, indicate those extra tabs. - // It's a heisenbug. It may arise from two TabFerns running - // at once. It may also be related to what appears to be - // a Chrome 61 regression - Ctl+N for a new window will - // sometimes reopen previously-closed tabs. However, I - // don't know - I can't repro reliably. - // I have reported the Ctl+N issue: - // https://bugs.chromium.org/p/chromium/issues/detail?id=762951 - - // To hack around this if it happens again, I am trying: - - if(win.tabs.length != expected_tab_count) { - log.warn('Win ' + win.id + ': expected ' + - expected_tab_count + ' tabs; got ' + - win.tabs.length + 'tabs.'); - } - let count_to_use = Math.min(expected_tab_count, win.tabs.length); - for(let idx=0; idx < count_to_use; ++idx) { - let tab_node_id = win_node.children[idx]; - let tab_val = D.tabs.by_node_id(tab_node_id); - if(!tab_val) continue; - - let tab = win.tabs[idx]; - - D.tabs.change_key(tab_val, 'tab_id', tab.id); - M.add_subtype(tab_node_id, K.NST_OPEN); - - // Update realtime values from the ctab to the tab_val - tab_val.win_id = win.id; - tab_val.index = idx; - tab_val.tab = tab; - tab_val.raw_url = tab.url || 'about:blank'; - tab_val.raw_title = tab.title || '## Unknown title ##'; - tab_val.isOpen = true; - - // Apply changes from the tab_val to the ctab - if(tab_val.isPinned) { - chrome.tabs.update(tab.id, {pinned: true}, - ignore_chrome_error); - } + let create_data = { + url: urls + , left: newWinSize.left + , top: newWinSize.top + , width: newWinSize.width + , height: newWinSize.height + } + if(BROWSER_TYPE !== 'ff') { + create_data.focused = true; + } - } //foreach tab - - // Another hack for the strange behaviour above: get rid of - // any tabs we didn't expect. This assumes the tabs we - // wanted come first in the window, which seems to be a safe - // assumption. - chrome.windows.get(win.id, {populate: true}, - function(win) { - if(win.tabs.length > expected_tab_count) { - log.warn('Win ' + win.id + ': expected ' + - expected_tab_count + ' tabs; got ' + - win.tabs.length + ' tabs --- pruning.'); - - let to_prune=[]; - for(let tab_idx = expected_tab_count; - tab_idx < win.tabs.length; - ++tab_idx) { - to_prune.push(win.tabs[tab_idx].id); - } //foreach extra tab - - log.warn('Pruning ' + to_prune); - chrome.tabs.remove(to_prune, ignore_chrome_error); - } //endif we have extra tabs - - // if a tab was clicked on, activate that particular tab - if(is_tab) { - chrome.tabs.highlight({ - windowId: node_val.win_id, - tabs: [node_val.index] - }, ignore_chrome_error); - } + let win_create_cbk = function win_create_cbk(cwin) { + // TODO: in Firefox, I'm not sure if this gets called after + // an error. Try to open a saved window with a tab + // for about:addons or about:debugging to trigger an error. + + // Note: In testing, this happens after the winOnCreated, + // tabOnCreated, and tabOnActivated events. I don't know + // if that's guaranteed, though. + window_is_being_restored = false; + + if(isLastError()) { + window.alert(_T('errCouldNotOpenWindow', + chrome.runtime.lastError)); + return; // with the state in the tree unchanged + } + + // Update the tree and node mappings + log.info('Adding nodeid map for winid ' + cwin.id); + connectChromeWindowToTreeWindowItem( + cwin, + { val: win_val, node: win_node }, + { repin: true } + ); - // Set the highlights in the tree appropriately - T.treeobj.flag_node(win_node.id); - flagOnlyCurrentTab(win); + // Set the highlights in the tree appropriately + T.treeobj.flag_node(win_node.id); + flagOnlyCurrentTab(cwin); - } //get callback - ); //windows.get + }; //win_create_cbk callback - } //create callback - ); //windows.created + log.info({'Creating window': create_data}); + + try { + chrome.windows.create(create_data, win_create_cbk); + + } catch(e) { + log.warn({'Could not create window': e, create_data}); + window_is_being_restored = false; + window.alert(_T('errCouldNotCreateWindow',e)); + return; + } } else { // it's a node type we don't know how to handle. log.error('treeOnSelect: Unknown node ' + node); @@ -1619,53 +1866,58 @@ function treeOnSelect(_evt_unused, evt_data) ////////////////////////////////////////////////////////////////////////// }}}1 // Chrome window/tab callbacks // {{{1 -function winOnCreated(win) +function winOnCreated(cwin) { - log.info({'Window created': win.id, + log.info({'Window created': cwin.id, "Restore?": (window_is_being_restored ? "yes" : "no"), - win + cwin }); //log.info('clearing flags winoncreated'); T.treeobj.clear_flags(); + if(window_is_being_restored) { - window_is_being_restored = false; - return; // don't create an extra copy - } + return; // don't create an extra copy - the chrome.window.create + } // callback in treeOnSelect will handle it. // Save the window's size - if(win.type === 'normal') { - winSizes[win.id] = getWindowSizeFromWindowRecord(win); - newWinSize = winSizes[win.id]; + if(cwin.type === 'normal') { + winSizes[cwin.id] = getWindowSizeFromWindowRecord(cwin); + newWinSize = winSizes[cwin.id]; // Chrome appears to use the last-resized window as its size // template even when you haven't closed it, so do similarly. // ... Well, maybe the last-resized window with a non-blank tab --- // not entirely sure. } - createNodeForWindow(win, K.WIN_NOKEEP); + createNodeForWindow(cwin, K.WIN_NOKEEP); saveTree(); // for now, brute-force save on any change. } //winOnCreated /// Update the tree when the user closes a browser window -function winOnRemoved(win_id) +function winOnRemoved(cwin_id) { - if(win_id == my_winid) return; // does this happen? + if(cwin_id == my_winid) return; // does this happen? - log.info({'Window removed': win_id}); + log.info({'Window removed': cwin_id}); // Stash the size of the window being closed as the size for // reopened windows. - if(win_id in winSizes) { - // TODO only do this is win_id's type is "normal" - newWinSize = winSizes[win_id]; - delete winSizes[win_id]; + if(cwin_id in winSizes) { + // TODO only do this is cwin_id's type is "normal" + newWinSize = winSizes[cwin_id]; + delete winSizes[cwin_id]; } - let node_val = D.windows.by_win_id(win_id); + let node_val = D.windows.by_win_id(cwin_id); if(!node_val) return; // e.g., already closed let node_id = node_val.node_id; if(!node_id) return; + + if(node_val.keep === K.WIN_KEEP) { + lastSavedClosedWindow_node_id = node_id; + } + let node = T.treeobj.get_node(node_id); if(!node) return; @@ -1794,7 +2046,7 @@ function initFocusHandler() } //leavingWindow /// The actual onFocusChanged event handler - function inner(win_id, _unused_internal) + function inner_onFocusChanged(win_id, _unused_internal) { let old_win_id = previously_focused_winid; @@ -1868,9 +2120,9 @@ function initFocusHandler() } } //endif to_tf - }; //inner + }; //inner_onFocusChanged - winOnFocusChanged = inner; + winOnFocusChanged = inner_onFocusChanged; } //initFocusHandler @@ -1902,15 +2154,18 @@ var tabOnCreated = (function(){ /// Make an ASQ step that will check if the window matches an existing /// window. - function make_merge_check_step(ctab) + function make_merge_check_step(ctab, win_val) { return function merge_check_inner(check_done) { //DEBUG log.debug({[`merge check tab ${ctab.id} win ${ctab.windowId}`]: ctab}); + let seq = ASQH.NowCCTry((cc)=>{ chrome.windows.get(ctab.windowId,{populate:true}, cc); + // If there's a prune pending, give the merge time to work + pruneWindowSetTimer(win_val); }); seq.then((done, cwin_or_err)=>{ @@ -1921,8 +2176,9 @@ var tabOnCreated = (function(){ let cwin = cwin_or_err; let merge_to_win = winAlreadyExistsInTree(cwin); - MERGE: if(merge_to_win && merge_to_win.val && - !merge_to_win.val.isOpen // don't hijack other open wins + MERGE: if(merge_to_win && merge_to_win.val + && !merge_to_win.val.isOpen // don't hijack other open wins + && merge_to_win.val.node_id ) { log.info({ [`merge ${cwin.id} Found merge target in tree for`]: cwin, @@ -1942,15 +2198,24 @@ var tabOnCreated = (function(){ break MERGE; } + if(merge_to_win.val.node_id === lastSavedClosedWindow_node_id) { + log.info(`merge ${cwin.id}: bail - I ` + + "won't merge with the most-recently-closed window"); + break MERGE; + } + // Detach the existing nodes from their chrome wins/tabs if(!destroy_subtree_but_not_widgets(merge_from_win_val.node_id)) { log.debug(`merge ${cwin.id}: bail - could not remove subtree for open window`); break MERGE; } - // Attach the old nodes to the wins/tabs - attachChromeWindowToSavedWindowItem(cwin, merge_to_win, false); + // Connect the old nodes to the wins/tabs + connectChromeWindowToTreeWindowItem(cwin, merge_to_win, false); + // TODO make sure the correct window is carrying the + // ordered_url_hash in the multidex. I think this might + // be where #119 is happening. } //endif existing (MERGE) }); @@ -1975,7 +2240,7 @@ var tabOnCreated = (function(){ hash = url.hash.slice(1); if(!hash) return false; - if(url.href.split("#")[0] !== + if(url.href.split('#')[0] !== chrome.runtime.getURL('/src/view/newtab.html') ) { return false; @@ -2021,8 +2286,15 @@ var tabOnCreated = (function(){ { log.info({'Tab created': ctab.id, ctab}); - let win_node_id = D.windows.by_win_id(ctab.windowId, 'node_id') - if(!win_node_id) return; + let win_val = D.windows.by_win_id(ctab.windowId); + let win_node_id = win_val ? win_val.node_id : undefined; + if(!win_node_id) { + log.info(`Unknown window ID ${ctab.windowId} - ignoring`); + return; + } + + // If there's a prune pending, give the merge time to work + pruneWindowSetTimer(win_val); let tab_node_id; @@ -2068,7 +2340,7 @@ var tabOnCreated = (function(){ // Design decision: after creating the node, check if it's a // duplicate. - seq.try(make_merge_check_step(ctab)); + seq.try(make_merge_check_step(ctab, win_val)); // .try => always run the following saveTree seq.then((done)=>{saveTree(true, done);}); @@ -2081,6 +2353,10 @@ var tabOnCreated = (function(){ function tabOnUpdated(tabid, changeinfo, ctab) { + let dirty = false; + let should_refresh_label = false; + let should_refresh_tooltip = false; + log.info({'Tab updated': tabid, 'Index': ctab.index, changeinfo, ctab}); let tab_node_val = D.tabs.by_tab_id(tabid); @@ -2090,12 +2366,16 @@ function tabOnUpdated(tabid, changeinfo, ctab) let node = T.treeobj.get_node(tab_node_id); tab_node_val.isOpen = true; //lest there be any doubt + log.debug({" Details for updated ctab":tabid,tab_node_val,node}); + // Caution: changeinfo doesn't always have all the changed information. // Therefore, we check changeinfo and ctab. // URL let new_raw_url = changeinfo.url || ctab.url || 'about:blank'; if(new_raw_url !== tab_node_val.raw_url) { + dirty = true; + should_refresh_tooltip = true; tab_node_val.raw_url = new_raw_url; M.updateOrderedURLHash(node.parent); // When the URL changes, the hash changes, too. @@ -2103,37 +2383,48 @@ function tabOnUpdated(tabid, changeinfo, ctab) // pinned let new_pinned = null; - let should_refresh_label = false; if('pinned' in changeinfo) { new_pinned = changeinfo.pinned; } else if('pinned' in ctab) { new_pinned = ctab.pinned; } - if(new_pinned !== null) { - tab_node_val.isPinned = new_pinned; + + if( (new_pinned !== null) && (tab_node_val.isPinned !== new_pinned) ) { + dirty = true; should_refresh_label = true; + tab_node_val.isPinned = new_pinned; } // title - let new_raw_title = changeinfo.title || ctab.title || 'Tab'; + let new_raw_title = changeinfo.title || ctab.title || _T('labelBlankTabTitle'); if(new_raw_title !== tab_node_val.raw_title) { - tab_node_val.raw_title = new_raw_title; + dirty = true; should_refresh_label = true; - } + should_refresh_tooltip = true; - if(should_refresh_label) { - M.refresh_label(tab_node_id); + tab_node_val.raw_title = new_raw_title; } // favicon let new_raw_favicon_url = changeinfo.favIconUrl || ctab.favIconUrl || null; if(new_raw_favicon_url !== tab_node_val.raw_favicon_url) { + dirty = true; tab_node_val.raw_favicon_url = new_raw_favicon_url; M.refresh_icon(tab_node_val); } - saveTree(); + if(should_refresh_label) { + M.refresh_label(tab_node_id); + } + + if(should_refresh_tooltip) { + M.refresh_tooltip(tab_node_id); + } + + if(dirty) { + saveTree(); + } // For some reason, Ctl+N plus filling in a tab doesn't give me a // focus change to the new window. Therefore, if the tab that has @@ -2205,39 +2496,64 @@ function tabOnRemoved(tabid, removeinfo) // If the window is closing, do not remove the tab records. // The cleanup will be handled by winOnRemoved(). - if(removeinfo.isWindowClosing) return; + if(removeinfo.isWindowClosing) { + log.debug({'Window is closing, so nothing to do for ctab':tabid,removeinfo}); + return; // TODO also mark this as non-recovered, maybe? + } let window_node_id = D.windows.by_win_id(removeinfo.windowId, 'node_id'); - if(!window_node_id) return; + if(!window_node_id) { + log.debug({'Bailing - no window_node_id for ctab':tabid,removeinfo}); + return; + } { // Keep the locals here out of the scope of the closure below. // Get the parent (window) let window_node = T.treeobj.get_node(window_node_id); - if(!window_node) return; - - // Get the tab's node - let tab_node_id = D.tabs.by_tab_id(tabid, 'node_id'); - if(!tab_node_id) return; - let tab_node = T.treeobj.get_node(tab_node_id); - if(!tab_node) return; + if(!window_node) { + log.debug({'Bailing - no window_node for ctab':tabid,removeinfo}); + return; + } - // Remove the node + // Get the node let tab_val = D.tabs.by_tab_id(tabid); // See if it's a tab we have already marked as removed. If so, // whichever code marked it is responsible, and we're off the hook. - if(!tab_val || tab_val.tab_id === K.NONE) return; + if(!tab_val || tab_val.tab_id === K.NONE) { + log.debug({"Bailing, but it's probably OK - no tab_val for ctab":tabid, tab_val, removeinfo}); + return; + } + + // Get the tab's node + if(!tab_val.node_id) { + log.debug({'Bailing - no tab_val.node_id for ctab':tabid,removeinfo}); + return; + } + let tab_node = T.treeobj.get_node(tab_val.node_id); + if(!tab_node) { + log.debug({'Bailing - no tab_node for ctab':tabid,removeinfo}); + return; + } + + log.debug({'Removing value for ctab':tabid,tab_val,removeinfo}); D.tabs.remove_value(tab_val); // So any events that are triggered won't try to look for a // nonexistent tab. + + log.debug({[`Removing tree node ${tab_node.id} for ctab`]:tabid,tab_node,tab_val,removeinfo}); T.treeobj.because('chrome','delete_node',tab_node); } + log.debug({'Updating tab index values after removing ctab':tabid,window_node_id,removeinfo}); + // Refresh the tab.index values for the remaining tabs updateTabIndexValues(window_node_id); + log.debug({'Tab index values updated after removing ctab':tabid,window_node_id,removeinfo}); + saveTree(); } //tabOnRemoved @@ -2294,11 +2610,22 @@ function tabOnAttached(tabid, attachinfo) updateTabIndexValues(new_win_val.node_id); } //tabOnAttached +/// Handle tab replacement, which can occur with preloads. E.g., #129. function tabOnReplaced(addedTabId, removedTabId) { - // Do we get this? - log.warn('Tab being replaced: added ' + addedTabId + '; removed ' + + log.info('Tab being replaced: added ' + addedTabId + '; removed ' + removedTabId); + let tab_val = D.tabs.by_tab_id(removedTabId); + if(!tab_val) { + log.warn('onReplaced: No tab found for removed' + + `tab ID ${removedTabId} - bailing`); + return; + } + + D.tabs.change_key(tab_val, 'tab_id', addedTabId); + log.info({ + [`Tab replacement ${removedTabId}->${addedTabId}: new value`]:tab_val + }); } //tabOnReplaced ////////////////////////////////////////////////////////////////////////// }}}1 @@ -2363,18 +2690,39 @@ function timedResizeDetector() ////////////////////////////////////////////////////////////////////////// }}}1 // Hamburger menu // {{{1 +/// Open a window, setting do_not_prune_right_now around the call. +let open_window_with_url = function(url) { + do_not_prune_right_now = true; + let seq = + K.openWindowForURL(url || 'about:blank'); + seq + .val(()=>{ + do_not_prune_right_now = false; + }) + .or(()=>{ + do_not_prune_right_now = false; + }); + return seq; +}; + /// Open a new window with the TabFern homepage. function hamAboutWindow() { - K.openWindowForURL('https://cxw42.github.io/TabFern/'); + open_window_with_url('https://cxw42.github.io/TabFern/'); } //hamAboutWindow() +/// Reload the TabFern window (or, at least, the tree iframe) +function hamReloadTree() +{ + window.location.reload(true); +} //hamReloadTree() + /// Open the Settings window. If ShowWhatIsNew, also updates the K.LASTVER_KEY /// information used by checkWhatIsNew(). function hamSettings() { // Actually open the window - K.openWindowForURL(chrome.extension.getURL( + open_window_with_url(chrome.extension.getURL( '/src/settings/index.html' + (ShowWhatIsNew ? '#open=last' : '')) ); @@ -2408,19 +2756,39 @@ function hamBackup() /// already present. It does not delete existing tabs/windows. function hamRestoreFromBackup() { - let importer = new Modules.importer(document, '.tabfern'); - importer.getFileAsString(function(text, filename){ + /// Process the text of the file once it's loaded + function processFile(text, filename) { + let ok; try { let parsed = JSON.parse(text); - if(loadSavedWindowsFromData(parsed) === false) { - window.alert("I couldn't load the file " + filename + ': ' + e); + ok = loadSavedWindowsFromData(parsed); + if(!ok) { + let errmsg = _T('errCouldNotLoadFile', filename, e); + log.warn({[errmsg]:e}); + window.alert(errmsg); } } catch(e) { - window.alert("File " + filename + ' is not something I can '+ - 'understand as a TabFern save file. Parse error code was: ' + - e); + let errmsg = _T('errCouldNotParseFile'); + log.warn({[errmsg + ' (exception thrown)']:e}); + window.alert(errmsg); } - }); + + // If we were successful, save. Otherwise, a reload of the extension + // before taking another action that triggers a save will result in + // the Restore never having happened. + if(ok) { + saveTree(); + } + } //processFile() + + try { + let importer = new Modules.importer(document, '.tabfern'); + importer.getFileAsString(processFile); + } catch(e) { + let errmsg = _T('errCouldNotRunImporter', e); + log.warn({[errmsg]:e}); + window.alert(errmsg); + } } //hamRestoreFromBackup() function hamRestoreLastDeleted() @@ -2430,7 +2798,8 @@ function hamRestoreLastDeleted() // Make v0 save data from the last-deleted-window URLs, just because // v0 is convenient, and the backward-compatibility guarantee of // loadSavedWindowsFromData means we won't have to refactor this. - let tabs=[] + let tabs=[]; + // TODO RESUME HERE --- convert user-facing text to _T() for(let url of lastDeletedWindow) { tabs.push({text: 'Restored', url: url}); } @@ -2473,7 +2842,7 @@ function hamSorter(compare_fn) function hamRunJasmineTests() { - K.openWindowForURL(chrome.extension.getURL('/test/index.html')); + open_window_with_url(chrome.extension.getURL('/test/index.html')); } // hamRunJasmineTests function hamSortOpenToTop() @@ -2509,7 +2878,7 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) // Add development-specific items, if any if(is_devel_mode) { items.splitItem = { - label: 'Split test', + label: _T('menuSplitTest'), action: function(){ if(window.parent && window.parent.doSplit) window.parent.doSplit(); @@ -2517,21 +2886,29 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) }; items.jasmineItem = { - label: 'Run Jasmine tests', + label: _T('menuJasmineTests'), action: hamRunJasmineTests, icon: 'fa fa-fort-awesome', separator_after: true, }; } //endif is_devel_mode + items.reloadItem = { + label: _T('menuReload'), + action: hamReloadTree, + icon: 'fa fa-refresh', + separator_after: true, + }; + items.infoItem = { - label: "Online info", + label: _T('menuOnlineInfo'), title: "The TabFern web site, with a basic usage guide and the credits", + // TODO also _T() the title fields throughout action: hamAboutWindow, icon: 'fa fa-info', }; items.settingsItem = { - label: "Settings and offline help", + label: _T('menuSettings'), title: "Also lists the features introduced with each version!", action: hamSettings, icon: 'fa fa-cog' + (ShowWhatIsNew ? ' tf-notification' : ''), @@ -2541,58 +2918,54 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) if(Array.isArray(lastDeletedWindow) && lastDeletedWindow.length>0) { items.restoreLastDeletedItem = { - label: "Restore last deleted", + label: _T('menuRestoreLastDeleted'), action: hamRestoreLastDeleted, }; } items.backupItem = { - label: "Backup now", + label: _T('menuBackupNow'), icon: 'fa fa-floppy-o', action: hamBackup, }; items.restoreItem = { - label: "Load contents of a backup", + label: _T('menuLoadBackupContents'), action: hamRestoreFromBackup, icon: 'fa fa-folder-open-o', separator_after: true, }; items.sortItem = { - label: 'Sort', + label: _T('menuSort'), icon: 'fa fa-sort', submenu: { openToTopItem: { - label: 'Open windows to top', + label: _T('menuSortOpenToTop'), title: 'Sort ascending by window name, case-insensitive, '+ 'and put the open windows at the top of the list.', action: hamSortOpenToTop, icon: 'fff-text-padding-top' }, azItem: { - label: 'A-Z', + label: _T('menuSortAZ'), title: 'Sort ascending by window name, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_text), icon: 'fa fa-sort-alpha-asc', }, zaItem: { - label: 'Z-A', + label: _T('menuSortZA'), title: 'Sort descending by window name, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_text_desc), icon: 'fa fa-sort-alpha-desc', }, numItem09: { - label: '0-9', + label: _T('menuSort09'), title: 'Sort ascending by window name, numeric, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_num), icon: 'fa fa-sort-numeric-asc', }, - // Test of sub-submenus --- jstree doesn't seem to correctly - // constrain them to the viewport. - //test: { label: 'test', submenu: - // { foo: {label:'foo'},bar:{label:'bar'},bat:{label:'bat'} }}, numItem90: { - label: '9-0', + label: _T('menuSort90'), title: 'Sort descending by window name, numeric, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_num_desc), icon: 'fa fa-sort-numeric-desc', @@ -2601,12 +2974,12 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) }; //sortItem items.expandItem = { - label: "Expand all", + label: _T('menuExpandAll'), icon: 'fa fa-plus-square', action: hamExpandAll, }; items.collapseItem = { - label: "Collapse all", + label: _T('menuCollapseAll'), icon: 'fa fa-minus-square', action: hamCollapseAll, }; @@ -2646,11 +3019,11 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) if(nodeType === K.IT_TAB) { let tabItems = { toggleBorderItem: { - label: 'Toggle top border', + label: _T('menuToggleTopBorder'), action: function(){actionToggleTabTopBorder(node.id, node, null, null)} }, editBulletItem: { - label: 'Add/edit a note', + label: _T('menuAddEditNote'), icon: 'fff-pencil', // Use K.nextTickRunner so the context menu can be @@ -2661,14 +3034,24 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) }, }; +// if(tab_val.isOpen) { +// tabItems.closeItem = { +// label: 'Close and remember', +// icon: 'fff-picture-delete', +// action: +// function(){actionCloseTabAndSave(node.id,node,null,null);} +// }; +// } + + return tabItems; - } + } //endif K.IT_TAB if(nodeType === K.IT_WIN) { let winItems = {}; winItems.renameItem = { - label: 'Rename', + label: _T('menuRename'), icon: 'fff-pencil', // Use K.nextTickRunner so the context menu can be @@ -2681,16 +3064,16 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) // Forget/Remember if( win_val.isOpen && (win_val.keep === K.WIN_KEEP) ) { winItems.forgetItem = { - label: "Forget but don't close", - title: "Do not save this window when it is closed", + label: _T('menuForget'), + title: _T('menuttForget'), icon: 'fa fa-chain-broken', action: function(){actionForgetWindow(node.id, node, null, null);} }; } else if( win_val.isOpen && (win_val.keep === K.WIN_NOKEEP) ) { winItems.rememberItem = { - label: "Remember", - title: "Save this window when it is closed", + label: _T('menuRemember'), + title: _T('menuttRemember'), icon: 'fa fa-link', action: function(){actionRememberWindow(node.id, node, null, null);} @@ -2699,7 +3082,7 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) if(win_val.isOpen) { winItems.closeItem = { - label: 'Close and remember', + label: _T('menuCloseAndRemember'), icon: 'fff-picture-delete', action: function(){actionCloseWindowAndSave(node.id,node,null,null);} @@ -2707,7 +3090,7 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) } winItems.deleteItem = { - label: 'Delete', + label: _T('menuDelete'), icon: 'fff-cross', separator_before: true, action: @@ -2843,6 +3226,14 @@ var treeCheckCallback = (function() .or((err)=>{ // Doesn't fire for invalid moves of pinned tabs in Chrome 63 L.log.warn({[`Couldn't move tab ${val.tab_id}`]:err}); + + // TODO #123: If the move failed, put the tree item back + // where it was before. E.g., in Vivaldi, dragging a tab's + // tree item from a regular window into the Settings window + // moves the tab in the tree, but we get here with message + // "Tabs can only be moved to and from normal windows," + // and the tab doesn't actually move in the view. + }); } else { @@ -2932,7 +3323,8 @@ var treeCheckCallback = (function() } //open_tab_within_window // --- The main check callback --- - function inner(operation, node, new_parent, node_position, more) + function inner_treeCheckCallback(operation, node, new_parent, + node_position, more) { // Fast bail when possible if(operation === 'copy_node') return false; @@ -3233,9 +3625,9 @@ var treeCheckCallback = (function() } //endif this is a non-dnd move return true; - } //inner + } //inner_treeCheckCallback - return inner; + return inner_treeCheckCallback; // Note on the code that doesn't check for more.dnd: // See https://github.com/vakata/jstree/issues/815 - the final node move @@ -3284,7 +3676,7 @@ function checkWhatIsNew(selector) { [K.LASTVER_KEY]: 'installed, but no version viewed yet' }, function() { ignore_chrome_error(); - K.openWindowForURL('https://cxw42.github.io/TabFern/#usage'); + open_window_with_url('https://cxw42.github.io/TabFern/#usage'); } ); } @@ -3377,13 +3769,16 @@ function delete_all_closed_nodes(are_you_sure) } //delete_all_closed_nodes ////////////////////////////////////////////////////////////////////////// }}}1 -// Startup / shutdown // {{{1 +// Startup / shutdown details // {{{1 + +// Initialization routines // {{{2 let custom_bg_color = false; /// Initialization that happens before the full DOM is loaded function preLoadInit() { + next_init_step('preload'); if(getBoolSetting(CFG_HIDE_HORIZONTAL_SCROLLBARS)) { document.querySelector('html').classList += ' tf--feature--hide-horizontal-scrollbars'; } @@ -3428,34 +3823,28 @@ function preLoadInit() body.classList += ` jstree-${getThemeName()}`; } - // Apply custom background, if any + // Apply custom background, if any. + // NOTE: need to keep this logic in sync with the validator for + // CFGS_BACKGROUND in src/common/common.js. TODO remove this duplication. BG: if(haveSetting(CFGS_BACKGROUND)) { let bg = getStringSetting(CFGS_BACKGROUND, '').trim(); if(bg.length < 2) break BG; // no valid color is one character - if(Modules['common/validation'].isValidColor(bg)) { + if(Validation.isValidColor(bg)) { custom_bg_color = bg; break BG; } - // not a color --- try to parse it as a URL - try { - let url = new URL(bg); - if(url.protocol === "file:" || - //url.protocol === "http:" || + // not a color --- try to parse it as a URL. + if(Validation.isValidURL(bg, + ['file', 'https', 'data', 'chrome-extension'])) { // For now, disallow http so we don't have to worry // about HTTP-hijacking attacks. I don't know of any // attack vectors in CSS background images off-hand, // but that doesn't mean there aren't any :) . - url.protocol === "https:" || - url.protocol === "data:" || - url.protocol === "chrome-extension:") { - custom_bg_color = `url("${url.href}")`; - } + custom_bg_color = `url("${bg}")`; break BG; - } catch(e) { - // do nothing } } //endif have a background setting @@ -3468,6 +3857,7 @@ function preLoadInit() /// Beginning of the onload initialization. function basicInit(done) { + next_init_step('basic initialization'); log.info('TabFern tree.js initializing view - ' + TABFERN_VERSION); Hamburger = Modules.hamburger('#hamburger-menu', getHamburgerMenuItems @@ -3500,6 +3890,7 @@ function basicInit(done) /// Called after ASQ.try(chrome.runtime.sendMessage) function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) { + next_init_step('create main tree'); if(ASQH.is_asq_try_err(win_id_msg_or_error)) { // This is fatal return done.fail("Couldn't get win ID: " + win_id_msg_or_error.catch); @@ -3543,6 +3934,7 @@ function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) drivers: [Modules.dmauro_keypress] }, function initialized(err) { + next_init_step('context-menu support'); if ( err ) { console.log({['Failed loading a shortcut driver! Initializing '+ 'context menu with no shortcut driver']:err}); @@ -3556,8 +3948,57 @@ function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) ); } //createMainTreeIfWinIdReceived_catch() +/// Called after ASQ.try(chrome.storage.local.get(LOCN_KEY)) +function moveWinToLastPositionIfAny_catch(done, items_or_err) +{ // move the popup window to its last position/size. + // If there was an error (e.g., nonexistent key), just + // accept the default size. + + next_init_step('reposition window'); + if(ASQH.is_asq_try_err(items_or_err)) { + log.warn({"Couldn't get saved location":$.extend({},items_or_err)}); + // Note: $.extend() used to force evaluation at the time of logging + } else { //we have a location + log.info({"Got saved location":$.extend({},items_or_err)}); + + let parsed = items_or_err[K.LOCN_KEY]; + if( (parsed !== null) && (typeof parsed === 'object') ) { + // + and || are to provide some sensible defaults - thanks to + // https://stackoverflow.com/a/7540412/2877364 by + // https://stackoverflow.com/users/113716/user113716 + let size_data = + { + 'left': +parsed.left || 0 + , 'top': +parsed.top || 0 + , 'width': Math.max(+parsed.width || 300, 100) + // don't let it shrink too small, in case something went wrong + , 'height': Math.max(+parsed.height || 600, 200) + }; + last_saved_size = $.extend({}, size_data); + chrome.windows.update(my_winid, size_data, + (win)=>{ + if(isLastError()) { + log.error(`Could not move window: ${chrome.runtime.lastError}`); + } else { + log.info({"Updated window size":$.extend({},win)}); + } + } + ); + } else { + log.warn({"Could not parse size from":$.extend({},items_or_err)}) + } //endif got an item else + + } //endif storage.local.get worked + + // Start the detection of moved or resized windows + setTimeout(timedResizeDetector, K.RESIZE_DETECTOR_INTERVAL_MS); + + done(); +} //moveWinToLastPositionIfAny_catch() + function addOpenWindowsToTree(done, cwins) { + next_init_step('add open windows to tree'); let dat = {}; let focused_win_id; @@ -3584,9 +4025,9 @@ function addOpenWindowsToTree(done, cwins) if(!existing_win || (existing_win.val && existing_win.val.isOpen)) { // Doesn't exist, or the duplicate is already open (e.g., if two // windows are open with the same set of tabs) - createNodeForWindow(cwin, K.WIN_NOKEEP); + createNodeForWindow(cwin, K.WIN_NOKEEP, true); //true=>no pruning } else { - attachChromeWindowToSavedWindowItem(cwin, existing_win); + connectChromeWindowToTreeWindowItem(cwin, existing_win); } //endif window already exists } //foreach window @@ -3602,6 +4043,7 @@ function addOpenWindowsToTree(done, cwins) function addEventListeners(done) { + next_init_step('add event listeners'); // At this point, the saved and open windows have been loaded into the // tree. Therefore, we can position the action groups. We already // saved the position, so do not need to specify it here. @@ -3669,47 +4111,21 @@ function addEventListeners(done) done(); } //addEventListeners -/// Called after ASQ.try(chrome.storage.local.get(LOCN_KEY)) -function moveWinToLastPositionIfAny_catch(done, items_or_err) -{ // move the popup window to its last position/size. - // If there was an error (e.g., nonexistent key), just - // accept the default size. - - if(!ASQH.is_asq_try_err(items_or_err)) { - let parsed = items_or_err[K.LOCN_KEY]; - if( (parsed !== null) && (typeof parsed === 'object') ) { - // + and || are to provide some sensible defaults - thanks to - // https://stackoverflow.com/a/7540412/2877364 by - // https://stackoverflow.com/users/113716/user113716 - let size_data = - { - 'left': +parsed.left || 0 - , 'top': +parsed.top || 0 - , 'width': Math.max(+parsed.width || 300, 100) - // don't let it shrink too small, in case something went wrong - , 'height': Math.max(+parsed.height || 600, 200) - }; - last_saved_size = $.extend({}, size_data); - chrome.windows.update(my_winid, size_data, ignore_chrome_error); - } - } //endif no error - - // Start the detection of moved or resized windows - setTimeout(timedResizeDetector, K.RESIZE_DETECTOR_INTERVAL_MS); - - done(); -} //moveWinToLastPositionIfAny_catch() - /// The last function to be called after all other initialization has /// completed successfully. function initTreeFinal(done) { - //return; // DEBUG - don't save + next_init_step('finalize'); + + // Tests of different ways of failing init - for debugging only + //throw new Error("oops"); // DEBUG + //return; // DEBUG - don't save if(!was_loading_error) { did_init_complete = true; // Assume the document is loaded by this point. - $(K.INIT_MSG_SEL).css('display','none'); + $(K.INIT_MSG_SEL).remove(); + $(K.INIT_PROGRESS_SEL).remove(); // just in case initialization took a long time, and the message // already appeared. @@ -3725,7 +4141,8 @@ function initTreeFinal(done) done(); } //initTreeFinal() -// ---------------------------------------------- +// ---------------------------------------------- }}}2 +// Shutdown routines // {{{2 /// Save the tree on window.unload function shutdownTree() @@ -3744,17 +4161,32 @@ function shutdownTree() } } //shutdownTree() +// ---------------------------------------------- }}}2 +// Error reporting // {{{2 + /// Show a warning if initialization hasn't completed. function initIncompleteWarning() { if(!did_init_complete) { // Assume the document is loaded by this point. - $(K.INIT_MSG_SEL).css('display','block'); + if(K && K.INIT_MSG_SEL) { + $(K.INIT_MSG_SEL).css('display','block'); + } else { + window.setTimeout(initIncompleteWarning, 500); + } + + if(K && K.INIT_PROGRESS_SEL) { + $(K.INIT_PROGRESS_SEL).css('display','block'); + } else { + window.setTimeout(initIncompleteWarning, 500); + } } } //initIncompleteWarning() -////////////////////////////////////////////////////////////////////////// -// MAIN // +// }}}2 + +//////////////////////////////////////////////////////////////////////// }}}1 +// MAIN // {{{1 function main(...args) { @@ -3770,6 +4202,9 @@ function main(...args) local_init(); + //$(K.INIT_PROGRESS_SEL).css('display','block'); //DEBUG + $(K.INIT_PROGRESS_SEL).text("waiting for browser"); + // Timer to display the warning message if initialization doesn't complete // quickly enough. window.setTimeout(initIncompleteWarning, K.INIT_TIME_ALLOWED_MS); @@ -3785,28 +4220,43 @@ function main(...args) // Run the main init steps once the page has loaded let s = ASQ(); callbackOnLoad(s.errfcb()); + // Note: on one test on Firefox, the rest of the chain never fired. + // Not sure why. s.then(basicInit) + .try((done)=>{ // Get our Chrome-extensions-API window ID from the background page. // I don't know a way to get this directly from the JS window object. // TODO maybe getCurrent? Not sure if that's reliable. + next_init_step('get window ID'); chrome.runtime.sendMessage({msg:MSG_GET_VIEW_WIN_ID}, ASQH.CC(done)); }) .then(createMainTreeIfWinIdReceived_catch) + + .try((done)=>{ + next_init_step('get saved location'); + // Find out where the view was before, if anywhere + chrome.storage.local.get(K.LOCN_KEY, ASQH.CC(done)); + }) + .then(moveWinToLastPositionIfAny_catch) + .then(loadSavedWindowsIntoTree) .then((done)=>{ + next_init_step('get open windows'); chrome.windows.getAll({'populate': true}, ASQH.CC(done)); }) .then(addOpenWindowsToTree) .then(addEventListeners) - .try((done)=>{ - // Find out where the view was before, if anywhere - chrome.storage.local.get(K.LOCN_KEY, ASQH.CC(done)); - }) - .then(moveWinToLastPositionIfAny_catch) .then(initTreeFinal) - //.or(TODO show "couldn't load" in the popup) + + .val(check_init_step_count) + + .or((err)=>{ + $(K.INIT_MSG_SEL).text( + $(K.INIT_MSG_SEL).text() + "\n" + err + ) + }); ; } // main() diff --git a/webstore/_locales/de/messages.json b/webstore/_locales/de/messages.json new file mode 100755 index 00000000..8609f660 --- /dev/null +++ b/webstore/_locales/de/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- Grüße!", "description": "de" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern Registerkarten-Manager und Backup-Tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Einstellungen" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Notiz hinzufügen oder bearbeiten" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Löschen (schließen; nicht speichern)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Umbenennen" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Schließen und speichern" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Löschen (schließen; nicht speichern)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "Neuer Fenstername?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Notiz für Tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Fenster \"$TITLE$\" löschen?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Registerkarte \"$TITLE$\" löschen?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Registerkarte \"$TITLE$\" und ihre Fenster löschen?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">J</span>a" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "j" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>ein" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">A</span>bbrechen" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "a" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Frag nicht nochmal (<abbr title=\"You can change this in Settings | Behaviour\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Nicht gespeichert" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Gespeicherte Registerkarten" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Gerettete Registerkarten" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (gerettete)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unbekannter Titel ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Registerkarte" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Neuladen" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Online-Informationen" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Einstellungen und Offline-Hilfe" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Retten das letzte-gelöschte Fenster" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Backup speichern" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Lade den Inhalt eines Backups" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Sortieren" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Öffne Fenster nach oben" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Alle erweitern" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Alle reduzieren" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Oberen Rand umschalten" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Notiz hinzufügen oder bearbeiten" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Umbenennen" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Vergessen aber nicht schließen" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Speichern dieses Fenster nicht, wenn es geschlossen wird" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Erinnern" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Speichern dieses Fenster nicht, wenn es geschlossen wird" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Schließen und erinnern" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Löschen" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "Fehler: I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "Fehler: I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "Fehler: I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "Fehler: I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "Fehler: File $FILENAME$ is not something I can understand as a TabFern save file. Pars error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Fehler: Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/webstore/_locales/en/messages.json b/webstore/_locales/en/messages.json index a1b9cd07..fc30c4a2 100755 --- a/webstore/_locales/en/messages.json +++ b/webstore/_locales/en/messages.json @@ -1,66 +1,326 @@ -{ - "l10nTabName": { - "message":"Localization" - ,"description":"name of the localization tab" - } - ,"l10nHeader": { - "message":"It does localization too! (this whole tab is, actually)" - ,"description":"Header text for the localization section" - } - ,"l10nIntro": { - "message":"'L10n' refers to 'Localization' - 'L' an 'n' are obvious, and 10 comes from the number of letters between those two. It is the process/whatever of displaying something in the language of choice. It uses 'I18n', 'Internationalization', which refers to the tools / framework supporting L10n. I.e., something is internationalized if it has I18n support, and can be localized. Something is localized for you if it is in your language / dialect." - ,"description":"introduce the basic idea." - } - ,"l10nProd": { - "message":"You <strong>are</strong> planning to allow localization, right? You have <em>no idea</em> who will be using your extension! You have no idea who will be translating it! At least support the basics, it's not hard, and having the framework in place will let you transition much more easily later on." - ,"description":"drive the point home. It's good for you." - } - ,"l10nFirstParagraph": { - "message":"When the options page loads, elements decorated with <strong>data-l10n</strong> will automatically be localized!" - ,"description":"inform that <el data-l10n='' /> elements will be localized on load" - } - ,"l10nSecondParagraph": { - "message":"If you need more complex localization, you can also define <strong>data-l10n-args</strong>. This should contain <span class='code'>$containerType$</span> filled with <span class='code'>$dataType$</span>, which will be passed into Chrome's i18n API as <span class='code'>$functionArgs$</span>. In fact, this paragraph does just that, and wraps the args in mono-space font. Easy!" - ,"description":"introduce the data-l10n-args attribute. End on a lame note." - ,"placeholders": { - "containerType": { - "content":"$1" - ,"example":"'array', 'list', or something similar" - ,"description":"type of the args container" - } - ,"dataType": { - "content":"$2" - ,"example":"string" - ,"description":"type of data in each array index" - } - ,"functionArgs": { - "content":"$3" - ,"example":"arguments" - ,"description":"whatever you call what you pass into a function/method. args, params, etc." - } - } - } - ,"l10nThirdParagraph": { - "message":"Message contents are passed right into innerHTML without processing - include any tags (or even scripts) that you feel like. If you have an input field, the placeholder will be set instead, and buttons will have the value attribute set." - ,"description":"inform that we handle placeholders, buttons, and direct HTML input" - } - ,"l10nButtonsBefore": { - "message":"Different types of buttons are handled as well. <button> elements have their html set:" - } - ,"l10nButton": { - "message":"in a <strong>button</strong>" - } - ,"l10nButtonsBetween": { - "message":"while <input type='submit'> and <input type='button'> get their 'value' set (note: no HTML):" - } - ,"l10nSubmit": { - "message":"a <strong>submit</strong> value" - } - ,"l10nButtonsAfter": { - "message":"Awesome, no?" - } - ,"l10nExtras": { - "message":"You can even set <span class='code'>data-l10n</span> on things like the <title> tag, which lets you have translatable page titles, or fieldset <legend> tags, or anywhere else - the default <span class='code'>Boil.localize()</span> behavior will check every tag in the document, not just the body." - ,"description":"inform about places which may not be obvious, like <title>, etc" - } -} +{ "encoding": { "message": "-*- coding: utf-8 -*- схема", "description": "en" } + + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern tab manager and backup tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Settings" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Add/edit label" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Edit text" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Close and save" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "New window name?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note for tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Delete window \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Delete tab \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Delete tab \"$TITLE$\" and its window?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">Y</span>es" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "y" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>o" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">C</span>ancel" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "c" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Don't ask again (<abbr title=\"You can change this in Settings | Behaviour\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Unsaved" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Saved tabs" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Recovered tabs" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Recovered)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unknown title ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Tab" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Reload" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Online info" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Settings and offline help" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restore last deleted" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Backup now" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Load contents of a backup" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Sort" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Open windows to top" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Expand all" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Collapse all" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Toggle top border" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Add/edit a note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Rename" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Forget but don't close" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Do not save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Remember" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Close and remember" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Delete" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "File $FILENAME$ is not something I can understand as a TabFern save file. Parse error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/webstore/_locales/fr/messages.json b/webstore/_locales/fr/messages.json new file mode 100755 index 00000000..f0ae8b37 --- /dev/null +++ b/webstore/_locales/fr/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- drôles d'œufs abîmés", "description": "fr" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern gestionnaire d'onglet et outil de sauvegarde" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Paramètres" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Ajouter/éditer étiquette" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Supprimer (fermer; ne pas sauver)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Editer texte" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Fermer et sauver" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Supprimer (fermer; ne pas sauver)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "Nom de la nouvelle fenêtre ?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note pour l'onglet \"$TITLE$\" ?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Supprimer la fenêtre \"$TITLE$\" ?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Supprimer l'onglet \"$TITLE$\" ?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Supprimer l'onglet \"$TITLE$\" et sa fenêtre ?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">O</span>ui" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "o" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>on" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">A</span>nnuler" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "a" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Ne plus demander (<abbr title=\"Vous pouvez changer ceci dans Paramètres | Comportement\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "Non enregistré" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Onglets enregistrés" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Onglets récupérés" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Récupéré)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Titre inconnu ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Onglet" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Exécuter Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Recharger" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "Info en ligne" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "Paramètres et aide hors ligne" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restaurer dernier effacement" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "Sauvegarder backup maintenant" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "Charger le contenu d'un backup" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "Trier" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Ouvrir les fenêtres en haut" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "Développer tout" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "Cacher tout" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Alterner affichage bordure supérieure" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Ajouter/éditer une note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Renommer" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Oublier mais ne pas fermer" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Ne pas sauver cette fenêtre quand elle est fermée" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Se souvenir" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Sauver cette fenêtre quand elle est fermée" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Fermer et se souvenir" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Supprimer" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "Je n'ai pas pu sauver l'arborescence: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "Je n'ai pas pu ouvrir la fenêtre: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "Je n'ai pas pu créer la fenêtre: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "Je n'ai pas pu charger $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "Je ne comprend pas le fichier $FILENAME$ comme étant un fichier de sauvegarde de TabFern. L'erreur d'analyse est: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Erreur inattendue pendant que j'essayais de charger le fichier. Le code d'erreur est: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/webstore/_locales/ru/messages.json b/webstore/_locales/ru/messages.json new file mode 100755 index 00000000..f1cf968a --- /dev/null +++ b/webstore/_locales/ru/messages.json @@ -0,0 +1,325 @@ +{ "encoding": { "message": "-*- coding: utf-8 -*- схема", "description": "ru" } + , "name_text": { "message": "-----------------------------------------" + ,"description": "Text for the Chrome Web Store listing and window titles" } + , "wsLongName": { + "message": "TabFern tab manager and backup tool" + ,"description":"The long name at the top of the Chrome Web Store window" + } + , "wsShortName": { + "message": "TabFern" + ,"description":"The short name, for the TabFern window title bar" + } + , "wsSettings": { + "message": "Settings" + ,"description":"The title for the Settings window" + } + + , "tooltip_text": { "message": "--------------------------------------------" + ,"description": "Text for tooltips, other than menu tooltips" } + , "ttEditTab": { + "message": "Add/edit label" + ,"description":"Tooltip for the pencil button on a tab's tree entry" + } + , "ttDeleteTab": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a tab's tree entry" + } + , "ttEditWin": { + "message": "Edit text" + ,"description":"Tooltip for the pencil button on a window's tree entry" + } + , "ttCloseWin": { + "message": "Close and save" + ,"description":"Tooltip for the close button on a window's tree entry" + } + , "ttDeletewin": { + "message": "Delete (close; don't save)" + ,"description":"Tooltip for the X button on a window's tree entry" + } + + , "dialog_text": { "message": "--------------------------------------------" + ,"description": "Text for dialog boxes" } + , "dlgpNewWindowName": { + "message": "New window name?" + ,"description":"Prompt for the user to enter a window name" + } + , "dlgpTabNote": { + "message": "Note for tab \"$TITLE$\"?" + ,"description":"Prompt for the user to enter a note for a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the Web page open in that tab)" + } + } + } + , "dlgpDeleteWindow": { + "message": "Delete window \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the window that will be deleted)" + } + } + } + , "dlgpDeleteTab": { + "message": "Delete tab \"$TITLE$\"?" + ,"description":"Prompt for whether the user wants to delete a tab" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + , "dlgpDeleteTabAndWindow": { + "message": "Delete tab \"$TITLE$\" and its window?" + ,"description":"Prompt for whether the user wants to delete the last tab in a window" + ,"placeholders":{ + "title": { + "content": "$1" + ,"example": "(this is the title of the tab that will be deleted)" + } + } + } + + , "dlgYesHTML": { + "message": "<span class=\"accel\">Y</span>es" + ,"description":"HTML code for a 'Yes' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgYesAccelerator": { + "message": "y" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgNoHTML": { + "message": "<span class=\"accel\">N</span>o" + ,"description":"HTML code for a 'No' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgNoAccelerator": { + "message": "n" + ,"description":"Accelerator (access key) for a 'No' dialog button." + } + , "dlgCancelHTML": { + "message": "<span class=\"accel\">C</span>ancel" + ,"description":"HTML code for a 'Cancel' dialog button. Includes a <span class=\"accel\" if there is an accelerator key." + } + , "dlgCancelAccelerator": { + "message": "c" + ,"description":"Accelerator (access key) for a 'Yes' dialog button." + } + , "dlgDoNotAskAgainHTML": { + "message": "Don't ask again (<abbr title=\"You can change this in Settings | Behaviour\">note</abbr>)" + ,"description":"HTML code for the 'Do not ask again' checkbox. The (<abbr>...</abbr>) part is an explanation that pops up when the user hovers the mouse over the word 'note'." + } + + , "label_text": { "message": "--------------------------------------------" + ,"description": "Text for tree labels" } + , "labelUnsaved": { + "message": "несохронённые вешалки" + ,"description":"The label in the tree for an unsaved window" + } + ,"labelSavedTabs": { + "message": "Сохронённые вешалки" + ,"description":"The label in the tree for a saved window without a user-provided name" + } + ,"labelRecoveredTabs": { + "message": "Recovered tabs" + ,"description":"The label in the tree for a window that was autosaved" + } + ,"labelRecoveredTabsPostfix": { + "message": " (Recovered)" + ,"description":"Added to the end of the label in the tree for a window that was autosaved" + } + ,"labelUnknownTitle": { + "message": "## Unknown title ##" + ,"description": "Label for a tab's tree node if we can't find a title for it" + } + ,"labelBlankTabTitle": { + "message": "Tab" + ,"description": "Label for a tab's tree node if we can't find a title for it on an update" + } + + , "menu_text": { "message": "--------------------------------------------" + ,"description": "Text for the hamburger and context menus" } + , "menuSplitTest": { + "message": "Split test" + ,"description":"The item in the hamburger (≡) menu for a split test." + } + , "menuJasmineTests": { + "message": "Run Jasmine tests" + ,"description":"The menu item to run tests using the 'Jasmine' program." + } + , "menuReload": { + "message": "Reload" + ,"description":"The menu item to reload the TabFern window" + } + , "menuOnlineInfo": { + "message": "онлайновая информация" + ,"description":"The menu item to open the blue and yellow 'About' Web page" + } + , "menuSettings": { + "message": "установки и автономная помощь" + ,"description":"The menu item to open the Settings page" + } + , "menuRestoreLastDeleted": { + "message": "Restore last deleted" + ,"description":"The menu item to restore the last-deleted window" + } + , "menuBackupNow": { + "message": "резервировать сейчас" + ,"description":"The menu item to save a copy of the tree to disk" + } + , "menuLoadBackupContents": { + "message": "загрузить содержания резерва" + ,"description":"The menu item to load tree items from disk" + } + , "menuSort": { + "message": "сортировка" + ,"description":"The text of the submenu with the sort options" + } + , "menuSortOpenToTop": { + "message": "Open windows to top" + ,"description":"The sort-submenu item to sort open windows to the top" + } + , "menuSortAZ": { + "message": "A-Z" + ,"description":"The sort-submenu item to sort in alphabetical order" + } + , "menuSortZA": { + "message": "Z-A" + ,"description":"The sort-submenu item to sort in reverse alphabetical order" + } + , "menuSort09": { + "message": "0-9" + ,"description":"The sort-submenu item to sort in numerical order" + } + , "menuSort90": { + "message": "9-0" + ,"description":"The sort-submenu item to sort in reverse numerical order" + } + , "menuExpandAll": { + "message": "увеличивать содержания" + ,"description":"The menu item to expand all trees" + } + , "menuCollapseAll": { + "message": "уменьшить содержания" + ,"description":"The menu item to collapse all trees" + } + , "menuToggleTopBorder": { + "message": "Toggle top border" + ,"description":"The context-menu item to toggle the top border on a tab's tree entry" + } + , "menuAddEditNote": { + "message": "Add/edit a note" + ,"description":"The context-menu item to add or edit a tab's note" + } + , "menuRename": { + "message": "Rename" + ,"description":"The context-menu item to rename a window's tree entry" + } + , "menuForget": { + "message": "Forget but don't close" + ,"description":"The context-menu item to mark a window as unsaved" + } + , "menuttForget": { + "message": "Do not save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as unsaved" + } + , "menuRemember": { + "message": "Remember" + ,"description":"The context-menu item to mark a window as saved" + } + , "menuttRemember": { + "message": "Save this window when it is closed" + ,"description":"The context-menu tooltip to mark a window as saved" + } + , "menuCloseAndRemember": { + "message": "Close and remember" + ,"description":"The context-menu item to mark a window as saved, then close it" + } + , "menuDelete": { + "message": "Delete" + ,"description":"The context-menu item to delete a window or tab's tree item" + } + + , "error_text": { "message": "--------------------------------------------" + ,"description": "Text for error messages" } + , "errCouldNotSave": { + "message": "I couldn't save the tree: $ERR$" + ,"description":"Error message from saveTree()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotOpenWindow": { + "message": "I couldn't open the window: $ERR$" + ,"description":"Error message from win_create_cbk()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotCreateWindow": { + "message": "I couldn't create the window: $ERR$" + ,"description":"Error message from treeOnSelect()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotLoadFile": { + "message": "I couldn't load $FILENAME$: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the error message from Chrome)" + } + } + } + , "errCouldNotParseFile": { + "message": "File $FILENAME$ is not something I can understand as a TabFern save file. Pars error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "filename": { + "content": "$1" + ,"example": "(this is the file the user wanted to load)" + } + , "err": { + "content": "$2" + ,"example": "(this is the exception from Chrome)" + } + } + } + , "errCouldNotRunImporter": { + "message": "Unexpected error while trying to load the file. Error code was: $ERR$" + ,"description":"Error message from hamRestoreFromBackup()" + ,"placeholders":{ + "err": { + "content": "$1" + ,"example": "(this is the exception from Chrome)" + } + } + } + + , "settings_text": { "message": "--------------------------------------------" + ,"description": "Text for the Settings page" } + + ,"IgnoreBelowHere___________________________________________________": { + "message":"ignore" + ,"description":"The lines below here do not need translation." + } + ,"modeline":{"description":"Keep the following on the last line of the file" + ,"message":" // vi: set ts=2 sts=2 sw=2 et ai fenc=utf8 ff=dos: //"}} diff --git a/webstore/assets/css/icons.css b/webstore/assets/css/icons.css index 390905a1..6c54a6aa 100755 --- a/webstore/assets/css/icons.css +++ b/webstore/assets/css/icons.css @@ -1,17 +1,17 @@ /* icons.css: Common icon definitions */ .jstree-themeicon-custom.tf-window.tfs-saved > a > i { - background-image: url(/assets/icons/folder-from-jstree-default-dark-32px.png); + background-image: url("/assets/icons/folder-from-jstree-default-dark-32px.png"); } /* For Chrome */ .jstree-themeicon-custom.visible-window-icon, .jstree-themeicon-custom.tf-window.tfs-open > a > i { - background-image: url(/assets/icons/monitor.png); /* or folder.png? */ + background-image: url("/assets/icons/monitor.png"); /* or folder.png? */ } .jstree-themeicon-custom.visible-saved-window-icon, .jstree-themeicon-custom.tf-window.tfs-open.tfs-saved > a > i { - background-image: url(/assets/icons/monitor_add.png); /* or folder.png? */ + background-image: url("/assets/icons/monitor_add.png"); /* or folder.png? */ } /* For Firefox */ @@ -28,36 +28,36 @@ /* Back to cross-browser */ .jstree-themeicon-custom.fff-page /*, .tf-tab > i*/ { - background-image: url(/assets/icons/page_white.png); + background-image: url("/assets/icons/page_white.png"); } .jstree-themeicon-custom.fff-monitor-add { - background-image: url(/assets/icons/monitor_add.png); + background-image: url("/assets/icons/monitor_add.png"); } .jstree-themeicon-custom.fff-monitor { - background-image: url(/assets/icons/monitor.png); + background-image: url("/assets/icons/monitor.png"); } .jstree-themeicon-custom.fff-page-white-with-red-banner { - background-image: url(/assets/icons/page_white_red_banner.png); + background-image: url("/assets/icons/page_white_red_banner.png"); } .jstree-themeicon-custom.fff-pencil, .vakata-context .fff-pencil { - background-image: url(/assets/icons/pencil.png); + background-image: url("/assets/icons/pencil.png"); } .jstree-themeicon-custom.fff-cross, .vakata-context .fff-cross { - background-image: url(/assets/icons/cross.png); + background-image: url("/assets/icons/cross.png"); } .jstree-themeicon-custom.fff-picture-delete, .vakata-context .fff-picture-delete { - background-image: url(/assets/icons/picture_delete.png); + background-image: url("/assets/icons/picture_delete.png"); } .jstree-themeicon-custom.fff-text-padding-top, .vakata-context .fff-text-padding-top { - background-image: url(/assets/icons/text_padding_top.png); + background-image: url("/assets/icons/text_padding_top.png"); } /* Class for icons with no content. Used in jstree.set_icon() when the @@ -69,17 +69,26 @@ } /* Icons for actions, which do use content rather than background-image. - * That way we do not have to set the size of the <i> manually. */ -.tf-action-button.fff-pencil { - content: url(/assets/icons/pencil.png); + * That way we do not have to set the size of the <i> manually. + * For Firefox compatibility, we use content in the ::after pseudo-elements, + * since Chrome supports content in regular elements but Firefox doesn't. */ + +.tf-action-button { + position: relative; + display: inline-block; + height: 16px; /* must match icon height */ +} + +.tf-action-button.fff-pencil::after { + content: url("/assets/icons/pencil.png"); } -.tf-action-button.fff-picture-delete { - content: url(/assets/icons/picture_delete.png); +.tf-action-button.fff-picture-delete::after { + content: url("/assets/icons/picture_delete.png"); } -.tf-action-button.fff-cross { - content: url(/assets/icons/cross.png); +.tf-action-button.fff-cross::after { + content: url("/assets/icons/cross.png"); } /* Background sizes in context menu are different. TODO fix this --- it is diff --git a/webstore/assets/css/spinjs.css b/webstore/assets/css/spinjs.css new file mode 100755 index 00000000..083d2761 --- /dev/null +++ b/webstore/assets/css/spinjs.css @@ -0,0 +1,26 @@ +@keyframes spinner-line-fade-more { + 0%, 100% { + opacity: 0; /* minimum opacity */ + } + 1% { + opacity: 1; + } +} + +@keyframes spinner-line-fade-quick { + 0%, 39%, 100% { + opacity: 0.25; /* minimum opacity */ + } + 40% { + opacity: 1; + } +} + +@keyframes spinner-line-fade-default { + 0%, 100% { + opacity: 0.22; /* minimum opacity */ + } + 1% { + opacity: 1; + } +} diff --git a/webstore/js/jstree-actions.js b/webstore/js/jstree-actions.js index 92a7dc85..8e4ff8f4 100644 --- a/webstore/js/jstree-actions.js +++ b/webstore/js/jstree-actions.js @@ -1,371 +1,377 @@ (function (factory) { - "use strict"; - if (typeof define === 'function' && define.amd) { - define('jstree-actions',['jquery', 'jstree'], factory); - } - else if(typeof module !== 'undefined' && module.exports) { - module.exports = factory(require('jquery'), require('jstree')); - } - else { - factory(jQuery, jQuery.jstree); - } + "use strict"; + if (typeof define === 'function' && define.amd) { + define('jstree-actions',['jquery', 'jstree'], factory); + } + else if(typeof module !== 'undefined' && module.exports) { + module.exports = factory(require('jquery'), require('jstree')); + } + else { + factory(jQuery, jQuery.jstree); + } }(function ($, _jstree_unused, undefined) { - "use strict"; - - if($.jstree.plugins.actions) { return; } - - /** - * stores all defaults for the actions plugin - * @name $.jstree.defaults.actions - * @plugin actions - */ - $.jstree.defaults.actions = { - /** - * How event propagation should be controlled after a click - * on an action button. - * - * - Set to `stop` to call `stopPropagation` after the callback - * - Set to `immediate` to call `stopImmediatePropagation` - * after the callback - * - Any other value (e.g., the default of `normal` will not - * change the propagation. - * - * @name $.jstree.defaults.actions.propagation - * @plugin actions - */ - propagation: 'normal' - }; - - $.jstree.plugins.actions = function (options, parent) { - - this._actions = {}; // indexed by node id - this._group_parms = {}; // The parameters of the group divs, indexed by node id. - - /** Make a group to hold grouped actions. Call this before calling add_action() - * with grouped: true. - * @param node_id <- the ID of the pertinent node - * @param opts <- a structure with option fields. - * @return the new group div's jquery object, or null - * - * possible opts are: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * class <- class(es) to apply to the new div - * text <- text to put in the new div - */ - this.make_group = function (node_id, opts) { - var after = (typeof opts.after == 'undefined') ? true : opts.after; - if (typeof opts.selector == 'undefined') return null; // TODO report error? - - // Note: when this is called, the DOM object may not yet exist. - - // Regularize and stash the parms - var parms = this._group_parms[node_id] = Object.assign({}, opts); - parms.class = parms.class || ''; - parms.text = parms.text || ''; - parms.selector = parms.selector || null; - // any falsy value => null, for regularity and to permit - // distinguishing undefined from null should you need to. - } - - /** Add a DOM element based on the selector, child, and after options. - * @param node_el {DOM Element} The root element of the node, generally <li>. - * Must have non-null `parentNode`. - * @param to_add_el {DOM Element} The element to add - * @param opts {object} Where to add the element. Has optional selector, - * optional child, and optional after, as described - * with reference to this._make_group(). - */ - this._add_dom_element = function(node_el, to_add_el, opts) { - var place; - if(opts.selector) { - place = node_el.querySelector(opts.selector); - } else { - place = node_el; - } - - if (opts.child) { - place.appendChild(to_add_el); - } else if (opts.after) { - place.parentNode.insertBefore(to_add_el, place.nextSibling); - } else { //before - place.parentNode.insertBefore(to_add_el, place); - } - }; - - /** Create the group div for a node. Returns the DOM object, or null. - * Uses the provided node_el because this.get_node(node_id, true) - * doesn't always succeed during redraw. - * @param node_id {string} The node - * @param node_el {DOM element} The current element for this node. - */ - this._create_group_for = function (node_id, node_el) { - if(!(node_id in this._group_parms)) return null; // TODO report error? - - var opts = this._group_parms[node_id]; - var group_el = document.createElement("div"); - group_el.className = opts.class; - group_el.textContent = opts.text; - - this._add_dom_element(node_el, group_el, opts); - - return group_el; - }; - - /** Add an action to a node or node(s). - * @param node_id Can be a single node id or an array of node ids. - * @param action An object representing the action that should be added to <node>. - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action will be added to all nodes. - * - * The action object can contain the following keys: - * id <- string An ID which identifies the action. The same ID can be shared across different nodes - * text <- string The action's text - * html <- string The action's html; used in preference to text if both are provided - * class <- string (a string containing all the classes you want to add to the action (space separated) - * event <- string The event on which the trigger will be called - * callback <- function that will be called when the action is clicked - * dataset <- optional object of key-value pairs that will be added as - * data-* attributes of the action's element. The values - * must be strings or support toString(). - * - * The action object can contain one of two types of location information: - * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. - * 2. If grouped is not true, the following can be used: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * - * NOTES: Please keep in mind that: - * - the id's are strictly compared (===) - * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar - */ - this.add_action = function (node_id, action) { - var self = this; - node_id = typeof node_id === 'object' ? node_id : [node_id]; - - for (var i = 0; i < node_id.length; i++) { - var _node_id = node_id[i]; - var actions = self._actions[_node_id] = self._actions[_node_id] || []; - - var should_redraw = false; - - if (!self._has_action(_node_id, action.id)) { - if(action.grouped && !(_node_id in this._group_parms)) { - continue; // TODO report error? - } - - var the_action = Object.assign({}, action); //our own copy - the_action.selector = the_action.selector || null; - actions.push(the_action); - should_redraw = true; - } - - if(should_redraw) this.redraw_node(_node_id); - - } - }; - - /** - * @param node_id Can be a single node id or an array of node ids - * @param action_id The ID of the action to be removed - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action_id will be removed from all nodes. - * - * The action_id is the unique identifier for each action. - * A special value "all" is allowed, in which case all the actions of node_id will be removed. - */ - this.remove_action = function (node_id, action_id) { - var self = this; - var node_ids = typeof node_id === 'object' ? node_id : - node_id === "all" ? Object.keys(this._actions).concat('all') : - [node_id]; - - for (var i = 0; i < node_ids.length; i++) { - node_id = node_ids[i]; - var actions = self._actions[node_id] || []; - var new_actions = []; - - for (var j = 0; j < actions.length; j++) { - var action = actions[j]; - if(action.id !== action_id && action_id !== "all") { - new_actions.push(action); - } - } - var ids = actions.map(function(x) { return x.id; }); - var new_ids = new_actions.map(function(x) { return x.id; }); - if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { - self._actions[node_id] = new_actions; - this.redraw_node(node_id); - } - } - }; - - /** - * Create the element for an action button. - * @param node_id {string} The node's ID - * @param action_id {string} The action's ID - */ - this._create_action = function (node_id, action_id) { - var self = this; - var action = this._get_action(node_id, action_id); - if (action === null) return null; - - var action_el = document.createElement("i"); - action_el.className = action.class; - if(action.html) { - action_el.innerHTML = action.html; - } else { - action_el.textContent = action.text || ''; - } - - // Set up element data-* values, if any - if( action_el.dataset && action.dataset && - (typeof action.dataset === 'object') && - (action.dataset !== null) - ) { - for(var key in action.dataset) { - action_el.dataset[key] = '' + action.dataset[key]; - } - } - - $(action_el).click(function(event) { - var node = self.get_node(action_el); - action.callback(node_id, node, - action_id, action_el, event); - - if(self.settings.actions.propagation==='stop') { - event.stopPropagation(); - } else if(self.settings.actions.propagation === 'immediate') { - event.stopImmediatePropagation(); - } - }); - - return { - "action": action, - "action_el": action_el - }; - }; - - /** - * Find an action of a node. - * @param node_id {string} The node's ID, or "all" - * @param action_id {string} The action's ID - * @return The action's data, or `null` if the action wasn't found. - */ - this._get_action = function (node_id, action_id) { - var actions = this._actions[node_id] || []; - var v = null; - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (action.id === action_id) { - //TODO: fill empty fields with default values? - v = action; - } - } - return v; - }; - - /** - * Add the given action to the DOM. - * @param node_el {DOM element} The node - * @param action The action's data record, including the - * already-created DOM element of the action button - */ - this._set_action = function (node_el, action) { - if (action === null) return; - - this._add_dom_element(node_el, action.action_el, action.action); - }; - - this._has_action = function (node_id, action_id) { - var found = false; - var actions = this._actions; - - if (actions.hasOwnProperty(node_id)) { - for (var i = 0; i < actions[node_id].length; i++) { - if (actions[node_id][i].id === action_id) found = true; - } - } - - if (this._actions.hasOwnProperty('all')) { - for (i = 0; i < actions['all'].length; i++) { - if (actions['all'][i].id === action_id) found = true; - } - } - - return found; - }; - - /** - * @param obj The node to redraw - */ - this.redraw_node = function (obj, deep, callback, force_draw) { - var self = this; - var node_id = typeof obj === "object" ? obj.id : obj; - var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); - if (node_el) { - //Check if we have any specific actions for this node - var actions = this._actions[node_id] || []; - var actions_all = this._actions["all"] || []; - - // Create the group if necessary - var group_el; - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } - - for (var i = 0; (!group_el) && (i < actions_all.length); i++) { - if(actions_all[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } - - // Populate the actions - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping action of node ' + node_id); - //console.log(actions[i]); - continue; - } - var _action = self._create_action(node_id, actions[i].id); - - if(actions[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } - - // Populate the "all" actions - for (i = 0; i < actions_all.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping "all" action at node ' + node_id); - //console.log(actions[i]); - continue; - } - _action = self._create_action("all", actions_all[i].id); - - if(actions_all[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } - - } //endif node_el - return node_el; - }; - - } + "use strict"; + + if($.jstree.plugins.actions) { return; } + + /** + * stores all defaults for the actions plugin + * @name $.jstree.defaults.actions + * @plugin actions + */ + $.jstree.defaults.actions = { + /** + * How event propagation should be controlled after a click + * on an action button. + * + * - Set to `stop` to call `stopPropagation` after the callback + * - Set to `immediate` to call `stopImmediatePropagation` + * after the callback + * - Any other value (e.g., the default of `normal` will not + * change the propagation. + * + * @name $.jstree.defaults.actions.propagation + * @plugin actions + */ + propagation: 'normal' + }; + + $.jstree.plugins.actions = function (options, parent) { + + this._actions = {}; // indexed by node id + this._group_parms = {}; // The parameters of the group divs, indexed by node id. + + /** Make a group to hold grouped actions. Call this before calling add_action() + * with grouped: true. + * @param node_id <- the ID of the pertinent node + * @param opts <- a structure with option fields. + * @return the new group div's jquery object, or null + * + * possible opts are: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * class <- class(es) to apply to the new div + * text <- text to put in the new div + */ + this.make_group = function (node_id, opts) { + var after = (typeof opts.after == 'undefined') ? true : opts.after; + if (typeof opts.selector == 'undefined') return null; // TODO report error? + + // Note: when this is called, the DOM object may not yet exist. + + // Regularize and stash the parms + var parms = this._group_parms[node_id] = Object.assign({}, opts); + parms.class = parms.class || ''; + parms.text = parms.text || ''; + parms.selector = parms.selector || null; + // any falsy value => null, for regularity and to permit + // distinguishing undefined from null should you need to. + } + + /** Add a DOM element based on the selector, child, and after options. + * @param node_el {DOM Element} The root element of the node, generally <li>. + * Must have non-null `parentNode`. + * @param to_add_el {DOM Element} The element to add + * @param opts {object} Where to add the element. Has optional selector, + * optional child, and optional after, as described + * with reference to this._make_group(). + */ + this._add_dom_element = function(node_el, to_add_el, opts) { + var place; + if(opts.selector) { + place = node_el.querySelector(opts.selector); + } else { + place = node_el; + } + + if (opts.child) { + place.appendChild(to_add_el); + } else if (opts.after) { + place.parentNode.insertBefore(to_add_el, place.nextSibling); + } else { //before + place.parentNode.insertBefore(to_add_el, place); + } + }; + + /** Create the group div for a node. Returns the DOM object, or null. + * Uses the provided node_el because this.get_node(node_id, true) + * doesn't always succeed during redraw. + * @param node_id {string} The node + * @param node_el {DOM element} The current element for this node. + */ + this._create_group_for = function (node_id, node_el) { + if(!(node_id in this._group_parms)) return null; // TODO report error? + + var opts = this._group_parms[node_id]; + var group_el = document.createElement("div"); + group_el.className = opts.class; + group_el.textContent = opts.text; + + this._add_dom_element(node_el, group_el, opts); + + return group_el; + }; + + /** Add an action to a node or node(s). + * @param node_id Can be a single node id or an array of node ids. + * @param action An object representing the action that should be added to <node>. + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action will be added to all nodes. + * + * The action object can contain the following keys: + * id <- string An ID which identifies the action. The same ID can be shared across different nodes + * text <- string The action's text + * html <- string The action's html; used in preference to text if both are provided + * class <- string (a string containing all the classes you want to add to the action (space separated) + * event <- string The event on which the trigger will be called + * callback <- function that will be called when the action is clicked + * dataset <- optional object of key-value pairs that will be added as + * data-* attributes of the action's element. The values + * must be strings or support toString(). + * + * The action object can contain one of two types of location information: + * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. + * 2. If grouped is not true, the following can be used: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * + * NOTES: Please keep in mind that: + * - the id's are strictly compared (===) + * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar + */ + this.add_action = function (node_id, action) { + var self = this; + node_id = typeof node_id === 'object' ? node_id : [node_id]; + + for (var i = 0; i < node_id.length; i++) { + var _node_id = node_id[i]; + var actions = self._actions[_node_id] = self._actions[_node_id] || []; + + var should_redraw = false; + + if (!self._has_action(_node_id, action.id)) { + if(action.grouped && !(_node_id in this._group_parms)) { + continue; // TODO report error? + } + + var the_action = Object.assign({}, action); //our own copy + the_action.selector = the_action.selector || null; + actions.push(the_action); + should_redraw = true; + } + + if(should_redraw) this.redraw_node(_node_id); + + } + }; + + /** + * @param node_id Can be a single node id or an array of node ids + * @param action_id The ID of the action to be removed + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action_id will be removed from all nodes. + * + * The action_id is the unique identifier for each action. + * A special value "all" is allowed, in which case all the actions of node_id will be removed. + */ + this.remove_action = function (node_id, action_id) { + var self = this; + var node_ids = typeof node_id === 'object' ? node_id : + node_id === "all" ? Object.keys(this._actions).concat('all') : + [node_id]; + + for (var i = 0; i < node_ids.length; i++) { + node_id = node_ids[i]; + var actions = self._actions[node_id] || []; + var new_actions = []; + + for (var j = 0; j < actions.length; j++) { + var action = actions[j]; + if(action.id !== action_id && action_id !== "all") { + new_actions.push(action); + } + } + var ids = actions.map(function(x) { return x.id; }); + var new_ids = new_actions.map(function(x) { return x.id; }); + if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { + self._actions[node_id] = new_actions; + this.redraw_node(node_id); + } + } + }; + + /** + * Create the element for an action button. + * @param node_id {string} The node's ID + * @param action_id {string} The action's ID + */ + this._create_action = function (node_id, action_id) { + var self = this; + var action = this._get_action(node_id, action_id); + if (action === null) return null; + + var action_el = document.createElement("i"); + action_el.className = action.class; + if(action.html) { + action_el.innerHTML = action.html; + } else { + action_el.textContent = action.text || ''; + } + + // Set up element data-* values, if any + if( action_el.dataset && action.dataset && + (typeof action.dataset === 'object') && + (action.dataset !== null) + ) { + for(var key in action.dataset) { + action_el.dataset[key] = '' + action.dataset[key]; + } + } + + // Title + if(action.title) { + action_el.title = action.title; + } + + $(action_el).click(function(event) { + var node = self.get_node(action_el); + action.callback(node_id, node, + action_id, action_el, event); + + if(self.settings.actions.propagation==='stop') { + event.stopPropagation(); + } else if(self.settings.actions.propagation === 'immediate') { + event.stopImmediatePropagation(); + } + }); + + return { + "action": action, + "action_el": action_el + }; + }; + + /** + * Find an action of a node. + * @param node_id {string} The node's ID, or "all" + * @param action_id {string} The action's ID + * @return The action's data, or `null` if the action wasn't found. + */ + this._get_action = function (node_id, action_id) { + var actions = this._actions[node_id] || []; + var v = null; + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; + if (action.id === action_id) { + //TODO: fill empty fields with default values? + v = action; + } + } + return v; + }; + + /** + * Add the given action to the DOM. + * @param node_el {DOM element} The node + * @param action The action's data record, including the + * already-created DOM element of the action button + */ + this._set_action = function (node_el, action) { + if (action === null) return; + + this._add_dom_element(node_el, action.action_el, action.action); + }; + + this._has_action = function (node_id, action_id) { + var found = false; + var actions = this._actions; + + if (actions.hasOwnProperty(node_id)) { + for (var i = 0; i < actions[node_id].length; i++) { + if (actions[node_id][i].id === action_id) found = true; + } + } + + if (this._actions.hasOwnProperty('all')) { + for (i = 0; i < actions['all'].length; i++) { + if (actions['all'][i].id === action_id) found = true; + } + } + + return found; + }; + + /** + * @param obj The node to redraw + */ + this.redraw_node = function (obj, deep, callback, force_draw) { + var self = this; + var node_id = typeof obj === "object" ? obj.id : obj; + var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); + if (node_el) { + //Check if we have any specific actions for this node + var actions = this._actions[node_id] || []; + var actions_all = this._actions["all"] || []; + + // Create the group if necessary + var group_el; + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } + + for (var i = 0; (!group_el) && (i < actions_all.length); i++) { + if(actions_all[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } + + // Populate the actions + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping action of node ' + node_id); + //console.log(actions[i]); + continue; + } + var _action = self._create_action(node_id, actions[i].id); + + if(actions[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } + + // Populate the "all" actions + for (i = 0; i < actions_all.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping "all" action at node ' + node_id); + //console.log(actions[i]); + continue; + } + _action = self._create_action("all", actions_all[i].id); + + if(actions_all[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } + + } //endif node_el + return node_el; + }; + + } })); +// vi: set ts=4 sts=4 sw=4 et ai: // diff --git a/webstore/js/multidex.js b/webstore/js/multidex.js index 268e44b7..d6c3e051 100644 --- a/webstore/js/multidex.js +++ b/webstore/js/multidex.js @@ -256,7 +256,7 @@ } let val = idx[key_val]; - if( (field_name===null) || + if( (field_name == null) || (typeof field_name === 'undefined') ) { return val; } else if(field_name in val) { diff --git a/webstore/js/spin-packed.js b/webstore/js/spin-packed.js new file mode 100644 index 00000000..8a5c2d05 --- /dev/null +++ b/webstore/js/spin-packed.js @@ -0,0 +1,295 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var spin_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); + +window.Spinner = spin_js__WEBPACK_IMPORTED_MODULE_0__["Spinner"]; + + +/***/ }), +/* 1 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Spinner", function() { return Spinner; }); +var __assign = (undefined && undefined.__assign) || Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; +}; +var defaults = { + lines: 12, + length: 7, + width: 5, + radius: 10, + scale: 1.0, + corners: 1, + color: '#000', + fadeColor: 'transparent', + animation: 'spinner-line-fade-default', + rotate: 0, + direction: 1, + speed: 1, + zIndex: 2e9, + className: 'spinner', + top: '50%', + left: '50%', + shadow: '0 0 1px transparent', + position: 'absolute', +}; +var Spinner = /** @class */ (function () { + function Spinner(opts) { + if (opts === void 0) { opts = {}; } + this.opts = __assign({}, defaults, opts); + } + /** + * Adds the spinner to the given target element. If this instance is already + * spinning, it is automatically removed from its previous target by calling + * stop() internally. + */ + Spinner.prototype.spin = function (target) { + this.stop(); + this.el = document.createElement('div'); + this.el.className = this.opts.className; + this.el.setAttribute('role', 'progressbar'); + css(this.el, { + position: this.opts.position, + width: 0, + zIndex: this.opts.zIndex, + left: this.opts.left, + top: this.opts.top, + transform: "scale(" + this.opts.scale + ")", + }); + if (target) { + target.insertBefore(this.el, target.firstChild || null); + } + drawLines(this.el, this.opts); + return this; + }; + /** + * Stops and removes the Spinner. + * Stopped spinners may be reused by calling spin() again. + */ + Spinner.prototype.stop = function () { + if (this.el) { + if (typeof requestAnimationFrame !== 'undefined') { + cancelAnimationFrame(this.animateId); + } + else { + clearTimeout(this.animateId); + } + if (this.el.parentNode) { + this.el.parentNode.removeChild(this.el); + } + this.el = undefined; + } + return this; + }; + return Spinner; +}()); + +/** + * Sets multiple style properties at once. + */ +function css(el, props) { + for (var prop in props) { + el.style[prop] = props[prop]; + } + return el; +} +/** + * Returns the line color from the given string or array. + */ +function getColor(color, idx) { + return typeof color == 'string' ? color : color[idx % color.length]; +} +/** + * Internal method that draws the individual lines. + */ +function drawLines(el, opts) { + var borderRadius = (Math.round(opts.corners * opts.width * 500) / 1000) + 'px'; + var shadow = 'none'; + if (opts.shadow === true) { + shadow = '0 2px 4px #000'; // default shadow + } + else if (typeof opts.shadow === 'string') { + shadow = opts.shadow; + } + var shadows = parseBoxShadow(shadow); + for (var i = 0; i < opts.lines; i++) { + var degrees = ~~(360 / opts.lines * i + opts.rotate); + var backgroundLine = css(document.createElement('div'), { + position: 'absolute', + top: -opts.width / 2 + "px", + width: (opts.length + opts.width) + 'px', + height: opts.width + 'px', + background: getColor(opts.fadeColor, i), + borderRadius: borderRadius, + transformOrigin: 'left', + transform: "rotate(" + degrees + "deg) translateX(" + opts.radius + "px)", + }); + var delay = i * opts.direction / opts.lines / opts.speed; + delay -= 1 / opts.speed; // so initial animation state will include trail + var line = css(document.createElement('div'), { + width: '100%', + height: '100%', + background: getColor(opts.color, i), + borderRadius: borderRadius, + boxShadow: normalizeShadow(shadows, degrees), + animation: 1 / opts.speed + "s linear " + delay + "s infinite " + opts.animation, + }); + backgroundLine.appendChild(line); + el.appendChild(backgroundLine); + } +} +function parseBoxShadow(boxShadow) { + var regex = /^\s*([a-zA-Z]+\s+)?(-?\d+(\.\d+)?)([a-zA-Z]*)\s+(-?\d+(\.\d+)?)([a-zA-Z]*)(.*)$/; + var shadows = []; + for (var _i = 0, _a = boxShadow.split(','); _i < _a.length; _i++) { + var shadow = _a[_i]; + var matches = shadow.match(regex); + if (matches === null) { + continue; // invalid syntax + } + var x = +matches[2]; + var y = +matches[5]; + var xUnits = matches[4]; + var yUnits = matches[7]; + if (x === 0 && !xUnits) { + xUnits = yUnits; + } + if (y === 0 && !yUnits) { + yUnits = xUnits; + } + if (xUnits !== yUnits) { + continue; // units must match to use as coordinates + } + shadows.push({ + prefix: matches[1] || '', + x: x, + y: y, + xUnits: xUnits, + yUnits: yUnits, + end: matches[8], + }); + } + return shadows; +} +/** + * Modify box-shadow x/y offsets to counteract rotation + */ +function normalizeShadow(shadows, degrees) { + var normalized = []; + for (var _i = 0, shadows_1 = shadows; _i < shadows_1.length; _i++) { + var shadow = shadows_1[_i]; + var xy = convertOffset(shadow.x, shadow.y, degrees); + normalized.push(shadow.prefix + xy[0] + shadow.xUnits + ' ' + xy[1] + shadow.yUnits + shadow.end); + } + return normalized.join(', '); +} +function convertOffset(x, y, degrees) { + var radians = degrees * Math.PI / 180; + var sin = Math.sin(radians); + var cos = Math.cos(radians); + return [ + Math.round((x * cos + y * sin) * 1000) / 1000, + Math.round((-x * sin + y * cos) * 1000) / 1000, + ]; +} + + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/webstore/manifest.json b/webstore/manifest.json index 7ea1213f..6d9bacdc 100755 --- a/webstore/manifest.json +++ b/webstore/manifest.json @@ -1,8 +1,8 @@ { - "name": "TabFern tab manager and backup tool", - "short_name": "TabFern", - "version": "0.1.16.1337", - "version_name": "0.1.16", + "name": "__MSG_wsLongName__", + "short_name": "__MSG_wsShortName__", + "version": "0.1.17.1337", + "version_name": "0.1.17", "offline_enabled": true, "manifest_version": 2, "minimum_chrome_version": "54", @@ -40,5 +40,10 @@ ], "web_accessible_resources": [ "assets/*" - ] + ], + "commands": { + "reveal-view": { + "description": "Open the TabFern window" + } + } } diff --git a/webstore/src/bg/background.js b/webstore/src/bg/background.js index d6242db0..1ef6e284 100755 --- a/webstore/src/bg/background.js +++ b/webstore/src/bg/background.js @@ -60,7 +60,7 @@ function moveTabFernViewToWindow(reference_cwin) // https://stackoverflow.com/users/930675/sean-bannister // When the icon is clicked in Chrome -chrome.browserAction.onClicked.addListener(function(tab) { +let onClickedListener = function(tab) { // If viewWindowID is undefined then there isn't a popup currently open. if (typeof viewWindowID === "undefined") { // Open the popup @@ -72,7 +72,7 @@ chrome.browserAction.onClicked.addListener(function(tab) { // Set a timer to bring the window to the front on another click // that follows fairly shortly. - { + if(tab) { let clickListener = function(tab) { if(viewWindowID && tab.windowId) { chrome.windows.get(tab.windowId, moveTabFernViewToWindow); @@ -89,7 +89,17 @@ chrome.browserAction.onClicked.addListener(function(tab) { chrome.browserAction.onClicked.addListener(clickListener); } -}); +} //onClickedListener() + +chrome.browserAction.onClicked.addListener(onClickedListener); + +let onCommandListener = function(cmd) { + console.log("Received command " + cmd); + if(cmd == 'reveal-view') { + onClickedListener(null); // null => no tab, so no summon + } +} //onCommandListener() +chrome.commands.onCommand.addListener(onCommandListener); // When a window is closed chrome.windows.onRemoved.addListener(function(windowId) { diff --git a/webstore/src/common/common.js b/webstore/src/common/common.js index a301ef75..9e6c98c8 100755 --- a/webstore/src/common/common.js +++ b/webstore/src/common/common.js @@ -11,7 +11,7 @@ console.log('TabFern common.js loading'); /// The TabFern extension friendly version number. Displayed in the /// title bar of the popup window, so lowercase (no shouting!). -const TABFERN_VERSION='0.1.16' //' alpha \u26a0' +const TABFERN_VERSION='0.1.17'; // When you change this, also update: // - manifest.json: both the version and version_name // - package.json @@ -39,47 +39,126 @@ const MSG_EDIT_TAB_NOTE = 'editTabNote'; ////////////////////////////////////////////////////////////////////////// // Names of settings, and their defaults // +/// An array to build the defaults in. Every property must have a default, +/// since the defaults array is also used to identify properties to be +/// saved/loaded. The JS types of the defaults must match the types +/// of the properties. +let _DEF = { __proto__: null }; + +/// An array of validators, used when loading settings. Each is a function +/// that returns a valid value for that setting, or `undefined` to use the +/// default. NOTE: returning "undefined" will trigger a warning on the console. +let _VAL = { __proto__: null }; +let _vbool = (v)=>{ return ((typeof v === 'boolean')?v:undefined)}; + // Booleans const CFG_ENB_CONTEXT_MENU = 'ContextMenu.Enabled'; +_DEF[CFG_ENB_CONTEXT_MENU] = true; +_VAL[CFG_ENB_CONTEXT_MENU] = _vbool; + const CFG_RESTORE_ON_LAST_DELETED = 'open-tree-on-restore-last-deleted'; +_DEF[CFG_RESTORE_ON_LAST_DELETED] = false; +_VAL[CFG_RESTORE_ON_LAST_DELETED] = _vbool; + const CFG_JUMP_WITH_SORT_OPEN_TOP = 'jump-to-top-when-sort-open-to-top'; +_DEF[CFG_JUMP_WITH_SORT_OPEN_TOP] = true; +_VAL[CFG_JUMP_WITH_SORT_OPEN_TOP] = _vbool; + const CFG_COLLAPSE_ON_STARTUP = 'collapse-trees-on-startup'; +_DEF[CFG_COLLAPSE_ON_STARTUP] = true; +_VAL[CFG_COLLAPSE_ON_STARTUP] = _vbool; + const CFG_OPEN_TOP_ON_STARTUP = 'open-to-top-on-startup'; +_DEF[CFG_OPEN_TOP_ON_STARTUP] = false; +_VAL[CFG_OPEN_TOP_ON_STARTUP] = _vbool; + const CFG_HIDE_HORIZONTAL_SCROLLBARS = 'hide-horizontal-scrollbars'; +_DEF[CFG_HIDE_HORIZONTAL_SCROLLBARS] = true; +_VAL[CFG_HIDE_HORIZONTAL_SCROLLBARS] = _vbool; + const CFG_SKINNY_SCROLLBARS = 'skinny-scrollbars'; +_DEF[CFG_SKINNY_SCROLLBARS] = false; +_VAL[CFG_SKINNY_SCROLLBARS] = _vbool; + const CFG_NEW_WINS_AT_TOP = 'open-new-windows-at-top'; +_DEF[CFG_NEW_WINS_AT_TOP] = true; +_VAL[CFG_NEW_WINS_AT_TOP] = _vbool; + const CFG_SHOW_TREE_LINES = 'show-tree-lines'; +_DEF[CFG_SHOW_TREE_LINES] = false; +_VAL[CFG_SHOW_TREE_LINES] = _vbool; + const CFG_CONFIRM_DEL_OF_SAVED = 'confirm-del-of-saved-wins'; +_DEF[CFG_CONFIRM_DEL_OF_SAVED] = true; +_VAL[CFG_CONFIRM_DEL_OF_SAVED] = _vbool; + const CFG_CONFIRM_DEL_OF_UNSAVED = 'confirm-del-of-unsaved-wins'; +_DEF[CFG_CONFIRM_DEL_OF_UNSAVED] = false; +_VAL[CFG_CONFIRM_DEL_OF_UNSAVED] = _vbool; + const CFG_CONFIRM_DEL_OF_SAVED_TABS = 'confirm-del-of-saved-tabs'; +_DEF[CFG_CONFIRM_DEL_OF_SAVED_TABS] = true; +_VAL[CFG_CONFIRM_DEL_OF_SAVED_TABS] = _vbool; + const CFG_CONFIRM_DEL_OF_UNSAVED_TABS = 'confirm-del-of-unsaved-tabs'; +_DEF[CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = false; +_VAL[CFG_CONFIRM_DEL_OF_UNSAVED_TABS] = _vbool; + +const CFG_URL_IN_TOOLTIP = 'tooltip-has-url'; +_DEF[CFG_URL_IN_TOOLTIP] = false; +_VAL[CFG_URL_IN_TOOLTIP] = _vbool; -// Strings +const CFG_TITLE_IN_TOOLTIP = 'tooltip-has-title'; +_DEF[CFG_TITLE_IN_TOOLTIP] = false; +_VAL[CFG_TITLE_IN_TOOLTIP] = _vbool; + +const CFG_PRUNE_NEW_WINDOWS = 'prune-new-windows'; +_DEF[CFG_PRUNE_NEW_WINDOWS] = false; +_VAL[CFG_PRUNE_NEW_WINDOWS] = _vbool; + +/// Not actually a setting, but an indicator that we loaded settings OK. +/// Used by src/settings/main.js. +const SETTINGS_LOADED_OK = '__settings_loaded_OK'; +_DEF[SETTINGS_LOADED_OK] = false; +_VAL[SETTINGS_LOADED_OK] = ()=>{return undefined;} + + + +// Not yet implemented - pending #35. Whether to open closed tabs when +// you click on the tree item for a partially-open window. +//const CFG_OPEN_REST_ON_CLICK = 'open-rest-on-win-click', +// CFG_OROC_DO = true, +// CFG_OROC_DO_NOT = false; +//_DEF[CFG_OPEN_REST_ON_CLICK] = CFG_OROC_DO_NOT; + +// Strings, including limited-choice controls such as radio buttons and dropdowns. const CFGS_BACKGROUND = 'window-background'; -const CFGS_SCROLLBAR_COLOR = 'skinny-scrollbar-color'; +_DEF[CFGS_BACKGROUND] = ''; +_VAL[CFGS_BACKGROUND] = (v)=>{ + if(!v) return ''; + if(Validation.isValidColor(v)) return v; + if(Validation.isValidURL(v, + ['file', 'https', 'data', 'chrome-extension'])) return v; + return undefined; +}; -// Other const CFGS_THEME_NAME = 'theme-name'; +_DEF[CFGS_THEME_NAME] = 'default-dark'; +_VAL[CFGS_THEME_NAME] = (v)=>{ + return (( v === 'default-dark' || v === 'default') ? v : undefined); +}; -const CFG_DEFAULTS = { - __proto__: null, - [CFG_ENB_CONTEXT_MENU]: true, - [CFG_RESTORE_ON_LAST_DELETED]: false, - [CFG_JUMP_WITH_SORT_OPEN_TOP]: true, - [CFG_COLLAPSE_ON_STARTUP]: true, - [CFG_OPEN_TOP_ON_STARTUP]: false, - [CFG_HIDE_HORIZONTAL_SCROLLBARS]: true, - [CFG_SKINNY_SCROLLBARS]: false, - [CFG_NEW_WINS_AT_TOP]: true, - [CFG_SHOW_TREE_LINES]: false, - [CFG_CONFIRM_DEL_OF_SAVED]: true, - [CFG_CONFIRM_DEL_OF_UNSAVED]: false, - [CFG_CONFIRM_DEL_OF_SAVED_TABS]: true, - [CFG_CONFIRM_DEL_OF_UNSAVED_TABS]: false, - [CFGS_THEME_NAME]: 'default-dark', - [CFGS_SCROLLBAR_COLOR]: '', // none by default +const CFGS_SCROLLBAR_COLOR = 'skinny-scrollbar-color'; +_DEF[CFGS_SCROLLBAR_COLOR] = ''; +_VAL[CFGS_SCROLLBAR_COLOR] = (v)=>{ + if(!v) return ''; + return ((Validation.isValidColor(v)) ? v : undefined); }; +/// The default values for the configuration settings. +const CFG_DEFAULTS = Object.seal(_DEF); +const CFG_VALIDATORS = Object.seal(_VAL); + ////////////////////////////////////////////////////////////////////////// // Test for Firefox // // Not sure if I need this, but I'm playing it safe for now. Firefox returns @@ -88,6 +167,8 @@ const CFG_DEFAULTS = { // Chrome code. Hopefully in the future I can test for null/undefined // in either browser, and get rid of this block. +BROWSER_TYPE=null; // unknown + (function(win){ let isLastError_chrome = ()=>{return (typeof(chrome.runtime.lastError) !== 'undefined');}; @@ -100,6 +181,7 @@ const CFG_DEFAULTS = { (info)=>{ // fullfillment if(info.name === 'Firefox') { win.isLastError = isLastError_firefox; + BROWSER_TYPE = 'ff'; } else { win.isLastError = isLastError_chrome; } @@ -110,6 +192,7 @@ const CFG_DEFAULTS = { } ); } else { // Chrome + BROWSER_TYPE = 'chrome'; win.isLastError = isLastError_chrome; } })(window); @@ -119,6 +202,12 @@ const CFG_DEFAULTS = { const SETTING_PREFIX = 'store.settings.'; +/// Get the raw value of a setting. Returns null if the key doesn't exist. +function getRawSetting(setting_name) +{ + return localStorage.getItem(SETTING_PREFIX + setting_name); +} //getSetting + /// Get the string value of a setting, if it is a string. function getStringSetting(setting_name, default_value = undefined) { @@ -177,10 +266,11 @@ function haveSetting(setting_name) /// @param setting_value {mixed} The value, which must be JSON.stringifiable. function setSetting(setting_name, setting_value) { + // TODO handle exceptions in some reasonable way. localStorage.setItem( SETTING_PREFIX + setting_name, JSON.stringify(setting_value) - ); + ); // JSON stringify so we can store more than just strings. } //setSetting /// Set a setting only if it's not already there. Parameters are as @@ -259,6 +349,9 @@ function loadCSS(doc, url, before) { ////////////////////////////////////////////////////////////////////////// // Miscellaneous functions // +/// Shortcut for i18n. Call _T("name") to pull the localized "name". +var _T = chrome.i18n.getMessage; + /// Ignore a Chrome callback error, and suppress Chrome's /// `runtime.lastError` diagnostic. Use this as a Chrome callback. function ignore_chrome_error() { void chrome.runtime.lastError; } diff --git a/webstore/src/common/validation.js b/webstore/src/common/validation.js index 800ec845..1f3fb53e 100755 --- a/webstore/src/common/validation.js +++ b/webstore/src/common/validation.js @@ -1,4 +1,5 @@ -// validation.js: Data-validation routines +/// validation.js: Data-validation routines. +/// NOTE: does NOT use common.js routines, so that common.js can use it. (function (root, factory) { let imports=[]; @@ -44,7 +45,25 @@ const RE = /^(rgb|hsl)a?\((-?[\d\.]+%?(deg|rad|grad|turn)?[,\s]+){2,3}[\s\/]*[\d\.]+%?\)$/i; return RE.test(color); } - } //isValidColor + }; //isValidColor + + /// Validate a URL. + /// @param test_url The URL to test + /// @param allowed_schemes {Optional array} If provided, only those schemes + /// (no colons) are allowed. + module.isValidURL = function(test_url, allowed_schemes) { + try { + let url = new URL(String(test_url)); + + if(Array.isArray(allowed_schemes)) { + let scheme = url.protocol.replace(/:$/,''); + if(allowed_schemes.indexOf(scheme) === -1) return false; + } + + return true; + } catch(e) { } // nop + return false; + }; //isValidURL return module; })); diff --git a/webstore/src/settings/index.html b/webstore/src/settings/index.html index 37f9e698..78796d2b 100755 --- a/webstore/src/settings/index.html +++ b/webstore/src/settings/index.html @@ -18,11 +18,17 @@ <link rel="stylesheet" href="custom.css" media="screen"> <link rel="stylesheet" href="/assets/css/icons.css" media="screen"> <link rel="stylesheet" href="/assets/css/spectrum.css" media="screen"> + <link rel="stylesheet" href="/assets/css/spinjs.css" media="screen"> <!-- JavaScripts --> + <script src="../common/validation.js"></script> <script src="../common/common.js"></script> + <script src="/js/loglevel.js"></script> + <script src="/js/spin-packed.js"></script> <script src="/js/tinycolor.js"></script> + <script src="/js/import-file.js"></script> + <script src="/js/export-file.js"></script> <script src="lib/mootools-core.js"></script> <!-- now $ is mootools --> <script src="lib/store.js"></script> @@ -40,7 +46,7 @@ <script src="/js/jquery-noconflict.js"></script> <!-- now $ is back to mootools - use jQuery() instead of $ --> - <script src="settings.js"></script> + <script src="main.js"></script> </head> <body class="no-select"> <div id="sidebar" class="fancy"> diff --git a/webstore/src/settings/main.js b/webstore/src/settings/main.js new file mode 100755 index 00000000..6cb993db --- /dev/null +++ b/webstore/src/settings/main.js @@ -0,0 +1,302 @@ +/// An object to hold the settings for later programmatic access +let settingsobj; + +/// jQuery alias, since $ is mootools +let $$ = jQuery; + +// Color picker //////////////////////////////////////////////////// {{{1 + +/// Create the color picker for the scrollbar color. +let createPicker = function createPicker() { + let picker = $$('#scrollbar-color-picker-label'); + + let orig_color = getStringSetting(CFGS_SCROLLBAR_COLOR); + if(!Validation.isValidColor(orig_color)) { + orig_color = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; + } + + // Replace the manifest entry with the color picker + $$(picker).spectrum({ + showInput: true, + allowEmpty:true, + showInitial: true, + preferredFormat: 'hex', + color: orig_color, + }); + + // Add the text that would otherwise have gone in the manifest + let newlabel = $$('<span>').text(i18n.get( + 'Skinny-scrollbar color ("X" for the default): ')) + .addClass('setting label'); + $$(picker).before(newlabel); + + // Handle updates + $$(picker).on('change.spectrum', (e, newcolor)=>{ + let colorstring; + if(!newcolor || !newcolor.toString) { + console.log('New color: default'); + colorstring = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; + } else { + console.log({'New color': newcolor.toString()}); + colorstring = String(newcolor.toString()); + } + + if(!colorstring || Validation.isValidColor(colorstring)) { + setSetting(CFGS_SCROLLBAR_COLOR, colorstring); + } else { + console.log('Invalid color'); + $$(picker).spectrum('set',orig_color); + } + }); +}; //createPicker + +// }}}1 +// Export/Import Settings ////////////////////////////////////////// {{{1 + +/// Pack the settings into an object to export. +/// TODO automate keeping this in sync with common.js. +function saveSettingsToObject() +{ + let retval = { __proto__: null }; + for(let key in CFG_DEFAULTS) { + retval[key] = getRawSetting(key); + } + return Object.seal(retval); +} //saveSettingsToObject + +/// Export the settings +function exportSettings(evt_unused) +{ + let date_tag = new Date().toISOString().replace(/:/g,'.'); + // DOS filenames can't include colons. + // TODO use local time - maybe + // https://www.npmjs.com/package/dateformat ? + let filename = 'TabFern settings backup ' + date_tag + '.tabfern_settings'; + + let saved_info = saveSettingsToObject(); + Fileops.Export(document, JSON.stringify(saved_info), filename); +} //exportSettings() + +/// Assign settings from an object we have loaded. +/// TODO automate keeping this in sync with common.js. +function loadSettingsFromObject(obj) { + let ok = true; + let errmsgs = ''; + function stash(m) { errmsgs += `<li>${m}</li>`; } + + log.info({'Loading settings from':obj}); + + for(let key in CFG_DEFAULTS) { + if(!obj[key]) { + log.info(`Setting ${key} not found`); + continue; // not an error + } + + // Get the value + let val; + try { + val = JSON.parse(String(obj[key])); + } catch(e) { + let m = `Non-JSON value for ${key} - skipping`; + log.warn(m); + stash(m); + ok = false; + continue; + } + + // Confirm its type + if(typeof val !== typeof CFG_DEFAULTS[key]) { + let m = `Setting ${key}: value is a ${typeof val} but should be `+ + `a ${typeof CFG_DEFAULTS[key]} - skipping`; + log.warn(m); + stash(m); + ok = false; + continue; + } + + // Run value-specific checks, e.g., for well-formedness. + if(CFG_VALIDATORS[key]) { + let val_output = CFG_VALIDATORS[key](val); + if(val_output === undefined) { + let m = `Setting ${key}: Value ${val} failed validation`; + log.warn(m); + //stash(m); // Not user-facing + val = CFG_DEFAULTS[key]; + } else { + val = val_output; + } + } + + // Set the value + if(typeof val === 'boolean' || typeof val === 'string') { + // We already checked that val is of the correct type above, + // so we can go ahead and set it. + setSetting(key, val); + } else { // This shouldn't happen, so it's a log.error if it does. + let m = `Unexpected type ${typeof val} for ${key} - skipping`; + log.error(m); + stash(m); + ok = false; + continue; + } + + } //foreach key + + return {ok, errmsgs}; +} //loadSettingsFromObject + +/// Import the settings +function importSettings(evt_unused) +{ + function processFile(text, filename) { + let spinner; + try { + spinner = new Spinner().spin( + $$('#import-settings').parent()[0] + ); + let parsed = JSON.parse(text); + let {ok, errmsgs} = loadSettingsFromObject(parsed); + if(!ok) { + let elem = $$('<div>').html( + '<p>I encountered error(s) while loading the file ' + + `'${filename}':</p><ul>${errmsgs}</ul>`); + $$('#import-settings').after(elem); + + } else { // success + // Let ourselves know, after reload, that it worked + setSetting(SETTINGS_LOADED_OK, true); + + // refresh all the controls by reloading + window.location.reload(true); + } + + } catch(e) { + window.alert("File " + filename + ' is not something I can '+ + 'understand as a TabFern settings file. Parse error code was: ' + + e); + } + if(spinner) spinner.stop(); + } //processFile() + + setSetting(SETTINGS_LOADED_OK, false); + let importer = Fileops.Importer(document, '.tabfern_settings'); + importer.getFileAsString(processFile); +} //importSettings() + +// }}}1 +// Main //////////////////////////////////////////////////////////// {{{1 + +function main() +{ + // Option 1: Use the manifest: + new FancySettings.initWithManifest(function (settings) { + $$('#settings-label').text(_T('wsSettings')); + + settingsobj = settings; + //settings.manifest.myButton.addEvent("action", function () { + // alert("You clicked me!"); + //}); + + // ---------------------------- + // Finish creating the page + createPicker(); // Skinny-scrollbar color picker + + // ---------------------------- + // Hook up events + $$('#import-settings').on('click', importSettings); + $$('#export-settings').on('click', exportSettings); + + if(getBoolSetting(SETTINGS_LOADED_OK)) { + let elem = $$('<div>').text("Settings loaded"); + $$('#import-settings').after(elem); + setSetting(SETTINGS_LOADED_OK, false); + } + // ---------------------------- + // open tab specified in a query parm, if known. + // See https://stackoverflow.com/a/12151322/2877364 + // Use location.hash instead of location.search since Chrome doesn't + // seem to navigate to chrome-extension://...&... . + let searchParams = new URLSearchParams(window.location.hash.slice(1)); + if(searchParams.has('open')) { + let whichtab = -1; // If other than -1, select that tab + + let openval = String(searchParams.get('open')); // Do we need the explicit String()? + let tabNames = Object.keys(settingsobj.tabs); + // These come out in definition order, as far as I know + + // Check for a tab number + let tabnum = Number(openval); + if(!isNaN(tabnum) && (tabnum|0)>=0 && (tabnum|0)<tabNames.length) { + whichtab = (tabnum|0); + } + + // Check for "last" as a special value + if(whichtab === -1 && openval.toLowerCase()==='last') { + whichtab = tabNames.length-1; + } + + // Jump to that tab. + if(whichtab !== -1) { + settingsobj.tabs[tabNames[whichtab]].bundle.activate(); + } + + } //endif &open=... parameter specified + }); + + // Option 2: Do everything manually: + /* + var settings = new FancySettings("My Extension", "icon.png"); + + var username = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "username", + "type": "text", + "label": i18n.get("username"), + "text": i18n.get("x-characters") + }); + + var password = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "password", + "type": "text", + "label": i18n.get("password"), + "text": i18n.get("x-characters-pw"), + "masked": true + }); + + var myDescription = settings.create({ + "tab": i18n.get("information"), + "group": i18n.get("login"), + "name": "myDescription", + "type": "description", + "text": i18n.get("description") + }); + + var myButton = settings.create({ + "tab": "Information", + "group": "Logout", + "name": "myButton", + "type": "button", + "label": "Disconnect:", + "text": "Logout" + }); + + // ... + + myButton.addEvent("action", function () { + alert("You clicked me!"); + }); + + settings.align([ + username, + password + ]); + */ +} //main() + +window.addEvent("domready", main); +// }}}1 + +// vi: set ts=4 sts=4 sw=4 et ai foldmethod=marker: // diff --git a/webstore/src/settings/manifest.js b/webstore/src/settings/manifest.js index a0aa1f3a..f3c07646 100755 --- a/webstore/src/settings/manifest.js +++ b/webstore/src/settings/manifest.js @@ -5,7 +5,7 @@ (function(root){ // Shortcuts for frequently-used items function icon(cls) { return `<i class="${cls}"></i>`; } - function issue(num) { return `(<a href="https://github.com/cxw42/TabFern/issues/${num|0}">#${num|0}</a>)`; } + function issue(num, noparen) { return `${noparen ? '' : '('}<a href="https://github.com/cxw42/TabFern/issues/${num|0}">#${num|0}</a>${noparen ? '' : ')'}`; } function brplain(text){return `<br/><span class="plain">${text}</span>`;} let ham = icon('fa fa-bars'); @@ -13,10 +13,12 @@ let settings = `${ham} ${gt} Settings ${gt}`; let refresh_message = " (refresh the TabFern window after you change this to make the change take effect)" -// Settings {{{2 + // Settings {{{2 // Assign the settings root.manifest = { - "name": "Settings - ver. "+TABFERN_VERSION+' - TabFern', + "name": + `${_T('wsSettings')} - ${_T('wsShortName')} (v${TABFERN_VERSION})`, + "icon": "/assets/fern16.png", "settings": [ @@ -35,6 +37,22 @@ ' of the list, information about recent feature additions'+ ' or changes.</p>' }, + { + "tab": i18n.get("Welcome / Help"), + "group": i18n.get("Import/Export"), + "name": "export-settings", + "id": "export-settings", + "type": "button", + "text": "Save settings to a file" + }, + { + "tab": i18n.get("Welcome / Help"), + "group": i18n.get("Import/Export"), + "name": "import-settings", + "id": "import-settings", + "type": "button", + "text": "Load settings from a file" + }, // Behaviour. Yeah, there's a "u" in there! { @@ -107,6 +125,7 @@ "type": "checkbox", "label": i18n.get('Prompt for confirmation before deleting <b>unsaved</b> windows'), }, + { "tab": i18n.get("Behaviour"), "group": i18n.get("Deleting tabs"), @@ -122,7 +141,28 @@ "label": i18n.get('Prompt for confirmation before deleting <b>tabs</b> in <b>unsaved</b> windows'), }, + { + "tab": i18n.get("Behaviour"), + "group": i18n.get("When Chrome..."), + "name": CFG_PRUNE_NEW_WINDOWS, + "type": "checkbox", + "label": i18n.get("Adds extra tabs to a new window I've just opened, get rid of them!"), + }, + { + "tab": i18n.get("Behaviour"), + "group": i18n.get("When Chrome..."), + 'group_html':true, + "type": "description", + "text": i18n.get("\u26a0 use this option only if you need it — it may not behave exactly as you would expect."), + }, + // Appearance + { + "tab": i18n.get("Appearance"), + "group": '', + "type": "description", + "text": i18n.get("Refresh the TabFern window to apply changes to these options. To refresh, click TabFern's title bar and hit F5."), + }, { "tab": i18n.get("Appearance"), "group": i18n.get("Scrollbars"), @@ -147,20 +187,15 @@ // placeholder - settings.js adds the actual control // after this. }, - { - "tab": i18n.get("Appearance"), - "group": i18n.get("Scrollbars"), - "type": "description", - "text": i18n.get("Refresh the TabFern window to apply changes to these options."), - }, - // Maybe add some theming options here? { "tab": i18n.get("Appearance"), "group": i18n.get("Tree"), "name": CFG_SHOW_TREE_LINES, "type": "checkbox", - "label": i18n.get('Show connecting lines between nodes' + refresh_message), + "label": i18n.get('Show connecting lines between nodes'), }, + + // Theming options { "tab": i18n.get("Appearance"), "group": i18n.get("Theme"), @@ -184,8 +219,7 @@ "group": i18n.get("Theme"), "type": "description", "text": -`${refresh_message}<br/> -The background can be specified as a CSS color name, rgb(r,g,b), hsl(h,s,l), +`The background can be specified as a CSS color name, rgb(r,g,b), hsl(h,s,l), or a URL (data, https, chrome-extension, or file). To use images from your local disk (file): <ul> @@ -195,6 +229,21 @@ bar (it will start with "file://")</li> <li>Paste the "file://..." URL into the box above.</li>` }, + { + "tab": i18n.get("Appearance"), + "group": i18n.get("Tooltips"), + "name": CFG_URL_IN_TOOLTIP, + "type": "checkbox", + "label": i18n.get("Show URL in each item's tooltip"), + }, + { + "tab": i18n.get("Appearance"), + "group": i18n.get("Tooltips"), + "name": CFG_TITLE_IN_TOOLTIP, + "type": "checkbox", + "label": i18n.get("Show page title in each item's tooltip"), + }, + // Features { "tab": "Features", @@ -257,8 +306,81 @@ bar (it will start with "file://")</li> "text": "X or + or don't show when these are dynamic" }, -// }}}2 + // }}}2 + // Credits {{{2 + { + "tab": i18n.get("Credits and thanks"), + "group": 'TabFern', + 'group_html':true, + "type": "description", + "text": +`TabFern is by Chris White (<a href="https://devwrench.wordpress.com">blog</a>, +<a href="https://github.com/cxw42/">GitHub</a>). I greatly appreciate +the following contributors! If I have accidentally missed you, please let +me know so I can correct the omission. All names below are in alphabetical +order.` + }, + + { + "tab": i18n.get("Credits and thanks"), + "group": i18n.get("Programming"), + 'group_html':true, + "type": "description", + "text": +`<ul><li><a href="https://github.com/r4j4h/">Jasmine Hegman</a></li></ul>` + }, + { + "tab": i18n.get("Credits and thanks"), + "group": i18n.get("Translation"), + 'group_html':true, + "type": "description", + "text": +`<ul> +<li><a href="https://github.com/Procyon-b/">Procyon-b</a> (French)</li> +<li><a href="https://github.com/rwexmd/">rwexmd</a> (Russian)</li> +</ul>` + }, + + // }}}2 // Changelog {{{1 + { + "tab": i18n.get("What's new?"), + "group": `Version 0.1.17${brplain('2018-09-02')}`, + 'group_html':true, + "type": "description", + "text": +`<ul> +<li class="gold-star">TabFern now has <b>500</b> users!!! +<b>Thank you</b> for using TabFern and helping the project!</li> +<li>The first version of TabFern was released one year ago today. +\u{1F382}</li> +<li>Partial translations into French and Russian. My thanks to the +translators! Please see the new Credits tab. ${issue(135)}</li> +<li>Tooltips on the action buttons. ${issue(117)}</li> +<li>${settings} Appearance ${gt} Tooltips now has options to show the +URL and title of each item in a tooltip on that item. This way you can +see URLs in the tree, and you can see long titles without having +to scroll. ${issue(104)}</li> +<li>You can now save and load settings from the ${i18n.get("Welcome / Help")} +tab. ${issue(92)}</li> +<li>When you open the TF window, it moves back to its last position +more quickly. ${issue(134)}</li> +<li><a href="https://vivaldi.com/">Vivaldi</a> support: +Basic TabFern functionality is now also available on the Vivaldi +browser. Vivaldi uses the +Chrome Web Store just like Chrome itself does, so installation in Vivaldi +is the same as installation in Chrome. ${issue(123)}</li> +<li><a href="https://getfirefox.com">Firefox Quantum</a> support: +If you're a developer, you can now load TabFern as a temporary add-on +and get at least the basic save/load/tab-switching. (Note that you can't +manipulate <tt>about:debugging</tt> because it's special to Firefox.) +${issue(100)}</li> +<li>There is now a "Reload" option on the menu +(${ham} ${gt} ${icon('fa fa-refresh')} Reload), in case TabFern +and Chrome get out of sync. ${issue(127)}</li> +<li>Bugfixes: ${issue(128,true)}, ${issue(129,true)}</li> +</ul>` + }, { "tab": i18n.get("What's new?"), "group": `Version 0.1.16${brplain('2018-03-08')}`, diff --git a/webstore/src/settings/settings.js b/webstore/src/settings/settings.js deleted file mode 100755 index a96c425c..00000000 --- a/webstore/src/settings/settings.js +++ /dev/null @@ -1,135 +0,0 @@ -/// An object to hold the settings for later programmatic access -let settingsobj; - -function createPicker($$) -{ - let picker = $$('#scrollbar-color-picker-label'); - - // Replace the manifest entry with the color picker - $$(picker).spectrum({ - showInput: true, - allowEmpty:true, - showInitial: true, - color: getStringSetting(CFGS_SCROLLBAR_COLOR), - }); - - // Add the text that would otherwise have gone in the manifest - let newlabel = $$('<span>').text(i18n.get( - 'Skinny-scrollbar color ("X" for the default): ')) - .addClass('setting label'); - $$(picker).before(newlabel); - - // Handle updates - $$(picker).on('change.spectrum', (e, newcolor)=>{ - let colorstring; - if(!newcolor || !newcolor.toString) { - console.log('New color: default'); - colorstring = CFG_DEFAULTS[CFGS_SCROLLBAR_COLOR]; - } else { - console.log({'New color': newcolor.toString()}); - colorstring = String(newcolor.toString()); - } - - setSetting(CFGS_SCROLLBAR_COLOR, colorstring); - }); -} //createPicker - -window.addEvent("domready", function () { - let $$ = jQuery; // since $ is mootools - - // Option 1: Use the manifest: - new FancySettings.initWithManifest(function (settings) { - settingsobj = settings; - //settings.manifest.myButton.addEvent("action", function () { - // alert("You clicked me!"); - //}); - - // ---------------------------- - // Create the color-picker for the skinny scrollbars - createPicker($$); - - // ---------------------------- - // open tab specified in a query parm, if known. - // See https://stackoverflow.com/a/12151322/2877364 - // Use location.hash instead of location.search since Chrome doesn't - // seem to navigate to chrome-extension://...&... . - let searchParams = new URLSearchParams(window.location.hash.slice(1)); - if(searchParams.has('open')) { - let whichtab = -1; // If other than -1, select that tab - - let openval = String(searchParams.get('open')); // Do we need the explicit String()? - let tabNames = Object.keys(settingsobj.tabs); - // These come out in definition order, as far as I know - - // Check for a tab number - let tabnum = Number(openval); - if(!isNaN(tabnum) && (tabnum|0)>=0 && (tabnum|0)<tabNames.length) { - whichtab = (tabnum|0); - } - - // Check for "last" as a special value - if(whichtab === -1 && openval.toLowerCase()==='last') { - whichtab = tabNames.length-1; - } - - // Jump to that tab. - if(whichtab !== -1) { - settingsobj.tabs[tabNames[whichtab]].bundle.activate(); - } - - } //endif &open=... parameter specified - }); - - // Option 2: Do everything manually: - /* - var settings = new FancySettings("My Extension", "icon.png"); - - var username = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "username", - "type": "text", - "label": i18n.get("username"), - "text": i18n.get("x-characters") - }); - - var password = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "password", - "type": "text", - "label": i18n.get("password"), - "text": i18n.get("x-characters-pw"), - "masked": true - }); - - var myDescription = settings.create({ - "tab": i18n.get("information"), - "group": i18n.get("login"), - "name": "myDescription", - "type": "description", - "text": i18n.get("description") - }); - - var myButton = settings.create({ - "tab": "Information", - "group": "Logout", - "name": "myButton", - "type": "button", - "label": "Disconnect:", - "text": "Logout" - }); - - // ... - - myButton.addEvent("action", function () { - alert("You clicked me!"); - }); - - settings.align([ - username, - password - ]); - */ -}); -// vi: set ts=4 sts=4 sw=4 et ai: // diff --git a/webstore/src/view/bundle_tree.js b/webstore/src/view/bundle_tree.js index 92237267..aa932014 100644 --- a/webstore/src/view/bundle_tree.js +++ b/webstore/src/view/bundle_tree.js @@ -8463,376 +8463,382 @@ // tabfern/js/jstree-actions.js (function (factory) { - "use strict"; - if (typeof define === 'function' && define.amd) { - define('jstree-actions',['jquery', 'jstree'], factory); - } - else if(typeof module !== 'undefined' && module.exports) { - module.exports = factory(require('jquery'), require('jstree')); - } - else { - factory(jQuery, jQuery.jstree); - } + "use strict"; + if (typeof define === 'function' && define.amd) { + define('jstree-actions',['jquery', 'jstree'], factory); + } + else if(typeof module !== 'undefined' && module.exports) { + module.exports = factory(require('jquery'), require('jstree')); + } + else { + factory(jQuery, jQuery.jstree); + } }(function ($, _jstree_unused, undefined) { - "use strict"; - - if($.jstree.plugins.actions) { return; } - - /** - * stores all defaults for the actions plugin - * @name $.jstree.defaults.actions - * @plugin actions - */ - $.jstree.defaults.actions = { - /** - * How event propagation should be controlled after a click - * on an action button. - * - * - Set to `stop` to call `stopPropagation` after the callback - * - Set to `immediate` to call `stopImmediatePropagation` - * after the callback - * - Any other value (e.g., the default of `normal` will not - * change the propagation. - * - * @name $.jstree.defaults.actions.propagation - * @plugin actions - */ - propagation: 'normal' - }; + "use strict"; - $.jstree.plugins.actions = function (options, parent) { + if($.jstree.plugins.actions) { return; } - this._actions = {}; // indexed by node id - this._group_parms = {}; // The parameters of the group divs, indexed by node id. + /** + * stores all defaults for the actions plugin + * @name $.jstree.defaults.actions + * @plugin actions + */ + $.jstree.defaults.actions = { + /** + * How event propagation should be controlled after a click + * on an action button. + * + * - Set to `stop` to call `stopPropagation` after the callback + * - Set to `immediate` to call `stopImmediatePropagation` + * after the callback + * - Any other value (e.g., the default of `normal` will not + * change the propagation. + * + * @name $.jstree.defaults.actions.propagation + * @plugin actions + */ + propagation: 'normal' + }; - /** Make a group to hold grouped actions. Call this before calling add_action() - * with grouped: true. - * @param node_id <- the ID of the pertinent node - * @param opts <- a structure with option fields. - * @return the new group div's jquery object, or null - * - * possible opts are: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * class <- class(es) to apply to the new div - * text <- text to put in the new div - */ - this.make_group = function (node_id, opts) { - var after = (typeof opts.after == 'undefined') ? true : opts.after; - if (typeof opts.selector == 'undefined') return null; // TODO report error? - - // Note: when this is called, the DOM object may not yet exist. - - // Regularize and stash the parms - var parms = this._group_parms[node_id] = Object.assign({}, opts); - parms.class = parms.class || ''; - parms.text = parms.text || ''; - parms.selector = parms.selector || null; - // any falsy value => null, for regularity and to permit - // distinguishing undefined from null should you need to. - } + $.jstree.plugins.actions = function (options, parent) { + + this._actions = {}; // indexed by node id + this._group_parms = {}; // The parameters of the group divs, indexed by node id. + + /** Make a group to hold grouped actions. Call this before calling add_action() + * with grouped: true. + * @param node_id <- the ID of the pertinent node + * @param opts <- a structure with option fields. + * @return the new group div's jquery object, or null + * + * possible opts are: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * class <- class(es) to apply to the new div + * text <- text to put in the new div + */ + this.make_group = function (node_id, opts) { + var after = (typeof opts.after == 'undefined') ? true : opts.after; + if (typeof opts.selector == 'undefined') return null; // TODO report error? + + // Note: when this is called, the DOM object may not yet exist. + + // Regularize and stash the parms + var parms = this._group_parms[node_id] = Object.assign({}, opts); + parms.class = parms.class || ''; + parms.text = parms.text || ''; + parms.selector = parms.selector || null; + // any falsy value => null, for regularity and to permit + // distinguishing undefined from null should you need to. + } - /** Add a DOM element based on the selector, child, and after options. - * @param node_el {DOM Element} The root element of the node, generally <li>. - * Must have non-null `parentNode`. - * @param to_add_el {DOM Element} The element to add - * @param opts {object} Where to add the element. Has optional selector, - * optional child, and optional after, as described - * with reference to this._make_group(). - */ - this._add_dom_element = function(node_el, to_add_el, opts) { - var place; - if(opts.selector) { - place = node_el.querySelector(opts.selector); - } else { - place = node_el; - } + /** Add a DOM element based on the selector, child, and after options. + * @param node_el {DOM Element} The root element of the node, generally <li>. + * Must have non-null `parentNode`. + * @param to_add_el {DOM Element} The element to add + * @param opts {object} Where to add the element. Has optional selector, + * optional child, and optional after, as described + * with reference to this._make_group(). + */ + this._add_dom_element = function(node_el, to_add_el, opts) { + var place; + if(opts.selector) { + place = node_el.querySelector(opts.selector); + } else { + place = node_el; + } - if (opts.child) { - place.appendChild(to_add_el); - } else if (opts.after) { - place.parentNode.insertBefore(to_add_el, place.nextSibling); - } else { //before - place.parentNode.insertBefore(to_add_el, place); - } - }; + if (opts.child) { + place.appendChild(to_add_el); + } else if (opts.after) { + place.parentNode.insertBefore(to_add_el, place.nextSibling); + } else { //before + place.parentNode.insertBefore(to_add_el, place); + } + }; - /** Create the group div for a node. Returns the DOM object, or null. - * Uses the provided node_el because this.get_node(node_id, true) - * doesn't always succeed during redraw. - * @param node_id {string} The node - * @param node_el {DOM element} The current element for this node. - */ - this._create_group_for = function (node_id, node_el) { - if(!(node_id in this._group_parms)) return null; // TODO report error? + /** Create the group div for a node. Returns the DOM object, or null. + * Uses the provided node_el because this.get_node(node_id, true) + * doesn't always succeed during redraw. + * @param node_id {string} The node + * @param node_el {DOM element} The current element for this node. + */ + this._create_group_for = function (node_id, node_el) { + if(!(node_id in this._group_parms)) return null; // TODO report error? - var opts = this._group_parms[node_id]; - var group_el = document.createElement("div"); - group_el.className = opts.class; - group_el.textContent = opts.text; + var opts = this._group_parms[node_id]; + var group_el = document.createElement("div"); + group_el.className = opts.class; + group_el.textContent = opts.text; - this._add_dom_element(node_el, group_el, opts); + this._add_dom_element(node_el, group_el, opts); - return group_el; - }; + return group_el; + }; - /** Add an action to a node or node(s). - * @param node_id Can be a single node id or an array of node ids. - * @param action An object representing the action that should be added to <node>. - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action will be added to all nodes. - * - * The action object can contain the following keys: - * id <- string An ID which identifies the action. The same ID can be shared across different nodes - * text <- string The action's text - * html <- string The action's html; used in preference to text if both are provided - * class <- string (a string containing all the classes you want to add to the action (space separated) - * event <- string The event on which the trigger will be called - * callback <- function that will be called when the action is clicked - * dataset <- optional object of key-value pairs that will be added as - * data-* attributes of the action's element. The values - * must be strings or support toString(). - * - * The action object can contain one of two types of location information: - * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. - * 2. If grouped is not true, the following can be used: - * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. - * If selector is missing or falsy, the <li> of the item itself is used. - * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> - * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. - * - * NOTES: Please keep in mind that: - * - the id's are strictly compared (===) - * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar - */ - this.add_action = function (node_id, action) { - var self = this; - node_id = typeof node_id === 'object' ? node_id : [node_id]; + /** Add an action to a node or node(s). + * @param node_id Can be a single node id or an array of node ids. + * @param action An object representing the action that should be added to <node>. + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action will be added to all nodes. + * + * The action object can contain the following keys: + * id <- string An ID which identifies the action. The same ID can be shared across different nodes + * text <- string The action's text + * html <- string The action's html; used in preference to text if both are provided + * class <- string (a string containing all the classes you want to add to the action (space separated) + * event <- string The event on which the trigger will be called + * callback <- function that will be called when the action is clicked + * dataset <- optional object of key-value pairs that will be added as + * data-* attributes of the action's element. The values + * must be strings or support toString(). + * + * The action object can contain one of two types of location information: + * 1. grouped <- (default false) if true, put this action in a div. Call make_group() first to set up this div. Actions are appended as children of the div. + * 2. If grouped is not true, the following can be used: + * selector <- a selector that would specify where to insert the action. Note that this is a plain JavaScript selector and not a jQuery one. + * If selector is missing or falsy, the <li> of the item itself is used. + * child <- (bool) optional - if true, insert the action as a child of the element matching <selector> + * after <- (bool) insert the action after (true, default) or before (false) the element matching the <selector> key. Ignored if <child> is true. + * + * NOTES: Please keep in mind that: + * - the id's are strictly compared (===) + * - the selector has access to all children on nodes with leafs/children, so most probably you'd want to use :first or similar + */ + this.add_action = function (node_id, action) { + var self = this; + node_id = typeof node_id === 'object' ? node_id : [node_id]; - for (var i = 0; i < node_id.length; i++) { - var _node_id = node_id[i]; - var actions = self._actions[_node_id] = self._actions[_node_id] || []; + for (var i = 0; i < node_id.length; i++) { + var _node_id = node_id[i]; + var actions = self._actions[_node_id] = self._actions[_node_id] || []; - var should_redraw = false; + var should_redraw = false; - if (!self._has_action(_node_id, action.id)) { - if(action.grouped && !(_node_id in this._group_parms)) { - continue; // TODO report error? - } + if (!self._has_action(_node_id, action.id)) { + if(action.grouped && !(_node_id in this._group_parms)) { + continue; // TODO report error? + } - var the_action = Object.assign({}, action); //our own copy - the_action.selector = the_action.selector || null; - actions.push(the_action); - should_redraw = true; - } + var the_action = Object.assign({}, action); //our own copy + the_action.selector = the_action.selector || null; + actions.push(the_action); + should_redraw = true; + } - if(should_redraw) this.redraw_node(_node_id); + if(should_redraw) this.redraw_node(_node_id); - } - }; + } + }; - /** - * @param node_id Can be a single node id or an array of node ids - * @param action_id The ID of the action to be removed - * - * The <node id> is the "id" key of each element of the "core.data" array. - * A special value "all" is allowed, in which case the action_id will be removed from all nodes. - * - * The action_id is the unique identifier for each action. - * A special value "all" is allowed, in which case all the actions of node_id will be removed. - */ - this.remove_action = function (node_id, action_id) { - var self = this; - var node_ids = typeof node_id === 'object' ? node_id : - node_id === "all" ? Object.keys(this._actions).concat('all') : - [node_id]; - - for (var i = 0; i < node_ids.length; i++) { - node_id = node_ids[i]; - var actions = self._actions[node_id] || []; - var new_actions = []; - - for (var j = 0; j < actions.length; j++) { - var action = actions[j]; - if(action.id !== action_id && action_id !== "all") { - new_actions.push(action); - } - } - var ids = actions.map(function(x) { return x.id; }); - var new_ids = new_actions.map(function(x) { return x.id; }); - if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { - self._actions[node_id] = new_actions; - this.redraw_node(node_id); - } - } - }; + /** + * @param node_id Can be a single node id or an array of node ids + * @param action_id The ID of the action to be removed + * + * The <node id> is the "id" key of each element of the "core.data" array. + * A special value "all" is allowed, in which case the action_id will be removed from all nodes. + * + * The action_id is the unique identifier for each action. + * A special value "all" is allowed, in which case all the actions of node_id will be removed. + */ + this.remove_action = function (node_id, action_id) { + var self = this; + var node_ids = typeof node_id === 'object' ? node_id : + node_id === "all" ? Object.keys(this._actions).concat('all') : + [node_id]; + + for (var i = 0; i < node_ids.length; i++) { + node_id = node_ids[i]; + var actions = self._actions[node_id] || []; + var new_actions = []; + + for (var j = 0; j < actions.length; j++) { + var action = actions[j]; + if(action.id !== action_id && action_id !== "all") { + new_actions.push(action); + } + } + var ids = actions.map(function(x) { return x.id; }); + var new_ids = new_actions.map(function(x) { return x.id; }); + if (ids.length != new_ids.length || ids.filter(function(n) { return new_ids.indexOf(n) === -1; }).length) { + self._actions[node_id] = new_actions; + this.redraw_node(node_id); + } + } + }; - /** - * Create the element for an action button. - * @param node_id {string} The node's ID - * @param action_id {string} The action's ID - */ - this._create_action = function (node_id, action_id) { - var self = this; - var action = this._get_action(node_id, action_id); - if (action === null) return null; + /** + * Create the element for an action button. + * @param node_id {string} The node's ID + * @param action_id {string} The action's ID + */ + this._create_action = function (node_id, action_id) { + var self = this; + var action = this._get_action(node_id, action_id); + if (action === null) return null; + + var action_el = document.createElement("i"); + action_el.className = action.class; + if(action.html) { + action_el.innerHTML = action.html; + } else { + action_el.textContent = action.text || ''; + } - var action_el = document.createElement("i"); - action_el.className = action.class; - if(action.html) { - action_el.innerHTML = action.html; - } else { - action_el.textContent = action.text || ''; - } + // Set up element data-* values, if any + if( action_el.dataset && action.dataset && + (typeof action.dataset === 'object') && + (action.dataset !== null) + ) { + for(var key in action.dataset) { + action_el.dataset[key] = '' + action.dataset[key]; + } + } - // Set up element data-* values, if any - if( action_el.dataset && action.dataset && - (typeof action.dataset === 'object') && - (action.dataset !== null) - ) { - for(var key in action.dataset) { - action_el.dataset[key] = '' + action.dataset[key]; - } - } + // Title + if(action.title) { + action_el.title = action.title; + } - $(action_el).click(function(event) { - var node = self.get_node(action_el); - action.callback(node_id, node, - action_id, action_el, event); + $(action_el).click(function(event) { + var node = self.get_node(action_el); + action.callback(node_id, node, + action_id, action_el, event); - if(self.settings.actions.propagation==='stop') { - event.stopPropagation(); - } else if(self.settings.actions.propagation === 'immediate') { - event.stopImmediatePropagation(); - } - }); + if(self.settings.actions.propagation==='stop') { + event.stopPropagation(); + } else if(self.settings.actions.propagation === 'immediate') { + event.stopImmediatePropagation(); + } + }); - return { - "action": action, - "action_el": action_el - }; - }; + return { + "action": action, + "action_el": action_el + }; + }; - /** - * Find an action of a node. - * @param node_id {string} The node's ID, or "all" - * @param action_id {string} The action's ID - * @return The action's data, or `null` if the action wasn't found. - */ - this._get_action = function (node_id, action_id) { - var actions = this._actions[node_id] || []; - var v = null; - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (action.id === action_id) { - //TODO: fill empty fields with default values? - v = action; - } - } - return v; - }; + /** + * Find an action of a node. + * @param node_id {string} The node's ID, or "all" + * @param action_id {string} The action's ID + * @return The action's data, or `null` if the action wasn't found. + */ + this._get_action = function (node_id, action_id) { + var actions = this._actions[node_id] || []; + var v = null; + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; + if (action.id === action_id) { + //TODO: fill empty fields with default values? + v = action; + } + } + return v; + }; - /** - * Add the given action to the DOM. - * @param node_el {DOM element} The node - * @param action The action's data record, including the - * already-created DOM element of the action button - */ - this._set_action = function (node_el, action) { - if (action === null) return; + /** + * Add the given action to the DOM. + * @param node_el {DOM element} The node + * @param action The action's data record, including the + * already-created DOM element of the action button + */ + this._set_action = function (node_el, action) { + if (action === null) return; - this._add_dom_element(node_el, action.action_el, action.action); - }; + this._add_dom_element(node_el, action.action_el, action.action); + }; - this._has_action = function (node_id, action_id) { - var found = false; - var actions = this._actions; + this._has_action = function (node_id, action_id) { + var found = false; + var actions = this._actions; - if (actions.hasOwnProperty(node_id)) { - for (var i = 0; i < actions[node_id].length; i++) { - if (actions[node_id][i].id === action_id) found = true; - } - } + if (actions.hasOwnProperty(node_id)) { + for (var i = 0; i < actions[node_id].length; i++) { + if (actions[node_id][i].id === action_id) found = true; + } + } - if (this._actions.hasOwnProperty('all')) { - for (i = 0; i < actions['all'].length; i++) { - if (actions['all'][i].id === action_id) found = true; - } - } + if (this._actions.hasOwnProperty('all')) { + for (i = 0; i < actions['all'].length; i++) { + if (actions['all'][i].id === action_id) found = true; + } + } - return found; - }; + return found; + }; - /** - * @param obj The node to redraw - */ - this.redraw_node = function (obj, deep, callback, force_draw) { - var self = this; - var node_id = typeof obj === "object" ? obj.id : obj; - var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); - if (node_el) { - //Check if we have any specific actions for this node - var actions = this._actions[node_id] || []; - var actions_all = this._actions["all"] || []; - - // Create the group if necessary - var group_el; - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } + /** + * @param obj The node to redraw + */ + this.redraw_node = function (obj, deep, callback, force_draw) { + var self = this; + var node_id = typeof obj === "object" ? obj.id : obj; + var node_el = parent.redraw_node.call(this, obj, deep, callback, force_draw); + if (node_el) { + //Check if we have any specific actions for this node + var actions = this._actions[node_id] || []; + var actions_all = this._actions["all"] || []; + + // Create the group if necessary + var group_el; + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } - for (var i = 0; (!group_el) && (i < actions_all.length); i++) { - if(actions_all[i].grouped) { - group_el = this._create_group_for(node_id, node_el); - break; - } - } + for (var i = 0; (!group_el) && (i < actions_all.length); i++) { + if(actions_all[i].grouped) { + group_el = this._create_group_for(node_id, node_el); + break; + } + } - // Populate the actions - for (var i = 0; i < actions.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping action of node ' + node_id); - //console.log(actions[i]); - continue; - } - var _action = self._create_action(node_id, actions[i].id); + // Populate the actions + for (var i = 0; i < actions.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping action of node ' + node_id); + //console.log(actions[i]); + continue; + } + var _action = self._create_action(node_id, actions[i].id); - if(actions[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } + if(actions[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } - // Populate the "all" actions - for (i = 0; i < actions_all.length; i++) { - if(actions[i].grouped && !group_el) { - //console.log('** Skipping "all" action at node ' + node_id); - //console.log(actions[i]); - continue; - } - _action = self._create_action("all", actions_all[i].id); + // Populate the "all" actions + for (i = 0; i < actions_all.length; i++) { + if(actions[i].grouped && !group_el) { + //console.log('** Skipping "all" action at node ' + node_id); + //console.log(actions[i]); + continue; + } + _action = self._create_action("all", actions_all[i].id); - if(actions_all[i].grouped) { - group_el.appendChild(_action.action_el); - } else { // not grouped - self._set_action(node_el, _action); - } - } + if(actions_all[i].grouped) { + group_el.appendChild(_action.action_el); + } else { // not grouped + self._set_action(node_el, _action); + } + } - } //endif node_el - return node_el; - }; + } //endif node_el + return node_el; + }; - } + } })); +// vi: set ts=4 sts=4 sw=4 et ai: // // Begin bundled file /////////////////////////////////////////////////// // tabfern/js/jstree-flagnode.js @@ -10039,7 +10045,7 @@ } let val = idx[key_val]; - if( (field_name===null) || + if( (field_name == null) || (typeof field_name === 'undefined') ) { return val; } else if(field_name in val) { diff --git a/webstore/src/view/const.js b/webstore/src/view/const.js index 56c3b79a..3b5fd0e3 100755 --- a/webstore/src/view/const.js +++ b/webstore/src/view/const.js @@ -53,6 +53,12 @@ INIT_TIME_ALLOWED_MS: 3000, // After this time, if init isn't done, // display an error message. INIT_MSG_SEL: 'div#init-incomplete', // Selector for that message + INIT_PROGRESS_SEL: 'div#init-progress', // Selector for the x/y progress indicator + + /// How long to wait after a winOnCreated or tabOnCreated before + /// checking whether pruning is required. This is to give merge + /// detection priority over pruning. + WIN_PRUNE_TIMER_MS: 200, ACTION_GROUP_WIN_CLASS: 'tf-action-group', // Class on action-group div ACTION_BUTTON_WIN_CLASS: 'tf-action-button', // Class on action buttons (<i>) @@ -99,28 +105,68 @@ /// Open a new window with a given URL. Also remove the default /// tab that appears because we are letting the window open at the /// default size. Yes, this is a bit like a FOUC, but oh well. - module.openWindowForURL = function(url) { + /// @return An ASQ instance + module.openWindowForURL = function(desired_url) { let win_id; // TODO is there a better way to pass data down // the sequence? - let tab0_id; ///< The tab automatically created with the window +// let tab0_id; ///< The tab automatically created with the window + let seq = ASQH.NowCC((cc)=>{ chrome.windows.create(cc); }) .then(function open_tab(done, new_win){ win_id = new_win.id; - tab0_id = new_win.tabs[0].id; - chrome.tabs.create({windowId: win_id, url: url}, ASQH.CC(done)); + // Chrome automatically creates a tab in a window, but + // Vivaldi does not. Handle either. +// if(new_win.tabs && new_win.tabs.length) { +// tab0_id = new_win.tabs[0].id; +// } + chrome.tabs.create({windowId: win_id, url: desired_url}, + ASQH.CC(done)); + }) + .then(function get_all_tabs(done){ + chrome.windows.get(win_id, {populate: true}, ASQH.CC(done)); }) - .then(function remove_old_tab(done){ - chrome.tabs.remove(tab0_id, ASQH.CC(done)); + .then(function remove_all_other_tabs(done, cwin){ + if(!cwin) { + done.fail("Could not query tabs"); + return; + } + + let gates = []; + + for(let tab of cwin.tabs) { + if(tab.url != desired_url) { + gates.push((done_gate)=>{ + log.info({'Pruning undesired tab': tab}); + chrome.tabs.remove(tab.id, ASQH.CC(done_gate)); + }); + } + } //foreach tab + +// if(tab0_id) { +// chrome.tabs.remove(tab0_id, ASQH.CC(done)); +// } else { +// done(); +// } + if(gates.length === 0) { + done(); + return; + } else { + let gate_seq = ASQ(); + gate_seq.all.apply(gate_seq, gates); + gate_seq.pipe(done); + } }) - .or(function(err){log_orig.error({'Load error':err, url});}) + .or(function(err){log_orig.error({'Load error':err, desired_url});}) ; // To start the sequence paused, use `let seq = ASQ().duplicate()` above // instead of just ASQ(). Then, to fire it off, `seq.unpause();`. + return seq; + } //openWindowForURL return Object.freeze(module); // all fields constant diff --git a/webstore/src/view/item_details.js b/webstore/src/view/item_details.js index bd09bfd1..76eb37c3 100755 --- a/webstore/src/view/item_details.js +++ b/webstore/src/view/item_details.js @@ -35,8 +35,10 @@ let module = {}; /// Map between open-tab IDs and node IDs. - /// Design decision: no fields named "parent" so I can distinguish - /// jstree node records from multidex values. + /// Design decisions: + /// - No fields named `parent` so I can distinguish jstree node + /// records from multidex values. + /// - All types of records have `raw_title` and `isOpen` fields. module.tabs = multidex( K.IT_TAB, //type [ //keys @@ -47,6 +49,7 @@ 'win_id', // from Chrome 'index', // in the current window 'tab', // the actual Tab record from Chrome + // TODO remove this --- tab_id should be enough 'being_opened', // true if we are manually opening the Chrome tab 'raw_url', // the tab's URL 'raw_title', // the tab's title. null => default. @@ -77,11 +80,13 @@ ], [ //other data 'win', // the actual Window record from chrome + // TODO? remove this --- win_id should be enough 'raw_title', // the window's title (e.g., "Window") 'isOpen', // whether the window is open or not 'keep', // whether the window should be saved or not //'raw_bullet', // User-provided text (brief). null => none // Not currently used. + 'prune_data', // {timer_id,cwin} of a setTimeout used for pruning ]); /// Find a node's value in the model, regardless of type. diff --git a/webstore/src/view/item_tree.js b/webstore/src/view/item_tree.js index d4a32eb9..d5da5421 100755 --- a/webstore/src/view/item_tree.js +++ b/webstore/src/view/item_tree.js @@ -194,6 +194,7 @@ jstreeTypes[K.IT_WIN] = { li_attr: { 'class': WIN_CLASS }, + //a_attr: { 'title': 'Unsaved window' }, //maybe? //icon: 'clear-icon', // We will overlay the actual icon in the CSS }; diff --git a/webstore/src/view/main.js b/webstore/src/view/main.js index 24dce99a..a07404a3 100755 --- a/webstore/src/view/main.js +++ b/webstore/src/view/main.js @@ -64,7 +64,7 @@ function initMain() W = window.frames[0]; // Thanks to https://stackoverflow.com/a/13913943/2877364 by // https://stackoverflow.com/users/1105384/shank - document.title = `TabFern (v${TABFERN_VERSION})`; + document.title = `${_T('wsShortName')} (v${TABFERN_VERSION})`; } //initMain ////////////////////////////////////////////////////////////////////////// diff --git a/webstore/src/view/model.js b/webstore/src/view/model.js index 09df4166..b512556d 100755 --- a/webstore/src/view/model.js +++ b/webstore/src/view/model.js @@ -63,6 +63,8 @@ /// Otherwise, all types will be checked. /// @return {Object} {val, node_id}. `val` is falsy if the /// given vorny was not found. + /// If #item_type was specified and the given item wasn't + /// of that type, also returns falsy. module.vn_by_vorny = function(val_or_nodey, item_type) { if(!val_or_nodey) return module.VN_NONE; @@ -92,8 +94,11 @@ return module.VN_NONE; } + if(item_type && (val.ty !== item_type)) { + return module.VN_NONE; + } return {val, node_id}; - } //vn_by_vorny + }; //vn_by_vorny() /// Determine whether a model has given subtype(s). /// @param vorny {mixed} The item @@ -110,62 +115,71 @@ if(!T.treeobj.has_multitype(node_id, ty)) return false; } return true; - } //has_subtype + }; //has_subtype() // }}}1 // Data-access routines //////////////////////////////////////////// {{{1 - /// Find a node's value in the model, regardless of type. - /// @param node_ref {mixed} If a string, the node id; otherwise, anything - /// that can be passed to jstree.get_node. + /// Find a node's value in the model, regardless of type (DEPRECATED). + /// TODO replace calls to this (currently only in tree.js) with calls + /// to module.vn_by_vorny(). + /// @param vorny {mixed} A vorny for the node /// @return ret {object} the value, or ===false if the node wasn't found. - module.get_node_val = function(node_ref) + module.get_node_val = function(vorny) { - // Get the node ID - let node_id; - - if(typeof node_ref === 'string') { - node_id = node_ref; - } else { - let node = T.treeobj.get_node(node_ref); - if(node === false) return false; - node_id = node.id; - } - - return D.val_by_node_id(node_id); + let {val} = module.vn_by_vorny(vorny); + return val || false; }; //get_node_val() - /// Get the textual version of raw_title for a window's value - module.get_win_raw_text = function(val) + /// Get the textual version of raw_title for an item. + /// @param vorny {mixed} the item + /// @return {string} + module.get_raw_text = function(vorny) { - if(val.raw_title !== null) { + let {val} = module.vn_by_vorny(vorny); + if(!val) return '** UNKNOWN **'; + // TODO throw? + + if(val.raw_title !== null) { // window or tab return val.raw_title; - } else if(val.keep) { - return 'Saved tabs'; - } else { - return 'Unsaved'; + } else if(val.keep === K.WIN_KEEP) { // default title for saved window + return _T('labelSavedTabs'); + } else if(val.ty === K.IT_WIN) { // def. title for ephem. win. + return _T('labelUnsaved'); + } else { // e.g., tabs with no raw_title. + // TODO see if this makes sense. + return "** no title **"; } - }; //get_win_raw_text() + }; //get_raw_text() - /// Mark window item #val as unsaved (forget #val). - /// @param val {Object} the item + /// Mark window item #vorny as unsaved (forget #vorny). + /// @param vorny {mixed} the item /// @param adjust_title {Boolean=true} Add unsaved markers if truthy /// @return {Boolean} true on success; false on error - module.mark_win_as_unsaved = function(val, adjust_title=true) { - if(!val || val.ty !== K.IT_WIN || !val.node_id) return false; + module.mark_win_as_unsaved = function(vorny, adjust_title=true) { + let {val, node_id} = module.vn_by_vorny(vorny, K.IT_WIN); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; val.keep = K.WIN_NOKEEP; - T.treeobj.del_multitype(val.node_id, K.NST_SAVED); + T.treeobj.del_multitype(node, K.NST_SAVED); if(adjust_title && (val.raw_title !== null)) { - val.raw_title = module.remove_unsaved_markers(val.raw_title) + - ' (Unsaved)'; + if(val.raw_title === _T('labelSavedTabs')) { + val.raw_title = _T('labelUnsaved'); + } else { + val.raw_title = + module.remove_unsaved_markers(val.raw_title) + + ` (${_T('labelUnsaved')})`; + } } - // If raw_title is null, get_win_raw_text() will return 'Unsaved', + // If raw_title is null, get_raw_text() will return _T('Unsaved'), // so we don't need to manually assign text here. module.refresh_label(val.node_id); module.refresh_icon(val); + module.refresh_tooltip(val); + return true; }; //mark_as_unsaved() @@ -177,29 +191,43 @@ module.remove_unsaved_markers = function(str) { if(!str) return str; str = str.toString(); - let re = /(\s+\(Unsaved\)){1,}\s*$/i; + let re = new RegExp( + `((${_T('labelUnsaved')})|` + // Just the "Unsaved" text + `(\\s+\\(${_T('labelUnsaved')}\\)){1,})\\s*$`, // Postfix "(Unsaved)" + 'u'); // u=> unicode + // Not using 'i' anymore per + // https://mathiasbynens.be/notes/es6-unicode-regex#recommendations + let matches = str.match(re); if(matches && matches.index > 0) { - return str.slice(0,matches.index); + return str.slice(0, matches.index); } else { return str; } - }; + }; //remove_unsaved_markers() /// Get the HTML for the node's label. The output can be passed /// directly to jstree.rename_node(). - /// @param val The multidex value for the item of interest + /// @param vorny {mixed} The item of interest, which + /// can be a window or a tab. /// @return A string - module.get_html_label = function(val) { + module.get_html_label = function(vorny) { + let {val} = module.vn_by_vorny(vorny); + if(!val) return false; + let retval = ''; if(val.isPinned) { // TODO make this optional? + // Note: for windows, isPinned is nonexistent, thus falsy. retval += '📌 '; // PUSHPIN } - let raw_text = module.get_win_raw_text(val); + let raw_text = module.get_raw_text(val); // raw_title, or default + + // Add the bullet, for tabs only. For windows, raw_bullet is + // always falsy. if(val.raw_bullet && typeof val.raw_bullet === 'string') { - // The first condition checks for null/undefined/&c., and also for - // empty strings. + // The first condition checks for null/undefined/&c., and also + // for empty strings. retval += '<span class="' + K.BULLET_CLASS + '">'; retval += Esc.escape(val.raw_bullet); @@ -211,26 +239,53 @@ } retval += '</span>'; - } + } //endif there's a raw_bullet retval += Esc.escape(raw_text); return retval; - }; + }; //get_html_label() // }}}1 // Item manipulation /////////////////////////////////////////////// {{{1 - /// Update the tree-node text for an item. + /// Update the tooltip for an item. + /// @param vorny {mixed} the item + /// @return {Boolean} truthy on success; falsy on failure. + module.refresh_tooltip = function(vorny) { + let {val, node_id} = module.vn_by_vorny(vorny); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; + + let strs = []; + if(getBoolSetting(CFG_TITLE_IN_TOOLTIP)) { + let raw_text = module.get_raw_text(val); // raw_title, or default + strs.push(raw_text); + } + if(getBoolSetting(CFG_URL_IN_TOOLTIP) && (val.ty === K.IT_TAB) ) { + strs.push(val.raw_url); + } + + let tooltip = strs.join('\n'); // '' if no tooltips + + if(tooltip !== node.li_attr.title) { + node.li_attr.title = tooltip; + T.treeobj.redraw_node(node); + } + + return true; + }; //refresh_tooltip() + + /// Update the tree-node text for an item from its details record. /// @param node_id {string} the node's ID (which doubles as the item's id) /// @return truthy on success, falsy on failure. - module.refresh_label = function(node_id) { - if(!node_id) return false; - let val = D.val_by_node_id(node_id); - if(!val) return false; - let retval = T.treeobj.rename_node(node_id, module.get_html_label(val)); + module.refresh_label = function(vorny) { + let {val, node_id} = module.vn_by_vorny(vorny); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return; + let retval = T.treeobj.rename_node(node, module.get_html_label(val)); return retval; - }; + }; //refresh_label() /// Update the icon of #vorny /// @param vorny {Mixed} The item @@ -238,7 +293,7 @@ module.refresh_icon = function(vorny) { let {val, node_id} = module.vn_by_vorny(vorny); let node = T.treeobj.get_node(node_id); - if(!val || !node_id || !node) return false; + if(!val || !node) return false; let icon; @@ -273,28 +328,39 @@ // generic page icon so we don't keep hitting the favIconUrl. return true; - } //refresh_icon + }; //refresh_icon() /// Mark the window identified by #win_node_id as to be kept. - /// @param win_node_id {string} The window node ID + /// @param win_vorny {mixed} The window node /// @param cleanup_title {optional boolean, default true} /// If true, remove unsaved markers from the raw_title. /// @return {Boolean} true on success; false on error - module.remember = function(win_node_id, cleanup_title = true) { - if(!win_node_id) return false; - let val = D.windows.by_node_id(win_node_id); - if(!val) return false; + module.remember = function(win_vorny, cleanup_title = true) { + let {val, node_id} = module.vn_by_vorny(win_vorny, K.IT_WIN); + let node = T.treeobj.get_node(node_id); + if(!val || !node) return false; val.keep = K.WIN_KEEP; - T.treeobj.add_multitype(win_node_id, K.NST_SAVED); + T.treeobj.add_multitype(node, K.NST_SAVED); if(cleanup_title) { - val.raw_title = module.remove_unsaved_markers( - module.get_win_raw_text(val)); + let new_title = + module.remove_unsaved_markers(module.get_raw_text(val)); + if( new_title === _T('labelSavedTabs') || + new_title === _T('labelUnsaved') + ) { + // If it's the default text, don't treat it as a user entry. + // It will be labelUnsaved if the user right-clicked on a + // fresh unsaved window and selected Remember. + val.raw_title = null; + } else { + val.raw_title = new_title; + } } - module.refresh_label(win_node_id); + module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; }; //remember() @@ -321,7 +387,7 @@ blake.update(databuf); } return blake.hexDigest(); - } //orderedHashOfStrings + }; //orderedHashOfStrings() /// Update the given node's ordered_url_hash to reflect its current children. /// @return {Boolean} True if the ordered_url_hash was set or was @@ -396,7 +462,8 @@ raw_title: null, raw_bullet: null, isOpen: false, - keep: undefined + keep: undefined, + prune_data: undefined }); if(!val) { @@ -406,9 +473,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return {val, node_id}; - } //vnRezWin + }; //vnRezWin() /// Add a model node/item for a tab, with the given parent. /// Does not process Chrome widgets. Instead, assumes the tab is @@ -451,9 +519,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return {val, node_id}; - } //vnRezTab + }; //vnRezTab() // }}}1 // Updating model items //////////////////////////////////////////// {{{1 @@ -473,7 +542,7 @@ // TODO report failure to add a type? } return true; - } //add_subtype + }; //add_subtype() /// Remove a subtype (K.NST_*) from an item. /// @param vorny {mixed} The item @@ -490,7 +559,31 @@ // TODO report failure to remove a type? } return true; - } //add_subtype + }; //del_subtype() + + // TODO? implement this? +// /// Modify #vorny with all the changes from #deltas in one go. +// module.change = function(vorny, deltas) { +// let {val, node_id} = module.vn_by_vorny(vorny); +// let node = T.treeobj.get_node(node_id); +// if(!deltas || (typeof deltas !== 'object') || !val || !node) return; +// +// /// A list of functions of this module to be called after +// /// the values are changed. +// let updates = []; +// +// // Update the internal data +// for(let k in deltas) { +// let v = deltas[k]; +// //TODO update val +// //TODO push the appropriate update +// } +// +// // Commit the updates to the visible tree +// for(let fn of updates) { +// module[fn](vorny); +// } +// }; //change() // }}}1 // Attaching Chrome widgets to model items ///////////////////////// {{{1 @@ -530,9 +623,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; - } //markWinAsOpen + }; //markWinAsOpen() /// Attach a Chrome tab to an existing tab item. /// Updates the item, but does not touch the Chrome tab. @@ -549,7 +643,7 @@ if(!val || !node_id) return false; if(val.isOpen || val.tab) { - log.info({'Refusing to re-mark already-open tab as open':val}); + log.info({'Refusing to re-mark already-open tab as open at ctab':ctab,val}); return false; } @@ -573,13 +667,14 @@ module.refresh_label(node_id); module.refresh_icon(val); // since favicon may have changed + module.refresh_tooltip(val); // Design decision: tree items for open windows always start expanded. // No one has requested any other behaviour, as of the time of writing. T.treeobj.open_node(node.parent); return true; - } //markTabAsOpen + }; //markTabAsOpen() // }}}1 // Removing Chrome widgets from model items //////////////////////// {{{1 @@ -611,9 +706,10 @@ module.refresh_label(node_id); module.refresh_icon(val); + module.refresh_tooltip(val); return true; - } //markWinAsClosed + }; //markWinAsClosed() /// Remove the connection between #tab_vorny and its Chrome tab. /// Use this when the Chrome tab has been closed. @@ -649,9 +745,11 @@ module.refresh_label(node_id); // TODO is this necessary? // Don't change icon - keep favicon + // Don't change tooltip - closing a tab doesn't affect the + // raw_title or raw_url. return true; - } //markTabAsClosed + }; //markTabAsClosed() // }}}1 // Removing model items //////////////////////////////////////////// {{{1 @@ -674,7 +772,7 @@ T.treeobj.delete_node(node_id); return true; - } //eraseTab + }; //eraseTab() /// Delete a window from the tree and the details. This will also /// erase any remaining child nodes of #win_vorny from the @@ -704,7 +802,7 @@ T.treeobj.delete_node(node_id); return true; - } //eraseWin + }; //eraseWin() // }}}1 diff --git a/webstore/src/view/tree.css b/webstore/src/view/tree.css index f852e9f4..f32d3ccb 100755 --- a/webstore/src/view/tree.css +++ b/webstore/src/view/tree.css @@ -53,6 +53,11 @@ color: #f33; } +#init-progress { + display: none; /* initially */ + color: #f33; +} + li.ephemeral-recovered, .tfs-recovered { background: darkslategray !important; } diff --git a/webstore/src/view/tree.html b/webstore/src/view/tree.html index 7ab36337..2961c124 100644 --- a/webstore/src/view/tree.html +++ b/webstore/src/view/tree.html @@ -22,6 +22,7 @@ <!-- this one should always be the last stylesheet listed --> <!-- Scripts, except for those loaded by require.js ~~~~~~~~~~~~~~~~~~~ --> + <script src="../common/validation.js" type="text/javascript"></script> <!-- not requirejs --> <script src="../common/common.js" type="text/javascript"></script> <!-- not requirejs --> <script src="/conf/require-config.js"></script> <!-- load before require.js --> @@ -34,9 +35,12 @@ <div id="tabfern-container"> + <!-- Error messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <div id="init-incomplete">Initialization not complete (or not yet complete). Changes will not be saved. - </div><!-- hidden if init does complete. --> + </div> + <div id="init-progress"></div> + <!-- the above two divs are removed if init does complete. --> <!-- Content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <div id="maintree"></div><!-- the window tree --> @@ -46,7 +50,8 @@ <!-- Modal dialog ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --> <!-- rmodal sets display:none on .modal automatically, so we don't - have to here. --> + have to here. Note that the text below will be replaced from + the localized messages.json. --> <div id="confirm-dialog" class="modal"> <div class="modal-dialog"> <form class="form-horizontal" method="get"> @@ -58,7 +63,7 @@ <input type="checkbox" name="cbNotAgain" id="cbNotAgain" class="form-control col-0p5" /> - <label for="cbNotAgain" + <label id="labelNotAgain" for="cbNotAgain" class="control-label col-xs-4 col-5"> Don't ask again (<abbr title="You can change this in Settings | Behaviour">note</abbr>) @@ -66,16 +71,17 @@ <!-- note: don't use <button type=submit>, which reloads the page. --> <div class="col-3"> </div> - <button data-which="yes" + <button id="confirm-dialog-yes" data-which="yes" class="btn btn-primary col-1" type="button" accesskey="y"> <span class="accel">Y</span>es </button> - <button data-which="no" class="btn col-1" + <button id="confirm-dialog-no" data-which="no" + class="btn col-1" type="button" accesskey="n"> <span class="accel">N</span>o </button> - <button data-which="cancel" + <button id="confirm-dialog-cancel" data-which="cancel" class="btn btn-default col-1" type="button" accesskey="c"> <span class="accel">C</span>ancel diff --git a/webstore/src/view/tree.js b/webstore/src/view/tree.js index 9c0d564b..21659db7 100755 --- a/webstore/src/view/tree.js +++ b/webstore/src/view/tree.js @@ -4,7 +4,8 @@ // TODO break more of this into separate modules -console.log('Loading TabFern ' + TABFERN_VERSION); +console.log(`============================================================= +Loading TabFern ${TABFERN_VERSION}`); ////////////////////////////////////////////////////////////////////////// // Modules // {{{1 @@ -60,7 +61,7 @@ let Module_dependencies = [ // Modules of TabFern itself 'view/const', 'view/item_details', 'view/sorts', 'view/item_tree', - 'view/model', 'common/validation' + 'view/model', ]; /// Make short names in Modules for some modules. shortname => longname @@ -80,8 +81,13 @@ var my_winid; ///< window ID of this popup window var currently_focused_winid = null; /// HACK to avoid creating extra tree items. +/// TODO? change this to an object with win node ID's as keys. Use one of the +/// tabs to carry the node ID for confirmation. var window_is_being_restored = false; +/// HACK to not prune windows we create! +var do_not_prune_right_now = false; + /// The size of the last-closed window, to be used as the /// size of newly-opened windows (whence the name). /// Should always have a valid value. @@ -100,6 +106,11 @@ var ShowWhatIsNew = false; /// Array of URLs of the last-deleted window var lastDeletedWindow; +/// Node ID of the last-closed saved window --- merging is prohibited with +/// this node. It's the last-closed saved and not the last-closed overall +/// because nodes for unsaved windows disappear with their windows. +var lastSavedClosedWindow_node_id = undefined; + /// Did initialization complete successfully? var did_init_complete = false; @@ -120,6 +131,41 @@ var Bypasser; ////////////////////////////////////////////////////////////////////////// }}}1 // Module setup, and utilities // {{{1 +// Initialization support +let init_step_num = 0; +const LAST_INIT_STEP = 13; + +/// Update the initialization status counter +let next_init_step = function(where) { + ++init_step_num; + + let msg = + `${init_step_num}/${LAST_INIT_STEP}${where ? (': ' + where): ''}`; + log.info({Initialization:msg}) + + let elem = $(K.INIT_PROGRESS_SEL); + if(elem.css('display') !== 'block') return; + + elem.text(msg); + + elem.hide().show(1); + // Force redraw - thanks to + // https://stackoverflow.com/users/122543/juank and + // https://stackoverflow.com/users/402517/l-poellabauer + // at https://stackoverflow.com/a/8840703/2877364 . + // I used (1) instead of (0) since I saw it not work once with (0). + +}; //next_init_step + +/// Tell me if I forgot to update LAST_INIT_STEP +let check_init_step_count = function() { + if(init_step_num !== LAST_INIT_STEP) { + let msg = `Step count was ${init_step_num} - update LAST_INIT_STEP`; + log.warn(msg); + window.alert(msg); + } +}; //check_init_step_count + /// Init those of our globals that don't require any data to be loaded. /// Call after Modules has been populated. function local_init() @@ -244,6 +290,95 @@ function unflagAllWindows() { ); }; +////////////////////////////////////////////////////////////////////////// }}}1 +// Chrome-related utilities // {{{1 + +/// Check the tabs in the window to make sure they match what we expect, +/// and prune any we don't expect. +/// This is because sometimes Chrome opens extra tabs but doesn't tell the +/// chrome.windows.create callback about them. +/// See https://bugs.chromium.org/p/chromium/issues/detail?id=762951 . +/// See also commit 70638829e5c35a8c86b556dc62ddb3920e4a5e92, +/// at tabfern/src/view/tree.js:997. +/// +/// @param cwin The Chrome window +/// @param expected_tab_count {Number} How many tabs to keep, or 0 to leave +/// the window empty except for a chrome://newtab. +function pruneWindow(cwin, expected_tab_count) +{ + if(!cwin || +expected_tab_count < 0 || do_not_prune_right_now) return; + let count = +expected_tab_count || 1; // 0 => one tab + + ASQH.NowCC( (cc)=>{ chrome.windows.get(cwin.id, {populate: true}, cc); } ) + + .or((err)=>{ + L.log.warn({'Could not get tabs in window': cwin, err}); + }) + + .then( (done, inner_cwin)=>{ + + if(inner_cwin.tabs.length === 0) { + //chrome.tabs.create( {windowId: inner_cwin.id, index: 0} ); + done.fail({"I can't check for pruning in zero-tab window":inner_cwin}); + return; + } + + let win_val = D.windows.by_win_id(inner_cwin.id); + + // Make sure the first tab is a new tab, if desired. + // However, if this is a saved window, don't mess with tab 0. + if( expected_tab_count === 0 && + !(win_val && win_val.keep === K.WIN_KEEP) + ) { + // Note: this is problematic if the last window to be open was a + // saved window, since this races against the merge check in + // tabOnCreated. That is the reason for pruneWindowSetTimer(). + let tabid = inner_cwin.tabs[0].id; + + let seq = ASQ(); // for error reporting + // Don't change chrome: URLs, since those might be things + // like History (Ctl+H). + if(!inner_cwin.tabs[0].url.match(/^chrome:/i)) { + log.info(`Setting tab ${tabid} to chrome:newtab`); + chrome.tabs.update(tabid, {url: 'chrome://newtab'}, + ASQH.CCgo(seq)); + } + // Currently we don't have anything else to do, so there's no + // seq.then() calls. However, using seq still causes asynquence + // to report any errors in the chrome.tabs.update() call. + + } //endif the caller wanted tab 0 === chrome://newtab + + if(inner_cwin.tabs.length <= count) { + done(); // No need to prune --- we're done + return; + } + + L.log.warn('Win ' + inner_cwin.id + ': expected ' + + count + ' tabs; got ' + + inner_cwin.tabs.length + ' tabs --- pruning.'); + + let to_prune=[]; + + // Assume that the unexpected tabs are all at the right-hand + // end, since that's the only behaviour I've observed. + + for(let tab_idx = count; + tab_idx < inner_cwin.tabs.length; + ++tab_idx) { + to_prune.push(inner_cwin.tabs[tab_idx].id); + } //foreach extra tab + + L.log.warn('Pruning ' + to_prune); + chrome.tabs.remove(to_prune, ASQH.CC(done)); + }) + + .or((err)=>{ + L.log.warn({'Could not prune tabs in window': cwin, err}); + }) + ; +} //pruneWindow + ////////////////////////////////////////////////////////////////////////// }}}1 // UI Controls // {{{1 @@ -262,7 +397,25 @@ function showConfirmationModalDialog(message_html) { let cleanup_cbk = cleanup.errfcb(); // pause the sequence // Modified from the rmodal sample at https://rmodal.js.org/ - $('#confirm-dialog-question').html(message_html); + let jqdlg = $('#confirm-dialog'); + jqdlg.find('#confirm-dialog-question').html(message_html); + + // i18n + const KEY_YES = _T('dlgYesAccelerator'); + const KEY_NO = _T('dlgNoAccelerator'); + const KEY_CANCEL = _T('dlgCancelAccelerator'); + + jqdlg.find('#confirm-dialog-yes') + .attr('accesskey', KEY_YES) + .html(_T('dlgYesHTML')); + jqdlg.find('#confirm-dialog-no') + .attr('accesskey', KEY_NO) + .html(_T('dlgNoHTML')); + jqdlg.find('#confirm-dialog-cancel') + .attr('accesskey', KEY_CANCEL) + .html(_T('dlgCancelHTML')); + jqdlg.find('#labelNotAgain') + .html(_T('dlgDoNotAskAgainHTML')); dlg = new (Modules['rmodal'])( document.getElementById('confirm-dialog'), @@ -307,11 +460,11 @@ function showConfirmationModalDialog(message_html) { ev.key.toLowerCase(): null; // Send the events on the next cycle. Not sure if this is required. - if(key === 'y') { + if(key === KEY_YES) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='yes']").click();}); - } else if(key === 'n') { + } else if(key === KEY_NO) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='no']").click();}); - } else if(key === 'c') { + } else if(key === KEY_CANCEL) { ASQ().val(()=>{$("#confirm-dialog .btn[data-which='cancel']").click();}); } else { dlg.keydown(ev); @@ -424,14 +577,14 @@ function saveTree(save_ephemeral_windows = true, cbk = undefined) chrome.storage.local.set(to_save, function() { if(!isLastError()) { + log.debug('Saved tree'); if(typeof cbk === 'function') { cbk(null, to_save[K.STORAGE_KEY]); } return; // Saved OK } //else there was an error - let msg = "TabFern: couldn't save: " + - chrome.runtime.lastError.toString(); + let msg = _T('errCouldNotSave', chrome.runtime.lastError.toString()); log.error(msg); window.alert(msg); // The user needs to know if(typeof cbk === 'function') cbk(new Error(msg)); @@ -461,14 +614,14 @@ function actionRenameWindow(node_id, node, unused_action_id, unused_action_el) if(!win_val) return; // TODO replace window.prompt with an in-DOM GUI. - let win_name = window.prompt('New window name?', - M.remove_unsaved_markers(M.get_win_raw_text(win_val))); + let win_name = window.prompt(_T('dlgpNewWindowName'), + M.remove_unsaved_markers(M.get_raw_text(win_val))); if(win_name === null) return; // user cancelled // A bit of a hack --- if the user hits OK on the default text for a // no-name window, change it to "Saved tabs." TODO find a better way. - if(win_name === 'Unsaved') { - win_val.raw_title = 'Saved tabs'; + if(win_name === _T('labelUnsaved') || win_name === '') { + win_val.raw_title = _T('labelSavedTabs'); } else { win_val.raw_title = win_name; } @@ -540,7 +693,7 @@ function actionCloseWindowButDoNotSave(node_id, node, unused_action_id, unused_a // the window is already gone, so the remove() throws. // See https://stackoverflow.com/a/45871870/2877364 by cxw - // TODO RESUME HERE --- don't actually proceed until + // TODO don't actually proceed until // the window is gone. Test case: window.beforeonunload=()=>true; } @@ -614,7 +767,7 @@ function actionDeleteWindow(node_id, node, unused_action_id, unused_action_el, // Remove the window's node and value let scrollOffsets = [window.scrollX, window.scrollY]; T.treeobj.delete_node(node_id); //also deletes child nodes - // TODO RESUME HERE --- don't actually delete the node until + // TODO don't actually delete the node until // the window is gone. Test case: window.beforeonunload=()=>true; window.scrollTo(...scrollOffsets); @@ -637,19 +790,24 @@ function actionDeleteWindow(node_id, node, unused_action_id, unused_action_el, } //doDeletion() // Prompt for confirmation, if necessary - if( is_internal || + let no_confirmation = !!is_internal; + + no_confirmation = no_confirmation || (win_val.keep === K.WIN_KEEP && - !getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)) || + !getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)); + + no_confirmation = no_confirmation || (win_val.keep === K.WIN_NOKEEP && !getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED)) - ) { // No confirmation required - just do it + + if(no_confirmation) { // No confirmation required - just do it doDeletion(); } else { // Confirmation required - showConfirmationModalDialog( - 'Delete window "' + M.get_win_raw_text(win_val) + '"?' - ) + (showConfirmationModalDialog( + _T('dlgpDeleteWindow', M.get_raw_text(win_val)) + )) // Processing after the dialog closes .val((result)=>{ @@ -704,22 +862,29 @@ function actionEditTabBullet(node_id, node, unused_action_id, unused_action_el) if(!val || val.ty !== K.IT_TAB) return; // TODO replace window.prompt with an in-DOM GUI. - let question = `Note for tab "${val.raw_title}"?`; - //DEBUG - //new_bullet='42'; + let question = _T('dlgpTabNote', val.raw_title); let new_bullet = window.prompt(question, val.raw_bullet || ''); if(new_bullet === null) return; // user cancelled val.raw_bullet = new_bullet; M.refresh_label(node_id); + // TODO if window is currently ephemeral, only remember if + // new_bullet is nonempty. M.remember(node.parent); - // assume that a user who bothered to add a note + // Assume that a user who bothered to add a note // wants to keep the window the note is in. saveTree(); } //actionEditTabBullet +///// Close the tab and save - NOT YET IMPLEMENTED +//function actionCloseTabAndSave(node_id, node, unused_action_id, unused_action_el) +//{ +// // TODO actionCloseTabButDoNotSave(node_id, node, unused_action_id, unused_action_el); +// saveTree(); +//} //actionCloseTabAndSave + /// Delete a tab's entry in the tree. /// @param node_id {string} the ID of the node to delete /// @param node the node to delete @@ -759,22 +924,38 @@ function actionDeleteTab(node_id, node, unused_action_id, unused_action_el, } //doDeletion() // Prompt for confirmation, if necessary + let prompt_name = 'dlgpDeleteTab'; // in messages.json let is_keep = (parent_val.keep === K.WIN_KEEP); let is_nokeep = (parent_val.keep === K.WIN_NOKEEP); + + // General rule... let need_confirmation = ( (is_keep && getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED_TABS)) || (is_nokeep && getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED_TABS)) ); - if( !need_confirmation || - (/^((chrome:\/\/newtab\/?)|(about:blank))$/i.test(tab_val.raw_url)) - ) { + // ... but we don't usually need confirmation for empty tabs... + if(/^((chrome:\/\/newtab\/?)|(about:blank))$/i.test(tab_val.raw_url)) { + need_confirmation = false; + } + + // ... except when such a tab (or any tab being deleted) is the last + // tab in its window. In that case, check the window settings as well. + if(parent_node.children.length === 1) { + if( (is_keep && getBoolSetting(CFG_CONFIRM_DEL_OF_SAVED)) || + (is_nokeep && getBoolSetting(CFG_CONFIRM_DEL_OF_UNSAVED)) ) { + need_confirmation = true; + prompt_name = 'dlgpDeleteTabAndWindow'; + } + } + + if(!need_confirmation) { // No confirmation required - just do it doDeletion(); } else { // Confirmation required - showConfirmationModalDialog( - 'Delete tab "' + M.get_html_label(tab_val) + '"?' + (showConfirmationModalDialog( + _T(prompt_name, M.get_html_label(tab_val))) ) // Processing after the dialog closes @@ -863,15 +1044,27 @@ function addTabNodeActions(tab_node_id) // "Actually" event check in treeOnSelect. //html: `<img src="/assets/icons/pencil.png" class=${K.ACTION_BUTTON_WIN_CLASS} />`, grouped: true, + title: _T('ttEditTab'), callback: actionEditTabBullet, dataset: { action: 'editBullet' } }); +// T.treeobj.add_action(tab_node_id, { +// id: 'closeTab', +// class: 'fff-picture-delete ' + K.ACTION_BUTTON_WIN_CLASS, +// text: '\xa0', +// grouped: true, +// title: "Close and save", +// callback: actionCloseTabAndSave, +// dataset: { action: 'closeTab' } +// }); + T.treeobj.add_action(tab_node_id, { id: 'deleteTab', class: 'fff-cross ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttDeleteTab'), callback: actionDeleteTab, dataset: { action: 'deleteTab' } }); @@ -884,8 +1077,13 @@ function addTabNodeActions(tab_node_id) function createNodeForTab(ctab, parent_node_id) { let {node_id, val} = M.vnRezTab(parent_node_id); - if(!node_id) return false; - M.markTabAsOpen(val, ctab); + if(!node_id) { + log.debug({"<M> Could not create record for ctab":ctab,parent_node_id}); + return false; + } + if(!M.markTabAsOpen(val, ctab)) { + log.debug({"<M> Could not mark tab as open":ctab,val}); + } addTabNodeActions(node_id); @@ -899,7 +1097,10 @@ function createNodeForTab(ctab, parent_node_id) function createNodeForClosedTabV1(tab_data_v1, parent_node_id) { let {node_id, val} = M.vnRezTab(parent_node_id); - if(!node_id) return false; + if(!node_id) { + log.debug({"<M> Could not create record for closed tab":tab_data_v1,parent_node_id}); + return false; + } // Copy properties into the details copyTruthyProperties(val, tab_data_v1, @@ -909,6 +1110,7 @@ function createNodeForClosedTabV1(tab_data_v1, parent_node_id) M.refresh_label(node_id); M.refresh_icon(val); + M.refresh_tooltip(val); if(tab_data_v1.bordered) M.add_subtype(val, K.NST_TOP_BORDER); @@ -932,6 +1134,7 @@ function addWindowNodeActions(win_node_id) class: 'fff-pencil ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttEditWin'), callback: actionRenameWindow, dataset: { action: 'renameWindow' } }); @@ -941,6 +1144,7 @@ function addWindowNodeActions(win_node_id) class: 'fff-picture-delete ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttCloseWin'), callback: actionCloseWindowAndSave, dataset: { action: 'closeWindow' } }); @@ -950,15 +1154,54 @@ function addWindowNodeActions(win_node_id) class: 'fff-cross ' + K.ACTION_BUTTON_WIN_CLASS, text: '\xa0', grouped: true, + title: _T('ttDeleteWin'), callback: actionDeleteWindow, dataset: { action: 'deleteWindow' } }); } //addWindowNodeActions +/// Set a timer to prune the window later. +/// @param win_val The value for the window +/// @param cwin (Optional) The Chrome window. If not provided, the +/// one stashed in win_val (if any) will be used. +let pruneWindowSetTimer = function(win_val, cwin) { + log.debug({"Bump of prune timer requested for":win_val, cwin}); + if(!win_val.prune_data) { + win_val.prune_data = {timer_id: undefined, cwin: cwin || undefined}; + } + + if(win_val.prune_data.timer_id) { + window.clearTimeout(win_val.prune_data.timer_id); + win_val.prune_data.timer_id = undefined; + } + + if(do_not_prune_right_now) return; + + log.debug({" Bumping prune timer for":win_val, cwin}); + win_val.prune_data.timer_id = window.setTimeout( + ()=>{ + if(!win_val.prune_data.cwin) return; + if(!did_init_complete) { + log.info({'Still initializing, so skipping prune-to-0 check of': + win_val.prune_data.cwin}); + return; + } + + if(do_not_prune_right_now) return; + log.info({'Checking for pruning-to-0 of':win_val.prune_data.cwin}); + pruneWindow(win_val.prune_data.cwin, 0); + // 0 => only leave a chrome://newtab there + win_val.prune_data = undefined; + // Once pruning is done, don't leave stale data around. + }, + K.WIN_PRUNE_TIMER_MS // UGLY HACK - give the merge check time to run + ); +} //pruneWindowSetTimer + /// Create a tree node for open Chrome window #cwin. /// @returns the tree-node ID, or falsy on error. -function createNodeForWindow(cwin, keep) +function createNodeForWindow(cwin, keep, no_prune) { if(!cwin || !cwin.id) return; @@ -967,10 +1210,13 @@ function createNodeForWindow(cwin, keep) let is_first = (!!cwin && getBoolSetting(CFG_NEW_WINS_AT_TOP)); let {node_id, val} = M.vnRezWin(is_first); - if(!node_id) return false; //sanity check + if(!node_id) { //sanity check + log.debug({"<M> Could not create tree node for open cwin":cwin,keep,no_prune}); + return false; + } M.markWinAsOpen(val, cwin); - if(keep) { + if(keep === K.WIN_KEEP) { M.remember(node_id, false); } else { M.mark_win_as_unsaved(val, false); @@ -985,6 +1231,13 @@ function createNodeForWindow(cwin, keep) } } + // Remove extra tabs if the user wants + if(!no_prune && !do_not_prune_right_now + && getBoolSetting(CFG_PRUNE_NEW_WINDOWS) + ) { + pruneWindowSetTimer(val, cwin); + } + return node_id; } //createNodeForWindow @@ -1001,22 +1254,30 @@ function createNodeForClosedWindowV1(win_data_v1) // Make a node for a closed window. The node is marked KEEP. // TODO don't mark it keep if it's ephemeral and still open. let {node_id, val} = M.vnRezWin(); - if(!node_id) return false; - M.remember(node_id, false); // Closed windows are KEEP by design + if(!node_id) { + log.debug({"<M> Could not create node for closed window":win_data_v1}); + return false; + } + + if(!M.remember(node_id, false)) { // Closed windows are KEEP by design + log.debug({"<M> Could not mark closed window as KEEP":win_data_v1, node_id, val}); + } // TODO restore ordered_url_hash // Mark recovered windows if(is_ephemeral) { - M.add_subtype(node_id, K.NST_RECOVERED); + if(!M.add_subtype(node_id, K.NST_RECOVERED)) { + log.debug({"<M> Could not add subtype RECOVERED":node_id, val, win_data_v1}); + } } // Update the item details let new_title; if( is_ephemeral && (typeof win_data_v1.raw_title !== 'string') ) { - new_title = 'Recovered tabs'; + new_title = _T('labelRecoveredTabs'); } else if(is_ephemeral) { // and raw_title is a string - new_title = String(win_data_v1.raw_title) + ' (Recovered)'; + new_title = String(win_data_v1.raw_title) + _T('labelRecoveredTabsPostfix'); } else { // not ephemeral let n = win_data_v1.raw_title; new_title = (typeof n === 'string') ? n : null; @@ -1026,6 +1287,7 @@ function createNodeForClosedWindowV1(win_data_v1) M.refresh_label(node_id); M.refresh_icon(val); + M.refresh_tooltip(val); addWindowNodeActions(node_id); @@ -1046,13 +1308,19 @@ function createNodeForClosedWindowV1(win_data_v1) /// Update #existing_win to connect to #cwin. Also hooks up all the /// ctabs. +/// Requires cwin.tabs.length === existing_win.node.children.length. +/// /// @param cwin {Chrome Window} The open window, populated with tabs. /// @param existing_win {object} An object with {val, node}, e.g., from /// winAlreadyExistsInTree(). -/// @param during_init {Boolean=false} If truthy, failures correspond to init failure. -/// \pre cwin.tabs.length === existing_win.node.children.length. -function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=false) +/// @param options {object={}} Options. Presently: +/// - during_init {Boolean=false} If truthy, failures correspond to init failure. +/// - +/// +/// @return truthy on success; falsy on failure +function connectChromeWindowToTreeWindowItem(cwin, existing_win, options = {}) { + let during_init = !!options.during_init; // Attach the open window to the saved window log.info({[`Attaching window ${cwin.id} to existing window in the tree`]: existing_win}); @@ -1078,10 +1346,11 @@ function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=fal }); if(during_init) was_loading_error = true; - return; // TODO handle this better + return false; // TODO handle this better } // If we reach here, cwin.tabs.length === existing_win.node.children.length. + for(let idx=0; idx < cwin.tabs.length; ++idx) { let tab_node_id = existing_win.node.children[idx]; let tab_val = D.tabs.by_node_id(tab_node_id); @@ -1090,15 +1359,29 @@ function attachChromeWindowToSavedWindowItem(cwin, existing_win, during_init=fal let ctab = cwin.tabs[idx]; // Do we need these? - ctab.url = ctab.url || 'about:blank'; - ctab.title = ctab.title || '## Unknown title ##'; + ctab.url = ctab.url || tab_val.raw_url || 'about:blank'; + ctab.title = ctab.title || tab_val.raw_title || _T('labelUnknownTitle'); + let pinned = tab_val.isPinned; M.markTabAsOpen(tab_node_id, ctab); + + // Apply changes from the tab_val to the ctab if requested. + // New tabs start out unpinned, so we may need to repin. + if(options.repin && pinned) { + chrome.tabs.update(ctab.id, {pinned: true}, ignore_chrome_error); + } } //foreach tab // Note: We do not need to update existing_win.val.ordered_url_hash. // Since we got here, we know that it was a match. -} //attachChromeWindowToSavedWindowItem() + + T.treeobj.redraw_node(existing_win.node); + + // Prune any extra tabs Chrome may have created due to bugs. + pruneWindow(cwin, existing_win.node.children.length); + + return true; +} //connectChromeWindowToTreeWindowItem() ////////////////////////////////////////////////////////////////////////// }}}1 // Loading // {{{1 @@ -1141,7 +1424,7 @@ function winAlreadyExistsInTree(cwin) /// Design decision: TabFern SHALL always be able to load older save files. /// Never remove a loader from this function. /// @post The new windows are added after any existing windows in the tree -/// @param {mixed} data The save data, parsed (i.e., not a JSON string) +/// @param {mixed} data The save data, parsed (i.e., not a JSON-encoded string) /// @return {number} The number of new windows, or ===false on failure. /// ** Note: 0 is a valid number of windows to load! var loadSavedWindowsFromData = (function(){ @@ -1193,7 +1476,9 @@ var loadSavedWindowsFromData = (function(){ let versionLoaders = { 0: loadSaveDataV0, 1: loadSaveDataV1 }; /// Populate the tree from the save data. - return function(data) + /// TODO throw on failure, so that the caller can report the details of + /// the error if desired. + return function loadSavedWindowsFromData_inner(data) { let succeeded = false; let loader_retval; // # of wins loaded @@ -1231,14 +1516,17 @@ var loadSavedWindowsFromData = (function(){ succeeded = true; } return (succeeded ? loader_retval : false); - } + } //loadSavedWindowsFromData_inner })(); //loadSavedWindowsFromData /// Load the saved windows from local storage - used as part of initialization. /// @param {function} next_action If provided, will be called when loading /// is complete. function loadSavedWindowsIntoTree(next_action) { + next_init_step('Load saved windows'); // TODO _T() the step names + chrome.storage.local.get(K.STORAGE_KEY, function(items) { + next_init_step('Got save data'); READIT: if(isLastError()) { @@ -1397,6 +1685,8 @@ function treeOnSelect(_evt_unused, evt_data) // Tabs case 'editBullet': actionEditTabBullet(node.id, node, null, null); break; +// case 'closeTab': +// actionCloseTabAndSave(node.id, node, null, null); case 'deleteTab': actionDeleteTab(node.id, node, null, null, evt_data.event); // as deleteWindow, above. @@ -1428,10 +1718,22 @@ function treeOnSelect(_evt_unused, evt_data) } if(is_tab && node_val.isOpen) { // An open tab - chrome.tabs.highlight({ - windowId: node_val.win_id, - tabs: [node_val.index] // Jump to the clicked-on tab - }, ignore_chrome_error); + + if(BROWSER_TYPE === 'ff') { + ASQ().promise( + browser.tabs.update(node_val.tab_id,{active:true}) + ) + .or((err)=>{ + log.warn({"Could not highlight tab":node_val,err}); + }); + + } else { //Chrome + chrome.tabs.highlight({ + windowId: node_val.win_id, + tabs: [node_val.index] // Jump to the clicked-on tab + }, ignore_chrome_error); + } + //log.info('flagging treeonselect' + node.id); T.treeobj.flag_node(node); win_id = node_val.win_id; @@ -1440,6 +1742,10 @@ function treeOnSelect(_evt_unused, evt_data) win_id = node_val.win_id; } else if( open_new_window ) { + // Ignore attempts to open two windows at once, since we currently + // can't handle them. + if(window_is_being_restored) return; + // A closed window or tab. Make sure we have the window. let win_node; let win_val; @@ -1467,123 +1773,64 @@ function treeOnSelect(_evt_unused, evt_data) for(let child_id of win_node.children) { let child_val = D.tabs.by_node_id(child_id); urls.push(child_val.raw_url); + // TODO: in Firefox, you can't call window.create with a URL of + // about:addons or about:debugging. } // Open the window window_is_being_restored = true; - chrome.windows.create( - { - url: urls - , focused: true - , left: newWinSize.left - , top: newWinSize.top - , width: newWinSize.width - , height: newWinSize.height - }, - function(win) { - // Update the tree and node mappings - log.info('Adding nodeid map for winid ' + win.id); - D.windows.change_key(win_val, 'win_id', win.id); - - win_val.isOpen = true; - win_val.keep = K.WIN_KEEP; // just in case - win_val.win = win; - //T.treeobj.set_type(win_node.id, K.NT_WIN_ELVISH); - M.add_subtype(win_node.id, K.NST_OPEN); - M.refresh_label(win_node.id); - M.refresh_icon(win_node); - - T.treeobj.open_node(win_node); - T.treeobj.redraw_node(win_node); - // Because open_node() doesn't redraw the parent, only - // its children, and opening the node changes the - // settings at this time. - - // In Chrome 61, with v0.1.4, I observed strange behaviour: - // the window would open extra tabs that were copies of - // items listed in `urls`. However, win.tabs.length sometimes - // would, and sometimes would not, indicate those extra tabs. - // It's a heisenbug. It may arise from two TabFerns running - // at once. It may also be related to what appears to be - // a Chrome 61 regression - Ctl+N for a new window will - // sometimes reopen previously-closed tabs. However, I - // don't know - I can't repro reliably. - // I have reported the Ctl+N issue: - // https://bugs.chromium.org/p/chromium/issues/detail?id=762951 - - // To hack around this if it happens again, I am trying: - - if(win.tabs.length != expected_tab_count) { - log.warn('Win ' + win.id + ': expected ' + - expected_tab_count + ' tabs; got ' + - win.tabs.length + 'tabs.'); - } - let count_to_use = Math.min(expected_tab_count, win.tabs.length); - for(let idx=0; idx < count_to_use; ++idx) { - let tab_node_id = win_node.children[idx]; - let tab_val = D.tabs.by_node_id(tab_node_id); - if(!tab_val) continue; - - let tab = win.tabs[idx]; - - D.tabs.change_key(tab_val, 'tab_id', tab.id); - M.add_subtype(tab_node_id, K.NST_OPEN); - - // Update realtime values from the ctab to the tab_val - tab_val.win_id = win.id; - tab_val.index = idx; - tab_val.tab = tab; - tab_val.raw_url = tab.url || 'about:blank'; - tab_val.raw_title = tab.title || '## Unknown title ##'; - tab_val.isOpen = true; - - // Apply changes from the tab_val to the ctab - if(tab_val.isPinned) { - chrome.tabs.update(tab.id, {pinned: true}, - ignore_chrome_error); - } + let create_data = { + url: urls + , left: newWinSize.left + , top: newWinSize.top + , width: newWinSize.width + , height: newWinSize.height + } + if(BROWSER_TYPE !== 'ff') { + create_data.focused = true; + } - } //foreach tab - - // Another hack for the strange behaviour above: get rid of - // any tabs we didn't expect. This assumes the tabs we - // wanted come first in the window, which seems to be a safe - // assumption. - chrome.windows.get(win.id, {populate: true}, - function(win) { - if(win.tabs.length > expected_tab_count) { - log.warn('Win ' + win.id + ': expected ' + - expected_tab_count + ' tabs; got ' + - win.tabs.length + ' tabs --- pruning.'); - - let to_prune=[]; - for(let tab_idx = expected_tab_count; - tab_idx < win.tabs.length; - ++tab_idx) { - to_prune.push(win.tabs[tab_idx].id); - } //foreach extra tab - - log.warn('Pruning ' + to_prune); - chrome.tabs.remove(to_prune, ignore_chrome_error); - } //endif we have extra tabs - - // if a tab was clicked on, activate that particular tab - if(is_tab) { - chrome.tabs.highlight({ - windowId: node_val.win_id, - tabs: [node_val.index] - }, ignore_chrome_error); - } + let win_create_cbk = function win_create_cbk(cwin) { + // TODO: in Firefox, I'm not sure if this gets called after + // an error. Try to open a saved window with a tab + // for about:addons or about:debugging to trigger an error. + + // Note: In testing, this happens after the winOnCreated, + // tabOnCreated, and tabOnActivated events. I don't know + // if that's guaranteed, though. + window_is_being_restored = false; + + if(isLastError()) { + window.alert(_T('errCouldNotOpenWindow', + chrome.runtime.lastError)); + return; // with the state in the tree unchanged + } + + // Update the tree and node mappings + log.info('Adding nodeid map for winid ' + cwin.id); + connectChromeWindowToTreeWindowItem( + cwin, + { val: win_val, node: win_node }, + { repin: true } + ); - // Set the highlights in the tree appropriately - T.treeobj.flag_node(win_node.id); - flagOnlyCurrentTab(win); + // Set the highlights in the tree appropriately + T.treeobj.flag_node(win_node.id); + flagOnlyCurrentTab(cwin); - } //get callback - ); //windows.get + }; //win_create_cbk callback - } //create callback - ); //windows.created + log.info({'Creating window': create_data}); + + try { + chrome.windows.create(create_data, win_create_cbk); + + } catch(e) { + log.warn({'Could not create window': e, create_data}); + window_is_being_restored = false; + window.alert(_T('errCouldNotCreateWindow',e)); + return; + } } else { // it's a node type we don't know how to handle. log.error('treeOnSelect: Unknown node ' + node); @@ -1619,53 +1866,58 @@ function treeOnSelect(_evt_unused, evt_data) ////////////////////////////////////////////////////////////////////////// }}}1 // Chrome window/tab callbacks // {{{1 -function winOnCreated(win) +function winOnCreated(cwin) { - log.info({'Window created': win.id, + log.info({'Window created': cwin.id, "Restore?": (window_is_being_restored ? "yes" : "no"), - win + cwin }); //log.info('clearing flags winoncreated'); T.treeobj.clear_flags(); + if(window_is_being_restored) { - window_is_being_restored = false; - return; // don't create an extra copy - } + return; // don't create an extra copy - the chrome.window.create + } // callback in treeOnSelect will handle it. // Save the window's size - if(win.type === 'normal') { - winSizes[win.id] = getWindowSizeFromWindowRecord(win); - newWinSize = winSizes[win.id]; + if(cwin.type === 'normal') { + winSizes[cwin.id] = getWindowSizeFromWindowRecord(cwin); + newWinSize = winSizes[cwin.id]; // Chrome appears to use the last-resized window as its size // template even when you haven't closed it, so do similarly. // ... Well, maybe the last-resized window with a non-blank tab --- // not entirely sure. } - createNodeForWindow(win, K.WIN_NOKEEP); + createNodeForWindow(cwin, K.WIN_NOKEEP); saveTree(); // for now, brute-force save on any change. } //winOnCreated /// Update the tree when the user closes a browser window -function winOnRemoved(win_id) +function winOnRemoved(cwin_id) { - if(win_id == my_winid) return; // does this happen? + if(cwin_id == my_winid) return; // does this happen? - log.info({'Window removed': win_id}); + log.info({'Window removed': cwin_id}); // Stash the size of the window being closed as the size for // reopened windows. - if(win_id in winSizes) { - // TODO only do this is win_id's type is "normal" - newWinSize = winSizes[win_id]; - delete winSizes[win_id]; + if(cwin_id in winSizes) { + // TODO only do this is cwin_id's type is "normal" + newWinSize = winSizes[cwin_id]; + delete winSizes[cwin_id]; } - let node_val = D.windows.by_win_id(win_id); + let node_val = D.windows.by_win_id(cwin_id); if(!node_val) return; // e.g., already closed let node_id = node_val.node_id; if(!node_id) return; + + if(node_val.keep === K.WIN_KEEP) { + lastSavedClosedWindow_node_id = node_id; + } + let node = T.treeobj.get_node(node_id); if(!node) return; @@ -1794,7 +2046,7 @@ function initFocusHandler() } //leavingWindow /// The actual onFocusChanged event handler - function inner(win_id, _unused_internal) + function inner_onFocusChanged(win_id, _unused_internal) { let old_win_id = previously_focused_winid; @@ -1868,9 +2120,9 @@ function initFocusHandler() } } //endif to_tf - }; //inner + }; //inner_onFocusChanged - winOnFocusChanged = inner; + winOnFocusChanged = inner_onFocusChanged; } //initFocusHandler @@ -1902,15 +2154,18 @@ var tabOnCreated = (function(){ /// Make an ASQ step that will check if the window matches an existing /// window. - function make_merge_check_step(ctab) + function make_merge_check_step(ctab, win_val) { return function merge_check_inner(check_done) { //DEBUG log.debug({[`merge check tab ${ctab.id} win ${ctab.windowId}`]: ctab}); + let seq = ASQH.NowCCTry((cc)=>{ chrome.windows.get(ctab.windowId,{populate:true}, cc); + // If there's a prune pending, give the merge time to work + pruneWindowSetTimer(win_val); }); seq.then((done, cwin_or_err)=>{ @@ -1921,8 +2176,9 @@ var tabOnCreated = (function(){ let cwin = cwin_or_err; let merge_to_win = winAlreadyExistsInTree(cwin); - MERGE: if(merge_to_win && merge_to_win.val && - !merge_to_win.val.isOpen // don't hijack other open wins + MERGE: if(merge_to_win && merge_to_win.val + && !merge_to_win.val.isOpen // don't hijack other open wins + && merge_to_win.val.node_id ) { log.info({ [`merge ${cwin.id} Found merge target in tree for`]: cwin, @@ -1942,15 +2198,24 @@ var tabOnCreated = (function(){ break MERGE; } + if(merge_to_win.val.node_id === lastSavedClosedWindow_node_id) { + log.info(`merge ${cwin.id}: bail - I ` + + "won't merge with the most-recently-closed window"); + break MERGE; + } + // Detach the existing nodes from their chrome wins/tabs if(!destroy_subtree_but_not_widgets(merge_from_win_val.node_id)) { log.debug(`merge ${cwin.id}: bail - could not remove subtree for open window`); break MERGE; } - // Attach the old nodes to the wins/tabs - attachChromeWindowToSavedWindowItem(cwin, merge_to_win, false); + // Connect the old nodes to the wins/tabs + connectChromeWindowToTreeWindowItem(cwin, merge_to_win, false); + // TODO make sure the correct window is carrying the + // ordered_url_hash in the multidex. I think this might + // be where #119 is happening. } //endif existing (MERGE) }); @@ -1975,7 +2240,7 @@ var tabOnCreated = (function(){ hash = url.hash.slice(1); if(!hash) return false; - if(url.href.split("#")[0] !== + if(url.href.split('#')[0] !== chrome.runtime.getURL('/src/view/newtab.html') ) { return false; @@ -2021,8 +2286,15 @@ var tabOnCreated = (function(){ { log.info({'Tab created': ctab.id, ctab}); - let win_node_id = D.windows.by_win_id(ctab.windowId, 'node_id') - if(!win_node_id) return; + let win_val = D.windows.by_win_id(ctab.windowId); + let win_node_id = win_val ? win_val.node_id : undefined; + if(!win_node_id) { + log.info(`Unknown window ID ${ctab.windowId} - ignoring`); + return; + } + + // If there's a prune pending, give the merge time to work + pruneWindowSetTimer(win_val); let tab_node_id; @@ -2068,7 +2340,7 @@ var tabOnCreated = (function(){ // Design decision: after creating the node, check if it's a // duplicate. - seq.try(make_merge_check_step(ctab)); + seq.try(make_merge_check_step(ctab, win_val)); // .try => always run the following saveTree seq.then((done)=>{saveTree(true, done);}); @@ -2081,6 +2353,10 @@ var tabOnCreated = (function(){ function tabOnUpdated(tabid, changeinfo, ctab) { + let dirty = false; + let should_refresh_label = false; + let should_refresh_tooltip = false; + log.info({'Tab updated': tabid, 'Index': ctab.index, changeinfo, ctab}); let tab_node_val = D.tabs.by_tab_id(tabid); @@ -2090,12 +2366,16 @@ function tabOnUpdated(tabid, changeinfo, ctab) let node = T.treeobj.get_node(tab_node_id); tab_node_val.isOpen = true; //lest there be any doubt + log.debug({" Details for updated ctab":tabid,tab_node_val,node}); + // Caution: changeinfo doesn't always have all the changed information. // Therefore, we check changeinfo and ctab. // URL let new_raw_url = changeinfo.url || ctab.url || 'about:blank'; if(new_raw_url !== tab_node_val.raw_url) { + dirty = true; + should_refresh_tooltip = true; tab_node_val.raw_url = new_raw_url; M.updateOrderedURLHash(node.parent); // When the URL changes, the hash changes, too. @@ -2103,37 +2383,48 @@ function tabOnUpdated(tabid, changeinfo, ctab) // pinned let new_pinned = null; - let should_refresh_label = false; if('pinned' in changeinfo) { new_pinned = changeinfo.pinned; } else if('pinned' in ctab) { new_pinned = ctab.pinned; } - if(new_pinned !== null) { - tab_node_val.isPinned = new_pinned; + + if( (new_pinned !== null) && (tab_node_val.isPinned !== new_pinned) ) { + dirty = true; should_refresh_label = true; + tab_node_val.isPinned = new_pinned; } // title - let new_raw_title = changeinfo.title || ctab.title || 'Tab'; + let new_raw_title = changeinfo.title || ctab.title || _T('labelBlankTabTitle'); if(new_raw_title !== tab_node_val.raw_title) { - tab_node_val.raw_title = new_raw_title; + dirty = true; should_refresh_label = true; - } + should_refresh_tooltip = true; - if(should_refresh_label) { - M.refresh_label(tab_node_id); + tab_node_val.raw_title = new_raw_title; } // favicon let new_raw_favicon_url = changeinfo.favIconUrl || ctab.favIconUrl || null; if(new_raw_favicon_url !== tab_node_val.raw_favicon_url) { + dirty = true; tab_node_val.raw_favicon_url = new_raw_favicon_url; M.refresh_icon(tab_node_val); } - saveTree(); + if(should_refresh_label) { + M.refresh_label(tab_node_id); + } + + if(should_refresh_tooltip) { + M.refresh_tooltip(tab_node_id); + } + + if(dirty) { + saveTree(); + } // For some reason, Ctl+N plus filling in a tab doesn't give me a // focus change to the new window. Therefore, if the tab that has @@ -2205,39 +2496,64 @@ function tabOnRemoved(tabid, removeinfo) // If the window is closing, do not remove the tab records. // The cleanup will be handled by winOnRemoved(). - if(removeinfo.isWindowClosing) return; + if(removeinfo.isWindowClosing) { + log.debug({'Window is closing, so nothing to do for ctab':tabid,removeinfo}); + return; // TODO also mark this as non-recovered, maybe? + } let window_node_id = D.windows.by_win_id(removeinfo.windowId, 'node_id'); - if(!window_node_id) return; + if(!window_node_id) { + log.debug({'Bailing - no window_node_id for ctab':tabid,removeinfo}); + return; + } { // Keep the locals here out of the scope of the closure below. // Get the parent (window) let window_node = T.treeobj.get_node(window_node_id); - if(!window_node) return; - - // Get the tab's node - let tab_node_id = D.tabs.by_tab_id(tabid, 'node_id'); - if(!tab_node_id) return; - let tab_node = T.treeobj.get_node(tab_node_id); - if(!tab_node) return; + if(!window_node) { + log.debug({'Bailing - no window_node for ctab':tabid,removeinfo}); + return; + } - // Remove the node + // Get the node let tab_val = D.tabs.by_tab_id(tabid); // See if it's a tab we have already marked as removed. If so, // whichever code marked it is responsible, and we're off the hook. - if(!tab_val || tab_val.tab_id === K.NONE) return; + if(!tab_val || tab_val.tab_id === K.NONE) { + log.debug({"Bailing, but it's probably OK - no tab_val for ctab":tabid, tab_val, removeinfo}); + return; + } + + // Get the tab's node + if(!tab_val.node_id) { + log.debug({'Bailing - no tab_val.node_id for ctab':tabid,removeinfo}); + return; + } + let tab_node = T.treeobj.get_node(tab_val.node_id); + if(!tab_node) { + log.debug({'Bailing - no tab_node for ctab':tabid,removeinfo}); + return; + } + + log.debug({'Removing value for ctab':tabid,tab_val,removeinfo}); D.tabs.remove_value(tab_val); // So any events that are triggered won't try to look for a // nonexistent tab. + + log.debug({[`Removing tree node ${tab_node.id} for ctab`]:tabid,tab_node,tab_val,removeinfo}); T.treeobj.because('chrome','delete_node',tab_node); } + log.debug({'Updating tab index values after removing ctab':tabid,window_node_id,removeinfo}); + // Refresh the tab.index values for the remaining tabs updateTabIndexValues(window_node_id); + log.debug({'Tab index values updated after removing ctab':tabid,window_node_id,removeinfo}); + saveTree(); } //tabOnRemoved @@ -2294,11 +2610,22 @@ function tabOnAttached(tabid, attachinfo) updateTabIndexValues(new_win_val.node_id); } //tabOnAttached +/// Handle tab replacement, which can occur with preloads. E.g., #129. function tabOnReplaced(addedTabId, removedTabId) { - // Do we get this? - log.warn('Tab being replaced: added ' + addedTabId + '; removed ' + + log.info('Tab being replaced: added ' + addedTabId + '; removed ' + removedTabId); + let tab_val = D.tabs.by_tab_id(removedTabId); + if(!tab_val) { + log.warn('onReplaced: No tab found for removed' + + `tab ID ${removedTabId} - bailing`); + return; + } + + D.tabs.change_key(tab_val, 'tab_id', addedTabId); + log.info({ + [`Tab replacement ${removedTabId}->${addedTabId}: new value`]:tab_val + }); } //tabOnReplaced ////////////////////////////////////////////////////////////////////////// }}}1 @@ -2363,18 +2690,39 @@ function timedResizeDetector() ////////////////////////////////////////////////////////////////////////// }}}1 // Hamburger menu // {{{1 +/// Open a window, setting do_not_prune_right_now around the call. +let open_window_with_url = function(url) { + do_not_prune_right_now = true; + let seq = + K.openWindowForURL(url || 'about:blank'); + seq + .val(()=>{ + do_not_prune_right_now = false; + }) + .or(()=>{ + do_not_prune_right_now = false; + }); + return seq; +}; + /// Open a new window with the TabFern homepage. function hamAboutWindow() { - K.openWindowForURL('https://cxw42.github.io/TabFern/'); + open_window_with_url('https://cxw42.github.io/TabFern/'); } //hamAboutWindow() +/// Reload the TabFern window (or, at least, the tree iframe) +function hamReloadTree() +{ + window.location.reload(true); +} //hamReloadTree() + /// Open the Settings window. If ShowWhatIsNew, also updates the K.LASTVER_KEY /// information used by checkWhatIsNew(). function hamSettings() { // Actually open the window - K.openWindowForURL(chrome.extension.getURL( + open_window_with_url(chrome.extension.getURL( '/src/settings/index.html' + (ShowWhatIsNew ? '#open=last' : '')) ); @@ -2408,19 +2756,39 @@ function hamBackup() /// already present. It does not delete existing tabs/windows. function hamRestoreFromBackup() { - let importer = new Modules.importer(document, '.tabfern'); - importer.getFileAsString(function(text, filename){ + /// Process the text of the file once it's loaded + function processFile(text, filename) { + let ok; try { let parsed = JSON.parse(text); - if(loadSavedWindowsFromData(parsed) === false) { - window.alert("I couldn't load the file " + filename + ': ' + e); + ok = loadSavedWindowsFromData(parsed); + if(!ok) { + let errmsg = _T('errCouldNotLoadFile', filename, e); + log.warn({[errmsg]:e}); + window.alert(errmsg); } } catch(e) { - window.alert("File " + filename + ' is not something I can '+ - 'understand as a TabFern save file. Parse error code was: ' + - e); + let errmsg = _T('errCouldNotParseFile'); + log.warn({[errmsg + ' (exception thrown)']:e}); + window.alert(errmsg); } - }); + + // If we were successful, save. Otherwise, a reload of the extension + // before taking another action that triggers a save will result in + // the Restore never having happened. + if(ok) { + saveTree(); + } + } //processFile() + + try { + let importer = new Modules.importer(document, '.tabfern'); + importer.getFileAsString(processFile); + } catch(e) { + let errmsg = _T('errCouldNotRunImporter', e); + log.warn({[errmsg]:e}); + window.alert(errmsg); + } } //hamRestoreFromBackup() function hamRestoreLastDeleted() @@ -2430,7 +2798,8 @@ function hamRestoreLastDeleted() // Make v0 save data from the last-deleted-window URLs, just because // v0 is convenient, and the backward-compatibility guarantee of // loadSavedWindowsFromData means we won't have to refactor this. - let tabs=[] + let tabs=[]; + // TODO RESUME HERE --- convert user-facing text to _T() for(let url of lastDeletedWindow) { tabs.push({text: 'Restored', url: url}); } @@ -2473,7 +2842,7 @@ function hamSorter(compare_fn) function hamRunJasmineTests() { - K.openWindowForURL(chrome.extension.getURL('/test/index.html')); + open_window_with_url(chrome.extension.getURL('/test/index.html')); } // hamRunJasmineTests function hamSortOpenToTop() @@ -2509,7 +2878,7 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) // Add development-specific items, if any if(is_devel_mode) { items.splitItem = { - label: 'Split test', + label: _T('menuSplitTest'), action: function(){ if(window.parent && window.parent.doSplit) window.parent.doSplit(); @@ -2517,21 +2886,29 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) }; items.jasmineItem = { - label: 'Run Jasmine tests', + label: _T('menuJasmineTests'), action: hamRunJasmineTests, icon: 'fa fa-fort-awesome', separator_after: true, }; } //endif is_devel_mode + items.reloadItem = { + label: _T('menuReload'), + action: hamReloadTree, + icon: 'fa fa-refresh', + separator_after: true, + }; + items.infoItem = { - label: "Online info", + label: _T('menuOnlineInfo'), title: "The TabFern web site, with a basic usage guide and the credits", + // TODO also _T() the title fields throughout action: hamAboutWindow, icon: 'fa fa-info', }; items.settingsItem = { - label: "Settings and offline help", + label: _T('menuSettings'), title: "Also lists the features introduced with each version!", action: hamSettings, icon: 'fa fa-cog' + (ShowWhatIsNew ? ' tf-notification' : ''), @@ -2541,58 +2918,54 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) if(Array.isArray(lastDeletedWindow) && lastDeletedWindow.length>0) { items.restoreLastDeletedItem = { - label: "Restore last deleted", + label: _T('menuRestoreLastDeleted'), action: hamRestoreLastDeleted, }; } items.backupItem = { - label: "Backup now", + label: _T('menuBackupNow'), icon: 'fa fa-floppy-o', action: hamBackup, }; items.restoreItem = { - label: "Load contents of a backup", + label: _T('menuLoadBackupContents'), action: hamRestoreFromBackup, icon: 'fa fa-folder-open-o', separator_after: true, }; items.sortItem = { - label: 'Sort', + label: _T('menuSort'), icon: 'fa fa-sort', submenu: { openToTopItem: { - label: 'Open windows to top', + label: _T('menuSortOpenToTop'), title: 'Sort ascending by window name, case-insensitive, '+ 'and put the open windows at the top of the list.', action: hamSortOpenToTop, icon: 'fff-text-padding-top' }, azItem: { - label: 'A-Z', + label: _T('menuSortAZ'), title: 'Sort ascending by window name, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_text), icon: 'fa fa-sort-alpha-asc', }, zaItem: { - label: 'Z-A', + label: _T('menuSortZA'), title: 'Sort descending by window name, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_text_desc), icon: 'fa fa-sort-alpha-desc', }, numItem09: { - label: '0-9', + label: _T('menuSort09'), title: 'Sort ascending by window name, numeric, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_num), icon: 'fa fa-sort-numeric-asc', }, - // Test of sub-submenus --- jstree doesn't seem to correctly - // constrain them to the viewport. - //test: { label: 'test', submenu: - // { foo: {label:'foo'},bar:{label:'bar'},bat:{label:'bat'} }}, numItem90: { - label: '9-0', + label: _T('menuSort90'), title: 'Sort descending by window name, numeric, case-insensitive', action: hamSorter(Modules['view/sorts'].compare_node_num_desc), icon: 'fa fa-sort-numeric-desc', @@ -2601,12 +2974,12 @@ function getHamburgerMenuItems(node, _unused_proxyfunc, e) }; //sortItem items.expandItem = { - label: "Expand all", + label: _T('menuExpandAll'), icon: 'fa fa-plus-square', action: hamExpandAll, }; items.collapseItem = { - label: "Collapse all", + label: _T('menuCollapseAll'), icon: 'fa fa-minus-square', action: hamCollapseAll, }; @@ -2646,11 +3019,11 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) if(nodeType === K.IT_TAB) { let tabItems = { toggleBorderItem: { - label: 'Toggle top border', + label: _T('menuToggleTopBorder'), action: function(){actionToggleTabTopBorder(node.id, node, null, null)} }, editBulletItem: { - label: 'Add/edit a note', + label: _T('menuAddEditNote'), icon: 'fff-pencil', // Use K.nextTickRunner so the context menu can be @@ -2661,14 +3034,24 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) }, }; +// if(tab_val.isOpen) { +// tabItems.closeItem = { +// label: 'Close and remember', +// icon: 'fff-picture-delete', +// action: +// function(){actionCloseTabAndSave(node.id,node,null,null);} +// }; +// } + + return tabItems; - } + } //endif K.IT_TAB if(nodeType === K.IT_WIN) { let winItems = {}; winItems.renameItem = { - label: 'Rename', + label: _T('menuRename'), icon: 'fff-pencil', // Use K.nextTickRunner so the context menu can be @@ -2681,16 +3064,16 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) // Forget/Remember if( win_val.isOpen && (win_val.keep === K.WIN_KEEP) ) { winItems.forgetItem = { - label: "Forget but don't close", - title: "Do not save this window when it is closed", + label: _T('menuForget'), + title: _T('menuttForget'), icon: 'fa fa-chain-broken', action: function(){actionForgetWindow(node.id, node, null, null);} }; } else if( win_val.isOpen && (win_val.keep === K.WIN_NOKEEP) ) { winItems.rememberItem = { - label: "Remember", - title: "Save this window when it is closed", + label: _T('menuRemember'), + title: _T('menuttRemember'), icon: 'fa fa-link', action: function(){actionRememberWindow(node.id, node, null, null);} @@ -2699,7 +3082,7 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) if(win_val.isOpen) { winItems.closeItem = { - label: 'Close and remember', + label: _T('menuCloseAndRemember'), icon: 'fff-picture-delete', action: function(){actionCloseWindowAndSave(node.id,node,null,null);} @@ -2707,7 +3090,7 @@ function getMainContextMenuItems(node, _unused_proxyfunc, e) } winItems.deleteItem = { - label: 'Delete', + label: _T('menuDelete'), icon: 'fff-cross', separator_before: true, action: @@ -2843,6 +3226,14 @@ var treeCheckCallback = (function() .or((err)=>{ // Doesn't fire for invalid moves of pinned tabs in Chrome 63 L.log.warn({[`Couldn't move tab ${val.tab_id}`]:err}); + + // TODO #123: If the move failed, put the tree item back + // where it was before. E.g., in Vivaldi, dragging a tab's + // tree item from a regular window into the Settings window + // moves the tab in the tree, but we get here with message + // "Tabs can only be moved to and from normal windows," + // and the tab doesn't actually move in the view. + }); } else { @@ -2932,7 +3323,8 @@ var treeCheckCallback = (function() } //open_tab_within_window // --- The main check callback --- - function inner(operation, node, new_parent, node_position, more) + function inner_treeCheckCallback(operation, node, new_parent, + node_position, more) { // Fast bail when possible if(operation === 'copy_node') return false; @@ -3233,9 +3625,9 @@ var treeCheckCallback = (function() } //endif this is a non-dnd move return true; - } //inner + } //inner_treeCheckCallback - return inner; + return inner_treeCheckCallback; // Note on the code that doesn't check for more.dnd: // See https://github.com/vakata/jstree/issues/815 - the final node move @@ -3284,7 +3676,7 @@ function checkWhatIsNew(selector) { [K.LASTVER_KEY]: 'installed, but no version viewed yet' }, function() { ignore_chrome_error(); - K.openWindowForURL('https://cxw42.github.io/TabFern/#usage'); + open_window_with_url('https://cxw42.github.io/TabFern/#usage'); } ); } @@ -3377,13 +3769,16 @@ function delete_all_closed_nodes(are_you_sure) } //delete_all_closed_nodes ////////////////////////////////////////////////////////////////////////// }}}1 -// Startup / shutdown // {{{1 +// Startup / shutdown details // {{{1 + +// Initialization routines // {{{2 let custom_bg_color = false; /// Initialization that happens before the full DOM is loaded function preLoadInit() { + next_init_step('preload'); if(getBoolSetting(CFG_HIDE_HORIZONTAL_SCROLLBARS)) { document.querySelector('html').classList += ' tf--feature--hide-horizontal-scrollbars'; } @@ -3428,34 +3823,28 @@ function preLoadInit() body.classList += ` jstree-${getThemeName()}`; } - // Apply custom background, if any + // Apply custom background, if any. + // NOTE: need to keep this logic in sync with the validator for + // CFGS_BACKGROUND in src/common/common.js. TODO remove this duplication. BG: if(haveSetting(CFGS_BACKGROUND)) { let bg = getStringSetting(CFGS_BACKGROUND, '').trim(); if(bg.length < 2) break BG; // no valid color is one character - if(Modules['common/validation'].isValidColor(bg)) { + if(Validation.isValidColor(bg)) { custom_bg_color = bg; break BG; } - // not a color --- try to parse it as a URL - try { - let url = new URL(bg); - if(url.protocol === "file:" || - //url.protocol === "http:" || + // not a color --- try to parse it as a URL. + if(Validation.isValidURL(bg, + ['file', 'https', 'data', 'chrome-extension'])) { // For now, disallow http so we don't have to worry // about HTTP-hijacking attacks. I don't know of any // attack vectors in CSS background images off-hand, // but that doesn't mean there aren't any :) . - url.protocol === "https:" || - url.protocol === "data:" || - url.protocol === "chrome-extension:") { - custom_bg_color = `url("${url.href}")`; - } + custom_bg_color = `url("${bg}")`; break BG; - } catch(e) { - // do nothing } } //endif have a background setting @@ -3468,6 +3857,7 @@ function preLoadInit() /// Beginning of the onload initialization. function basicInit(done) { + next_init_step('basic initialization'); log.info('TabFern tree.js initializing view - ' + TABFERN_VERSION); Hamburger = Modules.hamburger('#hamburger-menu', getHamburgerMenuItems @@ -3500,6 +3890,7 @@ function basicInit(done) /// Called after ASQ.try(chrome.runtime.sendMessage) function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) { + next_init_step('create main tree'); if(ASQH.is_asq_try_err(win_id_msg_or_error)) { // This is fatal return done.fail("Couldn't get win ID: " + win_id_msg_or_error.catch); @@ -3543,6 +3934,7 @@ function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) drivers: [Modules.dmauro_keypress] }, function initialized(err) { + next_init_step('context-menu support'); if ( err ) { console.log({['Failed loading a shortcut driver! Initializing '+ 'context menu with no shortcut driver']:err}); @@ -3556,8 +3948,57 @@ function createMainTreeIfWinIdReceived_catch(done, win_id_msg_or_error) ); } //createMainTreeIfWinIdReceived_catch() +/// Called after ASQ.try(chrome.storage.local.get(LOCN_KEY)) +function moveWinToLastPositionIfAny_catch(done, items_or_err) +{ // move the popup window to its last position/size. + // If there was an error (e.g., nonexistent key), just + // accept the default size. + + next_init_step('reposition window'); + if(ASQH.is_asq_try_err(items_or_err)) { + log.warn({"Couldn't get saved location":$.extend({},items_or_err)}); + // Note: $.extend() used to force evaluation at the time of logging + } else { //we have a location + log.info({"Got saved location":$.extend({},items_or_err)}); + + let parsed = items_or_err[K.LOCN_KEY]; + if( (parsed !== null) && (typeof parsed === 'object') ) { + // + and || are to provide some sensible defaults - thanks to + // https://stackoverflow.com/a/7540412/2877364 by + // https://stackoverflow.com/users/113716/user113716 + let size_data = + { + 'left': +parsed.left || 0 + , 'top': +parsed.top || 0 + , 'width': Math.max(+parsed.width || 300, 100) + // don't let it shrink too small, in case something went wrong + , 'height': Math.max(+parsed.height || 600, 200) + }; + last_saved_size = $.extend({}, size_data); + chrome.windows.update(my_winid, size_data, + (win)=>{ + if(isLastError()) { + log.error(`Could not move window: ${chrome.runtime.lastError}`); + } else { + log.info({"Updated window size":$.extend({},win)}); + } + } + ); + } else { + log.warn({"Could not parse size from":$.extend({},items_or_err)}) + } //endif got an item else + + } //endif storage.local.get worked + + // Start the detection of moved or resized windows + setTimeout(timedResizeDetector, K.RESIZE_DETECTOR_INTERVAL_MS); + + done(); +} //moveWinToLastPositionIfAny_catch() + function addOpenWindowsToTree(done, cwins) { + next_init_step('add open windows to tree'); let dat = {}; let focused_win_id; @@ -3584,9 +4025,9 @@ function addOpenWindowsToTree(done, cwins) if(!existing_win || (existing_win.val && existing_win.val.isOpen)) { // Doesn't exist, or the duplicate is already open (e.g., if two // windows are open with the same set of tabs) - createNodeForWindow(cwin, K.WIN_NOKEEP); + createNodeForWindow(cwin, K.WIN_NOKEEP, true); //true=>no pruning } else { - attachChromeWindowToSavedWindowItem(cwin, existing_win); + connectChromeWindowToTreeWindowItem(cwin, existing_win); } //endif window already exists } //foreach window @@ -3602,6 +4043,7 @@ function addOpenWindowsToTree(done, cwins) function addEventListeners(done) { + next_init_step('add event listeners'); // At this point, the saved and open windows have been loaded into the // tree. Therefore, we can position the action groups. We already // saved the position, so do not need to specify it here. @@ -3669,47 +4111,21 @@ function addEventListeners(done) done(); } //addEventListeners -/// Called after ASQ.try(chrome.storage.local.get(LOCN_KEY)) -function moveWinToLastPositionIfAny_catch(done, items_or_err) -{ // move the popup window to its last position/size. - // If there was an error (e.g., nonexistent key), just - // accept the default size. - - if(!ASQH.is_asq_try_err(items_or_err)) { - let parsed = items_or_err[K.LOCN_KEY]; - if( (parsed !== null) && (typeof parsed === 'object') ) { - // + and || are to provide some sensible defaults - thanks to - // https://stackoverflow.com/a/7540412/2877364 by - // https://stackoverflow.com/users/113716/user113716 - let size_data = - { - 'left': +parsed.left || 0 - , 'top': +parsed.top || 0 - , 'width': Math.max(+parsed.width || 300, 100) - // don't let it shrink too small, in case something went wrong - , 'height': Math.max(+parsed.height || 600, 200) - }; - last_saved_size = $.extend({}, size_data); - chrome.windows.update(my_winid, size_data, ignore_chrome_error); - } - } //endif no error - - // Start the detection of moved or resized windows - setTimeout(timedResizeDetector, K.RESIZE_DETECTOR_INTERVAL_MS); - - done(); -} //moveWinToLastPositionIfAny_catch() - /// The last function to be called after all other initialization has /// completed successfully. function initTreeFinal(done) { - //return; // DEBUG - don't save + next_init_step('finalize'); + + // Tests of different ways of failing init - for debugging only + //throw new Error("oops"); // DEBUG + //return; // DEBUG - don't save if(!was_loading_error) { did_init_complete = true; // Assume the document is loaded by this point. - $(K.INIT_MSG_SEL).css('display','none'); + $(K.INIT_MSG_SEL).remove(); + $(K.INIT_PROGRESS_SEL).remove(); // just in case initialization took a long time, and the message // already appeared. @@ -3725,7 +4141,8 @@ function initTreeFinal(done) done(); } //initTreeFinal() -// ---------------------------------------------- +// ---------------------------------------------- }}}2 +// Shutdown routines // {{{2 /// Save the tree on window.unload function shutdownTree() @@ -3744,17 +4161,32 @@ function shutdownTree() } } //shutdownTree() +// ---------------------------------------------- }}}2 +// Error reporting // {{{2 + /// Show a warning if initialization hasn't completed. function initIncompleteWarning() { if(!did_init_complete) { // Assume the document is loaded by this point. - $(K.INIT_MSG_SEL).css('display','block'); + if(K && K.INIT_MSG_SEL) { + $(K.INIT_MSG_SEL).css('display','block'); + } else { + window.setTimeout(initIncompleteWarning, 500); + } + + if(K && K.INIT_PROGRESS_SEL) { + $(K.INIT_PROGRESS_SEL).css('display','block'); + } else { + window.setTimeout(initIncompleteWarning, 500); + } } } //initIncompleteWarning() -////////////////////////////////////////////////////////////////////////// -// MAIN // +// }}}2 + +//////////////////////////////////////////////////////////////////////// }}}1 +// MAIN // {{{1 function main(...args) { @@ -3770,6 +4202,9 @@ function main(...args) local_init(); + //$(K.INIT_PROGRESS_SEL).css('display','block'); //DEBUG + $(K.INIT_PROGRESS_SEL).text("waiting for browser"); + // Timer to display the warning message if initialization doesn't complete // quickly enough. window.setTimeout(initIncompleteWarning, K.INIT_TIME_ALLOWED_MS); @@ -3785,28 +4220,43 @@ function main(...args) // Run the main init steps once the page has loaded let s = ASQ(); callbackOnLoad(s.errfcb()); + // Note: on one test on Firefox, the rest of the chain never fired. + // Not sure why. s.then(basicInit) + .try((done)=>{ // Get our Chrome-extensions-API window ID from the background page. // I don't know a way to get this directly from the JS window object. // TODO maybe getCurrent? Not sure if that's reliable. + next_init_step('get window ID'); chrome.runtime.sendMessage({msg:MSG_GET_VIEW_WIN_ID}, ASQH.CC(done)); }) .then(createMainTreeIfWinIdReceived_catch) + + .try((done)=>{ + next_init_step('get saved location'); + // Find out where the view was before, if anywhere + chrome.storage.local.get(K.LOCN_KEY, ASQH.CC(done)); + }) + .then(moveWinToLastPositionIfAny_catch) + .then(loadSavedWindowsIntoTree) .then((done)=>{ + next_init_step('get open windows'); chrome.windows.getAll({'populate': true}, ASQH.CC(done)); }) .then(addOpenWindowsToTree) .then(addEventListeners) - .try((done)=>{ - // Find out where the view was before, if anywhere - chrome.storage.local.get(K.LOCN_KEY, ASQH.CC(done)); - }) - .then(moveWinToLastPositionIfAny_catch) .then(initTreeFinal) - //.or(TODO show "couldn't load" in the popup) + + .val(check_init_step_count) + + .or((err)=>{ + $(K.INIT_MSG_SEL).text( + $(K.INIT_MSG_SEL).text() + "\n" + err + ) + }); ; } // main()